Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FE] 채팅 시 SSE에 의해 수신되는 내용이 늦게 반영되는 문제 해결 #960

Merged

Conversation

wzrabbit
Copy link
Collaborator

@wzrabbit wzrabbit commented Mar 23, 2024

[FE] 채팅 시 SSE에 의해 수신되는 내용이 늦게 반영되는 문제 해결

이슈번호

close #959

PR 내용

본 PR에서는 새로운 채팅을 입력할 때, 또는 SSE로부터 새로운 메시지를 수신받을 때 바로 새로운 채팅이 제대로 반영되지 않는 문제를 해결하였다.

  • 기존 채팅의 경우 SSE로부터 새로운 메시지를 수신했을 때 수신은 감지되나 채팅이 바로 업데이트되지 않고, 사용자가 채팅창에 새로운 내용을 입력하거나, 공지 체크박스를 작동시키는 등 채팅창과 상호작용을 한 경우에 그제서야 반응되는 문제가 있었다.
  • 본 PR 반영 이후에는 SSE로부터 새로운 메시지를 수신받았을 때 그 즉시 채팅창을 업데이트해 최신 채팅이 바로 보이도록 반영하였다.

원리 및 작동 화면은 하단의 코멘트에서 보충하여 설명한다.

@wzrabbit
Copy link
Collaborator Author

wzrabbit commented Mar 23, 2024

로컬 환경에서 SSE로 정해진 메시지를 보내주는 Node.js 서버를 실행하고, 2초마다 로컬에 메시지를 전송하도록 하여 테스트를 해 본 결과이다.

고치기 전

_2024_03_23_23_31_47_484.mp4
  • SSE 이벤트는 수신이 잘 감지되나(2초마다 콘솔에 뜬다), useFetchThreads 에 있는 쿼리는 실행이 즉각 되지 않아 채팅창에 변화가 없고, 채팅창에 무언가를 입력한 후에야 실행이 된다.

고친 후

_2024_03_23_23_38_02_917.mp4
  • SSE 이벤트 수신과 함께 useFetchThreads 에 있는 쿼리도 실행이 되어 즉각적인 채팅의 업데이트가 잘 반영되고 있다.
  • 로컬에서 날짜가 2024-12-31로 뜨는 거 버그 아니야, SSE 수신 시 데이터를 그렇게 짜놔서 그래

@wzrabbit
Copy link
Collaborator Author

wzrabbit commented Mar 23, 2024

문제 발생 원인

useSSE에서 채팅창을 갱신하기 위해 사용하는 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;
    }
  },
);

삽질하면서 알아낸 건데 이런 게 있는 줄은 몰랐다...

@wzrabbit
Copy link
Collaborator Author

wzrabbit commented Mar 23, 2024

Node.js SSE 서버로 간단하게 수신 테스트하는 방법

아까 위에서 Node.js 서버를 직접 로컬에서 실행시켜서 테스트했다고 했는데, 그 방법을 여기 공유해 보고자 함. 아쉽게도! msw에서 간단한 설정으로 무언가를 편리하게 할 수 있는 방법은 아니야. 쌩으로 서버 만들어서 테스트하는 방법이지. 다행히도 간편하게 해 볼 수 있는 라이브러리가 있더라고.

  1. npm init -y 를 입력해 package.json 을 생성한다. (팀바팀 폴더가 아닌 새로운 프로젝트에서!)
  2. 아래의 명령어를 입력하여 라이브러리를 설치한다.
npm install sse-fake-server
  1. fakeServer.js를 루트에 생성하고, 아래의 내용을 복붙한다.
const SSEServer = require('sse-fake-server');

SSEServer((client) => {
  setInterval(() => {
    client.send('대충 보내고 싶은 메시지');
  }, 2000);
});

간단하게 코드를 작성하기 위해 대충 보내고 싶은 메시지 라 썼지만, 실제로는 event:, data: 등으로 시작하는 메시지를 양식에 맞게 적어야겠지... 나는 이번 테스트를 위해 아래와 같이 작성했어.

client.send(
  `event: new_thread\ndata: { "id": ${Math.floor(
    Math.random() * 1234567
  )}, "type": "thread", "authorId": ${Math.floor(
    Math.random() * 1234567
  )}, "authorName": "MOCKSSE", "profileImageUrl": "https://vetsonparker.com.au/wp-content/uploads/2015/04/Rabbit-Facts.jpg", "isMe": false, "createdAt": "2024-12-31 00:30", "images": [], "content": "Fake SSE Server message from wizardrabbit. -- Time: ${new Date().toLocaleString()}" }\n\n`
);
  1. 밑줄 친 부분의 코드를 Ctrl + 왼클릭 하여 node_modules 내의 파일로 들어간다.
    image

  2. 블록설정한 부분의 코드를 res.write('data: ' + data + '\n\n');에서 res.write(data);로 변경한다. 스크린샷에서는 이미 변경이 완료된 모습이다.
    image
    이렇게 해야 event: 로 시작하는 메시지도 보낼 수 있기 때문.

  3. 메시지를 적었으면 터미널을 켜고 아래의 명령어를 입력해 준다.

node fakeServer.js

이렇게 하면 서버가 켜졌다는 메시지가 출력되며, 이후에는 SSE 서버 경로를 http://localhost:5555/stream 으로 변경하면 된다! 잘 작동하는지 확인해 보려면 주소로 직접 접속해 봐!
image

주의사항: 테스트할 때는 꼭 Bearer와 같은 인증 정보를 없앤 채로 테스트해! 안 그러면 CORS 관련 에러가 나더라!

복잡하지만 매번 PR쓰고 머지하고... 이 방법보다는 최소한 편해보여서 공유해 보고자 해

- 기존 로직의 경우 이전 값인 old의 값을 그대로 변경해 참조가 유지되었음
- 참조가 그대로 유지되면 tanstack-query는 값이 변경되지 않았다고 판단되어 갱신이 일어나지 않을 수 있음
- 따라서, 새로운 변수 newThread를 만들어 이를 반환하는 것으로 참조를 변경
@wzrabbit wzrabbit force-pushed the fix/채팅창-SSE-즉시-갱신-안되는-문제-해결 branch from f9c97b6 to 812ae4b Compare March 23, 2024 15:12
@wzrabbit wzrabbit marked this pull request as ready for review March 23, 2024 15:14
Copy link
Collaborator

@hafnium1923 hafnium1923 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋은 정보공유와 멋진 버그픽스 감사합니다.
요몇주간 바빠서 너무 늦게 확인한 것 같아서 미안하네,,,ㅠㅠ
언제든지 기능 피알올려줘도 돼! 최대한 빨리빨리 확인해볼게
수고 많았어 토끼야~!

@hafnium1923 hafnium1923 merged commit c5e7856 into develop Apr 3, 2024
1 check passed
@hafnium1923 hafnium1923 deleted the fix/채팅창-SSE-즉시-갱신-안되는-문제-해결 branch April 3, 2024 03:49
@pilyang pilyang mentioned this pull request Apr 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FE] 채팅 시 SSE에 의해 수신되는 내용이 늦게 반영되는 문제 해결하기
2 participants