웹개발을 한다면 개발자에게 있어서 비동기 함수는 필수적인 소양이다.
이번 포스팅에서는 내가 바닐라js를 다루면서 setTimeOut 비동기 함수를 사용하면서 겪었던 버그와 그 버그를 해결한 과정을 적어보겠다.
간단한 반응체크 측정게임을 만들어보면서 겪은 에러 위주로 포스팅할 것이다.
유튜브 조현영님의 동영상강좌 기능 + 셀프체크 기능에다가 내가 스스로 또다른 setTimeOut 함수를 추가하여 만들어보았다.
아래 깃헙주소에서 전체코드를 볼 수 있다.
https://github.com/kth990303/TH-s-Web/blob/master/es2021/response-check.html
알고리즘 설명
1. 파란 화면일 땐 게임대기 화면이다. 여기서 클릭하면 빨간 화면이 나오는데, 이 때는 클릭해선 안된다.
2. 1~3초 중에서 랜덤한 시간 전에는 빨간 화면이 뜨고, 후에는 초록화면이 뜬다.
3. 초록화면이 뜨면 가능한 한 빠르게 클릭한다.
만약 빨간 화면일 때 클릭하면 아래 화면이 뜬 후, 다시 게임 대기화면으로 이동한다.
setTimeOut은 어디에 쓰였는가?
1. 파란 화면(대기화면)에서 클릭하고 1~3초 후에 초록화면을 띄워줘야 할 때
2. 빨간화면에서 클릭하면 성급하다는 메시지를 띄우고 1초 후에 게임 대기화면으로 이동시켜줘야할 때
강의에서는 첫 번째 setTimeOut만 있었으며,
두 번째 setTimeOut은 내가 따로 추가하여 만들어보았다.
위와 같이 비동기함수가 두 군데에 쓰이기 때문에 주의해야한다.
뭐를 주의해야 하냐?
바로 더블클릭, 연속클릭하면 굉장히 많은 setTimeOut이 발동하여 의도치 않은 결과를 낳을 수 있는 버그가 발생한다는 점을 주의해야 한다!
해결방법
비동기함수가 적다면 사실 아래 방법으로 손쉽게 해결할 수 있다.
clearTimeOut 함수로 비동기함수를 clear해주자.
setTimeOut, setInterval 와 같은 비동기함수는 사실 반환할 때 그 함수의 id를 리턴해준다.
clearTimeOut(id)와 같이 작성할 경우 설정해주었던 비동기함수가 clear되면서 작동하지 않게 해주면 된다.
그런데, 비동기함수가 많거나 기능이 좀 많으면 위와 같은 방법으로 해결이 되지 않을 수도 있다고 생각할 수 있다.
나의 경우는 클릭 이벤트가 발생할 때, 빨간 화면에서 파란화면으로 1초 후에 넘어간 뒤에 clearTimeOut을 해주어야 했기 때문에 clearTimeOut을 쓰기에 상당히 애매한 경우였다.
else if(e.target.classList.contains('ready')){
// 1~3초 후에 초록화면이 뜨는 setTimeOut 함수를 clear
clearTimeout(timeOutId);
$screen.textContent='너무 성급하시군요! 다시 시작해주세요';
// 1초 후에 대기화면으로 이동시켜주고 싶어서 만든 setTimeOut
// 그러나 현재 화면에서 클릭 이벤트를 다시 발생시키면 이 setTimeOut은 또 생기고 또 생긴다.
fastTimeOutId = setTimeout(()=>{
$screen.classList.replace('ready', 'waiting');
$screen.textContent='클릭해서 시작하세요';
}, 1000);
}
이런 경우는 사용 후에 id를 null로 초기화시켜 해결하는 방법이 있다.
사실 위 코드는 fastTimeOutId 변수 위에 clearTimeOut(fastTimeOutId);를 하면 해결되는 문제이긴 한데,
존재하지 않는 id의 setTimeOut 함수를 clear하는 것보단, id가 null인지 체크하고 clear하는 것이 더 안전하므로 id를 사용 후에 null로 바꿔주는 작업을 거쳐주자.
else if(e.target.classList.contains('ready')){
// timeOutId를 리턴하는 setTimeOut함수가 있을 때에만 clear해주었다.
if(timeOutId){
clearTimeout(timeOutId);
timeOutId=null;
}
// fastTimeOutId를 리턴하는 setTimeOut함수가 진행되지 않았거나,
// 이미 clear가 진행된 경우에만 (즉, 정상적으로 대기화면으로 이동 후 다시 빨간화면 클릭했을 때만)
// setTimeOut함수를 실행하도록 조건문을 걸어주었다.
if(fastTimeOutId===null){
$screen.textContent='너무 성급하시군요! 다시 시작해주세요';
fastTimeOutId = setTimeout(()=>{
$screen.classList.replace('ready', 'waiting');
$screen.textContent='클릭해서 시작하세요';
}, 1000);
}
}
이제 빨간화면에서 연속클릭을 하든, 대기화면에서 연속클릭을 아무리 빠르게 날려도
알고리즘의 의도대로 잘 돌아감을 확인할 수 있다.
앞으로 더 수많은 비동기함수를 사용해볼텐데, 어서 많은 버그를 발견해서 팍팍 성장했으면 좋겠다.
(아, 물론 공부할 때에만 ^^ 실전에선 버그 안나길 기원)
'JS > VanillaJS' 카테고리의 다른 글
[VanillaJS] 바닐라js로 틱택토를 만들어보자 (2) (0) | 2021.10.22 |
---|---|
[VanillaJS] 바닐라js로 틱택토를 만들어보자 (0) | 2021.10.18 |
[VanillaJS] 바닐라JS의 setTimeOut을 이용한 간단한 로또추첨기 만들기 (0) | 2021.10.11 |
[VanillaJS] addEventListener, 콜백 함수 알아보기 (0) | 2021.10.09 |
[VanillaJS] html 태그를 선택하는 querySelector에 대해 알아보자 (2) | 2021.10.09 |