101 서버 구성 - 100-hours-a-week/KTB4-3rd-CLOUD GitHub Wiki

현재 팀 구성

Spring Boot (websocket): BE

Next.js: FE

FastAPI: AI

Node.js: Reverse Proxy

MySQL: DB

서버 구성

Nginx 단일 진입점으로 사용

현재 애플리케이션 서버가 1대라면 Nginx는 여러 서버 사이의 트래픽을 분산하는 역할이 아닌 다음 역할을 수행한다.

  • 외부 요청의 단일 진입점
  • Next.js와 Spring Boot 라우팅
  • FastAPI 내부 요청 전달
  • WebSocket 연결 전달
  • HTTPS 처리
  • 서비스별 포트 은닉
사용자
  ↓
Nginx
  ├ Next.js SSR
  ├ Spring Boot API
  └ FastAPI
  

트레이드오프

  • Nginx는 추가 인프라 비용이 거의 없음
  • 단일 EC2에서 서비스 라우팅을 단순화함
  • 향후 애플리케이션 서버가 여러 대가 되면 ALB 도입을 검토할 수 있음

EC2 1대에 애플리케이션은 컨테이너로 분리

V1은 초기 사용자에게 서비스 가치를 검증하는 단계이기 때문에 높은 확장성보다 기본적인 장애 격리와 빠른 복구를 우선한다

현재 예상 서비스 규모에서 여러 대의 애플리케이션 서버를 운영할 필요성이 높지 않다. 하지만 Next.js, Spring Boot, FastAPI가 하나의 프로세스나 실행 환경을 공유하면 다음 문제가 발생할 수 있다.

Next.js 빌드 또는 SSR 메모리 증가
  ↓
EC2 메모리 부족
  ↓
Spring Boot 또는 FastAPI에도 영향
  ↓
전체 서비스 장애

따라서 EC2는 1대를 사용하더라도 애플리케이션은 컨테이너 단위로 분리해야 한다.

결정사안

EC2 1대
  ├ Nginx 컨테이너
  ├ Next.js 컨테이너
  ├ Spring Boot 컨테이너
  └ FastAPI 컨테이너

각 컨테이너에 리소스 제한과 재시작 정책을 적용한다.

Dev와 Prod 인스턴스 분리

V1, V2, V3를 약 3주간 순차적으로 실제 사용자에게 제공할 예정이므로, Dev와 Prod 환경은 인스턴스 단위로 분리한다.

인프라 운영 목표

핵심 서비스 가치를 빠르게 구현·검증하고, 개발자 간 피드백을 신속하게 반영한다. 동시에 개발과 테스트 과정이 실제 사용자 서비스에 영향을 주지 않도록 운영 환경을 보호한다.

EC2 1대 내 논리적 분리를 선택하지 않는 이유

EC2 1대에서 Dev와 Prod를 컨테이너로 논리적으로 분리하더라도 다음 리스크는 남는다.

  • Dev 작업으로 인한 CPU·메모리·디스크 I/O 경합
  • 배포 과정에서 발생하는 서비스 중단
  • 하나의 EC2 장애가 Dev와 Prod에 동시에 영향을 주는 문제

인스턴스 분리가 필요한 이유

V1 운영 중 V2를 개발하고, V2 운영 중 V3를 개발하는 방식으로 진행한다. 이때 V1에서 축적된 사용자 데이터와 운영 상태는 V2와 V3에서도 이어져야 한다.

또한 프론트엔드와 백엔드가 서로 다른 레포지토리에서 병렬로 개발되기 때문에, 각자의 로컬 환경에서 기능이 정상적으로 동작하는 것만으로는 전체 서비스의 통합 상태를 검증하기 어렵다. 따라서 실제 배포 구조와 유사한 공유 테스트 환경이 필요하다.

