자바스크립트 - 객체 다루기

가위바위보 게임

1. 이미지 스프라이트

서버에 이미지를 요청하는 횟수를 줄이기 위한 기법

  • 이미지를 하나로 합쳐 background로 주고, CSS와 자바스크립트로 적절히 잘라서 화면에 표시해준다.
  • 가위바위보 게임에서는 이 이미지를 수시로 바꿔주어야 하는 작업을 해야하므로 자바스크립트 단에서 이미지를 주고 포지션 속성을 통해 x 좌표를 조정한다.
  • 이 때 공통점이 있으므로 객체로 묶어서 표현한다.
const rspX = {
  scissors: '0',
  rock: '-220px',
  paper: '-440px',
};

2. 일정 시간마다 반복하기: setInterval

  • setInterval(): 일정 시간마다 특정 행동을 반복하게 하는 함수
  • setTimeout() 함수를 이용해서도 가능함 (재귀함수)
const changeComputerhand = () => {
  if (computerChoice === 'scissors') {
    computerChoice = 'rock';
  } else if (computerChoice === 'rock') {
    computerChoice = 'paper';
  } else if (computerChoice === 'paper') {
    computerChoice = 'scissors';
  }
  $computer.style.background = `url(${IMG_URL}) ${rspX[computerChoice]} 0`;
  $computer.style.backgroundSize = 'auto 200px';
};
setInterval(changeComputerhand, 50);

// setTimeout() 이용하기
const changeComputerhand = () => {
  if (computerChoice === 'scissors') {
    computerChoice = 'rock';
  } else if (computerChoice === 'rock') {
    computerChoice = 'paper';
  } else if (computerChoice === 'paper') {
    computerChoice = 'scissors';
  }
  $computer.style.background = `url(${IMG_URL}) ${rspX[computerChoice]} 0`;
  $computer.style.backgroundSize = 'auto 200px';
  setTimeout(changeComputerhand, 50);
};
setTimeout(changeComputerhand, 50);
  • 함수는 자기 자신을 호출할 수 있는데, 이는 재귀함수라고 한다.
  • setInterval 함수 역시 정확한 시간을 나타내지는 못한다.
setInterval(() => {
  console.log('hello'); 
}, 1000);

// setTimeout으로 바꾸기
function hello() {
  console.log('hello');
  setTimeout(hello, 1000);
}
setTimeout(hello, 1000);
  • 엄연히 따지면 둘은 다르게 작동한다. 최대한의 간격 보장을 위한 함수는 setInterval이고, 그 간격을 보장하지 않아도 되면 setTimeout 함수를 사용하자.

3. 타이머 멈췄다 실행하기: cleartInterval

  • setInterval 함수 취소 방법: clearInterval
    setInterval 함수에는 반환값이 있는데, 반환값은 타이머에 대한 아이디로 나중에 이 값을 이용해 타이머를 제거할 수 있다. setTimeout 함수도 clearTimeout 함수로 취소 가능하다. 그러나 setTimeout 함수에 인수로 넣은 함수가 실행되기 전에 clearTimeout 함수를 호출해야 한다.
  • 타이머를 멈췄다 실행하기 위해서는 아래와 같이 작성해야 한다.
let intervalId = setInterval(changeComputerhand, 50);

const clickButton = () => {
  clearInterval(intervalId);
  // 점수 계산 및 화면 표시
  setTimeout(() => {
    intervalId = setInterval(changeComputerhand, 50);
  }, 1000);
};
  • 이렇게 작성하게 되면 버튼을 한 번 누르고 나면 다시 눌러도 멈추지 않고, 버튼을 누를수록 이미지 전환 속도가 빨라지는 버그가 발생하게 된다. 이를 해결하기 위한 방법에는 3가지가 있다.
    1) setTimeout 함수 안에도 clearInterval 함수를 호출하기
    2) removeEventListener를 이용하여 멈춰진 1초 동안 버튼을 클릭하지 못하게 막기 -> 버튼 이벤트를 막았다가 풀었다가 하는 것은 실수할 가능성도 많고 코드도 길어질 수 있으므로 추천하지 않음
    3) flag variable 선언하여 true, false 주기
let intervalId = setInterval(changeComputerhand, 50);

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

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

* removeEventListener 주의할 점

const fun = () => () => { 
  console.log('고차 함수입니다',); 
} 
태그.addEventListener('click', fun(1));
태그.removeEventListenter('click', fun(1));
  • 위와 같이 remove하면 remove 되지 않음!
    -> fun(1) === fun(1) -> false이기 때문에
  • 함수도 객체이므로 변수를 사용해서 참조해주어야 한다.
const fun = () => () => {
  console.log('',);
}
const fun1 = func(1);
fun1 === fun1;
태그.addEventListener('click', fun(1));
태그.removeEventListenter('click', fun(1));

4. 가위바위보 규칙 찾기

  • 승부를 결정해야 하는데 가위바위보에는 무승부도 존재하기 때문에 총 9가지의 경우의 수가 존재한다. if/else-if로 나타내면 상당히 길고 복잡해진다. 그렇다고 함수로 빼내기도 어렵기 때문에 이러한 경우에는 중복을 찾기 위해 수를 대입해보면 좋다.
  • 예를 들어 가위를 1, 바위를 0, 보를 -1이라고 하고 컴퓨터가 낸 값과 내가 낸 값을 빼보자. 이는 아래와 같이 점수표로 나타낼 수 있다.
  • 무승부: 0
  • 이기는 경우: 2 또는 -1
  • 지는 경우: 1 또는 -2
  • 가위바위보 뿐만 아니라 알고리즘 문제를 해결할 때에도 중복이 쉽게 보이지 않는다면 수를 대입하여 해결해보쟈
  • ||(OR)을 사용하는 코드는 배열의 includes를 사용하여 줄일 수 있다.
    ||으로 연결된 식이 많다면 includes를 사용하는 게 훨씬 깔끔하다.
diff === 2 || diff === -1
[2, -1].includes(diff); // 이런 식으로!

5. self-check & 최종 코드

  • 5판 3선승제로 만들기
    내 점수를 입력받는 변수와 컴퓨터 점수를 입력받는 변수를 따로 두어서 먼저 3이 되면 이기는 것으로 변경
    setTimeout이 없는 부분은 다시 실행되지 않으므로 게임을 더이상 진행할 수 없다.