채팅 supabase realtime 리펙토링

JeongChan·2025년 7월 9일

Troubleshooting

목록 보기
4/6
post-thumbnail

임시 방편으로 setTimeout을 사용하였다

채팅방에서 뒤로 갔다가 다시 들어오면 생기는 "WebSocket is closed before the connection is established" 오류인데

이전 구독을 해제 하기위해 WebSocket이 비동기로 닫히는 시간이 필요해서 이다.


📌 과정

수정 전 코드

팀장 님의 의견으로 구조 자체를 바꾸기로 하였다.

Supabase Realtime은 서버에서 변경 사항이 생겼을 때만 클라이언트로 데이터를 밀어주는 역할만 해주면 될 것 같고, TanStack Query는 클라이언트의 캐시와 UI 상태를 관리하는 쪽이 적절하다

이 말은 즉

1. Supabase Realtime

  • 서버 → 클라이언트로 변경 이벤트(INSERT/UPDATE/DELETE)푸시(push) 해주는 메시지 브로커 역할.
  • 하지만 이건 클라이언트 상태를 직접 관리하진 않음.
  • 단순히 "메시지가 새로 생겼어!" 또는 "업데이트 됐어!"라고 알려주는 용도.

2. TanStack Query (React Query)

  • 받아온 데이터를 캐시에 저장하고, UI에서 사용할 수 있게 상태를 유지함.
  • 데이터를 불러오고(refetch), 갱신하고(invalidate) 하는 등 클라이언트에서 데이터 흐름을 관리하는 주체.

각자 맞는 역할로 분리하는 것이다. 각각의 기능의 본질을 알지 못하여 역할을 혼동하여 막 코드를 작성한 탓이다..

📚 문제:

잘못된 구조 (지금 구조)

  • roomId 바뀔 때마다 useEffect에서:
    • 이전 구독 제거
    • 잠시 기다림 (setTimeout)
    • 다시 구독
  • Supabase는 메시지 수신 + onMessageInsert 호출 → 로컬 상태를 수동으로 갱신
  • 이 흐름은 복잡하고 WebSocket 충돌 위험이 있음 (겪는 문제)

📚해결:

1. 메시지 목록 불러오기 (React Query)

2. Supabase Realtime 설정



📚 요약:

BeforeAfter
Supabase Realtime이 메시지를 수신한 후 직접 setState()를 호출하여 UI에 렌더링Supabase Realtime은 오직 invalidateQueries()만 수행
ChatMessages에서 메시지 상태(useState)를 별도로 관리useMessages() (React Query)로 상태 일원화

변경 후 코드

📚 핵심 교훈:

  • 실시간 시스템은 “감지 → 알림”, 캐시 시스템은 “상태 저장소” 라는 역할 분리가 명확해야 유지보수가 쉽다.
  • TanStack Query와 Supabase를 함께 사용할 경우, 각각의 책임을 명확히 나누면 성능과 UX 모두 향상된다.
  • UI는 가능한 한 React Query 캐시에서 바로 읽고, Supabase는 “변화가 생겼다”는 신호만 줘야 한다.

📌 마무리

각 기능에는 알맞는 역할 이 있다. 다른 모양에 다른 퍼즐을 끼우면 그 당시에는 괜찮을 지 몰라도 연쇄적인 문제가 발생한다.
해당 기능들을 알아야 구조를 잡을때 좋은 아키텍쳐가 나온다.
겉할기 식으로 알고 있으니 생기는 문제들이다.
왜 라는 생각을 끊임 없이 해야한다. 적당히 매몰되지 않고서 본질, 즉 메타를 인지를 잘하느냐 에 따라 능률, 성능, 속도가 향상 될 수 있다고 느꼈다.

profile
Development Notes

0개의 댓글