전 직장을 퇴사하고 바닐라코딩 Prep에 참가해 JavaScript를 공부하던 중, SW 사관학교 정글 4기 모집 알림이 문자로 도착했다. 3기에서 과제를 다 완성시키지 못해 떨어졌었는데 이번엔 꼭 통과하고자 하는 마음이 불타올랐다! 🔥 (2기 때도 지원을 했었는데 떨어졌었다! ^.^)

 

 

 

 SW 사관학교 정글이란?

 코딩, 컴퓨터 공학에 대한 아무런 베이스가 없는 사람들이 CS 기초 지식을 습득할 수 있게 해주고, 지속적으로 성장할 수 있는 정예 개발자로 만들어주는 코딩 캠프이다. 크래프톤, 카이스트, 팀스파르타가 협력하여 만들어진 프로그램으로 카이스트에서 5개월간 진행된다.

 

 * 더 자세한 사항들은 정글 홈페이지에서 확인 가능합니다. https://swjungle.net/

 

SW사관학교 정글

5개월 간의 합숙 과정을 거쳐 5~10년에도 성장하는 개발자로 거듭나세요

swjungle.net

 

 

 

 지원과정

지원서 작성

- 경력, 코딩 관련 경험, 졸업 예정인 혹은 졸업한 대학교, 1분 영상, 4가지 항목의 질문 등

 코딩 관련 경험은 없어도 무방하다고 한다. 또한 내가 합격한 것을 보니 출신 학교는 전혀 상관 없을 것 같다. 1분 영상으로는 가장 몰입했던 경험을 요구한다.

 

 참고로 지원서를 작성하는 과정 중에 5만원을 결제하게 되는데, 이는 입학 시험을 준비할 수 있는 자료를 얻기 위함이다. 그리고 구글 폼으로 작성하기 때문에 지원서를 제출하고 나면 재확인이 불가능하다. 면접에 대비하여 사소한 것들 모두 다른 곳에 저장해두길 추천한다.

 

 

입학 시험 자료 공부

 정말 기초부터 학습할 수 있다. vscode 설치부터 HTML, CSS, JavaScript, Python 기초를 배우게 된다. 그리고 백엔드, 데이터베이스, AWS를 이용한 배포 영역까지 간단하게나마 경험할 수 있다. 해당 자료를 공부하면 작은 사이트를 만들어 다른 사람이 써볼 수 있게 하는 배포까지 경험이 가능한 것이다. 사이트가 어떻게 만들어지고 어떻게 배포가 되는지 전체적인 과정을 경험할 수 있어서 웹 개발을 경험해보기 정말 좋은 자료였다. 5만원이 정말 아깝지 않았다. 사실 나는 2, 3, 4기 모두 지원했기 때문에 15만원을 썼다! ^.^;; 헤헷! 😆

 

 

입학 시험

 4기는 토요일 10시에 시험이 오픈되고 문제를 확인할 수 있다. 그리고 완성까지 7시간이 주어진다.

 

 잘 되다가도 중요한 부분에서 에러가 계속 나서 갈아엎기를 몇 번 반복했다. 그래도 열심히 구글링하고 이것저것 시도해본 덕에 과제를 완성했다.

 

 과제에서 요구하는 것들을 갖추는 것에는 입학 자료에 힌트가 다 있다. 완벽히 구현하는 데에는 충분할 지 모르겠는데 입학자료를 충분히 이해하면 어느 정도 과제를 완성시킬 수 있다고 생각한다. 3기 때는 충분히 이해하지 않고 그냥 한 번 해보고 넘어가다보니 떨어졌었던 것 같다.

 

 

면접

 시험이 끝나고 나서 당일인가? 다음날 바로 결과가 나왔다. 3기 때 지인한테 듣기론, 과제를 완성하고도 떨어졌다는 사람이 있다는 소리를 들어서 떨어질 수도 있다는 생각이 들었는데 정말 다행이었다...

 

 면접 준비는 블로그 후기들을 정말 많이 참고했다. 타 블로그에 나오는 것처럼 지원서 바탕으로 면접 질문을 대비하면 될 것 같다. 나 같은 경우는 쪼오끔 사소한 부분도 질문을 하셨었다. 무엇보다 왜 소프트웨어 분야로 뛰어들고 싶은지, 왜 개발자가 되고 싶은지, 정글이 필요한 이유를 준비하면 될 것 같다.

 

 개발자가 왜 되고 싶은지에 대해서는 생각을 많이 했는데, '좀 더 나은 세상을 만들고 싶어서'라는 거창한 것 보다는 그저 영화에서 본 해커가 멋있어 보였다는 둥 키보드 치는 느낌이 좋다는 둥 생각한 대로 동작할 때 만족감이 든다는 둥 그냥 코딩이 왜 재밌게 느껴졌는지로 수렴되었다. 아무리 생각해도 거창한 이유는 없었기 때문이다. ㅎㅎㅎ

 

 

