Skip to content

3주차 릴리즈 노트

fru1tworld edited this page Jan 23, 2025 · 9 revisions

백엔드

문제 정의:

  • 단일 Nest 서버 구조로 인한 확장성 한계
  • 서비스 분리 및 상태 관리 필요성

핵심 CS 지식 및 적용:

  1. 객체지향 원칙
    • 단일책임원칙(SRP): API/WebSocket 서버 분리
    • 캡슐화: 각 서버의 독립적 기능 구현
    • 낮은 결합도: Stateless 아키텍처 구현
  2. WebSocket & Redis
    • Redis를 통한 상태 관리
    • Load Balancer 도입으로 확장성 확보

과정 요약

기존 코드 : 하나의 Nest 서버에서 API 와 WebSocket 연결 역할을 모두 수행

문제 : 유지보수 어려움, 수평 확장이 어려움, 상태 관리 어려움

개선 후 : API 서버와 WebSocket 서버 분리 후 Load-Balancer 서버 구현으로 WebSocket 서버 확장 가능하도록 변경 및 Redis 연결 상태 관리를 통해서 다양한 문제 해결

과정 소개

image

리팩터링 전 서버의 모습입니다.


image

위와 같은 구조로 되어있는데요.


image image

내부 구조를 살펴보면

위와 같은 구조로 코드가 작성되어있습니다.


이를 분리하기 위해 이러한 모습을 그려보았습니다. image image

WebSocket 서버와 API 서버를 분리해보았습니다.

실제로 이렇게 분리하기 위해 여러가지 방법을 고민해보았는데요.

일단 동일한 코드를 실행하고 Nginx를 통해 /ws /api 가 이미 나뉘어져있기 때문에 docker-container와 nginx를 다소 수정해서 실행해보았습니다.

image

이렇게 되면 중복되는 코드가 많은 문제가 발생하는데요.

image

필요한 부분만 남아있을 수 있도록 가지치기를 해보았습니다.

image

이렇게 불필요한 부분을 제거하고 보니 불필요한 모듈이 존재하는 것을 확인하였고

image

위와 같이 간소화해보았습니다.

image

두 모습을 비교하면 이러한 모습이 됩니다.

이제 WebSocket 서버를 확장하는 방법에 대해서 살펴보겠습니다.

다음과 같은 로직을 고민해보았습니다.

  1. 만약에 이미 연결된 방이 있다면 해당 방으로 접속해야한다.
  2. 연결된 방이 없다면 CPU 사용량이 가장 낮은 서버의 주소로 이동해서 새로운 접속을 해야한다.

이러한 로직을 구현하기 위해 중간에 Load-Balancer 서버가 필요해졌습니다.

image

그림으로 그려보면 위와 같은 모습이 됩니다.


이렇게 Load-Balancer 서버를 도입하고보니 WebSocKet의 상태를 알아야했었는데요.

중간에 미들웨어(Redis)를 통해 그러한 상태 관리 로직을 구현해보았습니다.

image

구성은 이렇게 되어있습니다.

각각의 서버는 각각의 책임과 역할을 수행할 수 있게 분리해보았습니다.

이러한 모습이 객체지향이 추구하는 것과 밀접하다는 생각이 들었는데요. 객체지향의 여러가지 원칙 중 단일책임원칙과 캡슐화를 고려하면서 작업을 해보았습니다.


객체 간 관심사가 분리되어 유지보수가 편리했었는데요.

유지보수 이야기를 마지막으로 해보겠습니다.

image image

저희 프로젝트는 각 노드가 WebSocket의 Room으로 되어있습니다.

이때 하위에 연결된 커넥션이 존재하는 경우 삭제했을 때 데이터가 남아있거나, 저장되어서 고아 객체(orphan object)가 됩니다.

image

고아 객체(orphan object)문제를 해결하기 위해 트리를 삭제 하기전 하위 트리를 모두 조회해서 연결된 커넥션이 있는지 확인을 해야했었는데요.

이때 Redis에서 상태를 저장하고 있기 때문에 Redis를 먼저 조회하고 남아있는 커넥션이 존재하는 경우 삭제를 하지 못하도록 설정하였습니다.

image

그렇게 될 경우 위와 같은 형태로 서버 아키텍처가 구성됩니다.

만약 기존 아키텍처였다면 SpaceService Class에 의존하고 있는 Class가 많아서 관련 Class를 모두 확인해가면서 작업을 해야했었는데 이렇게 분리가 되고나니 코드의 복잡도가 많이 감소하여서 쉽게 유지보수를 할 수 있었습니다.

Clone this wiki locally