Skip to content

실시간 서비스 도전기

LSM edited this page Dec 14, 2022 · 3 revisions

팀 undefined의 실시간 서비스 도전기

undefined의 목표

  • 우리의 목표는 실시간성을 띄는 서버를 제작하여 사용자와 사용자 혹은 사용자와 서비스 사이의 상호작용강화하고자 했다.
  • 상호작용강화하는 방식으로 사용자의 흥미를 유발하고, 더 높은 참여도를 끌어낼 수 있을 것이라 판단했다.

undefined의 고민

  • 실시간성을 띄는 서버는 어떻게 만들어야 할까?
    • 우선, 실시간 서비스를 위한 대표적인 프토로콜로 웹소켓을 고민하였다.
    • 웹 소켓은 일부 구형 브라우저에서 지원하지 않고, disconnect 되었을 때 다시 연결을 시도하는 로직을 클라이언트에서 직접 구현해주어야 한다.
    • 그러나, socket.IO 라이브러리를 사용할 경우, 사용 환경에 따라 폴링을 사용하고, 연결이 해제되었을 때 자동으로 재연결을 시도하는 등 문제 상황에 대한 fallback이 잘 이루어져 있어서 문제가 없을 것으로 판단했다.

undefined의 진행

  • 소켓 서버를 활용하고자 했을 때, 가장 먼저 고민이 되었던 것을 라이브러리 결정이다. Spring 서버에서 웹 소켓 서버를 사용할 경우, 서버와 클라이언트에서 sockjs 라는 새로운 라이브러리를 사용해야 한다는 추가적인 부담이 있었고, socket 서버를 구현하기로 결정하는 과정에서 고려한 라이브러리 또한 socket.IO 였기 때문에 소켓 서버는 typescript와 socket.IO로 구현하는 것으로 결정하였다.
  • 소켓을 활용한다는 결정을 하고, REST API 서버에 몰려 있던 API의 일부를 소켓 서버로 migration 하기로 결정하였다.
    • 실시간성이 필요한 API들을 대상으로 migration을 진행하고자 하였으며, 이에 따라 키워드 생성, 키워드 참여, 유저의 접속 상태 표시, 스레드 생성 및 스레드 삭제 등의 API의 migration이 결정되었다.

첫번째 문제, 관심사 분리

문제 인식

  • 소켓 서버를 활용하면서 가장 먼저 문제로 인식되었던 부분은, 관심사의 분리이다.
    • 소켓 서버는 event driven 방식으로 웹소켓이 연결되었을 때, client에 대해서 각각의 이벤트에 대응하는 callback 함수를 달아주는 방식으로 코드를 작성하게 된다.
    • 이렇게 되면, 소켓이 연결되었을 때 발생할 콜백 함수 내에서 모든 콜백 함수를 달아주게 되고, 모든 관심사가 해당 콜백 함수 내부로 집중되는 문제가 발생할 것으로 판단했다.

문제 접근

  • 관심사 분리라는 목표를 달성하기 위해, 우리는 우리가 지금껏 만들어왔었던 REST API 서버를 참고하였다. REST API 서버의 경우 각각의 도메인 별로 controller와 service, repository 레이어로 구성된 독립된 계층 구조를 가지고 있어 관심사를 쉽게 분리할 수 있었다.
  • 또한, 부스트캠프 과정 중 Express를 활용하여 Controller와 Service, Repository를 분리하려고 시도했던 경험이 있어 이와 유사한 구조로 구현할 수 있을 것이라 판단했다.

문제 해결

  • 해결 과정에 대한 자세한 내용은 다음 링크로 대체한다.

소켓 서버 코드 도메인별로 분리하기(feat. 수직적 레이어 구성)

두번째 문제, 부하와 서버 간의 관심사 분리

문제 인식

  • 소켓 서버를 구현하면서 마지막으로 문제로 인식되었던 부분은 서버 부하이다. 소켓 서버는 그 특성 상 서버와 클라이언트 사이에서 지속적으로 연결 상태를 유지해야 하고, 이런 연결 자체가 서버에 부하를 줄 수 있다고 생각했다.
  • 또한, 연결에 대한 정보가 메모리에 저장되기 때문에, 추후 스케일 아웃을 고려했을 때 각 소켓 서버들 사이에서 사태를 공유하기 위한 추가적인 조치가 필요할 것으로 판단했다.
  • 마지막으로 실시간성을 띄는 것이 좋을 것이라고 판단한 API들에 대해서 소켓 서버로의 migration을 예상했었지만, migration이 진행되면서 정확히 어떤 API가 실시간성을 띄는 것이 좋은가에 대한 기준이 모호했다는 것을 알게 되었고, 어떤 API를 어떤 서버에 구현해야 하는 지에 대한 의문이 커졌다.

문제 접근

  • 소켓 통신과 불가분한 문제라고 판단을 내렸다. 소켓 서버에서 구현하고자 하는 양방향 실시간 통신을 구현하기 위해서는 서버와 클라이언트 사이의 지속적인 연결이 불가피했다.
  • 실시간성을 띄는 것이 좋은 API가 어떤 것인가에 대해서 팀원들이 모두 함께 고민했고, 우리가 실시간성을 띄는 서비스를 구현하려고 하는 기본 목적에 대해서 새로 고민하게 되었다.
  • 우리가 가진 고민에 대해서 멘토링을 신청했다.

문제 해결

  • 멘토링과 우리가 그간 정리했던 고민들을 바탕으로 내렸던 결론은, 우리에게 소켓 서버가 과연 필요할까 였다.
  • 이 의문을 해소하기 위해서 우리는 이전에 미처 하지 못했던 고민을 마주했고, 이에 대한 과정은 다음 링크로 대체한다.

😯 WebSocket과 Polling 그리고 SSE

  • 소켓 서버는 더 없이 빠른 실시간성을 보장하지만, 그것을 위한 많은 오버헤드 또한 포함하고 있는 프로토콜이다. 항상 통신이 연결되어 있어야 하므로 그 자체가 부하가 된다.
  • 또한, 소켓 서버는 클라이언트에 연결 상태를 지속적으로 유지하기 때문에 stateless한 통신이 아니다. 즉, 서버에서 클라이언트에 대한 정보를 저장해두고 이를 활용해 통신을 진행하게 된다. 문제는, 서버를 스케일아웃 했을 때 각각의 서버에서 클라이언트에 대한 상태를 공유하거나, 이벤트를 공유해야 하여 추가적인 처리가 필요한 것으로 판단했다.
  • 우리는 이런 문제를 해결하기 위해서 short polling 방식을 활용하여 실시간 통신을 구현하기로 하였다.

undefined의 실시간 서버

  • 현재 우리의 실시간 서버는 short polling을 활용하여 구현된 상태이다.
  • 클라이언트에서는 일정한 시간을 간격으로 서버에 새로운 요청을 보내고, 서버에서 가장 최신의 데이터를 전달하게 된다.
  • 비록 게시글이 생성되자마자 다른 사람에게 바로 전달되지는 못하지만, 그럼에도 불구하고 UX에는 큰 영향이 없을 것으로 판단했다.

🏦 기술 부채란 무엇일까? (feat. 웹소켓을 떠나 보내며)

웹소켓. 여기 잠들다. 🪦

Clone this wiki locally