결과 발표

 면접을 끝내고, 또 바닐라코딩 Prep 멘토링을 끝내고 한숨 자고 난 후, 이것저것 하면서 쉬던 도중 혹시!? 하는 생각으로 Gmail을 확인했다. 오전 9시에 면접을 보았는데 4시간 이후인 오후 1시에 이미 결과 메일이 날라와있었다. 

 퇴사하고... 뿌옇게 보이던 미래가 조금씩 선명해보이기 시작했다. 개발자에 한 발자국 가까이 가게 되었다는 생각에 정말 기뻤지만 무엇보다 제대로 시작할 수 있다는 생각이 나를 더욱 기쁘게 했다. 그저 그런 개발자가 되긴 정말 싫었기 때문이다.

 

 

 

 개인적인 사족

 정글에 합격했지만 나에게는 2가지 선택지가 남아있었다.

 

 바닐라코딩 Prep에 계속 참여해서 바닐라코딩 부트캠프에 참여할 것인가? 아니면 정글에 참여할 것인가? 이미 후자로 마음이 기울었지만 그래도 후회하지 않도록 면밀히 따져보았다.

 

 먼저 바닐라코딩을 참여할 경우 코딩 테스트를 통과해야 되고, 집도 서울에 구해야 했다. 바닐라코딩 Prep에는 만족스러움을 느끼고 있었으나(바닐라코딩 Prep 후기는 따로 작성하겠다) 내가 코딩 테스트를 통과할지 의문이었다. 그리고 서울 생활을 해보게 된다는 것은 좋았으나 하필 2, 3월이라 집을 구하기 쉽지 않을 것 같았다.

 

 비용적인 측면도 무시할 수 없다. 바닐라코딩의 경우 정말 좋은 부트캠프로 소문이 자자하고 후기도 많으며 커뮤니티 또한 활발해 취업을 하고 난 후에도 도움이 많이 된다고 들었으나 가격이 Prep과 부트캠프까지 합치면 1500만원에 달한다. 퇴사하면서 '이정도는 미래를 위해 투자할만해!'라는 마음을 굳게 먹었지만, 비교적 적은 돈을 지불하는 정글에 합격하니 이런 것 까지 고려할 수 밖에 없었다. 그리고 유명한 IT 스타트업에서 HR로 일하는 친한 친구가 정글을 적극적으로 추천해주었다. 정글 수료생들의 수준이 정말 높았다고 했었다.

 

 바닐라코딩이든 정글이든 무엇이 나에게 좋은 선택이 될지는 6개월 뒤에 알 수 있을 것 같다.

 

 

 

 마치면서

 들뜨면 안된다는 것을 알면서도 설레이는 것은 어쩔 수 없다. 내가 언제 카이스트를 가보겠는가? 또 언제 대학 생활을 다시 해볼 수 있겠는가? 스스로 노력해서 얻어낸 기회들 중 가장 값진 경험이 되지 않을까 싶다.

 

 지금부터가 중요하다. 처음에 잘 배워두는 것이 얼마나 중요한 것인지 잘 알고 있다. 오로지 학습에만 몰두할 수 있게 현재 자취방도 잘 정리하고 갈 생각이다.

 

 

  • Number Baseball, 숫자 야구

 숫자 야구란, 랜덤한 세 자리 숫자가 주어지면 그 숫자가 무엇인지 맞혀보는 게임이다. 숫자와 숫자가 위치한 자리까지 일치하면 Strike, 자리는 일치하지 않으나 포함되어 있으면 Ball로 처리된다. 예를 들면, 랜덤한 숫자가 375이고 내가 745라고 예상했다면 7은 자리는 일치하지 않으나 포함되어 있으나 1 Ball로 처리되고, 4는 아예 포함되어 있지 않으니 Strike나 Ball 둘다 아니다. 5는 숫자와 자리까지 일치하니 1 Strike가 된다.

 

 숫자 야구에 대한 자세한 설명 : https://namu.wiki/w/%EC%88%AB%EC%9E%90%EC%95%BC%EA%B5%AC

 

 크게 막히는 부분은 없었다. 비효율적일 순 있으나 내가 설계한 대로 동작이 잘되니 뿌듯하다. 중간 중간에 구글링으로 필요한 것을 찾아내는 방법도 어느 정도 익혀가는 것 같다. 특히 MDN을 되도록이면 참고하려고 했다. 1차적인 자료를 읽어서 제대로 이해하고 써먹는 것이 나중에 응용하는 데에 있어 좋을 것이라 생각했기 때문이다. 뭐든 뿌리를 알아야 되지 않을까!

 

  • HTML
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <link
      href="https://fonts.googleapis.com/css?family=Pacifico&display=swap"
      rel="stylesheet"
    />
    <link
      href="https://fonts.googleapis.com/css?family=Varela+Round&display=swap"
      rel="stylesheet"
    />
    <link rel="icon" href="/images/favicon.ico" />
    <link rel="stylesheet" type="text/css" href="style.css" />
    <title>Baseball Project</title>
  </head>

  <body>
    <section>
      <div class="image-box">
        <img src="images/vanilla_coding_logo.png" />
      </div>
      <h1>Baseball</h1>

      <!-- 야구게임 Start -->

      <div class="contents">
        <div class="clickButton">
          <button id="startButton">Start</button>
          <button id="restartButton">Restart</button>
        </div>
        <br />
        <div class="notificationBox">
          <p id="notice">[Notice!]</p>
          <ul>
            <li><p>Press the start button at first</p></li>
            <li><p>Please enter only number</p></li>
            <li><p>Don't enter duplicate number</p></li>
          </ul>
        </div>
        <div id="numberBox"></div>
        <div class="strikeBall">
          <div id="strike">Strike</div>
          <div id="ball">Ball</div>
          <div id="chance"></div>
        </div>
        <input
          type="number"
          id="inputNumber"
          oninput="handleOnInput(this, 3)"
          placeholder="What is your number?"
        />
      </div>

      <!-- 야구게임 End -->
    </section>

    <script src="index.js"></script>
  </body>
