또 카운터 함수. 인터벌 함수가 실행되어야 하지 않는 경우에는 00:00:00 값이 리턴되어야 하는데, 한 번 실행되어서 값이 변경되었다가 인터벌이 종료되는 것 같다. 그리고 나서 다른 정상 동작해야하는 컴포넌트를 마운트하면, 제대로 트리거가 되지 않는지 한 번 새로고침을 해주어야 정상 동작한다.
문제가 뭔지 되짚어보자..
먼저 타이머 함수가 적용되고 있는 컴포넌트 함수는 아래와 같다. useGetTimeRemain이라는 훅에 종료시간을 전달해서 timeRemain이라는 값을 가져오고 있다. 그 값을 그대로 렌더링하고 있다.
이 useGetTimeRemain이라는 훅은 아래와 같이 생겼다. 먼저 현재 시간을 상태값으로 관리하고, finishedAt - realTime 을 한 timeRemain을 상태값으로 관리하고 있다. timeRemain이 state이기 때문에, 실시간으로 변경하면 화면에 그대로 렌더링이 된다.
여기서 또 useInterval이라는 훅을 호출해서, realTime을 1000밀리세컨드에 한 번씩 새롭게 업데이트하고 그에 따라 timeRemain도 변경한다. realTime을 주시하다가 남은 시간이 0이하로 내려가면, 인터벌을 종료하도록한다.
일단 Interval 훅 자체에는 큰 문제가 없는것으로 생각이 된다. 조건을 확인하여 해당 조건에 부합할때만 interval이 실행이 되고, 또 컴포넌트가 언마운트 되는 경우 해제도 되고 있다.
내가 생각한 문제점은 timeDiff라는 shouldContinue라는 조건 상태를 변경시켜주는 부분이 제대로 적용이 되지 않아서 인 것 같다. 먼저 컴포넌트가 렌더링되는 시점에 setShouldContinue 조건을 업데이트 시켜주고, 언마운트 되는 경우에 이를 다시 초기화시킬 필요가 있었다. 이런 기준을 가지고 아래와 같이 리팩토링 후 정상 동작을 확인했다.
먼저 기존과 동일하게 time 과 shouldCountinue는 상태값으로 등록해주었다. 다른 함수 내에서 이 값을 업데이트할 필요가 있었기 때문이다. 그리고 useInterval 훅을 통해 시간을 업데이트하고 카운트를 해주는 updateTime 이라는 함수를 만들었다. 그냥 기존에 useInterval로 넘겨주던 훅을 함수화 했다고 보면 된다.
참고로 기존에는 timeDiff라는 시간차를 밖에서 const로 선언해주고 있었는데, 이 부분 때문에 시간차 업데이트가 제대로 되지 않는 것 같았다. 이 함수에서는 매번 1000 ms 마다 실행이 될 때 이 값을 종료시간 - 현재시간으로 계속 계산하고 있다. 또 화면에 표시해줄 hours, minutes, seconds 같은 경우도 매번 새롭게 계산하여 업데이트한다. 이를 매초 상태로 저장한다.
만약 이렇게 계산을 진행하다가 남은 시간이 없는 경우에는, interval의 조건인 shouldContinue를 false로 바꿔주고, 시간도 종료시간인 00:00:00으로 초기화시킨다.
원래와 동일하게 interval을 1000ms 로 실행시켜주고, ussEffect 훅을 통해서 컴포넌트가 첫 마운트 되자마자 일단 updateTime 함수를 실행시켜서 화면에 업데이트시킬 남은 시간을 먼저 계산해버린다.
그 다음 부분이 조금 어렵고 헷갈리는 부분인데, 컴포넌트의 마운트 언마운트와 이 문제가 관련이 있다고 생각해 useEffect 훅에 관하여 챗gpt에게 코드 리팩토링을 요청했다. 그랬더니 위와 같이 setShouldContinue 함수를 마운트시에 다시 true로 설정하고, unmount 시에 false로 설정하도록 고쳐주었다.
앞서 지속적으로 문제가 되었던 shouldContinue 는 이 코드에서 컴포넌트가 언마운트가 될 때 false 값으로 변하도록 하고 있다. 만약에 마운트시에 shouldContinue 상태를 true로 변경해주지 않으면, 이전 렌더링에서 상태가 종료되어 shouldContinue 가 false로 변해있을 경우 이 상태값을 참조해 interval이 재대로 실행이 되지 않았던 것이다. 그래서 저 상태에서 마운트시에 실행되는 setShouldContinue(true)를 지워버리면, 종료된 상태의 컴포넌트를 마운트 시켰다가, 다시 나와서 진행중인 상태의 컴포넌트를 마운트 시켰을 때 카운트가 정상적으로 동작하지 않고 멈춰버린다.
여기서 한 가지 나의 의문점은, 분명 처음 이 useGetTimeRemain 훅을 호출할 때 아래와 같이 초기값이 설정되도록 하였는데, 이게 매번 갱신이 되지 않는 것인지, 어떻게 이전 렌더링의 값을 기억을 하는것인가에 대한 부분이다. 나의 생각으로는 useGetTimeRemain 이라는 훅도 하나의 함수이기 때문에 매번 재실행이 되면서 이 안에서 선언된 값도 다시 초기화가 될것 같았기 때문이다.
그리고 shouldContinue 값을 직접 출력하며 확인해본 결과, 나의 예상과는 다르게 shouldContinue는 이전 마운트에서 변경된 false값을 그 다음 렌더링에서도 기억하고 있었다. (충격)
내가 생각한 그 마운트와 언마운트에 관련된 문제가 맞았고, 그 이유는 클로저 때문이다. useState나 useEffect같은 리액트의 훅은 클로저의 원리를 기반으로 하여 구현되었다. 클로저를 통해 값에 접근하고 이를 토대로 업데이트를 하기 때문에 새롭게 마운트가 된다고 하더라도 이전 상태값을 계속 기억하게 되는 것이다. 따라서 useEffect를 사용할 때는 마운트, 언마운트시에 상태를 클리어해주는 로직이 필요한 경우가 있을 수 있다.
'프로젝트 > 데일리 옥션' 카테고리의 다른 글
# 작업일지 14,15 / 웹소켓 연결, 차트에 실시간 데이터 업데이트하기(Chart.js) (0) | 2023.02.24 |
---|---|
# 작업일지 12,13 / STOMP publish 요청 성공! + 에러 처리는..? (0) | 2023.02.22 |
# 작업일지 10 / 웹소켓, STOMP 프로토콜 (0) | 2023.02.18 |
# 작업일지 9 / Chart.js 적용, 경매가 유효성 검사 (0) | 2023.02.18 |
#8 작업일지 / interval함수 리팩토링, 게시글 상세페이지 통신 로직 작성 (0) | 2023.02.15 |