• 카테고리

    질문 & 답변
  • 세부 분야

    프론트엔드

  • 해결 여부

    해결됨

RSP Hooks 방식의 UseEffect에서 setInterval이 '한번만' 동작하는 이유를 알고싶습니다!

23.07.12 20:53 작성 조회수 396

0

useEffect(() => {
    interval.current = setInterval(changeHand, 100);
    console.log("다시 실행");
    return () => {
      console.log("종료");
      clearInterval(interval.current);
    };
  }, []);

위 코드는 '가위바위보' hooks에서 useEffect를 사용하는 코드 중 일부입니다. 제가 궁금한 것은 위 코드에서 왜 setInterval을 해주었는데도 '주먹'에서 '가위'로 바뀌고 멈추는가 입니다.

 

 componentDidMount() {
    // 비동기 요청 많이 한다고 함.
    // 해제안해주면 메모리를 많이 먹음
    this.interval = setInterval(this.changeHand, 100);
  }

클래스로 만든 rsp 코드에서는 컴포넌트가 생성되고 setInterval을 하면 주먹-가위-보가 100ms 주기로 잘 바뀝니다.

useEffect()에서 두 번째 인자로 아무것도 넣어주지 않으면 초기 렌더링 될 시에만 실행되는 것으로 알고 있는데요, 한 번 실행된 코드에 setInterval이 있기 때문에 가위로만 바뀌는 게 아닌 100ms마다 주먹-가위-보로 반복되어야 하는 거 아닌가요?

 

이와 별개로 어떤 유료 리액트 코스보다 좋은 강의 감사드립니다.


답변 2

·

답변을 작성해보세요.

1

useEffect의 []에 changeHand를 안 넣어서 그렇습니다. 한 번만 바뀌는 게 아닙니다.

주먹->가위->가위->가위->가위->...로 바뀌는겁니다. 그래서 눈치를 못채신거고요.

0

Taejin Kim님의 프로필

Taejin Kim

질문자

2023.07.12

제가 useEffect를 잘 못 이해하고 있는 걸까요?

제 예상은 아래와 같습니다.

setInterval 이 실행되고 changeHand를 하면( 주먹->가위)로 바뀐 후 재렌더링(changeHand안에 setState 있기 때문에)이 되고

재렌더링 이후, changeHand(interval 이후) 다시 호출되어 (가위 -> 보)로 바뀜.

////////////////////////////////////////////////////////////////////////

useEffect(() => { interval.current = setInterval(changeHand, 100);

위에서 changeHand가 100ms마다 실행 되는데 강사님 말대로 주먹 -> 가위 -> 가위 .. 이런 식이라면

  const [imgCoord, setImgCoord] = useState(rspCoords.바위);

위의 imgCoord가 setImgCoord()로 인해 바뀌었어도 계속 rspCoords.바위인 형태로 계속 유지 된다는 건데 이 부분이 제대로 이해가 안 갑니다

 

useEffect에서 []안에 넣지 않았으므로 changeHand가 예전 참조값을 갖고 있어서 changeHand에서 갖고 있는 imgCoord도 첫 참조값인 바위입니다. 그러니까 changeHand는 평생 주먹을 가위로만 바꾸고 있는 겁니다.

[] 안에 값을 넣는 행위는 그 함수를 최신으로 갱신하는 겁니다. 자바스크립트의 클로저 문제가 딱 이 경우입니다.

Taejin Kim님의 프로필

Taejin Kim

질문자

2023.07.13

하지만 강사님 두가지만 더 질문하겠습니다

  1. useEffect에서 changeHand에는 setImgCoord()가 있습니다. 제가 알기로 클로저는 외부 변수가 바뀌면 바뀐 외부변수를 참조하는 걸로 알고있습니다. 위의 경우는 setImgCoord()로 imgCoord를 바꿔도 그 바뀐 것을 참조하지 못하는 이유가 뭔가요? setImgCoord()가 비동기이기 때문인가요?

  2. 강사님께서 예전 질문에 대한 답으로 "클로저 문제는 반복문과 비동기 함수가 있어야 발생합니다." 라고 말씀하셨는데 클래스로 만든 rsp의 componentDidMount()에서는 반복문은 존재하지 않는데 클로저 문제라고 하셔서 그 부분이 이해가 안됩니다.

    image

    "const {imgCoord} = "에서 imgCoord가 계속 "RspCoord.바위"를 가지고 있기 때문에 문제가 된다고 생각하고 이는 반복문과는 관련이 없어 보입니다. 하지만 강의에서 클로저 문제라고 언급하셨기에 헷갈려서 질문드립니다

 

 

https://www.inflearn.com/questions/343852/%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%95%A8%EC%88%98-%EA%B4%80%EB%A0%A8-%EC%A7%88%EB%AC%B8%EC%9D%B4-%EC%9E%88%EC%8A%B5%EB%8B%88%EB%8B%A4

imgCoord가 계속 바위를 참조하고 있는 겁니다. 그래서 setImgCoord(rspCoords.가위);가 실행되는 것이고요. 외부 변수를 참조하고 있는 것은 맞는데 최신 RSP가 아니라 과거의 RSP를 참조하고있는게 문제죠. 과거의 RSP안에 과거의 changeHand가 과거의 imgCoord를 참조하고 있는데, RSP가 최신으로 바뀌어봤자 useEffect에서 참조를 갱신하지 않았으니 과거의 changeHand를 참조해서 과거의 imgCoord를 참조하는 겁니다.

클로저는 함수랑 외부 변수가 있으면 클로저고요. 그게 주로 문제가 되는 상황은 비동기 함수 안에 있을 때인 겁니다. 사람들이 흔히 말하는 '클로저 문제'가 그것이고요. 실제로는 참조때문에 발생하는 문제는 다양합니다. 반복문과 비동기함수가 없어도 참조 문제는 발생할 수 있고요. 사람들이 말하는 '클로저 문제'는 반복문+비동기함수 문제입니다.

함수가 리렌더링되면 새롭게 RSP 함수가 실행됩니다. 근데 useEffect, useCallback, useState는 갱신을 안해주면 과거의 RSP함수의 값을 참조하고 있습니다. 매번 새로운 값을 생성해서 참조하면 성능이 느려지고 리렌더링도 빈번해지니까요. 즉 새로운 changeHand 함수는 애초에 호출이 되지를 않습니다. 과거 RSP의 changeHand가 호출되고있는겁니다. 그리고 과거 RSP가 클로저에서 참조하는 컨텍스트가 돼서 메모리 정리가 안 되는 것이고요.