</html>
  • CSS
body {
  background-image: url("./images/bg.jpeg");
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
  font-family: "Varela Round", sans-serif;
}

.image-box {
  display: flex;
  justify-content: center;
  align-items: center;
}

img {
  width: 700px;
  height: 200px;
}

h1 {
  font-family: "Pacifico", cursive;
  text-align: center;
  font-size: 36px;
}

.contents {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

.notificationBox {
  padding: 15px;
  border: 3px solid black;
  border-radius: 5px;
  background-color: white;
  font-size: 20px;
  font-weight: 300;
  color: black;
}

#notice {
  padding-top: 0px;
  text-align: center;
  font-size: 25px;
  font-weight: 900;
}

#numberBox {
  margin-top: 30px;
  width: 400px;
  height: 200px;
  border: 3px solid black;
  border-radius: 3px;
  background-color: yellow;
  display: flex;
  text-align: center;
  justify-content: center;
  flex-direction: column;
  font-size: 50px;
}

.strikeBall {
  margin-top: 20px;
  display: flex;
  width: 200px;
  height: 25px;
  border: 3px solid black;
  border-radius: 3px;
  background-color: rgb(20, 207, 231);
  justify-content: center;
}

#strike {
  margin-top: 2px;
  margin-right: 30px;
}

#ball {
  margin-top: 2px;
  margin-right: 30px;
}

#chance {
  margin-top: 2px;
}

#inputNumber {
  margin-top: 20px;
  margin-bottom: 20px;
}
  • JavaScript
var numberArray = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
var startButton = document.querySelector("#startButton");
var restartButton = document.querySelector("#restartButton");
var inputNumber = document.querySelector("#inputNumber");
var numberBox = document.querySelector("#numberBox");
var strikeNumber = document.querySelector("#strike");
var ballNumber = document.querySelector("#ball");
var chance = document.querySelector("#chance");
let randomArray = [];
let chanceNumber = 10;
chance.textContent = chanceNumber;

inputNumber.style.display = "none";

restartButton.addEventListener("click", function reload() {
  location.reload();
});

startButton.addEventListener("click", function createNumber() {
  inputNumber.style.display = "";
  startButton.remove();
  numberBox.textContent = "? ? ?";

  for (var i = 0; i < 3; i++) {
    var randomIndex = Math.floor(Math.random() * numberArray.length);
    var randomValue = numberArray[randomIndex];
    numberArray.splice(randomIndex, 1);
    randomArray.push(randomValue);
  }

  alert("Here is a new number. Guess what it is!");
  console.log(randomArray);
});

function handleOnInput(el, maxlength) {
  if (el.value.length > maxlength) {
    el.value = el.value.substr(0, maxlength);
  }
}

