이후 리팩토링이 이뤄지며 최종 코드와 다를 수 있음.
⚠️ 문제상황
WebSocket에서 JOIN 이벤트를 수신한 뒤 Zustand store의 상태를 업데이트하고, 매칭 화면에서 useEffect로 해당 상태 변화를 감지해 채팅방 화면으로 이동하도록 구현했다.
matchingStore.tsx
handleMessage: (data) => {
if (data.event === "JOIN") {
set({
matchingStatus: "COMPLETE",
chatRoomId: data.payload.chatRoomId
});
}
};
index.tsx (matching)
useEffect(() => {
if (matchingStatus === 'COMPLETE') {
router.replace(`/(main)/${chatRoomId}`);
}
}, [matchingStatus]);
하지만 실제로는:
- WebSocket 이벤트는 정상적으로 수신 됨
- store의 matchingStatus도 "COMPLETE"로 정상 변경됨
- useEffect도 실행됨
- 그런데 router.replace()에 사용되는 chatRoomId가 null이어서 화면 전환이 일어나지 않음
🔎 원인
문제의 원인은 근본적으로는 useEffect 의존성 설계 미스였다.
Zustand에서 두 상태를 set()함수로 한 번에 업데이트 하기 때문에 useEffect 실행 시점에는 matchingStatus와 더불어 chatRoomId도 함께 반영되어 있을 것이라고 가정하였고 따라서 useEffect 내부에서 두 상태를 모두 사용하면서도 한 상태만 의존성 배열에 넣는 안일한 코드를 작성하였다.
☑️ 해결
useEffect에서 사용하는 두 변수 모두의 변화를 감지하도록 chatRoomId도 의존성 배열에 추가한다.
useEffect(() => {
if (matchingStatus === 'COMPLETE' && chatRoomId) {
router.replace(`/(main)/${chatRoomId}`);
}
}, [matchingStatus, chatRoomId]);
또는 useEffect의 실질적인 실행 트리거를 명확히 해서 문제를 해결할 수도 있다. 문제 상황이 발생했을 당시 코드에서는 라우팅에 실질적으로 필요한 상태는 chatRoomId의 상태변화였다.
⚠️상태와 실제 행동 조건이 분리되어 있던 상황이었다.
따라서 matchingStatus가 아닌 chatRoomId의 상태변화를 관찰하게 하면 원하는 대로 동작하게 된다.
useEffect(() => {
if (chatRoomId) {
router.replace(`/(main)/${chatRoomId}`);
}
}, [chatRoomId]);