Skip to content

찬의 고민과 해결

today-is-first edited this page Jun 4, 2025 · 2 revisions

고민과 해결 : 실시간 채팅 기능 설계 및 고도화

🧭 도입 배경

서비스 특성상 사용자 간의 실시간 소통이 중요하다고 판단하여, 채팅 기능을 필수로 구현하게 되었습니다.
단순한 메시지 전달을 넘어서 즉각적인 반응성과 안정성, 그리고 구조의 확장성까지 고려할 필요가 있었고, 이에 따른 기술적인 고민이 많았습니다.

Polling vs WebSocket

🧠 고민 1: 어떤 방식으로 실시간 통신을 구현할 것인가?

초기에는 Polling 방식과 WebSocket 방식 사이에서 선택을 고민했습니다.

슬라이드23

Polling 방식

  • 일정 주기로 서버에 메시지가 있는지를 확인합니다.
  • 구현은 간단하지만, 실시간성이 떨어지고 네트워크/서버 자원 소모가 큽니다.

WebSocket 방식

  • 최초 연결 이후에는 서버와 클라이언트가 양방향으로 지속적인 통신을 유지할 수 있습니다.
  • 메시지를 서버가 즉시 Push할 수 있어, 실시간 통신이 가능합니다.

실시간성이 보장되고 처음 연결 이후 웹소켓으로 업그레이드 되어 HTTP 헤더를 불필요하게 보내지 않아 효율적인 리소스 사용하는 WebSocket 방식을 채택하게 되었습니다.



🔍 기술 스택 선택: WebSocket API vs Socket.IO

슬라이드24

처음에는 HTML5 기반 WebSocket API를 고려했지만, 다음과 같은 이유로 Socket.IO 기반의 기술 스택을 선택하게 되었습니다.

WebSocket

  • HTML5 기반의 경량 프로토콜입니다.
  • 하지만 자동 재연결, 이벤트 네임스페이스, 브라우저 호환성 측면에서 직접 구현이 필요합니다.

Socket.IO

  • 자동 재연결, Fallback 지원, Room/Namespace 관리 등 고급 기능을 기본적으로 제공합니다.
  • Java 환경에서는 Netty-socket.io를 통해 비교적 간편하게 구축할 수 있습니다.

결과적으로 안정성과 유지보수 측면에서 Socket.IO가 적합하다고 판단하여 Netty-socket.io를 적용했습니다.



🧱 고민 2: 대량 메시지 로딩으로 인한 성능 저하

슬라이드25

초기에는 채팅방에 입장하면 모든 메시지를 한 번에 가져오는 구조였는데, 메시지가 많아질수록 클라이언트 렌더링 지연 및 사용자 경험 저하 문제가 발생했습니다.

해결책: Lazy Loading 적용

  • 최초 진입 시, 최근 메시지 일부만 불러오도록 변경했습니다.
  • 이후 사용자가 상단으로 스크롤할 때 이전 메시지를 순차적으로 로딩하도록 구현하였습니다.

이 방식으로 초기 로딩 속도를 개선하고, 사용자에게도 자연스러운 UX를 제공할 수 있었습니다.



🧠 고민 3: 네트워크 요청 중복 및 최신 데이터 미반영 문제

슬라이드27

React Query를 활용하여 중복 요청을 방지하고 캐싱 처리에 성공했지만, POST나 PATCH 등의 변화 이후에도 화면에 즉시 반영되지 않는 문제가 있었습니다.

해결책

  • mutate 이후 invalidateQueries를 호출하여 관련 캐시를 무효화하고 데이터를 재조회하도록 했습니다.

이를 통해 사용자에게 항상 최신 상태를 보여줄 수 있도록 개선하였고, 동시에 서버 부하도 최소화할 수 있었습니다.



⚙️ 고민 4: 과도한 이벤트로 인한 서버 요청 폭주

슬라이드28

입력창이나 버튼 클릭과 같이 사용자의 반복적인 액션이 발생하는 경우,
서버에 동일한 요청이 지나치게 많이 전송되는 문제가 있었습니다.

해결책: Debounce & Throttle 적용

  • 입력 및 클릭 이벤트에 Debounce를 적용하여 사용자의 행동이 멈춘 시점에만 요청을 전송하도록 제어하였습니다.
  • 일부 구간은 Throttle을 활용하여 일정 간격으로만 요청이 발생하도록 제한하였습니다.

이러한 최적화를 통해 서버 부하를 줄이고, 전체 시스템 안정성을 높일 수 있었습니다.