inputNumber.addEventListener("keypress", function (key) {
  if (key.key === "Enter") {
    var inputValue = inputNumber.value;
    var inputValueArray = Array.from(inputValue);
    console.log(inputValueArray);

    if (inputValueArray.length < 3) {
      alert("Please enter 3 digit number");
      return;
    }

    var strike = 0;
    var ballcount = 0;

    for (var i = 0; i < 3; i++) {
      if (inputValueArray[i] === randomArray[i]) {
        strike += 1;
      }
      if (randomArray.indexOf(inputValueArray[i]) >= 0) {
        ballcount++;
      }
    }

    var ball = ballcount - strike;

    if (ball < 0) {
      ball = 0;
    }

    strikeNumber.textContent = strike + "S";
    ballNumber.textContent = ball + "B";

    console.log("strike : " + strike);
    console.log("ball : " + ball);

    if (strike === 3) {
      numberBox.textContent = randomArray.join("");
      alert(`YES! That's right!`);
    }

    chanceNumber = chanceNumber - 1;
    chance.textContent = chanceNumber;

    if (chanceNumber === 0) {
      alert("Over! Please click restart button");
      inputNumber.remove();
    }
  }
});

 HTML과 CSS에서는 그저 배웠던 것들을 적용하면 되었기 때문에 크게 문제가 되는 건 없었다. JavaScript를 짤 때 몇 가지 문제에 직면해 애를 먹었다. 그 문제와 해결은 다음과 같이 했다.

 

 0. 숫자 비교 등은 어떻게 할 것인가!

 -> 일단 배열 형태로 랜덤한 숫자가 생성되게 하고, 유저가 입력하는 숫자도 Array.from으로 배열로 바뀌게 했다. 그 후 for문을 사용해 랜덤 숫자와 유저의 숫자를 비교했고 일치하면 strike의 숫자가 올라가게 했다. ball은 indexOf를 사용해서 count했다. 해당 숫자가 랜덤 숫자(randomArray)중에 존재하기만 해도 1이 올라간다.(ballcount++)

 

 0-1. 이 때 문제가 생기는 것은 ball이다.

 -> 랜덤한 숫자가 375이고 유저의 예상 숫자가 357이라면 strike는 정상적으로 1이 뜨나 ball은 3이 떠버린다. 1strike 2ball이 떠야되기 때문에 이는 잘못된 것이다. 하지만 간단하다. ball - strike해주면 된다 ^.^ ball - strike이 진짜 ball이기 때문에 앞의 ball은 ballcount로 만들었다.

 

 1. 세 자리가 중복되지 않으면서 랜덤한 숫자 생성

 -> 랜덤한 세 자리 숫자 생성을 위해 Math.floor와 Math.random을 사용했고, 중복되지 않도록 한 번 뽑힌 숫자는 splice로 제거 했다.

 

 2. 3자리 숫자만 입력하게 하기

 -> input의 type이 number일 때는 maxlength와 minlength가 안먹힌다. 그래서 handleOnInput을 사용해주었고 입력받은 숫자가 3자리 미만일 때는 곧바로 return이 되어 inputNumber에 추가된 함수가 종료되도록 하고 alert창을 띄우게 했다.

 

 3. 여러번 start를 누를 경우, input에 여러번 입력할 경우 의도하지 않은 바가 나타난다.

 -> 위 설계는 여러번 start를 누른다고 계속해서 랜덤한 3자리 숫자가 나타나지 않는다... 그리고 start를 누르지 않았는데 input에 입력하면 10번의 chance가 입력할 때마다 감소한다. 이것들은 단순하게 때에 따라 display를 none하는 것을 이용했다. 

 

 

 

 

 다른 사람이 어떻게 작업했는지는 모르겠다. 당연히 나보다 잘했겠지... 일부러 안 찾아보고 되는 대로 raw하게 해보자 생각하며 시도했는데 좀 퀄리티가 처참한 것 같다 ㅎㅎㅎ

  • Koans

 JavaScript-Koans : https://github.com/mrdavidlaing/javascript-koans

 

 Koans는 선문답이라는 뜻이다. 그냥 먼저 묻고 답한다는 건가? ㅎㅎㅎ

 

 Github에서 남이 작성한 코드를 다운 받는 것도 처음이고, Readme를 읽는 것도 처음이었다. expect, it, describe를 본 적이 없으며 영어로 된 것들을 해석한다고 1문제부터 막혔었다. 진짜 쉬운 문제였는데...

 

 JavaScript의 문법에 대해 기초적인 것들을 다루는 문제였다. 사실 완전히 다 이해하진 못했지만 어느 정도 감을 익혔고 자신감이 붙었다. MDN도 진짜 많이 뒤져봤다.

 

이정도면 약과인가...?

 

 후에 JavaScript를 더 사용해보고 다시 풀어봐야 겠다.

 

맨 처음 화면이다.

 

