앵귤러에 일반적인 자바스크립트 타이머 스크립트를 적용하던 중 다음과 같은 오류가 발생했다.
TypeError: Cannot read properties of undefined (reading ‘split’)
타이머 시작 부분에서 setInterval로 1초 간격으로 callback펑션을 호출하여 callback펑션 안에서 시간을 1초씩 줄이는 스크립트였는데 해당 기능을 angular에 맞게 클래스 내부 변수 및 메소드로 분리하고 남은시간 변수(interval)를 클래스의 변수로 빼두고 테스를 하니 callback펑션 안에서 남은시간 변수(this.interval)를 참조하지 못했다.
기존 자바스크립트에서 타이머 시작부분
let interval = setInterval(callback, 1000);
기존 자바스크립트에서 시간을 1초씩 빼는 콜백 펑션
function callback() {
let timer = timer2.split(":");
let minutes = parseInt(timer[0], 10);
let seconds = parseInt(timer[1], 10);
--seconds;
minutes = seconds < 0 ? --minutes : minutes;
if (minutes === 0 && seconds === 0) clearInterval(interval);
seconds = seconds < 0 ? 59 : seconds;
seconds = seconds < 10 ? "0" + seconds : seconds;
$(".count-number").html(minutes + ":" + seconds);
timer2 = minutes + ":" + seconds;
}
원인
왜냐하면 interval로 함수를 호출한 이후의 this는 앵귤러 클래스의 this가 아니라 호출한 펑션 그 자체를 뜻하기 때문이다.
풀어서 얘기하자면 callback펑션 안에서 this.interval로 앵귤러 클래스의 interval변수를 참조시키려 했으나 this가 호출된 앵귤러 클래스가 아닌 callback펑션 그 자체를 의미하므로 interval이라는 변수가 undefined가 되어 에러메세지가 발생한 것이다.
원래 자바스크립트에서는 함수호출시 인자 및 this가 암묵적으로 함수 내부로 전달되는데 경우에 따라 this는 자기참조변수이기도 해서 문제가 발생하는 것이다.
this가 가리키는 값은 함수 호출 방식에 의해 동적으로 결정된다는 것을 이해하는 것이 핵심이다.
해결방법
이 문제를 해결하는 가장 쉬운 방법은 setInterval에서 펑션을 바로 호출하지 말고 () => 화살표 함수로 호출하면 된다.
화살표 함수로 함수를 선언하면 this가 상위 스코프의 this를 가리키므로 의도했던 대로 현재 class의 interval변수를 가리키게 된다.
setInterval(callback, 1000); 이 부분을 다음과 같이 변경한다.
setInterval( () => { this.callback() }, 1000);
아울러 개발자라면 자바스크립트의 this개념은 한번 정리해두고 넘어갈 필요가 있다. 특히 참조 링크의 마지막 게시물을 추천한다.
(초급때는 의외로 이 부분을 이해하지 못해 삽질을 많이 하게 됨 ^^;)
참조 링크
영어에 익숙치 않다면 다음 포스팅도 추천한다.