• 카테고리

    질문 & 답변
  • 세부 분야

    프론트엔드

  • 해결 여부

    미해결

useInterval.js 커스텀 훅에서, 왜 첫번째 useEffect를 계속 반복해야할까요?

23.12.20 21:57 작성 조회수 178

0

안녕하세요 제로초님. 리액트 강의 언제나 잘 듣고 있습니다. 양질의 강의 너무 감사합니다. 제로토님의 '커스텀 훅으로 우아하게 인터벌하기' 강의를 듣고 난 후 개인적으로 코드를 분석해보고 있었는데, 공식 문서도 찾아보고 개인적으로 테스트를 굴려봐도 해결되지 않는 의문이 있어 찾아왔습니다. 먼저, 함수 컴포넌트 파일에서 아래 <1번> 처럼 구성하였기 때문에, 변수 changeHand에는 모양만 같을 뿐 저장공간이 다른 새로운 함수가 매 랜더링 시 마다 만들어진 후 useInterval에 매개변수로 넘어가겠구나.. 하고 이해했습니다. <1번> const changeHand = () => { console.log(imgCoord); if (imgCoord===rspCoords.r) { setImgCoord(rspCoords.s); } else if (imgCoord === rspCoords.s) { setImgCoord(rspCoords.p); } else if (imgCoord === rspCoords.p) { setImgCoord(rspCoords.r); } } useInterval(changeHand, isRunning ? 100 : null); 그리고 useInterval 함수와 아래 <2번> 코드가 컴포넌트가 새로 랜더링 될 때 마다 실행 될 것이라고도 이해했습니다. <2번> useEffect(()=>{ savedCallback.current = callback; }); 그러니 즉, 아래 두 코드를 해석해보자면 매 랜더링 시 마다 새로운 changeHand 함수를 만들어, useInterval을 호출해 매개변수로 넘겨준 다음 useRef 훅으로 saveCallback.current에 저장해주고 있다.. 라고 이해했습니다. (잘 이해한게... 맞을까요..?) 그리고 이렇게 저장된 changeHand 함수는 <3번> 코드의 setInterval(tick, delay)에 의해, delay초 마다 한번씩 실행되게 된다는 내용 까지 이해했습니다. <3번> useEffect(()=>{ function tick() { savedCallback.current(); } if (delay !== null) { let id = setInterval(tick, delay); return () => { clearInterval(id);} } }, [delay]); 일단 저는 '왜 첫번째 useEffect(2번 코드)를 계속 반복해야할까요?' 라는 질문의 답에 대해서, 매 랜더링 시 마다 컴포넌트와 컴포넌트 함수들은 새로이 만들어지기 때문에, 마찬가지로 savedCallback에도 이전 함수가 아니라 새로 만들어진 함수를 저장해야하기 때문이겠구나.. 라고 생각했었습니다. 하지만 이 가정이 맞다면, 컴포넌트가 재실행되며 이전 컴포넌트 객체였던 것은 생명주기 순서에 따라 새로 랜더링 되기 전에 삭제될 테니, 이전 컴포넌트 객체와 함수들은 저장공간에 남아있지 말아야 할 것입니다. 그래서 별도로 배열 변수를 만들어, 랜더링 시 마다 만들어지는 changeHand 함수를 순서대로 저장한 후 한 20번 째 랜더링 쯤에서 두 번째로 만들어진 changeHand 함수를 콘솔창에 찍어봤었습니다. 저는 이 과정을 통해 undefind 에러가 발생하거나 null값이 출력될 줄 알았는데(더이상 메모리에 없는 함수 객체일테니..) 잘만 실행되더군요 ㅠㅠㅠ 제가 어떤 부분을 이해하지 못하고 있는 걸까요..? (아예 통째로 잘못 이해하고 있는 것도 같고... ㅠㅠ) 감사합니다. 

답변 2

·

답변을 작성해보세요.

1

객체와 함수가 왜 사라진다고 생각하시는 건가요? 자바스크립트에서는 객체와 함수는 참조가 남아있는 한 계속 유지됩니다.

1

질문 줄바꿈 다시 해주시면 안 될까요? 읽을수가없네요 ㅠㅠ