하나하나씩 초록색으로 바뀌면서 성취감이 장난 아니다.

 위에 enlightenment라는 단어가 있는데 깨우침, 이해, ... 혹은 계몽?이라는 뜻이라고 한다. 다 맞췄다고 해서 깨우쳤다고 할 수 없을 것 같고, 응용할 수 있을 때 비로소 깨우쳤다고 말할 수 있을 것 같다. 헤헷

 대참사다. 2시간 정도만에 만들긴 했지만 중복이 엄청나다. 동작이 잘되고 나름 응용을 섞었기 때문에 보람차긴 하지만 for문을 이용했으면 상당히 압축되었을건데 아쉽다. 코딩 실력이 아직 거기까진 부족한 것 같다. 아무래도 JavaScript 기본서 책 1권이 필요하다는 생각이 들었다.

 

  • Carousel, 캐러셀

 Carousel은 회전목마 혹은 수하물 컨베이어 벨트를 뜻하는 단어이다. 둘의 공통점은 반복해서 돌아간다는 점이다. 웹에서 Carousel은 이미지 등의 컨텐츠를 노출시키기 위한 UI로 활용된다.

 

 1. 요구 사항

 위 UI를 봤을 때 우리는 직감하는 것이 몇 가지 있다. 왼쪽, 오른쪽 버튼을 누르면 흰색 네모 칸의 컨텐츠가 바뀔 것이고, 아래의 점들을 누르면 해당 점에 일치하는 컨텐츠로 흰색 네모 칸이 바뀔 거라는 것이다.

 

 2. 나의 풀이

 이번 퀴즈는 Boilerplate Code가 제공되어 해당 코드를 재활용했다.

 

 * Boilerplate Code란 여러 곳에서 재활용 될 수 있는, 반복적으로 비슷한 형태를 띄는 코드를 뜻한다. 1890년대 광고나 컬럼에 반복적으로 사용되는 텍스트를 찍어내기 위해 강철판에 텍스트를 새겨 인쇄를 했었는데, 이것이 어원이 되었다.

 

 - HTML

 

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      href="https://fonts.googleapis.com/css?family=Pacifico&display=swap"
      rel="stylesheet"
    />
    <link
      href="https://fonts.googleapis.com/css?family=Varela+Round&display=swap"
      rel="stylesheet"
    />
    <link rel="icon" href="/images/favicon.ico" />
    <link rel="stylesheet" type="text/css" href="style.css" />
    <title>Carousel Project</title>
  </head>

  <body>
    <section>
      <div class="image-box">
        <img id="logo" src="images/vanilla_coding_logo.png" />
      </div>
      <h1>Carousel</h1>
      <h1 class="nowNumber">Number : 1</h1>

      <!-- Carousel Start -->
      <div class="contents">
        <button id="left"></button>
        <img class="image1" id="vanilla" src="images/image-1.png" />
        <button id="right"></button>
      </div>

      <div class="dots">
        <button id="dot1"></button>
        <button id="dot2"></button>
        <button id="dot3"></button>
        <button id="dot4"></button>
        <button id="dot5"></button>
      </div>
      <!-- Carousel End -->
    </section>

    <script src="index.js"></script>
  </body>
</html>

 CSS, JavaScript는 모두 파일 형태로 불러오는 방식이 사용되었다. 그리고 nowNumber를 추가하여 현재 컨텐츠가 몇 번째 컨텐츠인지 알 수 있게 하였고 left와 rigth 버튼, 점 버튼들을 추가했다.

 

 - CSS

body {
  background-image: url("./images/bg.jpeg");
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
  font-family: "Varela Round", sans-serif;
}

h1 {
  font-family: "Pacifico", cursive;
  text-align: center;
  font-size: 36px;
  margin: 0;
}

.image-box {
  text-align: center;
}

#logo {
  width: 800px;
}

.contents {
  display: flex;
  justify-content: center;
}

#left {
  border-radius: 100px;
  border: none;
  background-image: url("./images/left.png");
  background-position: center;
  background-size: cover;
  background-color: yellow;
  width: 50px;
  height: 50px;
  margin: auto;
}

#right {
  border-radius: 100px;
  border: none;
  background-image: url("./images/right.png");
  background-position: center;
  background-size: cover;
  background-color: yellow;
  width: 50px;
  height: 50px;
  margin: auto;
}

#vanilla {
  border-radius: 3px;
  border: 2px solid gray;
  background-position: center;
  background-size: cover;
  width: 400px;
  height: 400px;
}

.dots {
  display: flex;
  justify-content: center;
  margin-top: 30px;
}

#dot1,
#dot2,
#dot3,
#dot4,
#dot5 {
  margin-right: 20px;
  border: 1px solid black;
  border-radius: 100px;
  width: 10px;
  height: 15px;
  background-color: yellow;
}

 

 - JavaScript

var dot1 = document.querySelector("#dot1");
var dot2 = document.querySelector("#dot2");
var dot3 = document.querySelector("#dot3");
var dot4 = document.querySelector("#dot4");
var dot5 = document.querySelector("#dot5");

