inflearn logo
강의

Course

Instructor

Learning React while making web games

Elegantly Intervalize with Custom Hooks

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

462

dbjoung

3 asked

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값이 출력될 줄 알았는데(더이상 메모리에 없는 함수 객체일테니..) 잘만 실행되더군요 ㅠㅠㅠ 제가 어떤 부분을 이해하지 못하고 있는 걸까요..? (아예 통째로 잘못 이해하고 있는 것도 같고... ㅠㅠ) 감사합니다. 

react

Answer 2

1

zerocho

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

1

zerocho

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

2

hestonpablo

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


안녕하세요, 제로초 님.

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

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

먼저, 함수 컴포넌트 파일에서 아래 <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 값이 출력될 줄 알았는데(더 이상 메모리에 없는 함수 객체일 테니...) 잘만 실행되더군요. ㅠㅠ

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

감사합니다.

0

dbjoung

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

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

npm run dev 실행 시 포트가 안뜨는 문제

0

202

2

timeouts.current를 useEffect 의 input값으로 넣었을때

0

85

2

렌더링 테스트 코드 (Hooks)

0

80

1

Cannot find package 'react-refesh' 이런 에러 뜨시는 분들 보세요.

0

149

1

해당 에러 뜨는 분들 보세요. "Uncaught TypeError: ReactDom.createRoot is not a function"

1

191

1

강사님 레포지토리에서 코드 복사 시 master 브랜치 말고 react18 브랜치꺼 복붙하세요ㅠㅠ

0

95

1

useMemo와 useCallback 사용 시기

0

205

2

onRightClickTd가 작동을 하지 않습니다.

0

226

1

action.type 불러오는 방식

0

222

2

onClickRedo 질문

0

172

1

const Try = require(./Try) 빨간줄

0

248

1

npx webpack 실행시

0

313

1

지뢰찾기 강좌에서 빈칸들 한번에 열기 파트에서 여쭤보고싶은부분이 있어서 글 올립니다.

0

234

1

강좌에서 다루지 않은 기능들은 어떻게 학습하면 좋을까요?

0

311

1

react devtool이 enable 않됩니다.

0

530

2

React 랜더링이 되지 않습니다.

0

409

2

비동기로 동작하는 setState에 대해서

0

331

1

npm run dev 할 때 에러발생

0

478

2

memo, PureComponenet, shouldComponentUpdate 관련 질문

0

206

1

devMiddleware의 필요성

0

352

1

리액트에서 화살표 함수를 사용해야하는 이유

0

931

2

path.join관련질문

0

280

2

2-9. 웹팩 데브 서버와 핫 리로딩 설치과정 시 에러

0

371

1

next.js 에서 이와 비슷한 예제를 돌리고있는데 react랑 달라서 질문 드립니

1

488

4