끝난줄 알았는데 끝나지 않았던 SSE의 저주.. 사실 고치고 나서 새로운 버그를 발견했다고 하면 믿으시겠나요? ㅎ 그냥그냥 sse쓰지마십셔
요토가 떠먹여준 버그 픽스 + 블로그 입니다. 아주 이슈와 PR에 야무지게 작성해줘서 이렇게 날먹해도 되나 싶습니다ㅎ.. 암튼 요수리마수리토깽에게 치얼스✨
<고치기 전>
<고친 후>
고치기 전에는 빈번하게 채팅이 늦게 올라오는 경우가 발생했다. 정확히는, 메시지를 수신받은 후 사용자가 스레드 리스트 컴포넌트의 state를 변경시키는 행동(채팅창에 무언가를 입력, 공지 여부 체크박스 선택/해제)을 할 때에서야 비로소 반영되지 않았던 최신 메시지가 반영되는 문제이다.
SSE에서 채팅창을 갱신하기 위해 사용하는 setQueryData
의 경우, 함수 형태로 사용할 때 기존 데이터(oldData
)와 새롭게 갱신할 데이터(newData
)의 참조가 서로 달라야 한다. 더 어렵게 이야기하면 immutable하게 변수를 관리해야만 한다. 그렇지 않을 경우, tanstack-query에서 새로운 값으로 갱신되었음을 감지하지 못해 업데이트가 일어나지 않을 수 있다고 한다.
기존 방식의 경우, old
에 직접 변경사항을 내고 old
를 반환했기 때문에 참조가 변하지 않는다.
<고치기 전>
queryClient.setQueryData<InfiniteData<ThreadsResponse>>(
['threadData', teamPlaceId],
(old) => {
if (old) {
old.pages[0].threads = [newThread, ...old.pages[0].threads];
return old;
}
},
);
<고친 후>
oldData
에서 아예 새로운 변수인 newData
를 만들어 이를 반환해 참조가 변하도록 변경한다.
queryClient.setQueryData<InfiniteData<ThreadsResponse>>(
['threadData', teamPlaceId],
(oldData) => {
if (oldData) {
const newFirstPageThreads: ThreadsResponse = {
threads: [newThread, ...oldData.pages[0].threads],
};
const newData = {
pageParams: oldData.pageParams,
pages:
oldData.pages.length === 1
? [newFirstPageThreads]
: [newFirstPageThreads, ...oldData.pages.slice(1)],
};
return newData;
}
},
);
이러면 버벅거리는 문제는 해결된다 ^-^
새로운 버그는 재연결시 채팅데이터 조회요청을 보내는 이 부분에서 발생하는 것으로 유추되는데, 재연결과 채팅 송신이 겹쳐지면 보낸쪽에서 채팅을 볼 수 없는 문제…
eventSource.addEventListener('connect', () => {
queryClient.invalidateQueries([['threadData', teamPlaceId]]);
});