var img = document.querySelector("#vanilla");
var nowNumber = document.querySelector(".nowNumber");

dot1.addEventListener("click", function changeImage1() {
  img.setAttribute("src", "images/image-1.png");
  img.classList.remove("image1", "image2", "image3", "image4", "image5");
  img.classList.add("image1");
  nowNumber.textContent = "Number : 1";
});

dot2.addEventListener("click", function changeImage2() {
  img.setAttribute("src", "images/image-2.png");
  img.classList.remove("image1", "image2", "image3", "image4", "image5");
  img.classList.add("image2");
  nowNumber.textContent = "Number : 2";
});

dot3.addEventListener("click", function changeImage3() {
  img.setAttribute("src", "images/image-3.png");
  img.classList.remove("image1", "image2", "image3", "image4", "image5");
  img.classList.add("image3");
  nowNumber.textContent = "Number : 3";
});

dot4.addEventListener("click", function changeImage4() {
  img.setAttribute("src", "images/image-4.png");
  img.classList.remove("image1", "image2", "image3", "image4", "image5");
  img.classList.add("image4");
  nowNumber.textContent = "Number : 4";
});

dot5.addEventListener("click", function changeImage5() {
  img.setAttribute("src", "images/image-5.png");
  img.classList.remove("image1", "image2", "image3", "image4", "image5");
  img.classList.add("image5");
  nowNumber.textContent = "Number : 5";
});

var left = document.querySelector("#left");
var right = document.querySelector("#right");

left.addEventListener("click", function changeImageLeft() {
  if (img.className === "image1") {
    img.setAttribute("src", "images/image-5.png");
    img.classList.remove("image1", "image2", "image3", "image4", "image5");
    img.classList.add("image5");
    nowNumber.textContent = "Number : 5";
  } else if (img.className === "image2") {
    img.setAttribute("src", "images/image-1.png");
    img.classList.remove("image1", "image2", "image3", "image4", "image5");
    img.classList.add("image1");
    nowNumber.textContent = "Number : 1";
  } else if (img.className === "image3") {
    img.setAttribute("src", "images/image-2.png");
    img.classList.remove("image1", "image2", "image3", "image4", "image5");
    img.classList.add("image2");
    nowNumber.textContent = "Number : 2";
  } else if (img.className === "image4") {
    img.setAttribute("src", "images/image-3.png");
    img.classList.remove("image1", "image2", "image3", "image4", "image5");
    img.classList.add("image3");
    nowNumber.textContent = "Number : 3";
  } else if (img.className === "image5") {
    img.setAttribute("src", "images/image-4.png");
    img.classList.remove("image1", "image2", "image3", "image4", "image5");
    img.classList.add("image4");
    nowNumber.textContent = "Number : 4";
  }
});

right.addEventListener("click", function changeImageRight() {
  if (img.className === "image1") {
    img.setAttribute("src", "images/image-2.png");
    img.classList.remove("image1", "image2", "image3", "image4", "image5");
    img.classList.add("image2");
    nowNumber.textContent = "Number : 2";
  } else if (img.className === "image2") {
    img.setAttribute("src", "images/image-3.png");
    img.classList.remove("image1", "image2", "image3", "image4", "image5");
    img.classList.add("image3");
    nowNumber.textContent = "Number : 3";
  } else if (img.className === "image3") {
    img.setAttribute("src", "images/image-4.png");
    img.classList.remove("image1", "image2", "image3", "image4", "image5");
    img.classList.add("image4");
    nowNumber.textContent = "Number : 4";
  } else if (img.className === "image4") {
    img.setAttribute("src", "images/image-5.png");
    img.classList.remove("image1", "image2", "image3", "image4", "image5");
    img.classList.add("image5");
    nowNumber.textContent = "Number : 5";
  } else if (img.className === "image5") {
    img.setAttribute("src", "images/image-1.png");
    img.classList.remove("image1", "image2", "image3", "image4", "image5");
    img.classList.add("image1");
    nowNumber.textContent = "Number : 1";
  }
});