특히 V1의 핵심 기능인 매칭은 단순히 API 응답 여부만 확인하는 기능이 아니다.

  • 여러 사용자가 동시에 참여하는 상황
  • 택시팟 정원 초과 방지
  • 동일 사용자의 중복 참여 방지
  • 트랜잭션 Lock 동작
  • 매칭 이후 채팅방 생성
  • 참여자와 정산 정보의 일치 여부

이러한 시나리오는 하나의 로컬 환경에서 개발자 한 명이 테스트하는 것보다, 공유된 Dev 환경에서 여러 사용자가 동시에 접속해 검증하는 것이 적절하다.

Prod 환경에 개발 변경사항을 바로 배포할 경우, QA가 실제 사용자 서비스 위에서 진행된다. 이 경우 백엔드와 프론트엔드 개발자가 Prod 환경에서 함께 테스트해야 하므로 QA 작업이 배포의 병목이 될 수 있다. 또한 오류가 발견되면 테스트와 수정, 재배포가 실제 사용자 이용과 동시에 발생한다.

특히 사용자가 빠른 교통 정보와 매칭을 기대하는 시간대에 개발 변경사항으로 서비스 응답이 느려지거나, 매칭 정보·채팅방·정산 데이터에 문제가 발생하면 사용자 경험이 저하되고 이탈로 이어질 수 있다.

따라서 실제 사용자가 이용하는 Prod 환경과 개발·통합 테스트가 수행되는 Dev 환경을 인스턴스 단위로 분리한다.

로컬 환경
  └── 개인 단위 기능 개발 및 기본 테스트

Dev 환경
  └── 프론트엔드·백엔드 통합 테스트
  └── 다중 사용자 매칭 시나리오 검증
  └── QA 및 배포 전 검증

Prod 환경
  └── 실제 사용자 서비스 제공

Dev 환경은 모든 변경사항을 별도로 운영하기 위한 목적이 아니라, 여러 레포지토리의 변경사항을 통합하고 Prod 배포 전에 서비스 핵심 시나리오를 검증하기 위한 환경이다.

운영 방식

Prod: V1 서빙
Dev: V2 개발·테스트
        ↓
Prod: V2 서빙
Dev: V3 개발·테스트
        ↓
Prod: V3 서빙

버전별로 Prod 인스턴스를 새로 만드는 것이 아니라, 하나의 Prod 환경을 유지하면서 검증이 완료된 버전을 순차적으로 배포한다.

비용 절감 방법

비용 부담을 줄이기 위해 Dev 인스턴스는 Prod보다 작은 사양으로 구성하고, 개발·테스트가 필요한 시간에만 실행한다.

결론

MVP 단계에서는 고가용성을 확보하기 위한 분리가 아니라, 개발·테스트 작업이 실제 사용자 서비스에 영향을 주지 않도록 하기 위한 운영 경계 분리가 목적이다. 따라서 현재처럼 V1부터 V3까지 서비스를 연속적으로 서빙하는 일정에서는 EC2 1대의 논리적 분리보다 Dev와 Prod 인스턴스 분리가 적절하다.

DB를 컨테이너 단위로 분리

사용자가 보존되기를 기대하는 상태

V1에서는 다음과 같은 정보가 DB에 저장된다.

  • 사용자가 참여한 택시팟
  • 택시팟 참여자와 정원 정보
  • 채팅방과 채팅 기록
  • 정산 정보
  • 커뮤니티 게시글

서비스 관점의 의미

이 정보는 단순한 일회성 조회 데이터가 아니라, 사용자가 서비스를 이용한 결과이자 이후 경험을 이어가기 위한 상태다.

사용자는 서비스를 다시 이용할 때 기존에 참여한 택시팟, 채팅 기록, 정산 정보, 작성한 게시글을 확인할 수 있기를 기대한다. 데이터가 사라지면 사용자는 서비스를 다시 이용할 수 있더라도 기존 경험을 이어갈 수 없다.

특히 다음과 같은 문제가 발생하면 서비스 신뢰도가 낮아질 수 있다.

  • 매칭 정보가 사라지는 경우
  • 채팅 기록을 확인할 수 없는 경우
  • 참여자 또는 정산 정보가 일치하지 않는 경우
  • 작성한 커뮤니티 게시글이 사라지는 경우