byn님의 프로필

byn

2023.12.21

지나가다 포맷팅 해드렸습니다. ㅎ


안녕하세요, 제로초 님.

리액트 강의 언제나 잘 듣고 있습니다. 양질의 강의 너무 감사합니다.

제로초 님의 "커스텀 훅으로 우아하게 인터벌하기" 강의를 듣고 난 후, 개인적으로 코드를 분석해 보고 있었는데, 공식 문서도 찾아보고 개인적으로 테스트를 굴려봐도 해결되지 않는 의문이 있어 찾아왔습니다.

먼저, 함수 컴포넌트 파일에서 아래 <1번>처럼 구성하였기 때문에, 변수 changeHand에는 모양만 같을 뿐 저장공간이 다른 새로운 함수가 렌더링 될 때마다 만들어진 후 useInterval에 인자로 넘어가겠구나... 하고, 이해했습니다.

// <1번>

const changeHand = () => {
  console.log(imgCoord)

  if (imgCoord === rspCoords.r) {
    setImgCoord(rspCoords.s)
  } else if (imgCoord === rspCoords.s) {
    setImgCoord(rspCoords.p)
  } else if (imgCoord === rspCoords.p) {
    setImgCoord(rspCoords.r)
  }
}

useInterval(changeHand, isRunning ? 100 : null)

그리고 useInterval 함수와 아래 <2번> 코드가 컴포넌트가 새로 렌더링 될 때마다 실행될 것이라고도 이해했습니다.

// <2번>

useEffect(() => {
  savedCallback.current = callback
})

그러니 즉, 아래 두 코드를 해석해 보자면 렌더링 될 때마다 새로운 changeHand 함수를 만들어, useInterval을 호출해 매개변수로 넘겨준 다음 useRef 훅으로 saveCallback.current에 저장해주고 있다... 라고 이해했습니다. (잘 이해한게... 맞을까요...?)

그리고 이렇게 저장된 changeHand 함수는 <3번> 코드의 setInterval(tick, delay)에 의해, delay초 마다 한 번씩 실행되게 된다는 내용까지 이해했습니다.

// <3번>

useEffect(() => {
  function tick() {
    savedCallback.current()
  }

  if (delay !== null) {
    let id = setInterval(tick, delay)
    return () => {
      clearInterval(id)
    }
  }
}, [delay])

일단 저는 "왜 첫 번째 useEffect(2번 코드)를 계속 반복해야 할까요?"라는 질문의 답에 대해서, 렌더링 될 때마다 컴포넌트와 컴포넌트 내부의 함수들은 새로 만들어지기 때문에, 마찬가지로 savedCallback에도 이전 함수가 아니라 새로 만들어진 함수를 저장해야 하기 때문이겠구나... 라고 생각했었습니다.

하지만 이 가정이 맞는다면, 컴포넌트가 재실행되며 이전 컴포넌트 객체였던 것은 생명주기 순서에 따라 새로 렌더링 되기 전에 삭제될 테니, 이전 컴포넌트 객체와 함수들은 저장공간에 남아있지 말아야 할 것입니다.

그래서 별도로 배열 변수를 만들어, 렌더링 될 때마다 만들어지는 changeHand 함수를 순서대로 저장한 후 한 20번째 렌더링 쯤에서 두 번째로 만들어진 changeHand 함수를 콘솔에 찍어봤었습니다. 저는 이 과정을 통해 undefined 에러가 발생하거나 null 값이 출력될 줄 알았는데(더 이상 메모리에 없는 함수 객체일 테니...) 잘만 실행되더군요. ㅠㅠ

제가 어떤 부분을 이해하지 못하고 있는 걸까요...? (아예 통째로 잘못 이해하고 있는 것도 같고... ㅠㅠ)

감사합니다.

aeu J.님의 프로필

aeu J.

질문자

2023.12.21

아. 참조가 남아있다면 객체와 함수도 계속 남아있는군요. 그 사실을 몰랐네요... 머쓱.. 넘 감사합니다. 그리고 질문글 가독성이 완전 엉망인 것도 모르고 있었네요 byn님 포맷팅 너무 감사합니다.

두분 다 즐거운 하루 되세요..!