완성된 화면

 

 먼저 점들을 클릭할 때 해당 class의 name들을 모두 지워버리고(remove) 점 순서에 따라 일치하는 이미지를 추가하고 class를 부여했다.(add)

 

 화살표를 누를 때는 현 화면이 1번째 였다면 왼쪽 클릭 시 5번째로, 2번째 였다면 1번째로 이동하게 하는 매커니즘을 구상했다. 이 때에도 점을 클릭했을 때와 마찬가지로 기존 class의 name들을 모두 지워버리고(remove) 이동했을 때 일치해야 하는 이미지를 추가하고 class를 부여했다.(add) 오른쪽과 똑같이 동작되도록 했다.

 

 추가로 현 이미지가 몇 번째 이미지인지 boiler plate에는 없어서 nowNumber.textContent를 추가해 몇 번째 이미지인지 인지할 수 있게 했다.

 

 

 

 그렇다. 이보다 비효율적일 수는 없다. for문을 활용하지 못하는 적절한 예시이다. 코드가 짧아서 다행이지만 길어지고 부하가 많아질수록 비효율성을 두드러지게 나타날 것이다.

 

 정상적으로 동작된다는 것에 만족하지만, 중복은 프로그래밍에서 상당한 비효율성을 불러온다. 책을 사든 구글링을 하든 문법을 좀 배워서 타개해봐야겠다.

  • Background Changer

 HEX Color Code란 RGB 방식의 '색상 코드' 표기법이다. #뒤에 여섯 자리의 글자가 오는데, 이는 0~9까지의 숫자 4개와 A~F까지의 알파벳 2개로 이루어져 있다. 예를 들면, #75E9A3, #F0C433 이런 것이다. 한 자리에  16가지가 들어갈 수 있으므로 16진수라고 보면 되겠다. 그렇다면 이런 헥스 코드로 만들 수 있는 색상의 수는 총 16^6으로 16,777,216개이다.

 

 1. 요구 사항

 - 위와 같은 UI를 만든다.

 - CLICK ME 버튼을 클릭 시 #3474FF(헥스 코드)가 랜덤하게 바뀐다.

 - CLICK ME 버튼을 클릭 시 헥스 코드가 바뀜과 동시에, 바뀐 헥스 코드에 일치하는 배경색으로 바뀐다.

 

 2. 나의 풀이

3시간 정도 걸린 것 같다.

 

 - HTML + CSS

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <title>Background Changer</title>
    <style>
      .wrap {
        height: 100vh;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
      }

      p {
        font-size: xx-large;
      }

      button {
        font-size: xx-large;
        padding: 20px;
        border-radius: 5px;
        background-color: gray;
        color: white;
        border: black;
      }
    </style>
    <script defer src="js_vanilla_BackgroundChanger.js"></script>
  </head>
  <body>
    <div class="wrap">
      <p>
        HEX COLOR :
        <span>#0021FQ</span>
      </p>
      <button>CLICK ME</button>
    </div>
  </body>
</html>

 display: flex과 flex-direction: Column을 통해 HEX COLOR 문단과 CLICK ME 버튼을 열로 나열했다.

 align-items: center와 justify-content: center를 통해 HEX COLOR 문단과 CLICK ME 버튼을 정중앙에 배치시켰다.

 defer src를 통해 JavaScript 파일을 불러왔다.

 

 - JavaScript

 

var arr = [
  "0",
  "1",
  "2",
  "3",
  "4",
  "5",
  "6",
  "7",
  "8",
  "9",
  "A",
  "B",
  "C",
  "D",
  "E",
  "F",
];

var button = document.querySelector("button");
var body = document.querySelector("body");
var span = document.querySelector("span");

button.addEventListener("click", function hexCode() {
  let result = "#";

  for (var i = 0; i < 6; i++) {
    var randomNumber = Math.floor(Math.random() * arr.length);
    var randomIndex = arr[randomNumber];
    result += randomIndex;
  }

  body.setAttribute("style", "background-color: " + result);
  span.textContent = result;
});

 일단 헥스 코드를 구성하는 16가지의 글자로 이루어진 배열부터 만들었다. 이부분은 힌트를 참조했다...

 

 CLICK ME라는 버튼을 "click"하면 function hexCode()가 실행되도록 이벤트를 추가했다.

 

 - function hexCode() 

1. 일단 헥스 코드가 #부터 시작할 수 있게, 또한 계속해서 변수가 재할당 될 수 있도록 let result = "#"을 선언했다.

2. 헥스 코드는 #을 제외한 6개의 글자로 이루어지므로 6번 반복되는 for문을 만들었다.

3. 0부터 1미만의 난수를 추출하는 Math.random과 뒤의 소수를 없애는, 내림시키는 Math.floor를 사용하면 무작위의 정수를 출력시킬 수 있다.

 

 Math.random() 실행 시 0.336288..., 0.749572..., 0.182934과 같은 0과 1사이의 난수가 추출된다. 여기에 10을 곱하면, 즉 Math.random() * 10을 실행하면 3.36288..., 7.49572..., 1.82934...와 같은 숫자가 출력된다. 여기에 Math.floor까지 실행하면, 즉 Math.floor(Math.random() * 10) 실행 시 숫자가 내림되므로 3, 7, 1과 같은 0부터 10미만의 정수가 랜덤하게 출력되는 것이다.

 

 중간에 10이 아니라 16을 곱했다면 0부터 16미만의 정수가 랜덤하게 출력된다. 이를 통해서 배열의 Index가 랜덤하게 추출되는 코드를 짤 수 있는데, 그대로 짠 것이 var randomNumber이다. 이러한 0부터 16미만의 랜덤한 정수를 arr 배열의 Index로 넣으면, arr 배열의 value들을 랜덤하게 추출할 수 있다.

 

 그리고 result += randomIndex를 통해, for문 안에서 6번 실행될 때마다 문자열이 계속해서 더해질 수 있도록 했다.

