[자바스크립트] 인프런 - 렛츠기릿 자바스크립트 - 7강

June·2021년 8월 3일
0

가위바위보 순서도 그리기

<html>
<head>
  <meta charset="utf-8" />
  <title>가위바위보</title>
  <style>
    #computer {
      width: 142px;
      height: 200px;
    }
  </style>
</head>

<body>
<div id="computer"></div>
<div>
  <button id="scissors" class="btn">가위</button>
  <button id="rock" class="btn">바위</button>
  <button id="paper" class="btn"></button>
</div>
<div id="score">0</div>
<script>
  const $computer = document.querySelector('#computer');
  const $score = document.querySelector('#score');
  const $rock = document.querySelector('#rock');
  const $scissors = document.querySelector('#scissors');
  const $paper = document.querySelector('#paper');
  const IMG_URL = './rsp.png';
  $computer.style.background = `url(${IMG_URL}) -464px 0`;
  $computer.style.backgroundSize = 'auto 200px';
...
</script>
</body>
</html>

이미지 파일이 가위바위보가 하나로 합쳐져 있는데, 인터넷 익스플로러만해도 한번에 가져올 수 있는 파일의 개수가 제한이 있어 이렇게 했다. 가져와서 background px을 옮겨서 잘라서 쓴다.

타이머 사용하기(setInterval)

  const rspX = {
    scissors: '0', // 가위
    rock: '-220px', // 바위
    paper: '-440px', // 보
  };

객체를 이용함으로서 그룹화를 했다. 각 이미지의 좌표를 나타내기 위함이다.

const rspX = {
  scissors: '0', // 가위
  rock: '-220px', // 바위
  paper: '-440px', // 보
};

let computerChoice = 'scissors';
const changeComputerHand = () => {
  if (computerChoice === 'scissors') { // 가위면
    computerChoice = 'rock';
  } else if (computerChoice === rspX.rock) {
    computerChoice = 'paper';
  } else {
    computerChoice = 'scissors';
  }
  $computer.style.background = 'url(${IMG_URL}) ${rspX[computerChoice]} 0';
  $computer.style.backgroundSize = 'auto 200px';
}

setInterval(changeComputerHand, 50);

참고로 rspX.computerChoice라하면 안된다. rspX.computerChoice는 rspX["computerChoice"]와 같다. []안에는 값이 들어가야하고 점 뒤에는 문자열 자리다.

setInterval은 setTimeout으로 대체가 가능하다. 재귀적으로 구현하면.

setInterval(() => {
  console.log("hello");
}, 1000);
function hello() {
  console.log("hello");
  setTimeout(hello, 1000);
}
setTimeout(hello, 1000);

둘은 거의 비슷하지만 약간 다르다. 아래 재귀는 만약 console.log 부분이 시간이 많이 걸리는 거라면 1초 보장이 더 안될 수도 있다.

타이머 멈췄다 다시 실행하기

let 아이디 = setInterval(함수, 밀리초);
clearInterval(아이디);

setInterval 함수는 반환값이 있다. 반환값은 타이머에 대한 아이디(숫자)로, 나중에 이 값을 이용해 타이머를 제거할 수 있다. 마찬가지로 setTimeout 함수도 clearTimeout 함수로 취소할 수 있다. 단, setTimeout 함수에 인수로 넣은 함수가 실행되기 전에 clearTimeout을 호출해야 한다.

let intervalId = setInterval(changeComputerHand, 50);

const clickButton = () => {
  clearInterval(intervalid);
  // 점수 계산 및 화면 표시
  setTimeout(() => {
    intervalId = setInterval(changeComputerHand, 50);
  }, 1000);
};
$rock.addEventListener('click', clickButton);
$scissors.addEventListener('click', clickButton);
$paper.addEventListener('click', clickButton);

타이머마다 아이디가 달라지기 때문에, 달라지는 아이디를 항상 변수에 저장해놔야한다.

