문제점을 이해하기 위해
몇 가지 console.log문을 일단 추가해봅시다
위치는 QuestionTimer.jsx 파일이고
timeout을 설정하는 Effect에
하나 추가할 겁니다
SETTING TIMEOUT이라고 작성해줍니다
그리고 이것을 두 번째 useEffect 호출에 복사해서 붙여넣을 겁니다
interval을 설정하는 위치입니다
그리고 여기에 SETTING INTERVAL을 작성합니다
이제 저장하고 다시 불러와서 콘솔을 열면
로그 두 번 나타나는 것을 알 수 있습니다
그러나 이는 strict mode (엄격 모드)에 있기 때문에 일어나는 현상인데요
엄격 모드는 메인 JSX파일 안에 있는데요
이 앱 자체가 StrictMode에 둘러싸여 있습니다
그리고 엄격 모드에서는
모든 컴포넌트 함수를 보이지 않게 두 번 호출합니다
이것은 특정한 문제를 잡아내는 데에 도움이 됩니다
새로고침을 하면 또 확인할 수 있는 점은
타이머가 만료되면서 아무 일도 일어나지 않습니다
그러나 얼마 후에 새로운 타이머가 설정되지만
새로운 interval은 설정되지 않습니다
여전히 오래된 로그만 있습니다
그런데 이 타이머는 계속해서 초기화 됩니다
SETTING TIMEOUT 앞에, 여기 증가하고 있는
이 숫자를 통해 이해할 수 있는 것은
막 2에서 3으로 변경되었습니다
명백하게 여기 Effect 함수가
계속해서 실행된다는 것입니다
그런데 QuestionTimer 컴포넌트는
퀴즈가 렌더링 될 때 한번만 렌더링되어야 하지만
그 후에 재생성되지 않습니다
물론, Quiz 컴포넌트는 답변이 선택될 때마다
다시 렌더링됩니다
QuestionTimer의 컴포넌트 instance (인스턴스)는 변경되지 않습니다
이는 예전 JSX 코드의 일부였고
현재 새로운 JSX 코드의 일부입니다
그래서 이론상으로 이 Effect 함수는 재실행되어서는 안됩니다
예를 들어 interval이 설정된 이 Effect 함수는
다시 실행되지 않습니다
로그에서 보이는 것처럼
이는 그저 다시 실행된
timeout Effect 함수입니다
그래서 이 Effect 함수가
계속해서 실행되는 사실은
여기 의존성과 연관되어 있음이 틀림없습니다
의존성 중 하나는 timeout 입니다
이값은 절대 바뀌지 않습니다
이는 Effect 함수가 다시 작동되도록 할 수 없습니다
배우신 것처럼
주변의 컴포넌트 함수가 다시 실행되거나
의존성의 값이 바뀌지 않으면
Effect 함수는 다시 실행되지 않습니다
그리고 timeout의 경우 그럴 일이 없다는 것입니다
항상 10,000이 되도록 설정되어 있습니다
그러면 분명 여기 이 함수가 변경된 것이 틀림없습니다
그것이 여기에서 유일하게 가능한 설명이 되겠습니다
그리고 이것이 여기서 일어나는 일입니다
전 섹션에서 배우신 것처럼
자바에서 함수는 값이자 객체입니다
그리고 여기처럼 함수가 생성되면
JSX 코드가 평가될 때
생성되고 있는 메모리 안의 새로운 객체가 됩니다
전과 완전히 똑같은 로직과 코드를 포함하더라도
이는 기술적으로 여전히 메모리 내 새로운 값입니다
이 Quiz 컴포넌트 내의 JSX 코드가
재평가 될 때마다
새로운 함수가 여기에 생성됩니다
그리고 이 JSX 코드가 이 Quiz 컴포넌트 안의 상태가
업데이트 될 때마다 재평가됩니다
그리고 그 업데이트는 유저가 답을 고를 때마다 작동합니다
이제, 이를 우회하기 위해선 할 일이 있습니다
지난 섹션에서 또한 배운 것과 같이
새로운 hook (훅)을 사용해야 합니다
이는 의존성이 변경되어 필요하지 않는 한
함수가 재생성 되지 않도록 해줍니다
여기엔 useCallback 훅을 사용해야 합니다
이 훅을 이용하여 여기 이 함수를 감싸겠습니다
이제 훅의 규칙을 위반하지 않도록 하기 위해
그리고 끼워 넣은 코드 안에서 훅을 사용하기 위해
여기 handleSelectAnswer 아래쪽으로 올라가서
handleSkipAnswer라고 불리는 새로운 상수를 추가합니다
이것이 타이머가 만료되면 일어나는 현상이기 때문입니다
그리고 여기에 useCallback의 반환 값을 보관할 겁니다
그리고 이 함수를 여기에 감싸겠습니다
그 함수는 여기 아래에 있는 함수입니다
여기서 잘라내서
이처럼 useCallback을 감쌉니다
그러고 나서, 이전 섹션에서 배우셨 듯이
useCallback은 또한 의존성 배열을 필요로 합니다
이 배열은 여기서 사용될 지 모르는 모든 의존성을 나열합니다
이제 여기에 의존성을 사용합니다
handleSelectAnswer 함수를 사용하고 있습니다
그래서 기술적으로 이를 추가할 필요가 있는 겁니다
이 배열에 의존성으로 추가합니다
왜냐하면 이는 여기 컴포넌트 함수에서
생성된 값이므로
이는 속성과 상태에 의존합니다
그러므로 간접적으로 이 함수는
속성과 상태에 의존하고
여기에 useCallback도 사용해야합니다
그리고 handleSelectAnswer를 생성해야 합니다
여기 이 함수를 useCallback으로 감싸는 방식입니다
그리고 이 의존성 목록 또한 추가해야 합니다
그러나 여기 목록에는 아무것도 추가하지 않아도 됩니다
왜냐하면 이 handleSelectAnswer 함수에서
상태나 속성 그리고 이에 의존하는 다른 어떠한값도
사용하고 있지 않기 때문입니다
이와 같은 함수를 업데이트하는 상태는
여기에 추가될 필요가 없습니다
리액트가 그들이 절대 바뀌지 않도록 보장할 겁니다
이제 지금까지 한 것들로
주변의 컴포넌트 함수가 다시 실행되었다고 해서
재생성 되지 않도록 함수를 설정해줬습니다
그리고 이제 timeout에 전달한
handleSkipAnswer가 되었습니다
그렇게 하고 다시 불러오게 되면
timeout과 interval이 초기에 설정되는 것을 볼 수 있습니다
그 후가 아닌 초기입니다
새로운 질문으로 넘어가면서
timeout이 재설정되지 않습니다
이제 아직 타이머 만료 후에 있는 이상한 정지 기간이 존재합니다
이 문제는 다음 답변이나 질문으로
바로 넘어가지 않는 상황이었습니다
이 부분을 다음 강의에서 다루겠습니다