선택지별 트레이드오프

컨테이너에서 영속인 데이터를 저장하기 위해서는 별도로 PV나 볼륨으로 데이터 저장을 주기적으로 해야한다.

구성 장점 감수해야 할 위험
Prod EC2 + MySQL 컨테이너 + PV 가장 저렴함, 구조가 단순함, 네트워크 구성이 적음 EC2 장애와 리소스 경합을 공유함, 백업·복구를 직접 관리해야 함
별도 DB EC2 애플리케이션과 CPU·메모리·디스크를 분리할 수 있음 서버 비용과 운영 대상 증가, 백업·패치·복구를 직접 관리해야 함
RDS MySQL DB 백업·스냅샷·복구·운영 부담 감소, 애플리케이션과 생명주기 분리 비용 증가, AWS 의존성, 네트워크와 보안 설정 필요

RDS 비용 비교

아래와 같이 가정했을 때, 컴퓨팅 비용은 $0.051/hour, 월 730시간 기준 약 $37, 한화 약 5만6만 원 수준에 +알파 정도 된다고 예상한다. 3개월 기본 비용이 1518만원 정도로 전체 예산에서 20% 전후로 비중을 차지하게 되는데 V1에서는 서비스 서빙에서 데이터의 정합성이 우선적으로 봐야하는 가치라고 생각하지만 MVP 때부터 도입을 해야하는지에 대한 근거가 불명확하다.

  • AWS 서울 리전
  • RDS MySQL Single-AZ
  • db.t4g.small
  • 개발 DB는 Dev EC2의 MySQL 컨테이너 사용
  • Multi-AZ와 Replica는 사용하지 않음

고민되는 지점

만약 DB를 같은 EC2에 두게 된다면 근거는 다음과 같다.

  1. DAU 1500명과 현재 예상 RPS 수준만으로 DB 컨테이너에 다운될 가능성이 높지 않다.
  2. 데이터 보호가 충분해서가 아니라 다음과 같은 비용·운영상의 단순성의 이점을 얻고 호스트 장애와 리소스 경합 위험을 감수하는 것을 선택했다.
  • 인프라 비용 절감
  • 관리해야 할 서버 수 감소
  • 네트워크 구성 단순화
  • 낮은 트래픽에서 독립적인 DB 확장이 아직 필요하지 않음
                         사용자
                           │
              ┌────────────┴────────────┐
              │                         │
        app.example.com          dev.example.com
              │                         │
              ▼                         ▼
┌─────────────────────────┐ ┌─────────────────────────┐
│       Prod EC2          │ │        Dev EC2          │
│                         │ │                         │
│  ┌───────────────────┐  │ │  ┌───────────────────┐  │
│  │ Nginx             │  │ │  │ Nginx             │  │
│  │ 단일 진입점          │  │ │  │ 단일 진입점          │  │
│  └─────────┬─────────┘  │ │  └─────────┬─────────┘  │
│            │            │ │            │            │
│  ┌─────────┼─────────┐  │ │  ┌─────────┼─────────┐  │
│  │         │         │  │ │  │         │         │  │
│  ▼         ▼         ▼  │ │  ▼         ▼         ▼  │
│ Next.js  Spring   FastAPI│ │ Next.js  Spring   FastAPI│
│ SSR       Boot    AI API │ │ SSR       Boot    AI API │
│           +WS             │ │           +WS            │
│                           │ │                          │
│  ┌─────────────────────┐  │ │  ┌─────────────────────┐ │
│  │ MySQL 컨테이너        │  │ │  │ MySQL 컨테이너        │  │
│  │ Prod 데이터          │  │ │  │ Dev 데이터            │  │
│  │ 별도 생명주기          │  │ │  │ 별도 생명주기        │  │
│  └─────────────────────┘  │ │  └─────────────────────┘  │
└─────────────────────────┘ └─────────────────────────────┘