현재 clickButton 다섯 번 호출하면, setTimeout 다섯번 호출되고, setInterval도 다섯번 호출된다. 인터벌 1번, 2번, 3번, 4번, 5번 (얘만 interValid), 그 다음에 버튼을 클릭하면 5번만 취소.

const clickButton = () => {
  clearInterval(intervalid);
  // 점수 계산 및 화면 표시
  setTimeout(() => {
    clearInterval(intervalid);
    intervalId = setInterval(changeComputerHand, 50);
  }, 1000);
};

clearInterval이 setTimeout안에도 들어가서 코드 중복이라 생각할 수 있지만, 비동기기 때문에 setTimeout은 1초뒤에 실행된다. 중복이 아니다.

removeEventListener 주의점

지금 같은 버튼을 연타하면 생기는 문제를 해결하는 중인데, 아예 버튼을 클릭하지 못하게 할 수도 있다. removeEventListener를 이용하면 된다. 그리고 1초 뒤에 다시 버튼을 클릭하게 할 수 있다.

또는 clickable이라는 변수를 만들어서 if문으로 1초뒤에 true로 만들어주는 방식을 이용할 수도 있다. 이벤트 리스너를 추가했다가 제거했다를 반복하는 과정에 문제가 생길 수도 있다.

  let clickable = true;
  const clickButton = () => {
    if (clickable) {
      clearInterval(intervalId);
      clickable = false;
      // 점수 계산 및 화면 표시
      setTimeout(() => {
        clickable = true;
        intervalId = setInterval(changeComputerHand, 50);
      }, 1000);
    }
  }; 

removeEventListener가 안먹히는 예제를 보자

const fun = () => () => {
  console.log('고차함수입니다',);
}
fun(1) === fun(1)
// 태그.addEventListener('click', fun(1));
// 태그.removeEventListener('click', fun(1));

이 경우 remove가 안되는데, addEventListener에 인자로 들어가는 함수랑 removeEventListener에 인자로 들어가는 함수랑 같아야 하낟. 하지만 fun(1)===fun(1)이 아니다. 함수도 객체이기 때문이다. 객체는 변수에 넣어서 참조해줘야 같다고 나온다. 따라서 아래는 참이다.

const a = {};
const b= a;
a === b; //true

const fun1 = func(1);
fun1 === fun1; //true

따라서 아래와 같이 해야 한다.

const fun = () => () => {
  console.log('고차함수입니다',);
}
const fun1 = func(1);
태그.addEventListener('click', fun1);
태그.removeEventListener('click', fun1);

가위바위보 규칙 찾기

  const scoreTable = {
    rock: 0,
    scissors: 1,
    paper: -1,
  };

  // clickButton 5번 호출, 인터벌 1번, 2번, 3번, 4번, 5번(얘만 intervalId)
  //  그 다음에 버튼을 클릭하면 5번만 취소
  let clickable = true;
  let score = 0;
  const clickButton = () => {
    if (clickable) {
      clearInterval(intervalId);
      clickable = false;
      // 점수 계산 및 화면 표시
      const myChoice = event.target.textContent === '바위' 
        ? 'rock' 
        : event.target.textContent === '가위' 
          ? 'scissors' 
          : 'paper';
      const myScore = scoreTable[myChoice];
      const computerScore = scoreTable[computerChoice];
      const diff = myScore - computerScore;

      let message;
      // 2, -1은 승리조건이고, -2, 1은 패배조건, 점수표 참고
      if ([2, -1].includes(diff)) {
        score += 1;
        message = '승리';
      } else if ([-2, 1].includes(diff)) {
        score -= 1;
        message = '패배';
      } else {
        message = '무승부';
      }
      $score.textContent = `${message} 총: ${score}`;
      setTimeout(() => {
        clickable = true;
        intervalId = setInterval(changeComputerHand, 50);
      }, 1000);
    }
  }; 

  $rock.addEventListener('click', clickButton);
  $scissors.addEventListener('click', clickButton);
  $paper.addEventListener('click', clickButton);

셀프 체크 - 3판 2선승제로 만들기

0개의 댓글