이제 퀴즈 앱이
조금씩 모양을 잡아가고 있습니다
여기 게임 종료 화면을 만들기 전에
이용자들에게 알려줘야 할 내용이 있습니다
답을 맞춘 질문의 숫자 등의 정보입니다
그걸 하기 전에, 이 퀴즈에 추가적인 기능을 추가하고 싶습니다
바로 모든 질문에 대해서 답을 하기 위해
한정된 시간이 주어지도록 하는 것입니다
예를 들어 각 질문에 15초를 줄 수 있습니다
그리고 작은 진행 표시줄을 하나 넣고 싶습니다
시간이 만료되어 가면서 천천히 줄어들 것입니다
그리고 타이머가 만료된다면
다음 질문으로 넘어갈 것입니다
답하지 못한 질문은 넘기는 것입니다
즉 사용자가 제한 시간 안에 답하지 않으면
아무 답변도 등록되지 않을 겁니다
이를 적용하기 위해서 이제 useEffect를 활용할 겁니다
하지만 당연하게도, 보이시다시피
이 Quiz 컴포넌트는 이미
복잡하고 클 대로 커졌습니다
그러기에 이 타이머를
완전히 새로운 컴포넌트에 맡길 겁니다
QuestionTimer 컴포넌트 파일을 추가할 거고요
파일 이름은 QuestionTimer.jsx입니다
그 파일 안에서
QuestionTimer라는 새로운 컴포넌트 함수를 내보내겠습니다
이 파일의 목적은
이 진행 표시줄을 나타내는 것입니다
표준 progress 요소를 사용합니다
이 컴포넌트는
진행 표시줄을 나타내는 것 외에도 용도가 있습니다
바로 진행 표시줄을 관리하는 것입니다
시작해 보겠습니다
question time이라는 ID를 부여합니다
형식 편집을 위함입니다
그러고 나서 몇 가지 로직을 추가해 줍니다
일정 시간 후에 만료되는 타이머를 설정하기 위해
빌트인 setTimeout 함수를 이용합니다
이전에 사용한 적이 있습니다
이제는 얼마 동안의 시간 후에
타이머가 만료되는지 정의해야 합니다
그리고 여기, QuestionTimer가
구성 및 변경이 가능한 컴포넌트 였으면 좋겠습니다
즉 고정된 시간을 이 컴포넌트에 설정하지 않고
컴포넌트 내에 정의될 수 있게 하되
이 QuestionTimer 컴포넌트가 사용되고 있는 위치입니다
그러므로 속성 객체의 구조를 분해해서(destructure)
timeout 속성을 넣어줍니다
바로 여기 타이머에 의해 설정되는 timeout입니다
그러면 예를 들어
15000밀리초는 15초가 됩니다
그리고 타이머가 만료되면
부모 컴포넌트에 알려야 합니다
그래서 만약 Quiz 컴포넌트에서 QuestionTimer를 사용한다면
다음 질문으로 넘어가야 한다고
통지하게 됩니다
왜냐하면 Quiz 컴포넌트가
활성화된 질문들을 관리하는 곳이기 때문입니다
그러므로, QuestionTimer는 새로운 속성을 받아야 합니다
이는 타이머가 완료될 때 타이머 안에서부터
호출해 와야 하는 함수입니다
예를 들어 onTimeout으로 하겠습니다
그래서 여기에서 이 타이머 안에서 onTimeout을 불러옵니다
그리고 이 코드를 줄여주고 그냥 onTimeout을 설정할 수도 있습니다
그러면 timeout이 만료될 경우 브라우저에 의해서
불려와야 하는 함수가 됩니다
지금까지는 useEffect를 사용하고 있지 않습니다
왜냐하면 이것이 부수 효과임에도 불구하고
지금의 단계에선 useEffect의 사용을
필요로 하는 Effect가 아니기 때문입니다
왜냐하면 지금은 무한 루프의 위험성이 없고
여기선 컴포넌트의 상태를 업데이트 하지 않고
또 아직 이용할 수 없는 요소와
상호 작용을 하려고 하지 않기 때문입니다
그러므로 이 코드는 그대로 둬도 됩니다
여기에서 해야 하는 작업이 하나 더 있습니다
바로 이 진행 표시줄을
업데이트 하도록 작업하는 것입니다
그러므로 interval이 필요한데
이 interval은 매 수 밀리초 마다 코드를 실행할 겁니다
그리하여 타이머를 업데이트 할 수 있도록 합니다
그러므로 이 진행 표시줄이 수 밀리초마다
상태가 변경될 때 다시 렌더링되도록
이 컴포넌트에 상태가
몇 개 필요합니다
그래서 리액트에서 useState를 가져와서
여기 등록된 상태가
남은 시간이 되겠습니다
remainingTime을 넣고 setRemainingTime을 설정합니다
상태의 초기값은 여기 timeout 값이어야 합니다
그게 이 타이머의 남은 시간이기 때문입니다
시점은 이 컴포넌트가 렌더링 될 때입니다
이것을 매 10 밀리초마다 업데이트해 보겠습니다
꼭 10밀리초는 아니어도 비슷하면 됩니다
배우신대로 당연히 set interval이 필요합니다
왜냐하면 10 또는 100 또는 어떤 숫자의 밀리초건 간에
여기에 있는 함수가 실행해야 하는
값을 정의해야 하기 때문입니다
그리고 이 함수에서, 남은 시간을
100 밀리초씩 빼며 업데이트 하고 싶습니다
아니면 여기서 원하시는 빈도로 정하시면 됩니다
이 상태값은 이전에 보관된 값을
기반으로 업데이트되기 때문에 함수 형식이 필요합니다
그리고 새로운 값은 prevRemainingTime에서
100 혹은 설정한 시간을 빼면 됩니다
그리고 이는 당연하게도 무한 루프를 생성하게 됩니다
상태를 업데이트 하기 때문인데요
이는 여기의 컴포넌트 함수를 재실행할 것이며
새로운 interval을 만들게 됩니다
바로 상태를 다시 업데이트 하는 interval입니다
그러면 이 컴포넌트 함수를 호출하는
다수의 interval들이 생기게 됩니다
그리고 그것은 우리가 원하지 않는 상황입니다
이제 바로 useEffect를 사용해 보겠습니다
이제 이 코드를 useEffect로 감쌀 수 있는데
이 Effect 함수를 useEffect와 이 의존성 배열로
전달하면 됩니다
그리고 이 setInterval 코드를
useEffect 안으로 옮겨야 합니다
이 코드가 계속 재실행되지 않도록 하기 위해서입니다
대신 의존성이 바뀔 때만 재실행되도록 하겠습니다
그리고 여기에는 추가해야 하는
의존성이 없습니다
여기에는 속성과 상태값만
추가하는데
이 Effect 함수에는 그 둘 중에 어느 것도 사용하지 않습니다
이제 timeout 코드를
useEffect로 감싸야 합니다
그렇게 하지 않으면 남은 시간을
100 밀리초마다 업데이트 할 때
이 컴포넌트 함수가 재실행되고
이 타이머가 재생성되기 때문입니다
그렇게 되면
다수의 타이머가 실행될 것입니다
그러므로 여기에 또 다른 useEffect 호출을 추가하고
이 코드를 useEffect 함수 안으로 옮겨 줍니다
그리고 여기에 이제 의존성을 추가할 필요가 있습니다
이유는 이 Effect 함수에 사용되는
두 가지의 의존성이 있기 때문입니다
여기에 두 개의 속성을 사용하는데
이는 timeout과 onTimeout 속성입니다
함수이지만 여전히 속성로 취급됩니다
그래서 여기에 timeout과 onTimeout을 의존성으로 추가합니다
이는 이 Effect 함수가 만약 의존성이 변경될 경우에
재실행 되도록 하기 위해서 입니다
그리고 이렇게 하는 이유는
부모 컴포넌트가 QuestionTimer의 timout이
변경되어야 하는 지를 결정하기 때문입니다
그리고 타이머를 초기화 하고 다시 설정해야 합니다
이 상태로 이 작업이 끝난 것은 아닙니다
코드를 나중에 수정할 것입니다만
일단은 좋은 시작 상태입니다
그리고 이제 RemainignTime을 이용해서
이 진행 표시줄을 업데이트할 수 있습니다
여기서 timeout 기간 동안 최댓값을 설정해야 합니다
timeout 속성에서 받는 것입니다
그리고 현재의 값은 RemainingTime이 되어야 합니다
여기서 중요한 점은
이 RemainingTime은 초기에 여기 이 timeout값입니다
그러나 이 값은 시간이 만료되어 가면서 점점 적어집니다
이걸로 이제 Quiz 컴포넌트로 돌아가도 됩니다
그리고 거기에 이 QuestionTimer 컴포넌트를 가져오겠습니다
위치는 QuestionTimer.jsx입니다
방금 그 컴포넌트를 여기에 사용해줍니다
그리고 그것을 그 question 디브 안에 여기의 question text
바로 위에 표시하고 싶습니다
즉 여기에 이 QuestionTimer를 내보내겠습니다
그리고 이제 timeout과 onTimeout 속성을
설정해야만 합니다
timeout은 그 타이머의 기간이 될 것입니다
밀리초 단위가 될 것이고요
10초 길이의 타이머를 쓰고 싶다면
여기에 10000을 넣어줘야 합니다
왜냐하면 1000밀리 초는 10초이기 때문입니다
그리고 onTimeout은 당연하게도 타이머가 만료되면
재실행되는 함수가 되어야 합니다
그리고 여기에 handleSefectAnswer를 실행할 수 있습니다
그러나 값은 null (널)로 전달하겠습니다
여기 이 함수 혹은 UserAnswers 배열로
새로운 entry (엔트리)를 넣어줍니다
그러나 추가하는 값은 널로 해서
이용 가능한 답변으로부터
오는 답변이 아니도록 하되
해당 질문에 그 어떠한 답변도 선택되지 않았다고
알려주는 placeholder (플레이스홀더)가 되도록 하겠습니다
그렇게 해서 저장하고 새로고침을 하면
만료되는 타이머가 보이고
만료된 후에는 아무 일도 일어나지 않습니다
시간이 좀 지난 후에는 작동하지만
만료되자마자 바로 작동하지는 않습니다
대신에, 보이시는 것처럼, 다시 불러오면
이 타이머가 만료되고
다음 문제로 넘어갈 때까지
어느 정도를 기다려야 합니다
또한 보이시는 것처럼 여기에 타이머가 없습니다
초기화 되는 대신에 빈 상태로 유지됩니다
시간이 좀 지나면 만료되기는 합니다
보이지는 않지만 실행은 되고 있는 것 같습니다
만료된 타이머가 아직도 실행된다는 뜻이므로
이같이 고쳐야 할 부분이 조금 있습니다