ex)

i = 0, result = "#" + "A" = "#A"

i = 1, result = "#A" + "6" = "#A6"

i = 2, result = "#A6" + "B" = "#A6B"

...

 

4. 마지막으로 setAttribute를 통하여 body에 style 속성을 추가하고 background-color가 result 값(for문을 마치면 최종적으로는 헥스 코드가 되어있다)에 맞게 설정되도록 했다. 또한 textContent를 통해 UI에 나타나는 헥스 코드 또한 result 값이 나타나도록 했다. 

  • Event

 자바스크립트에서 가장 동적인 움직임을 줄 수 있는 부분이지 않나 싶다. 웹 사이트에서 우리가 화면의 일부를 클릭하거나 마우스를 갖다대면 여러가지 동작이 일어난다. 이러한 대부분의 동작(이벤트)들을 자바스크립트로 구현할 수 있다.

 

- addEventListener

 addEventListener를 통해 다양한 동작을 구현할 수 있다.

 위 사진과 같은 경우, link라는 변수에 class가 titlelink인 요소 중 가장 처음 것('Don't start with ~~~')을 넣었다. 그리고 이 link에 addEventListener를 통해 mouseover, 즉 마우스를 오버(갖다댔을 때)했을 때 우리가 원하는 function()이 실행된다. 여기서는 123을 출력하는 것이다.

 

 또 가장 쉬운 경우는 click이다. rank라는 변수에 class가 rank인 요소의 가장 첫 요소를 넣었다. 그리고 이 rank를 click했을 때 alert를 띄우는 function이 실행되도록 addEventListener를 사용했다.

 

 즉 addEventListener('a', b)로 어느 요소에 Event를 추가할 수 있는데, a는 어떤 동작이 취해지면 되는지, 즉 트리거가 오면 되고 b에는 그 트리거가 만족되었을 때 실행되었으면 하는 function이 오면 된다. 여기서 a 인자는 event type, b 인자는 event handler 혹은 event listener라고 칭한다.

 

  • Event 객체

 function(), 이 괄호 사이에 event라는 매개 변수를 넣어보자.

 위 코드는 tbody를 클릭했을 때 event가 출력되도록 짠 코드이다. tbody는 news.ycombinator.com의 대부분을 차지하는데 이 공간의 어느 곳을 클릭하든 event라는 매개변수가 출력되는 것이다. 위와 같은 경우 event라는 매개 변수에 내가 클릭한 곳, 동작에 대한 정보가 담기는 것이다.

 

 x 좌표, y 좌표 등등 여러가지 정보가 담겨 있다. 그 중에서 주로 사용하는 것이 target과 currentTarget이다.

target

 쉽게 말해서 내가 클릭한 곳이 HTML 파일로 어디에 속하는지를 보여줄 수 있는 정보가 target이다.

 

currentTarget

 currentTarget은 내가 addEventListener한, 즉 Event를 부여한 곳에 대한 정보가 담겨 있는 것이다. 여기서 우리는 tbody에 대해 function(event)가 일어나도록 했으므로 currentTarget은 tbody태그라고 보면 된다. ycombinator 사이트의 거의 대부분이라고 봐도 된다. event가 일어나게 되는 범위라고 생각하면 이해하기 쉬울 듯 하다.

 

 위의 코드는 tbody 아무데나 클릭했을 때 currentTarget(여기선 tbody)의 HTML 코드를 <div>123</div>로 바꾸도록 동작하게 하는 코드이다.

 

 event라고 지어 놓은 매개 변수는 보통 이벤트 객체라고 부른다.

 

 이벤트 관련 참고 자료 : https://developer.mozilla.org/ko/docs/Web/Events

 

이벤트 참조 | MDN

DOM 이벤트는 발생한 흥미로운 것을 코드에 알리기 위해 전달됩니다. 각 이벤트는 Event 인터페이스를 기반으로한 객체에 의해 표현되며 발생한 것에 대한 부가적인 정보를 얻는데 사용되는 추가

developer.mozilla.org

 MDN에 어떤 이벤트가 가능한지 잘 정리되어 있다. 굳이 외울 필요가 없고 필요할 때 해당 자료를 찾아보면 된다. 엄청 많기 때문에 외우기 어렵다.

+ Recent posts