단순한 라이브 채팅이 이번 릴리스를 망칠 뻔했습니다
기본적인 라이브 채팅 시스템이 제 최신 릴리스를 거의 망칠 뻔했습니다.
단순해 보입니다. 주변 사용자를 보여주고 대화하게 하면 되니까요. 하지만 기술적인 실체는 훨씬 더 어렵습니다. 라이브 채팅을 지오로케이션(geolocation) 및 평점 시스템과 연결해야 했습니다. 이로 인해 내부적으로 엄청난 복잡성이 발생했습니다.
아키텍처:
• 프론트엔드: React 및 TypeScript. • 백엔드: Express와 WebSockets를 사용한 Node.js. • 데이터베이스: 사용자 및 평점을 위한 PostgreSQL. • 캐시: 활성 세션 및 접속 상태(presence)를 위한 Redis.
지오로케이션 문제
위치 기반 사용자 매칭은 쉽지 않습니다. 다음과 같은 많은 예외 상황(edge cases)을 처리해야 했습니다:
- 위도 및 경도 저장.
- 거리를 쿼리하기 위한 Postgres 확장 기능 사용.
- 위치 권한을 거부하는 사용자 처리.
- 앱을 열지 않은 상태에서 사용자가 이동할 때 발생하는 오래된 데이터 관리.
다시 이 작업을 한다면, 위치 수집과 매칭을 서로 다른 서비스로 분리하겠습니다.
라이브 채팅의 현실
WebSockets는 실시간 기능을 제공하지만, 혼란도 함께 가져옵니다. 저는 서로 다른 서버 간에 메시지를 전송하기 위해 Redis pub/sub를 사용했습니다.
가장 어려웠던 부분은 다음과 같습니다:
- 사용자가 예기치 않게 연결을 끊었을 때 연결 정리(cleanup).
- 사용자가 나간 채팅방으로 메시지가 전송되는 것 방지.
- 유령 세션(ghost sessions)을 생성하지 않고 재연결 처리하기.
단순한 플래그(flags)를 사용하는 것보다 정식 상태 머신(state machine)을 사용하는 것이 더 낫다는 것을 배웠습니다.
평점 시스템
평점은 직접 만들기 전까지는 사소해 보입니다. 저는 다음 사항들을 보장해야 했습니다:
- 사용자가 동일한 세션에 두 번 평점을 남길 수 없음.
- 프로필 조회 시 평점이 빠르게 집계됨.
- 앱 전체에서 데이터 일관성 유지.
중복을 방지하기 위해 세션 ID에 유니크 제약 조건(unique constraints)을 사용했고, 성능을 위해 평균값을 캐싱했습니다.
배운 점
오늘 이 시스템을 다시 구축한다면, 세 가지를 바꾸겠습니다:
- 매칭과 채팅을 별도의 모듈로 분리.
- WebSocket 연결 상태에 대해 명시적인 모델링 사용.
- 첫날부터 채팅과 매칭을 위한 더 나은 로그와 메트릭 추가.
소프트웨어를 구축하는 것은 일련의 작은 결정 과정입니다. 이러한 결정들이 시스템이 깔끔할지 아니면 취약할지를 결정합니다.
라이브 채팅이나 매칭 시스템을 구축해 보신 적이 있나요? 여러분이라면 무엇을 다르게 하시겠습니까?
출처: https://dev.to/jaeger974/simple-live-chat-almost-sank-this-release-2pn7