클로저Closure가 되버린 내부함수가 일으키는 오류 (feat. setInterval)

shinetiger·2022년 7월 11일
1
post-thumbnail

목차

  • 문제발생
  • 분석
  • 원인
  • 대안
  • 결론
  • 참고 사이트

문제발생

setInterval을 통해서 1초마다 1씩 늘어나는 카운트 기능을 만드려고 했지만

아래와 같이 한번만 카운트되고 콘솔창에서는 1초간격으로 0만 출력되고 있다(+1이 되지 않음)

분석

  1. App에서 리턴된 state는 값이 바뀌어 정상적으로 렌더가 된다.
  2. 그러나 중첩함수인 countingSecond함수 안에 있는 second는 초기값인 0만 계속 출력하고 있다.
  3. 외부함수인 App 에서 state의 콘솔을 찍어보면 최초엔 0이 출력되지만 그 다음에는 1만 계속 출력된다.

그러나 중첩함수인 countingSecond 에서 콘솔을 찍어보면 0만 출력된다.
그렇다면 분석 결과 문제는 왜 값이 바뀌어 갱신된 state 값을 가져오지 못하는가? 로 정리 할 수 있다.

원인

분석할때는 countingSecond 함수를 짚었지만 문제의 원인은 중첩함수인 setSecond가 클로저 가 되었기 때문이다.
클로저가 무엇인가를 따지기 이전에 현재 코드의 스코프 구조를 짚고 넘어가보자.

위의 그림을 바탕으로 다시한번 설명해 보자면,

가장 외부함수인 App 에서 const [second, setSecond] = useState(0) 라고 적었기 때문에 현재 state 는 0이다.

  • 그리고 중첩함수인 setSecond 에서는 second+1 를 통해 state를 1로 업데이트 시키고 있다.
  • 가장 내부에 있는 console.log 에서는 state는 0 이다.

여기서 우리는 setSecond 에서 second 에 대한 선언을 하지 않았는데도

외부함수에 있는 App 에 있는 선언값을 참조 해서 쓰고 있다는 사실을 알 수 있다.

간략하게 설명해보자면 이런것이다.

JS의 함수는 중첩함수에 선언되지 않는 데이터를 외부함수에서 참조하는데
이때 반방향성적으로(중첩함수 → 외부함수) 참조한다.
그리고 클로저는 이 과정에서 최초로 참조한 값을 계속 가지고 있게 된다.

렉시컬 스코프인 App이 호출이 되어 종료가 되면 지역변수second도 종료가 되어
실행 컨텍스트 스택에서 완전하게 제거가 되었지만 렉시컬 환경까지는 손을 대지 않기 때문에
중첩함수인 setSecond가 지역변수second를 자유변수로 참조한다.

그래서 다시 그림을 그려보자면 이렇게된다.

대안

setSecond에 콜백함수를 넣는것이다. setSecond(call=> second+1)

setSecond의 콜백함수의 첫번째 매개변수는 state값을 가져오는데, 이렇게 되면

콜백함수가 클로저가 되어 call을 참조하게 된다. 

아래는 안읽어도 되는 나의 기이한 풀이방법..(콜백 이전에 알았다)
새로운 클로저문제를 발생시킬 수 있다.


2022.07.04 
이 방법은 또 다른 클로저를 만들어서 문제를 더 복잡하게 만들 수 있다는 피드백을 받았다. 

첫번째로는 newTime 라는 변수를 만들고 setState 값에 넣는것을 생각했다.

정상적으로 실행이 된다.

왜일까.

스코프 구조를 다시 살펴보자.

countingSecond 안에 newTime = second 라는 코드를 넣은 덕분에

setSecond는 App 까지 가지 않아도 참조할 수 있고,

newTime 은 갱신된 state값(second)을 받아 줄 수도 있다.

그러니 setSecond가 클로저가 되어도 갱신된 값을 받아 올 수 있는것이다.


결론

오류를 해결하려면 갱신된 state를 가져오고,

state를 어떻게 갱신할지 식을 넣어주는 중간저장소 같은 역할이 필요하다. 

그리고 대안은 해결법이 되지 못한다. setInterval의 특성상 clearInterval을 해주어야 하는데 그 과정도 빠졌으니까..

이 트러블슈팅의 근본적인 해결법은 useInterval 이라는 커스텀 훅을 사용하는 것이다. 

참고사이트

https://mingule.tistory.com/65

profile
의문을 질문으로 바꾸는 개발자

0개의 댓글