컨테이너 기반 서비스 확장 및 운영 자동화 설계 - 100-hours-a-week/KTB4-4th-wiki GitHub Wiki

목차

1. 현재 서비스의 성장 상황

Need U V1은 빠른 시장 검증을 위해 MAU 약 1,000명 규모를 대상으로 단일 EC2 기반으로 출시하였다.

4단계에서는 서비스 출시 약 14개월 후, 초기 사용자 검증을 넘어 친구 관계 기반 Organic Growth와 외부 커뮤니티 유입이 이어지고, V2 기능이 추가되면서 사용자 수와 서비스 이용 빈도가 크게 증가한 상황을 가정한다.

또한 V1에서는 외부 LLM API를 사용하지만, V2에서는 Runpod GPU Instance에 AI Model을 직접 배포하여 Inference를 수행하는 구조로 전환하는 상황을 가정한다.

1.1 성장 시점을 약 14개월 후로 설정한 이유

Consumer Social 서비스는 초기 사용자 검증 이후 친구 관계 기반 확산이나 Viral Event를 통해 빠르게 성장할 수 있다.

a16z는 Seed-stage Consumer Social App의 월 사용자 성장률을 다음과 같이 제시한다.

  • OK: 월 20%
  • Good: 월 35%
  • Great: 월 50%

또한 Social App은 매월 동일한 속도로 성장하기보다 Viral Moment에서 Step-change 형태의 성장이 발생할 수 있다고 설명한다.

근거: a16z - Do You Have Lightning In a Bottle? How to Benchmark Your Social App

유사 Social Gifting 서비스인 Wrapp 역시 출시 후 약 14개월 만에 Active User 100만 명을 확보한 사례가 있다. 여기서 Active User는 단순 다운로드가 아니라 앱을 다운로드하고 최소 한 번 이상 선물을 보낸 사용자를 의미한다.

근거: TechCrunch - Social Gifting App Wrapp Hits 1 Million Users

따라서 Need U에서는 약 14개월 동안 다음과 같은 공격적인 Growth Scenario를 설정한다.

출시 ~ 3개월

→ 초기 사용자 확보
→ 핵심 기능 검증

3 ~ 6개월

→ 친구 관계 기반 Organic Growth
→ 외부 커뮤니티 유입
→ 반복 사용자 증가

6 ~ 14개월

→ V2 기능 추가
→ 반복 사용 및 재방문 증가
→ 친구 관계 기반 확산
→ 일부 Viral Growth
→ 사용자 및 Traffic 증가

약 14개월

→ 기존 단일 EC2 구조의
   확장성·가용성·운영 방식 재검토

14개월은 업계의 고정된 성장 기준이 아니라 Need U 4단계 설계를 위한 내부 Scenario 시점이다.

1.2 MAU

V1 MAU 약 1,000명을 시작점으로 a16z의 Great = 월 50% 성장 Benchmark를 공격적인 성장 Scenario에 적용한다.

MAU

= 1,000 × (1.50)^14
≈ 291,900명

MAU 약 291,900명

규모로 성장한 상황을 가정한다. 이는 V1 대비 약 292배 증가한 규모이다.

월 50% 성장이 실제로 14개월 동안 정확하게 유지될 것이라는 예측이 아니라, 빠르게 성장한 서비스를 표현하기 위한 4단계 Capacity Planning Scenario로 사용한다.

1.3 DAU

V2에서는 다음과 같이 사용자가 반복적으로 서비스를 이용할 수 있는 기능이 추가된다.

  • 오늘의 추천
  • 친구 목록 및 친구 상세
  • 친구 취향·관심사 조회
  • 선물 추천
  • 상품 의견 요청
  • AI 대화 및 취향 분석
  • 추천 및 만족도 피드백
  • 생일·기념일·친구 가입 등의 알림

따라서 V1보다 서비스 내에서 반복적으로 탐색하고 재방문할 수 있는 경로가 증가한다.

Need U 접속
↓
메인 / 친구 정보 확인
↓
친구 상세 및 취향 탐색
↓
추천 탐색
↓
상품 상세 / AI 기능 이용

a16z의 Consumer Social App DAU/MAU Benchmark는 다음과 같다.

  • OK: 25%
  • Good: 40%
  • Great: 50%+

Need U는 매일 반드시 사용하는 SNS보다는 선물이라는 상황적 목적이 강한 서비스이므로 가장 보수적인 **25%**를 적용한다.

DAU

= 291,900 × 25%
= 72,975명

DAU 약 72,975명

으로 가정한다.

1.4 사용자 1회 이용당 API Request 수

V1에서는 한 번의 주요 서비스 이용 과정에서 약 21회의 API Request가 발생한다고 가정하였다.

V2에서는 다음이 추가된다.

V1
약 21회

+

알림 상태 / 목록
추천 추가 탐색
상품 상세
추천 피드백
상품 클릭 Event
AI / 취향 관련 V2 Interaction

모든 사용자가 모든 기능을 사용하는 것은 아니므로 최대 Request를 모두 합산하지 않고

사용자 1회 이용당 약 25~30회의 API Request

가 발생한다고 가정한다.

이는 외부 Benchmark가 아니라 V2 화면 및 기능정의서를 기준으로 설정한 내부 Capacity Planning 값이다.

1.5 Peak Traffic

DAU 약 72,975명 중 특정 시간대에 사용자가 집중되는 상황을 다음과 같이 구분한다.

Peak Hour 집중도 Peak Hour 사용자 예상 RPS
DAU 10% 약 7,298명 약 51~61 RPS
DAU 15% 약 10,946명 약 76~91 RPS
DAU 20% 약 14,595명 약 101~122 RPS
DAU 25% 약 18,244명 약 127~152 RPS

대표 Scenario는 DAU 20% 집중으로 설정한다.

Peak Hour User

= 72,975 × 20%
= 14,595명
Peak Hour Requests

= 14,595 × 25~30
= 약 364,875~437,850 Requests/hour

이를 RPS로 환산하면 약 101~122 RPS이다.

대표 Peak Traffic = 약 101~122 RPS

로 사용한다.

여기서 Peak Hour 집중도 10~25%와 대표값 20%는 외부 Benchmark가 아니라 시간대별 이용량 편차를 고려하기 위한 내부 Scenario이다.

1.6 AI Traffic

V1에서는 외부 Provider가 LLM GPU Capacity를 담당한다.

Need U Backend
↓
External LLM API

V2에서는 다음 구조로 변경된다.

AWS Backend
↓
AI Server
↓
Runpod GPU Server
↓
vLLM
↓
AI Model

AI Server는 AI 요청에 대한 API 처리와 모델 호출을 담당하고, 실제 Inference는 Runpod GPU Server에서 수행한다.

Peak 사용자의 약 40%가 AI 기능을 이용하고, 사용자 1회 AI 이용에서 평균 3회의 Inference가 발생한다고 가정하면 다음과 같다.

14,595 × 40%
= 5,838 AI Users

5,838 × 3
= 17,514 Inference Requests/hour

17,514 ÷ 3,600
≈ 4.87 RPS

대표 AI Inference Traffic = 약 4.87 RPS

로 가정한다.

AI Infrastructure는 AI 팀에서 별도로 관리하므로 이후 AWS Application 확장 설계에서는 Backend Scaling과 AI Scaling을 분리하여 판단한다.

항목 V1 V2 (가정) 변화
MAU 약 1,000명 약 291,900명 약 292배
DAU 약 80~140명 약 72,975명 약 521~912배
Request / 이용 약 21회 약 25~30회 증가
대표 Peak Traffic 약 0.16 RPS 약 101~122 RPS 약 630~760배
AI Traffic 약 0.047 RPS 약 4.87 RPS 약 104배

2. V1 인프라의 한계점

2.1 V1 인프라 구조

Need U V1은 초기 사용자 검증을 목적으로 설계하였다. V1의 목표는 대규모 트래픽을 처리하는 것이 아니라, 카카오 로그인 → AI 대화 기반 취향 수집 → 친구 선물 추천 → 카카오 선물하기 아웃링크로 이어지는 핵심 사용자 흐름을 안정적으로 구현하는 것이었다. 따라서 초기에는 인프라 운영 및 개발 복잡도를 낮추는 방향으로 구성하였다.

V1의 인프라는 하나의 EC2 Instance에 Nginx, Spring Boot, Redis, MySQL을 함께 배치하는 구조이다. Nginx는 정적 파일 제공 및 Backend Proxy 역할을 담당한다.

                    User
                      │
                      ▼
                  Route 53
                      │
                      ▼
                 Elastic IP
                      │
                      ▼
              ┌─────────────┐
              │     EC2     │
              │  t3.medium  │
              ├─────────────┤
              │    Nginx    │
              │             │
              │ Spring Boot │
              │    Redis    │
              │    MySQL    │
              └─────────────┘
                      │
                      ▼
               External LLM API

V1의 예상 Peak Traffic은 약 0.16 RPS 수준이었기 때문에 하나의 EC2에서 모든 구성 요소를 운영하는 구조로도 초기 서비스 검증이 가능했다. 또한 별도의 Load Balancer나 여러 Application Server를 운영하지 않아도 되어 인프라 구성을 단순하게 유지할 수 있었다.

그러나 V2에서는 약 14개월간의 서비스 성장과 기능 확장을 가정하면서 MAU 약 291,900명, DAU 약 72,975명, 대표 Peak Traffic 약 101~122 RPS까지 규모가 증가한다.

이에 따라 V1의 단일 EC2 구조를 그대로 유지하기에는 다음과 같은 한계가 발생한다.

2.2 Backend Single Point of Failure

V1에서는 Nginx, Spring Boot, Redis, MySQL이 모두 하나의 EC2 Instance에 존재한다.

              EC2 1대
       ┌───────────────┐
       │ Nginx         │
       │ Spring Boot   │
       │ Redis         │
       │ MySQL         │
       └───────────────┘
              │
              ▼
          EC2 장애
              │
              ▼
       Backend 전체 영향

따라서 하나의 EC2 Instance에 장애가 발생하면 해당 서버에서 실행 중인 Backend와 Redis, MySQL이 동시에 영향을 받는다.

특히 Backend가 하나의 EC2 Instance에 종속되어 있기 때문에 단일 EC2 장애가 Backend 전체의 서비스 중단으로 이어질 수 있다.

V1에서는 초기 사용자 검증 단계에서 인프라 구성을 단순하게 유지하는 것이 중요했기 때문에 이러한 구조를 사용하였다. 그러나 V2에서는 서비스 규모가 증가하므로 특정 Backend 실행 단위의 장애가 전체 API 중단으로 이어지지 않도록 Backend를 다중화할 필요가 있다.

→ V2 요구사항: Backend 다중화

2.3 배포 시 Backend 중단

V1에서는 Backend가 하나의 EC2 Instance에서 실행되므로 새로운 버전을 배포할 때 기존 Backend Container를 중지한 후 새로운 버전을 실행하는 방식으로 배포한다.

기존 Backend Container
        │
        ▼
      중지
        │
        ▼
새로운 Backend Container 실행
        │
        ▼
      서비스 재개

이 과정에서 기존 Backend가 중지된 시점부터 새로운 Backend가 정상적으로 실행되기 전까지 API 요청을 처리할 Backend가 존재하지 않게 된다.

V1에서는 Peak Traffic이 약 0.16 RPS 수준으로 낮았기 때문에 계획된 배포 시간에 짧은 서비스 중단을 허용할 수 있었다.

그러나 V2에서는 Peak Traffic이 101~122 RPS까지 증가하므로 동일한 방식으로 배포할 경우 배포 중 발생하는 요청이 처리되지 못하거나 오류가 발생할 가능성이 높아진다.

따라서 V2에서는 여러 Backend 실행 단위 중 일부가 요청을 처리하는 동안 다른 실행 단위를 교체할 수 있는 구조가 필요하다.

→ V2 요구사항: 다중 Backend + 배포 중 기존 요청 처리 유지

2.4 트래픽 증가에 따른 Backend 확장 필요

V1의 Peak Traffic은 약 0.16 RPS였지만, V2에서는 대표 Peak Traffic을 101~122 RPS로 가정한다. 이는 V1 대비 약 630~760배 증가한 수준이다.

V1에서는 다음과 같이 하나의 EC2에서 모든 Backend 요청을 처리한다.

V1

0.16 RPS
   │
   ▼
┌─────────────┐
│ EC2 1대     │
│ Spring Boot │
└─────────────┘

V2의 증가한 트래픽을 동일한 구조에서 처리하면 요청이 하나의 EC2 Instance에 집중된다.

V2

101~122 RPS
      │
      ▼
┌─────────────┐
│ EC2 1대     │
│ Spring Boot │
└─────────────┘
      │
      ▼
CPU / Memory 부하 증가

EC2 Instance의 CPU와 Memory 사양을 높이는 Scale-Up을 적용하면 하나의 서버가 처리할 수 있는 용량을 증가시킬 수 있다.

그러나 Scale-Up은 하나의 Backend 실행 단위에 처리 용량이 집중되는 구조이며, 트래픽이 증가할 때마다 더 높은 사양의 Instance로 변경해야 한다.

또한 V1에서는 Spring Boot뿐만 아니라 Redis와 MySQL도 동일한 EC2의 자원을 사용하고 있기 때문에 Backend의 트래픽 증가가 하나의 서버 자원에 함께 영향을 준다.

따라서 V2에서는 하나의 EC2의 사양을 높이는 것만으로 대응하기보다, Backend 실행 단위를 여러 개로 구성하여 증가한 요청을 분산 처리할 수 있는 Scale-Out 구조를 적용한다.

→ V2 요구사항: Backend Scale-Out

2.5 Database 처리 부하 증가

V1에서는 MySQL이 Spring Boot, Redis와 함께 하나의 EC2 Instance에서 실행된다.

V1의 Peak Traffic은 약 0.16 RPS 수준이었기 때문에 Database 역시 초기 서비스 검증을 위한 단일 MySQL 구조로 운영할 수 있었다.

V2에서는 대표 Peak Traffic이 101~122 RPS까지 증가한다. 상세 DB 분석에서는 API 명세를 기준으로 Query/API를 산정해 대표 DB QPS 157~189, Capacity Planning 상한을 약 243 QPS로 사용한다.

따라서 V1처럼 MySQL을 Backend와 동일한 EC2에서 실행할 경우 Backend 요청 처리와 Database 연산이 동일한 CPU와 Memory 자원을 공유하게 된다.

또한 Backend를 여러 실행 단위로 Scale-Out하더라도 Database가 하나의 Instance에 그대로 존재한다면 여러 Backend에서 발생한 요청이 결국 하나의 Database로 집중된다.

             ALB
              │
        ┌─────┴─────┐
        ▼           ▼
     Backend A   Backend B
        │           │
        └─────┬─────┘
              ▼
          MySQL 1대
              │
        DB 요청 집중

따라서 V2에서는 Backend와 Database의 실행 환경을 분리하여 Database가 Backend의 서버 자원을 공유하지 않도록 할 필요가 있다.

현재 상세 산정 기준의 Peak Write는 약 49 QPS다. 이 수치만으로 Sharding이나 Read Replica를 선제 도입하지 않고, CPU·Memory·Disk I/O·Connection·Query Latency를 확인한 뒤 확장한다.

→ V2 요구사항: Backend와 Database 실행환경 분리

2.6 AI Inference 환경 분리

V1에서는 Backend가 외부 LLM API를 직접 호출하므로 AI Model의 GPU 자원을 직접 관리하지 않는다.

그러나 V2에서는 GPU Instance에 AI Model을 직접 배포하여 Inference를 수행한다. 이 경우 AI Model의 Inference 부하와 Backend API 부하를 하나의 실행환경에서 처리하면 서로 다른 자원 요구사항이 결합될 수 있다.

특히 V2의 대표 AI Inference Traffic은 약 4.87 RPS이며, 실제 GPU 부하는 단순 요청 수뿐만 아니라 Model Size, Input Token, Output Token, Batch 처리 방식 등에 따라 달라질 수 있다.

따라서 AI 요청을 처리하는 API Server와 실제 Model Inference를 수행하는 GPU Server를 분리한다.

Backend
│
▼
AI Server
│
▼
GPU Server
│
vLLM
│
AI Model

AI Server는 HTTP API, 요청 검증, Prompt 구성 및 Model Server 호출을 담당하고, GPU Server는 vLLM을 통해 실제 Model Inference를 담당한다.

또한 AI Server와 GPU Server는 서로 다른 지표를 기준으로 확장 여부를 판단한다.

→ V2 요구사항: AI API 처리환경과 GPU Model Serving 환경 분리

2.7 V1 한계에서 V2 요구사항 도출

V1의 단일 EC2 구조는 0.16 RPS 수준의 초기 트래픽을 처리하기에는 적절했지만, V2의 예상 트래픽과 서비스 규모를 고려하면 다음과 같은 한계가 발생한다.

V1의 한계 V2에서의 문제 V2 개선 방향
EC2 1대에 Backend 집중 EC2 장애 시 Backend 전체 영향 Backend 다중화
단일 Backend 실행 단위 배포 시 Backend 요청 처리 중단 Rolling Deployment
하나의 EC2에 Backend 요청 집중 101~122 RPS 처리 시 CPU/Memory 부하 증가 Backend Scale-Out
Spring Boot / Redis / MySQL이 동일 EC2에 존재 Backend 트래픽 증가가 동일 서버 자원에 영향을 줌 구성 요소 실행환경 분리
Backend에서 외부 LLM API 사용 AI API 부하와 GPU Inference 부하가 결합됨 AI Server와 GPU Server 분리

따라서 V2에서는 V1의 단일 EC2 구조를 단순히 더 높은 사양의 Instance로 교체하는 것이 아니라, Backend를 여러 실행 단위로 구성하여 트래픽을 분산하고, 실행 단위별로 확장 및 배포할 수 있는 구조로 변경한다.

3. V2 운영 목표

Need U V2는 MAU 약 291,900명, DAU 약 72,975명, Peak API Traffic 약 101~122 RPS를 가정한다.

Need U는 사용자가 친구의 생일, 기념일, 약속 전 등 선물을 준비해야 하는 시점에 사용하는 이벤트성 서비스이다. 따라서 평상시와 특정 이벤트 시점의 사용량 차이가 발생하며, Peak Traffic에 맞춰 항상 동일한 규모의 Backend를 유지하기보다 트래픽에 따라 Backend 실행 단위를 탄력적으로 조절할 수 있는 구조가 필요하다.

V2의 서비스 가용성 목표는 99% 이상으로 둔다.

99% 가용성 기준

30일 기준
720시간 × 1%
= 7.2시간
= 약 7시간 12분

다만 99%라는 수치만으로 모든 컴포넌트를 고가용성 구성으로 확정하지 않는다.

V2의 핵심 목표는 단일 Backend 실행 단위의 장애가 Backend 전체 중단으로 이어지지 않도록 하는 것이다.

4. Backend

4.1 Scale-Up / Scale-Out 판단

Need U V2의 예상 Peak API Traffic은 약 101~122 RPS이다. 이 수치만 보면 반드시 여러 서버가 필요한 수준이라고 단정하기 어렵다.

Scale-Up을 하면 하나의 Backend EC2 사양을 높여 처리량을 늘릴 수 있다.

Backend EC2 1대
↓
더 높은 CPU / Memory 사양
↓
처리 가능한 요청량 증가

따라서 현재 예상 트래픽 처리량만을 기준으로 보면 Scale-Up도 가능한 선택지이다.

그러나 Scale-Up은 Backend를 한 대로 운영하기 때문에 해당 인스턴스에 장애가 발생하면 Backend 전체가 중단된다.

Backend EC2 장애
↓
Backend 전체 중단

반면 Scale-Out은 여러 Backend 인스턴스를 운영하여 개별 인스턴스의 장애가 전체 서비스 중단으로 이어지는 것을 방지할 수 있다.

Need U V2에서 Scale-Out을 선택하는 핵심 이유는 처리량 자체보다 단일 인스턴스 장애를 격리하고 Backend 전체 중단을 방지하기 위해서다. 따라서 Backend는 Scale-Out 구조로 구성한다.

단, 현재 트래픽 추정만으로 실제 필요한 Backend 인스턴스 수와 EC2 사양을 확정하지 않는다. 실제 인스턴스 수와 사양은 추후 부하 테스트 및 CPU, Memory, 응답시간 등의 운영 지표를 기준으로 결정한다.

4.2 Backend 실행환경 선택

V1에서는 EC2 1대에서 Container 기반으로 Backend를 실행한다.

V2에서는 4.1에서 결정한 Backend Scale-Out 구조를 적용하여 여러 Backend 실행 단위를 운영하고, Peak Traffic 발생 시 실행 단위를 자동으로 증가시킬 수 있어야 한다.

또한 Need U는 생일, 기념일, 이벤트 등 특정 시점에 사용량이 집중되는 서비스이다. 따라서 평상시와 이벤트 발생 시의 Backend Traffic 차이가 크며, Peak Traffic에 맞춰 Backend 실행 단위를 탄력적으로 조절할 필요가 있다.

한편 Need U는 알림 발송을 위해 Spring Batch를 사용하므로, 상시 요청을 처리하는 Backend API와 Batch 작업을 동일한 실행 단위에서 운영하기보다 독립적인 실행 단위로 분리하는 것이 필요하다.

따라서 V2의 Backend 실행환경은 다음 요구사항을 기준으로 비교한다.

  • 기존 Container 기반 Backend를 유지할 수 있는가
  • Backend의 자동 Scale-Out을 지원할 수 있는가
  • 이벤트성 Peak Traffic에 따라 실행 단위를 탄력적으로 조절할 수 있는가
  • Spring Batch를 Backend API와 독립적인 실행 단위로 운영할 수 있는가
  • Container 실행을 위한 EC2 인프라 용량을 직접 관리해야 하는가

4.2.1 EC2 + Container

V1과 동일하게 EC2 인스턴스에서 Container를 직접 실행하는 방식이다.

EC2
├── Backend Container
└── Spring Batch Container

장점

  • V1의 EC2 + Container 구조를 그대로 유지할 수 있다.
  • EC2의 CPU, Memory 등 인프라 자원을 직접 제어할 수 있다.
  • 기존 구조에서 변경되는 인프라 요소가 적다.

단점

  • Container의 실행 및 배포 환경을 직접 관리해야 한다.
  • Backend Scale-Out을 적용할 경우 여러 Container와 실행 자원을 직접 관리해야 한다.
  • Peak Traffic에 대응하기 위해 Container뿐만 아니라 EC2 인스턴스의 용량도 함께 관리해야 한다.

Need U V2에서는 Backend Scale-Out과 Spring Batch를 함께 운영해야 하므로, V1과 같이 EC2에서 Container를 직접 관리하는 방식은 실행 단위와 인프라 관리 부담이 증가한다.

따라서 EC2 + Container는 선택하지 않는다.

4.2.2 ECS + EC2

ECS를 사용하여 Container를 Task 단위로 관리하고, EC2 인스턴스를 Container 실행을 위한 인프라로 사용하는 방식이다.

ECS
│
├── Backend Service
│   ├── Task 1
│   │   └── Backend Container
│   └── Task 2
│       └── Backend Container
│
└── Spring Batch
    └── Batch Task
        └── Spring Batch Container

EC2
└── Container 실행 자원 제공

ECS에서 Task는 하나 이상의 Container를 실행하는 단위이며, Service는 원하는 Task 수를 유지하고 Health Check 및 Auto Scaling 등을 관리할 수 있다.

장점

  • 기존 V1의 Container 기반 Backend를 유지할 수 있다.
  • ECS가 Container를 Task 단위로 관리할 수 있다.
  • ECS Service Auto Scaling을 통해 Backend Task 수를 자동으로 조절할 수 있다.
  • Spring Batch를 Backend와 별도의 Task로 실행할 수 있다.
  • EC2 인스턴스의 CPU, Memory 등 실행 자원을 직접 제어할 수 있다.

단점

ECS가 Task를 관리하더라도 Container를 실행할 EC2 인스턴스의 용량은 직접 관리해야 한다.

예를 들어 Backend Task가 증가하면 다음과 같은 문제가 발생할 수 있다.

Backend Task 증가
       ↓
EC2 자원 부족
       ↓
EC2 인스턴스 추가 필요
       ↓
EC2 용량 관리 필요

즉, ECS + EC2에서는 Container의 Scale-Out과 EC2 인프라 용량 관리가 함께 필요하다.

Need U V2는 이벤트성 Traffic 특성으로 특정 시점에 Backend Task가 증가할 수 있으므로, Task Scale-Out과 함께 EC2 실행 용량까지 관리해야 하는 운영 부담이 발생한다.

따라서 ECS + EC2는 선택하지 않는다.

4.2.3 ECS + Fargate

ECS에서 Container를 Task 단위로 관리하고, Fargate를 Container의 실행환경으로 사용하는 방식이다.

ECS
│
├── Backend Service
│   ├── Task 1
│   │   └── Backend Container
│   └── Task 2
│       └── Backend Container
│
└── Spring Batch
    └── Batch Task
        └── Spring Batch Container

Fargate
└── 각 Task의 실행환경 제공

Fargate는 ECS Task를 실행하기 위한 서버리스 Container 실행환경으로, Task에 필요한 CPU와 Memory 등의 실행 자원을 지정하여 사용할 수 있다. 따라서 Container를 실행하기 위한 EC2 인스턴스의 생성 및 용량 관리는 직접 수행하지 않는다.

장점

  • V1에서 사용하던 Container 기반 Backend를 유지할 수 있다.
  • ECS가 Backend Container를 Task 단위로 관리할 수 있다.
  • ECS Service Auto Scaling을 통해 Backend Task 수를 자동으로 조절할 수 있다.
  • Backend와 Batch의 실행 자원을 독립적으로 구성할 수 있다.
  • Task 실행에 필요한 EC2 인스턴스의 용량을 직접 관리하지 않아도 된다.
  • 이벤트성 Peak Traffic 발생 시 Backend Task를 증가시키고, Traffic 감소 시 다시 줄이는 탄력적인 운영이 가능하다.

단점

  • EC2 기반 실행환경에 비해 실행 자원에 대한 직접적인 제어가 제한된다.
  • 24시간 실행되는 Backend에서는 EC2 기반 방식보다 컴퓨팅 비용이 증가할 수 있다.
  • V1의 EC2 기반 Container 실행환경에서 ECS + Fargate로 구조를 변경해야 한다.

Need U 적용

Need U는 생일, 기념일, 이벤트 등 특정 시점에 사용자가 집중되는 서비스이므로 평상시와 Peak 시점의 Traffic 차이가 발생한다.

따라서 Backend를 Peak Traffic에 맞춰 항상 많은 실행 단위로 유지하기보다 다음과 같이 최소 실행 단위를 유지하면서 Traffic에 따라 Task 수를 탄력적으로 조절하는 구조가 적합하다.

평상시
Backend Task 2개
        ↓
Peak Traffic
Backend Task 증가
        ↓
Traffic 감소
Backend Task 감소

Scale-Out 자체는 ECS + EC2에서도 가능하지만, ECS + Fargate는 Task 증가에 맞춰 EC2 용량을 별도로 관리하지 않아도 된다는 점이 핵심 차이다.

또한 Spring Batch는 Backend API와 별도의 Task로 실행하므로, Backend API와 Batch 작업의 실행 자원을 분리하면서 이벤트성 Traffic에 따라 Backend 실행 단위를 탄력적으로 조절할 수 있다.

4.2.4 Serverless

Lambda와 같은 Serverless 환경에서 Backend를 실행하는 방식이다.

장점

  • 서버 인프라를 직접 관리하지 않아도 된다.
  • 요청량에 따라 실행 환경을 자동으로 확장할 수 있다.
  • 사용량 기반으로 비용이 발생하므로 호출량이 적은 경우 비용 절감이 가능하다.

단점

  • Need U의 SSE 기반 AI Chat과 장시간 연결되는 요청에 대한 별도의 구조 검토가 필요하다.
  • 현재 V2의 요구사항을 해결하기 위해 전체 Backend를 Serverless로 전환해야 할 필요성이 명확하지 않다.

Need U는 이미 Container 기반 Backend를 사용하고 있으며, V2에서는 Container를 유지하면서 Backend Scale-Out과 Spring Batch의 실행 단위 분리가 주요 요구사항이다.

따라서 전체 Backend를 Serverless로 전환하기보다 기존 Container 구조를 유지할 수 있는 ECS + Fargate를 선택하는 것이 적합하다.

따라서 Serverless는 선택하지 않는다.

4.2.5 실행환경 비용 비교

구성 기준 시간당 월간 (730시간) 가격 근거
EC2 + Auto Scaling t3.medium 1대 $0.0418 $30.51 AWS T3 인스턴스 가격
ECS + EC2 t3.medium 1대 $0.0418 $30.51 AWS ECS 가격
ECS + Fargate Linux, 2 vCPU + 4GB $0.11356 $82.90 AWS Fargate 가격
Lambda 사용량 기반 별도 계산 필요 별도 계산 필요 AWS Lambda 가격
  • 컴퓨팅 비용만 비교한 값이며 ALB, NAT Gateway, CloudWatch, EBS/스토리지, 데이터 전송 등은 제외했다.
  • Fargate는 Task 1개를 24시간 계속 실행한다고 가정했다.

근거:

비용 해석

Fargate는 단순 시간당 컴퓨팅 비용만 비교하면 EC2보다 저렴한 방식은 아니다. 따라서 가격 자체를 Fargate 선택의 근거로 사용하지 않는다.

Need U에서는 이벤트성 Traffic으로 인해 필요한 Backend 실행 단위가 시간에 따라 달라질 수 있으므로, 고정된 EC2 인프라 용량을 직접 확보하고 관리하기보다 Task 단위로 필요한 실행 자원을 탄력적으로 운영할 수 있다는 점을 선택 근거로 사용한다.

4.2.6 Backend 실행환경 최종 선택

Need U V2의 Backend 실행환경은 ECS + Fargate로 구성한다.

요구사항 ECS + Fargate
기존 Container 기반 Backend 유지 가능
Backend Task 단위 관리 가능
Backend 자동 Scale-Out 가능
이벤트성 Peak Traffic 대응 가능
Spring Batch 독립 실행 가능
EC2 용량 직접 관리 불필요

V2에서는 Backend Service의 최소 Task를 2개로 유지하고, Peak Traffic 발생 시 ECS Service Auto Scaling을 통해 Task 수를 증가시킨다. Traffic이 감소하면 다시 Task 수를 축소한다.

Spring Batch는 Backend Task와 분리된 별도의 Batch Task로 실행한다.

Redis는 Backend Task에 포함하지 않고 Backend와 AI Server가 공통으로 사용하는 별도의 Redis Container로 운영한다. Backend와 AI Server가 Scale-Out되더라도 각 Task는 동일한 Redis를 사용한다.

Redis 자체의 Scale-Out은 Backend 및 AI Server의 Scale-Out과 별도로 판단한다. 현재 V2 설계에서는 Redis 자체의 처리량이나 자원 사용량에 대한 병목 근거가 없으므로 Redis의 Scale-Out은 적용하지 않는다.

최종 구조는 다음과 같다.

                              ECS
                               │
                 ┌─────────────┴─────────────┐
                 │                           │
          Backend Service               Spring Batch
                 │                           │
          ┌──────┴──────┐                Batch Task
          │             │
        Task 1        Task 2
          │             │
      Backend       Backend
      Container     Container
          │             │
          └──────┬──────┘
                 │
              Fargate

        Redis
          │
    Redis Container

4.3 Fargate 운영 구성

4.3.1 Backend Task Definition 및 Task 사양

V2 Backend Task는 초기 기준으로 1 vCPU / 2GB Memory를 할당한다.

ECS Task Definition
├── Backend Container
│   ├── Image: ECR Backend Image
│   ├── CPU: 1 vCPU
│   ├── Memory: 2 GB
│   └── Port: Backend Port
└── Network Mode: awsvpc

초기 Task 사양은 Spring Boot 기반 REST API의 공개 성능 벤치마크를 참고하여 선정한다. 해당 벤치마크에서는 Spring Boot와 PostgreSQL을 사용하는 환경에서 Read 비율을 80:20으로 구성했으며, 1 CPU / 2GB 환경에서 약 200~300 RPS의 지속 부하를 처리한 결과를 제시한다.

근거: Load Testing - how many HTTP requests/second can a Single Machine handle?

Need U V2 역시 Read 비율을 80:20으로 가정하고 있으며, 예상 Peak API Traffic은 101~122 RPS이다. 따라서 해당 벤치마크를 초기 참고 기준으로 삼아 1 vCPU / 2GB를 Backend Task의 초기 사양으로 설정한다.

다만 벤치마크 환경과 Need U는 애플리케이션 로직, DB 조회 패턴, 응답 크기 및 실행 환경이 다르므로 벤치마크의 처리량을 Need U에 직접 적용하지 않는다. 특히 V2 Backend는 일반적인 CRUD API뿐만 아니라 AI Chat의 SSE 기반 장시간 연결도 처리하므로, 실제 부하 테스트에서 일반 API 요청과 SSE 동시 연결을 함께 검증한다.

이후 Peak Traffic 101~122 RPS를 기준으로 부하 테스트를 수행하여 Task당 처리 가능한 요청량, CPU 및 Memory 사용량, 응답시간, 오류율 및 SSE 동시 연결 처리량을 측정하고, 결과에 따라 최종 Task 사양을 조정한다.

4.3.2 Backend Task 개수

V2 Backend Service의 초기 설정은 다음과 같다.

항목 설정
Minimum Task 2개
Desired Task 2개
Maximum Task 부하 테스트 후 결정
초기 Task 사양 1 vCPU / 2GB

최소 2개 Task를 유지해 단일 Task 장애가 Backend 전체 중단으로 이어지지 않도록 한다.

Backend Service

Task 1 ── Backend Container
Task 2 ── Backend Container

4.3.3 Maximum Task 결정 기준

V2의 예상 Peak Traffic은 약 101~122 RPS이다.

그러나 현재는 Backend Task 1개가 안정적으로 처리할 수 있는 최대 RPS에 대한 부하 테스트 결과가 없기 때문에, Peak Traffic만으로 필요한 Task 개수를 산정할 수 없다.

따라서 Maximum Task는 임의의 고정값으로 결정하지 않고, V2 Peak Traffic을 기준으로 부하 테스트를 수행한 후 결정한다. 부하 테스트에서는 다음 항목을 측정한다.

  • Task당 안정적으로 처리 가능한 요청량
  • CPU 및 Memory 사용량

측정 결과를 바탕으로 Peak Traffic을 처리하는 데 필요한 Task 수를 산정하고, 이에 따라 Maximum Task를 설정한다.

4.3.4 Backend Auto Scaling

Task 개수는 고정하지 않고 ECS Service Auto Scaling으로 조절한다.

             Traffic 증가
                  ↓
          Auto Scaling 판단
                  ↓
        Backend Task 증가
                  ↓
        2 → 3 → 4 → 5 Tasks

반대로 이벤트가 끝나면 다음과 같이 축소한다.

             Traffic 감소
                  ↓
          Auto Scaling 판단
                  ↓
        Backend Task 감소
                  ↓
             5 → 4 → 3 → 2

Scaling 기준

초기 Scale-Out 기준은 ECS Service Average CPU Utilization 60%로 설정한다.

AWS는 ECS Auto Scaling에서 Scale-Out 및 Task 프로비저닝에 발생하는 지연에 대비하기 위해 여유 용량(Headroom)을 유지하고, Scaling Target을 자원 사용률의 60~80% 범위로 설정하는 것을 권장한다. Need U는 이벤트 발생 시 트래픽이 집중되는 특성이 있으므로, 해당 권장 범위의 하한인 60%를 초기 Target 값으로 설정한다.

근거: AWS ECS - Capacity and Availability Best Practices

다만 60%가 Need U Backend에 최적의 값이라고 확정할 수는 없으므로, 대표 Peak Traffic인 101~122 RPS를 기준으로 부하 테스트를 수행하여 최종값을 검증한다.

  • Target CPU Utilization = 60% (초기값)
  • Minimum Tasks = 2
  • Maximum Tasks = 부하 테스트 후 결정

Backend Task의 CPU 사용률이 Target 수준을 지속적으로 초과하면 ECS Service Auto Scaling 정책에 따라 Desired Task Count가 조정되며, ECS Service는 변경된 Desired Count에 맞춰 Task를 생성하거나 종료한다.

특히 Need U Backend는 AI Chat의 SSE 기반 장시간 연결을 처리하므로, CPU 사용률만으로 Backend의 실제 부하를 충분히 판단할 수 있는지 검증한다. 부하 테스트에서는 다음 지표를 함께 측정한다.

  • CPU Utilization
  • Memory Utilization
  • ALB Request Count per Target
  • SSE 동시 연결 수
  • 응답시간 및 오류율

부하 테스트 결과 CPU 사용률과 실제 요청 처리량, 응답시간 또는 SSE 동시 연결 수 사이에 차이가 확인되는 경우, CPU 외 지표를 추가 Scaling 기준으로 사용하는 것을 검토한다. 최종 Target CPU Utilization 및 Maximum Tasks는 부하 테스트 결과를 기반으로 결정한다.

4.3.5 Backend ALB 구성

Scale-Out된 Backend Task에 Traffic을 분배하기 위해 **Application Load Balancer(ALB)**를 사용한다.

V2에서는 이 ALB를 Backend 전용으로 분리하지 않고 Frontend와 공유한다. ALB Listener Rule을 사용하면 하나의 ALB에서 요청 Path에 따라 서로 다른 Target Group으로 Routing할 수 있다.

Internet
   │
   ▼
ALB :443
   │
   ├── /api/*  ──→ Backend Target Group
   │                ├── Backend Task 1
   │                ├── Backend Task 2
   │                └── ... Task N
   │
   └── Default /* ─→ Frontend Target Group
                    └── Frontend Task(s)

Backend와 Frontend는 각각 별도의 Target Group을 사용한다.

Fargate Task는 awsvpc Network Mode를 사용하므로 Target Type은 instance가 아니라 **ip**로 설정한다.

이 구조를 사용하면 Frontend 전용 ALB를 추가하지 않아도 되며, Frontend와 Backend의 외부 HTTPS Entry Point를 하나로 통합할 수 있다.

AI Server는 사용자에게 직접 노출되는 서비스가 아니므로 이 Internet-facing ALB의 외부 Routing 대상에 포함하지 않는다. Backend와 AI Server 간 내부 통신은 ECS Service Connect를 사용한다.

근거:

4.3.6 Backend ALB Health Check

ALB Target Group의 Health Check를 통해 Backend Task의 정상 여부를 확인한다.

항목 설정
Protocol HTTP
Port Backend Port
Path /actuator/health
Interval 5초
Timeout 5초
Healthy Threshold 2회
Unhealthy Threshold 2회
Success Code 200

Health Check 경로는 Spring Boot Actuator의 /actuator/health를 사용한다.

위 설정은 초기값이며 실제 Need U Backend의 Task 시작 및 안정화 시간을 확인한 후 최종값을 조정한다.

근거:

4.3.7 Spring Batch

Spring Batch는 Backend Service에 포함하지 않고 별도의 ECS Task로 운영한다.

ECS
│
├── Backend Service
│   ├── Task 1
│   │   └── Backend Container
│   ├── Task 2
│   │   └── Backend Container
│   └── ...
│
└── Spring Batch
    └── Batch Task
        └── Spring Batch Container

Backend API는 24시간 실행하지만 Batch는 알림 발송 작업이 필요한 시점에 실행하도록 분리한다.

따라서 Batch 작업이 실행되더라도 Backend Task의 CPU/Memory 자원을 직접 사용하지 않는다.

4.3.8 Backend ECS Service 배포 전략

Need U의 Backend는 여러 Fargate Task로 구성되므로, 새로운 Backend 버전을 배포할 때 기존 Task를 한 번에 모두 종료하지 않고 점진적으로 새로운 버전으로 교체할 수 있다.

Need U는 생일, 기념일, 이벤트 등 특정 시점에 사용량이 집중되는 서비스이므로, 배포 과정에서 기존 요청 처리가 중단되지 않도록 기존 Task를 유지하면서 새로운 Task를 단계적으로 배포하는 것이 필요하다.

이에 따라 V2 Backend의 배포 방식은 Rolling Update를 적용한다.

Rolling Update는 기존 Task와 새로운 버전의 Task가 배포 과정에서 일시적으로 함께 실행되면서 새로운 버전의 Task가 정상적으로 동작하는 것을 확인한 후 기존 Task를 점진적으로 교체하는 방식이다.

반면 Blue/Green 배포는 기존 환경과 새로운 환경을 별도로 구성한 후 검증이 완료되면 트래픽을 새로운 환경으로 전환하는 방식으로, 즉각적인 전환 및 빠른 Rollback에 유리하지만 추가 실행 환경이 필요하다.

현재 Need U에서는 별도의 신규 환경을 유지하면서 배포 전환을 수행해야 할 명확한 요구사항보다, 여러 Backend Task를 유지하면서 기존 요청 처리를 중단하지 않고 새로운 버전으로 교체하는 것이 중요하다.

따라서 추가적인 환경 구성 및 운영 복잡도를 줄이면서 무중단에 가까운 점진적 배포가 가능한 Rolling Update를 선택한다.

5. Frontend

Need U V2 Frontend는 Next.js 16.3.5 기반으로 동작하며, output: "standalone"과 Docker Image를 이용해 node server.js 형태로 실행한다.

Frontend 예상 Origin Traffic은 약 4~12 RPS로, 처리량 자체만 보면 단일 실행 단위로도 충분할 가능성이 높다. 다만 V2에서는 Application Layer의 공통 운영 원칙을 단일 Task 장애가 해당 서비스 전체 중단으로 이어지지 않도록 하는 것으로 둔다.

따라서 Frontend도 Backend와 AI Server와 동일하게 ECS + Fargate에서 최소 2개 Task를 유지한다. Frontend 2개 Task는 처리량 확보를 위한 구성이 아니라 단일 Task 장애 격리와 배포 중 요청 처리 유지를 위한 구성이다.

근거:

5.1 Frontend 예상 Origin Traffic

V2의 Peak Hour 사용자는 약 14,595명으로 가정하였다.

Backend API 요청 전체가 Next.js Server 요청으로 이어지는 것은 아니다. 정적 Resource는 Cache를 통해 Origin 요청을 줄일 수 있고, 실제 Next.js Server 부하는 SSR, Server Component, 요청 기반 Server 기능의 사용 비율에 따라 달라진다.

따라서 Backend의 Peak API Traffic인 101~122 RPS를 Frontend Server Traffic으로 그대로 사용하지 않는다.

현재 Capacity Planning에서는 Peak Hour 사용자 기준으로 다음 Scenario를 사용한다.

사용자당 Server 처리 요청 Peak Hour 요청 예상 Origin RPS
1회 14,595 약 4.05 RPS
2회 29,190 약 8.11 RPS
3회 43,785 약 12.16 RPS

Frontend Origin Traffic 내부 Scenario: 약 4~12 RPS

이는 실제 측정값이 아니므로 운영 전 부하 테스트에서 CPU, Memory, 응답시간, SSR 처리시간 등을 함께 확인한다.

5.2 Frontend Task 수 및 Scale-Out 판단

현재 예상 Traffic만 보면 Frontend가 2개 Task를 필요로 한다는 처리량 근거는 충분하지 않다. Frontend의 최소 Task를 2개로 설정하는 이유는 성능이 아니라 가용성이다.

초기 설정은 다음과 같다.

항목 초기값
Minimum Task 2개
Desired Task 2개
Maximum Task 부하 테스트 및 가용성 요구에 따라 결정
실행환경 ECS + Fargate

Frontend Task가 1개만 존재할 경우 해당 Task가 Process Crash, OOM, Health Check 실패 또는 Fargate Infrastructure 문제로 종료되면 Replacement Task가 정상화되기 전까지 Frontend 요청을 처리할 실행 단위가 없어질 수 있다.

2개 Task를 유지하면 하나의 Task에 장애가 발생하더라도 다른 Task가 요청을 계속 처리하고, ECS Service가 Desired Count를 유지하기 위해 Replacement Task를 실행할 수 있다.

Frontend Service
├── Task 1 ─ 정상 요청 처리
└── Task 2 ─ 정상 요청 처리

Task 1 장애
↓
Task 2가 요청 처리 유지
↓
ECS가 Replacement Task 실행
↓
다시 2개 Task 상태로 복구

따라서 Backend와 AI Server에 적용한 단일 Task 장애 격리 원칙을 Frontend에도 동일하게 적용한다.

Frontend Traffic이 증가해 2개 Task의 처리 용량을 초과하는 경우에는 CPU, Memory, SSR Response Time, Error Rate를 기준으로 Maximum Task와 Auto Scaling 적용 여부를 결정한다.

5.3 Frontend 실행환경 선택

V2 Frontend 실행환경 후보를 다음 기준으로 비교한다.

기준 EC2 + Docker ECS + EC2 ECS + Fargate
기존 Docker Image 유지 가능 가능 가능
ECS 기반 배포 통일 불가 가능 가능
EC2 Host 직접 관리 필요 필요 불필요
Task 장애 시 자동 재생성 별도 구성 가능 가능
향후 Scale-Out 가능 가능 가능
Backend / AI와 운영 모델 통일 낮음 중간 높음

EC2 + Docker는 현재 Traffic만 보면 충분히 가능한 선택이다. 그러나 V2에서 Backend와 AI Server가 ECS + Fargate를 사용하므로 Frontend까지 동일한 실행환경을 사용하면 Image 관리, 배포, Health Check, Task 교체, Monitoring 방식을 통일할 수 있다.

따라서 Frontend도 ECS + Fargate를 선택한다.

5.4 Frontend 비용 비교

Frontend는 0.5 vCPU + 1GB Task를 2개 상시 운영하는 것을 초기값으로 둔다.

구성 기준 월 예상 비용
EC2 + Docker t3.micro + gp3 20GB × 2 약 $22.62
ECS + Fargate 0.5 vCPU + 1GB × 2 Tasks 약 $41.46
차이 약 $18.84 / month

Fargate가 단순 Compute 비용 측면에서는 더 비싸지만, V2에서는 다음 운영 통일성과 Host 관리 제거를 함께 고려한다.

  • Frontend / Backend / AI의 ECR 기반 Image 관리
  • ECS Service / Task Definition 기반 배포
  • Fargate 기반 Host 관리 제거
  • CloudWatch / ECS Metric 운영 방식 통일
  • Task 장애 시 ECS Service의 자동 Replacement
  • 향후 Task 수 증가 시 구조 변경 없이 확장

ECS 자체를 사용한다는 이유로 동일한 추가 비용이 발생하는 것이 아니라, Fargate Task에 할당한 vCPU / Memory 사용량에 따라 비용이 발생한다.

근거:

5.5 ALB 공유 여부

Frontend와 Backend는 동일한 Internet-facing Application Load Balancer를 공유한다.

Application Load Balancer는 Listener Rule을 이용해 요청 조건에 따라 서로 다른 Target Group으로 Routing할 수 있다. 따라서 Frontend 전용 ALB를 별도로 생성하지 않고 하나의 ALB에서 Frontend와 Backend Traffic을 분리한다.

Internet
   │
   ▼
ALB :443
   │
   ├── /api/*  ──→ Backend Target Group
   │                ├── Backend Task 1
   │                └── Backend Task 2
   │
   └── Default /* ─→ Frontend Target Group
                    ├── Frontend Task 1
                    └── Frontend Task 2

Frontend와 Backend는 각각 별도의 Target Group을 사용한다.

Target Group 대상
Frontend Target Group Frontend ECS Service
Backend Target Group Backend ECS Service

Fargate는 awsvpc Network Mode를 사용하므로 Target Group의 Target Type은 **ip**로 설정한다.

AI Server는 사용자에게 직접 노출되지 않으므로 Internet-facing ALB의 Target Group에 포함하지 않는다. Backend와 AI Server 간 통신은 ECS Service Connect를 사용하며, 자세한 구성은 9.9에서 정리한다.

근거:

5.6 Frontend Health Check

Frontend ECS Service는 ALB Target Group Health Check를 사용한다.

다만 현재 Frontend 전용 Health Endpoint가 정의되어 있지 않으므로 /health와 같은 경로를 임의로 확정하지 않는다. 구현 시 다음 중 하나를 선택한다.

  • Next.js 전용 Health Endpoint 추가
  • 실제 서비스가 정상 응답하는 경량 경로 사용

Health Check를 통과하지 못한 Frontend Task는 ALB의 신규 요청 대상에서 제외되고, ECS Service는 Desired Count 2를 유지하기 위해 Replacement Task를 실행한다. 다른 정상 Task는 계속 요청을 처리한다.

근거:

5.7 Frontend 배포 전략

Frontend도 Backend / AI와 동일하게 ECS Rolling Update를 사용한다.

Frontend Task 1 / Task 2 실행 중
↓
새 Frontend Image 배포
↓
새 Task 실행
↓
ALB Health Check
↓
Healthy 확인
↓
기존 Task를 순차적으로 교체

Minimum / Desired Task를 2개로 유지하므로 Rolling Deployment 중 일부 Task를 교체하는 동안 다른 정상 Task가 사용자 요청을 처리할 수 있다.

다만 Rolling Deployment만으로 모든 종류의 장애와 중단을 보장 없이 제거할 수 있는 것은 아니므로 Health Check, Deployment Circuit Breaker, CloudWatch Alarm 및 실제 배포 테스트를 함께 사용한다.

5.8 Frontend 최종 선택

Frontend 실행환경: ECS + Fargate / Minimum 2 Tasks

초기 운영값은 다음과 같다.

항목 설정
Service Frontend ECS Service
Minimum Task 2개
Desired Task 2개
Maximum Task 부하 테스트 / 가용성 요구 후 결정
Task 사양 0.5 vCPU / 1GB
Load Balancer Backend와 동일 ALB 공유
Target Group Frontend 전용 Target Group
Deployment Rolling Update
Image Registry Amazon ECR

Frontend Task 수는 처리량이 아니라 단일 Task 장애 격리를 위해 최소 2개를 유지하며, 실제 Traffic 증가에 따른 Scale-out은 부하 테스트와 운영 Metric을 기준으로 별도로 판단한다.

6. DB

V2 DB 설계는 다음 순서로 진행한다.

Traffic / Query 분석
↓
DB QPS 산정
↓
Data 누적 규모 산정
↓
Table / Index / Working Set 분석
↓
CPU / Memory / Storage / I/O 요구사항 산정
↓
Backend DB 운영 방식 결정
↓
Sharding 도입 필요성 검토
↓
AI Vector DB 설계

6.1 DB Traffic 및 Query 분석

6.1.1 Peak API Traffic

1장에서 산정한 값을 DB Capacity Planning의 입력값으로 사용한다.

  • Peak Hour 사용자: 약 14,595명
  • 사용자 1회 이용당 API Request: 약 25~30회
  • Peak API Traffic: 약 101~122 RPS

DB QPS는 이 API RPS에 Endpoint별 Query/API를 적용해 계산한다.

6.1.2 대표 DB Query 정의

DB 부하를 분석하기 위해 Need U의 주요 기능에서 발생하는 Query Pattern을 정리한다. 대표 Query 선정 기준은 다음과 같다.

  • 반복 호출되는 Query
  • Data가 많이 누적되는 Table을 사용하는 Query
  • JOIN이 발생하는 Query
  • Filtering이 발생하는 Query
  • Sorting이 발생하는 Query
  • Pagination이 발생하는 Query
  • 지속적으로 Write가 발생하는 Query
  • Index에 따라 성능 차이가 커질 수 있는 Query

Q1. 사용자 및 친구 상세 조회

관련 Table: users, user_taste_profiles

SELECT
    u.id,
    u.nickname,
    u.profile_image_url,
    u.birth_date,
    tp.recent_tastes,
    tp.recent_interests,
    tp.ai_summary,
    tp.selected_tastes
FROM users u
LEFT JOIN user_taste_profiles tp
    ON tp.user_id = u.id
WHERE u.id = ?;
users PK Lookup
↓
user_taste_profiles 1:1 JOIN

users.id, user_taste_profiles.user_id를 이용하는 PK Lookup + 1:1 JOIN이다. Profile에는 TEXT, JSON 형태의 Data가 존재할 수 있으므로 Row Count뿐 아니라 실제 Row Size도 중요하다.

Q2. 친구 목록 조회

관련 Table: friends, users

SELECT
    f.friend_user_id,
    f.is_favorite,
    u.nickname,
    u.profile_image_url,
    u.birth_date
FROM friends f
JOIN users u
    ON u.id = f.friend_user_id
WHERE f.owner_user_id = ?
ORDER BY ...
LIMIT ?;
friends 조회
↓
users JOIN
↓
정렬
↓
Cursor Pagination

주요 Index 후보는 INDEX(owner_user_id)이며, 즐겨찾기 조건까지 함께 사용하는 경우 INDEX(owner_user_id, is_favorite)를 검토한다.

Q3. 선물 추천 목록 조회

관련 Table: gift_recommendations, products

SELECT
    gr.id,
    gr.score,
    gr.reason,
    gr.taste_keywords,
    p.id,
    p.name,
    p.category,
    p.price,
    p.image_url
FROM gift_recommendations gr
JOIN products p
    ON p.id = gr.product_id
WHERE gr.user_id = ?
  AND p.price BETWEEN ? AND ?
  AND p.status = 'ACTIVE'
ORDER BY gr.score DESC, gr.id DESC
LIMIT ?;
사용자 추천 조회
↓
상품 JOIN
↓
가격 / 상태 Filtering
↓
score 정렬
↓
Pagination

주요 Index 후보: INDEX(user_id, score, id)

Q4. 개인 추천 목록 조회

관련 Table: personal_recommendations, products

SELECT
    pr.id,
    pr.score,
    pr.reason,
    p.id,
    p.name,
    p.price,
    p.image_url,
    p.category
FROM personal_recommendations pr
JOIN products p
    ON p.id = pr.product_id
WHERE pr.user_id = ?
ORDER BY pr.score DESC, pr.id DESC
LIMIT ?;

주요 Index 후보: INDEX(user_id, score, id)

Q3과 Q4는 Recommendation List 계열 Query로 함께 분석한다.

Q5. 알림 목록 조회

관련 Table: notifications, notification_events, notification_types

SELECT
    n.id,
    n.read_at,
    n.created_at,
    ne.title,
    ne.content,
    nt.type
FROM notifications n
JOIN notification_events ne
    ON ne.id = n.notification_event_id
JOIN notification_types nt
    ON nt.id = ne.notification_type_id
WHERE n.receiver_user_id = ?
  AND n.expires_at > NOW()
ORDER BY n.created_at DESC
LIMIT ?;

특성: Multi-table JOIN, 사용자 Filtering, 유효 기간 조건, 생성 시간 Sorting, Pagination

주요 Index 후보: INDEX(receiver_user_id, created_at)

Q6. AI 대화 메시지 조회

관련 Table: ai_messages

SELECT
    id,
    sender_type,
    content,
    created_at
FROM ai_messages
WHERE room_id = ?
  AND deleted_at IS NULL
  AND id < ?
ORDER BY id DESC
LIMIT ?;
전체 ai_messages
↓
room_id로 범위 축소
↓
Cursor 기준 탐색
↓
일부 Row 반환

주요 Index 후보: INDEX(room_id, id)

Q7. 상품 피드백 저장

관련 Table: product_feedbacks

INSERT INTO product_feedbacks (...)
VALUES (...);
UPDATE product_feedbacks
SET feedback_type = ?,
    dislike_reason = ?,
    updated_at = NOW()
WHERE user_id = ?
  AND product_id = ?;

주요 Index 후보: INDEX(user_id, product_id)

사용자-상품별 하나의 Feedback만 유지한다면 Unique Constraint를 검토한다.

Q8. 상품 링크 클릭 저장

관련 Table: product_clicks

INSERT INTO product_clicks (
    user_id,
    product_id,
    recommendation_type,
    clicked_at,
    follow_up_at
)
VALUES (?, ?, ?, NOW(), ?);

지속적으로 Row가 추가되는 Event성 Write Query이다.

6.1.3 대표 Query 분석 결과

Query 기능 주요 DB 특성 유형
Q1 사용자·친구 상세 PK Lookup + 1:1 JOIN Read
Q2 친구 목록 JOIN + Pagination Read
Q3 선물 추천 JOIN + Filtering + Sorting Read
Q4 개인 추천 JOIN + Sorting + Pagination Read
Q5 알림 목록 Multi-table JOIN + Filtering + Sorting Read
Q6 AI 메시지 Range Scan + Cursor Pagination Read
Q7 상품 피드백 INSERT / UPDATE Write
Q8 상품 클릭 INSERT Write

Need U V2에는 다음과 같은 DB 작업이 존재한다.

PK Lookup
JOIN
Multi-table JOIN
Filtering
Sorting
Range Scan
Pagination
INSERT
UPDATE

동일한 Query 한 건이라도 PK 단건 조회와 JOIN + Filtering + Sorting은 처리 비용이 다를 수 있다. 또한 사용 Table 수와 SQL 실행 횟수는 같지 않다.

사용 Table 수
≠
SQL 실행 횟수

대표 Query는 이후 다음 항목에 사용한다.

Index 설계
↓
Table / Index Size 산정
↓
EXPLAIN ANALYZE
↓
Working Set 분석
↓
Load Test
↓
CPU / Memory / I/O 병목 분석

6.1.4 API 명세 기반 Query/API 분석

현재 실제 SQL Log가 없으므로 정확한 실행 횟수가 아니라 정상적인 구현을 전제로 한 Query/API 범위를 사용한다.

API 예상 Query/API
개인 추천 목록 1~2
미읽음 알림 상태 1
알림 목록 1~2
알림 읽음 1
마이페이지 1~2
설정 조회 1~2
알림 설정 변경 1~4
취향 조회 1
취향 수정 1~2
AI 대화 시작 1~2
AI 메시지 전송 2~4
AI 메시지 조회 1
취향 분석 상태 조회 1
취향 분석 확정 2~3
친구 목록 1~2
친구 상세 1~3
친구 즐겨찾기 1
친구 선물 추천 1~2
상품 상세 1~3
상품 피드백 1~2
상품 링크 클릭 1

21개 주요 Endpoint의 비가중 평균은 다음과 같다.

기준 Query/API
하한 평균 약 1.10
중간값 평균 약 1.55
상한 평균 약 2.00

이는 Traffic 가중 평균이 아니라 API 명세 기반 비가중 평균이다.

6.1.5 Peak DB QPS 계산

DB QPS
=
API RPS
×
평균 Query/API

하한 Scenario

101.35 × 1.10 ≈ 111 QPS
121.63 × 1.10 ≈ 134 QPS

약 111~134 QPS

중간 Scenario

101.35 × 1.55 ≈ 157 QPS
121.63 × 1.55 ≈ 189 QPS

약 157~189 QPS

상한 Scenario

101.35 × 2.00 ≈ 203 QPS
121.63 × 2.00 ≈ 243 QPS

약 203~243 QPS

Capacity Planning에서는 보수적 상한인 약 243 DB QPS를 Peak 검증 기준으로 사용한다.

6.1.6 Read / Write 비율

Need U V2는 Read-heavy 서비스로 판단한다. 대표 Workload Mix는 Read : Write ≈ 80 : 20이다.

Peak DB QPS 약 243에 적용하면 다음과 같다.

Read
243 × 0.8
≈ 194 QPS

Write
243 × 0.2
≈ 49 QPS

따라서 Load Test의 Peak Workload는 다음을 기준으로 한다.

Total ≈ 243 QPS
Read  ≈ 194 QPS
Write ≈  49 QPS

6.2 Data 누적 규모 산정

6.2.1 누적 가입자 Capacity Scenario

V2의 MAU와 누적 가입자는 동일하지 않으므로 누적 Data 관점의 별도 Capacity Scenario를 둔다.

현재 보수적 Scenario는 누적 가입자 약 695K이다. (K = 1,000)

6.2.2 14개월 누적 Active User-Day

Notification, AI Chat처럼 시간에 따라 누적되는 Data는 최종 DAU를 14개월 전체에 동일하게 적용하지 않고 월별 성장을 반영한다.

월별 MAU
= 이전 월 MAU × 1.5

월별 DAU
= MAU × 25%

14개월 누적 Active User-Day는 약 6.55M이다. (M = 1,000,000)

6.2.3 V2 예상 Row 수

users — 가입자당 1 Row: 약 695K Row

user_taste_profiles — Onboarding Completion 70% Scenario

695K × 70% ≈ 487K

약 487K Row

friends — 사용자당 평균 친구 관계 30개

695K × 30 ≈ 20.85M

약 20.85M Row

personal_recommendations — 사용자당 최대 100개, 최신 List만 유지

695K × 100 ≈ 69.5M

최대 약 69.5M Row

gift_recommendations

695K × 100 ≈ 69.5M

최대 약 69.5M Row

Recommendation 전체는 69.5M + 69.5M = 139M으로 최대 약 139M Row이다.

notifications

누적 Active User-Day ≈ 6.55M
1 Active User-Day ≈ 1 Notification

약 6.55M Row

ai_chat_rooms — AI 기능 이용률 40% Scenario

6.55M × 40% ≈ 2.62M

약 2.62M Row

ai_messages — 평균 3 Turn / Session, 한 Turn당 User + AI Message 2개

3 Turn × 2 Message = 6 Message / Session
2.62M × 6 ≈ 15.71M

약 15.71M Row

product_feedbacks

I_rec = 누적 Recommendation Impression
product_feedbacks ≈ I_rec × 1.75%

product_clicks

product_clicks ≈ I_rec × 9.1%

products — 상품 Pool Capacity Scenario: 약 4K Row

6.2.4 예상 Row 수 정리

Table 예상 Row 성격
users 약 695K Capacity Scenario
user_taste_profiles 약 487K Benchmark Scenario
friends 약 20.85M Benchmark Scenario
personal_recommendations 최대 69.5M Upper Bound
gift_recommendations 최대 69.5M Upper Bound
notifications 약 6.55M 누적 Scenario
ai_chat_rooms 약 2.62M 누적 Scenario
ai_messages 약 15.71M 3 Turn Scenario
product_feedbacks I_rec × 1.75% Benchmark Scenario
product_clicks I_rec × 9.1% Benchmark Scenario
products 약 4K Capacity Scenario

6.3 Table / Index / Memory 규모

6.3.1 Table Data Size

Table Data Size
≈ Row 수 × 평균 Row Size

1차 Payload 계산에서는 InnoDB Record Header와 Transaction 관련 Overhead를 약 18B로 두고 계산한다.

friends

Row ≈ 43B
20.85M × 43B ≈ 0.90GB

notifications

≈ 66~74B / Row
6.55M × 66~74B ≈ 0.43~0.48GB

ai_chat_rooms

≈ 51~67B / Row
2.62M × 51~67B ≈ 0.13~0.18GB

personal_recommendations

고정 영역 ≈ 62B / Row
69.5M × 62B ≈ 4.31GB

약 4.31GB + reason

gift_recommendations

69.5M × 62B ≈ 4.31GB

약 4.31GB + reason + taste_keywords

Recommendation 고정 Row Data 합계는 4.31GB + 4.31GB ≈ 8.62GB이다.

6.3.2 Index Size

InnoDB Secondary Index에는 Secondary Index Column과 PK가 함께 포함된다. 아래 값은 실제 B-Tree 전체 크기가 아니라 Index Key Payload Estimate이다.

friends

INDEX(owner_user_id)
≈ 16B / Entry
20.85M × 16B ≈ 0.33GB

personal_recommendations

INDEX(user_id, score, id)
≈ 20B / Entry
69.5M × 20B ≈ 1.39GB

gift_recommendations — 약 1.39GB

Recommendation Index 합계는 약 2.78GB이다.

notifications — 약 0.16GB

ai_messages — 약 0.25GB

6.3.3 주요 Data + Index 규모

Recommendation Table 기준은 다음과 같다.

Row Data      ≈ 8.62GB
Index Payload ≈ 2.78GB
합계          ≈ 11.40GB

다른 주요 Table까지 포함하면 현재 계산 가능한 Data + Index Payload는 약 13GB 이상이다.

실제 Storage에는 다음이 추가된다.

  • reason
  • taste_keywords
  • AI Message Content
  • Profile TEXT / JSON
  • B-Tree Page Overhead
  • Redo / Undo
  • Temporary Data
  • Log

6.3.4 Peak-access Data 상한

전체 DB가 RAM에 모두 올라오는 것은 아니다. 현재 계산 가능한 Peak-access Upper Bound는 다음과 같다.

friends

14,595 × 30 = 437,850 Row

Row Data + Index 약 25.8MB

Recommendations

14,595 × 100 × 2 = 2.919M Row

고정 Row Data + Index 약 239MB + 문자열

현재 계산 가능한 Peak-access 영역은 다음과 같다.

friends         ≈  25.8MB
Recommendations ≈ 239MB
합계            ≈ 265MB + α

α에는 Notification, AI Message, User, Profile, Product, 가변 문자열, Page Overhead 등이 포함된다.

6.3.5 Memory 요구사항

전체 DB Size가 약 13GB 이상이라고 해서 RAM도 13GB 이상이어야 하는 것은 아니다.

현재 계산 가능한 Peak-access 영역은 약 265MB + α이므로, 최초 Memory 검증 기준은 4GiB로 설정한다. Memory 병목이 확인되면 8GiB Instance와 비교한다.

확인 Metric:

  • FreeableMemory
  • SwapUsage
  • Buffer Pool Hit
  • ReadIOPS
  • ReadLatency
  • Query p95 / p99

6.4 CPU / Load Test / Storage 요구사항

6.4.1 CPU 요구사항

현재 DB QPS는 대표 ≈ 157~189 QPS, 보수적 Peak ≈ 243 QPS이다.

QPS를 vCPU로 직접 환산하지 않는다. 243 QPS의 PK Lookup과 243 QPS의 JOIN + Filter + Sort는 CPU Cost가 다르기 때문이다.

따라서 비용을 고려한 최초 CPU 검증 기준은 2 vCPU로 설정한다.

6.4.2 DB Load Test 기준

Test 단계는 다음과 같다.

약 160 QPS
↓
약 190 QPS
↓
약 243 QPS

최종 검증 목표는 약 243 DB QPS이며, Workload Mix는 Read : Write ≈ 80 : 20이다. 최종 단계는 Read ≈ 194 QPS, Write ≈ 49 QPS이다.

확인 Metric:

  • CPUUtilization
  • Database Load
  • Average Active Sessions
  • CPU Wait
  • FreeableMemory
  • SwapUsage
  • ReadIOPS / WriteIOPS
  • ReadLatency / WriteLatency
  • DiskQueueDepth
  • Average Query Latency, p95, p99

6.4.3 CPU 병목 판단

다음 현상이 나타나면 CPU 병목 가능성이 높다.

CPUUtilization 지속적 포화
CPU Wait 증가
Database Load 증가
Throughput 증가 정체
Query Latency 증가

이 경우 2 vCPU → 4 vCPU로 Scale-up하여 비교한다.

6.4.4 Storage 요구사항

현재 계산 가능한 주요 Data + Index Payload는 약 **13GB+**이다. 실제 Storage에는 다음이 추가된다.

  • 가변 문자열
  • 실제 Index Page
  • Redo / Undo
  • Temporary Data
  • Log
  • 향후 Data 증가

따라서 초기 Storage는 gp3 50GiB로 설정한다. 50GiB는 I/O 성능 향상을 위한 값이라기보다 Storage Capacity Margin 확보를 위한 값이다.

6.4.5 IOPS 요구사항

1 Query
≠
1 Disk I/O

Buffer Pool Hit가 발생하면 Disk Read 없이 처리할 수 있고, Cache Miss가 발생하면 하나의 Query가 여러 Page I/O를 발생시킬 수 있다. 따라서 DB QPS를 IOPS로 직접 환산하지 않는다.

초기 gp3 기준은 3,000 IOPS, 125 MiB/s이며 Load Test에서는 다음을 확인한다.

  • ReadIOPS / WriteIOPS
  • ReadLatency / WriteLatency
  • DiskQueueDepth

6.5 Backend DB 초기 요구사항

항목 기준
Peak API Traffic 약 101~122 RPS
Query/API 하한 평균 약 1.10
Query/API 중간 평균 약 1.55
Query/API 상한 평균 약 2.00
대표 DB QPS 약 157~189 QPS
Peak Capacity DB QPS 약 243 QPS
Peak Read QPS 약 194 QPS
Peak Write QPS 약 49 QPS
CPU 2 vCPU부터 검증
Memory 4GiB부터 검증
Storage gp3 50GiB
IOPS 3,000
Throughput 125MiB/s
Engine RDS MySQL 8.4

2 vCPU / 4GiB는 이미 V2 부하를 처리할 수 있다고 증명된 최소 사양이 아니라, 약 243 DB QPS를 검증하기 위한 최소 비용 Benchmark 시작점으로 사용한다.

6.6 Backend DB 운영 방식 및 Instance 선정

6.6.1 EC2 vs RDS

Need U V2의 Backend Transaction DB는 RDS for MySQL을 사용한다.

EC2에 MySQL을 직접 운영하면 Infrastructure 비용은 더 낮을 수 있지만, 팀이 직접 다음을 관리해야 한다.

  • MySQL 설치
  • Configuration
  • Patch
  • Backup
  • 복구 Test
  • Monitoring
  • Replication
  • Failover
  • Storage
  • Snapshot
  • Access Control

Need U DB에는 사용자, 친구, 취향, 추천, 알림, 상품, Feedback, Click, AI 대화 등 핵심 서비스 Data가 저장된다.

제한된 팀 Resource를 DB Server 자체 운영보다 Query 최적화, Index 설계, 서비스 개발, 성능 분석에 집중하기 위해 RDS for MySQL을 선택한다.

6.6.2 RDS 배포 방식

V2 Backend DB는 초기에는 Single-AZ DB Instance로 운영한다.

Single-AZ 선택 이유

Need U의 V2 가용성 목표는 99% 이상이며, 30일 기준 허용 가능한 누적 중단 시간은 약 7시간 12분이다.

Multi-AZ는 다른 Availability Zone에 Standby DB를 두고 Primary 장애 시 자동 Failover를 제공하므로 가용성 측면에서는 더 유리하다. 다만 Standby를 추가로 유지해야 하므로 비용이 증가하며, 현재 Need U는 아직 실제 운영에서 DB 장애 빈도나 매우 짧은 DB 복구시간이 요구된다는 근거가 없다.

또한 Backend Application은 ECS Task를 여러 개 두어 단일 Task 장애를 격리하지만, DB까지 같은 수준으로 다중화해야 하는 것은 아니다. Application 장애와 DB 장애의 발생 가능성·복구 비용·추가 비용을 별도로 판단한다.

따라서 V2 초기에는 다음 이유로 Single-AZ를 선택한다.

  • 가용성 목표가 **99%+**로, 초기 단계부터 Multi-AZ가 필수인 수준으로 정하지 않음
  • DB 장애나 AZ 장애가 실제 운영에서 반복된 근거가 아직 없음
  • Multi-AZ Standby 유지 비용을 초기부터 부담하지 않음
  • RDS의 Automated Backup, Snapshot, Point-in-Time Recovery를 사용해 Data 복구 수단은 확보
  • 실제 장애 시간과 서비스 영향이 커질 경우 Multi-AZ로 전환 가능

단, Single-AZ는 Instance 또는 AZ 장애 시 다른 Standby로 자동 Failover되지 않는다. 따라서 이 선택은 Availability Risk를 없애는 것이 아니라, 현재 목표와 비용을 기준으로 해당 Risk를 수용하는 것이다.

V2 초기
RDS MySQL
└── Single-AZ

Availability 요구 증가
또는 DB 장애 영향 증가
↓
Multi-AZ 전환

Multi-AZ DB Instance

Primary
AZ-A
  │
  │ Synchronous Replication
  ▼
Standby
AZ-B

Multi-AZ의 Standby는 일반 Application Read Traffic을 처리하는 Read Replica가 아니라 장애조치(Failover)를 위한 Standby다.

다음 조건에서는 Multi-AZ 전환을 검토한다.

  • DB 장애로 인한 서비스 중단 시간이 99% 목표를 위협
  • DB 장애 허용 시간이 더 짧아짐
  • 실제 운영에서 Instance / AZ 장애 영향이 반복적으로 확인됨
  • 핵심 사용자 Flow에서 DB 중단 허용이 어려워짐

근거:

Read Replica

Need U의 Peak DB Traffic은 약 243 QPS, Read : Write를 80 : 20으로 가정하면 Read 약 194 QPS / Write 약 49 QPS이다.

AWS의 RDS for MySQL 성능 예시에서는 db.r6g.xlarge 단일 Primary가 단순 SELECT 중심 Workload에서 약 8,000 Read QPS를 처리한 사례가 있다. Instance Class와 Query 복잡도가 달라 직접적인 처리량 비교값으로 사용할 수는 없지만, Need U의 약 194 Read QPS만으로 Read Replica를 선제 도입할 근거는 부족하다.

비교 Read 규모
Need U V2 약 194 Read QPS
AWS RDS for MySQL 예시 단일 Primary 약 8,000 Read QPS

따라서 V2 초기에는 Read Replica를 적용하지 않는다.

부하 테스트는 160 → 190 → 243 QPS까지 수행하고, Peak 구간에서 Read Query가 CPU / I/O / Query Latency 병목으로 확인될 때 Read Replica를 추가한다.

근거:

6.6.3 RDS Instance 후보

Instance vCPU Memory 유형 적용 조건
db.t4g.medium 2 4GiB Burstable 최초 Benchmark
db.t4g.large 2 8GiB Burstable Memory 병목
db.m7g.large 2 8GiB General Purpose 지속 CPU 문제
db.m7g.xlarge 4 16GiB General Purpose CPU 처리량 부족

6.6.4 Backend DB 1차 Instance 선정

최초 Benchmark 환경은 다음과 같다.

RDS for MySQL 8.4
Single-AZ

db.t4g.medium
2 vCPU
4GiB Memory

gp3
50GiB
3,000 IOPS
125MiB/s

초기 Instance는 위 사양을 Benchmark 시작점으로 사용하며, 실제 Load Test 결과에 따라 다음과 같이 조정한다.

Memory 병목
db.t4g.medium → db.t4g.large

지속 CPU 부하 / Burstable 한계
→ db.m7g.large

2 vCPU 처리량 자체 부족
→ db.m7g.xlarge

6.7 Sharding 도입 검토

6.7.1 Sharding 개념

Sharding은 하나의 Database에 저장되던 Data를 여러 Database Instance로 나누어 저장하는 Horizontal Scaling 방식이다.

Application
     │
     ├── Shard A
     ├── Shard B
     ├── Shard C
     └── Shard D

주요 목적은 다음과 같다.

  • 단일 Database Storage 한계 완화
  • 단일 Instance의 CPU / Memory / I/O 한계 완화
  • Write Traffic 분산
  • 매우 큰 Table의 Data 분산

다만 Sharding을 적용하면 Routing Logic, Cross-Shard JOIN, Cross-Shard Transaction, Data Migration 등이 복잡해진다.

6.7.2 Range-based Sharding

Shard Key의 값 범위로 나눈다.

Shard A
user_id 1 ~ 1M

Shard B
user_id 1M ~ 2M

Shard C
user_id 2M ~ 3M

장점: Range Query에 유리하고 Data 위치를 이해하기 쉽다.

한계: 증가하는 Key의 경우 마지막 Shard에 Write가 몰릴 수 있고, Shard별 Data 분포가 불균형해질 수 있다.

6.7.3 Hash-based Sharding

Shard Key를 Hash한 값을 이용해 분산한다.

hash(user_id) % 4

0 → Shard A
1 → Shard B
2 → Shard C
3 → Shard D

장점: Data를 비교적 균등하게 분산하고 Write Traffic 분산에 유리하며, 증가하는 ID에도 Hot Shard를 줄일 수 있다.

한계: 연속된 Key가 여러 Shard로 흩어지며 Range Query가 여러 Shard에 걸칠 수 있다.

6.7.4 Directory-based Sharding

별도의 Mapping 정보로 Data가 위치한 Shard를 관리한다.

user_id    shard
----------------
1001       A
1002       C
1003       B

장점: Data 배치를 유연하게 변경할 수 있고 특정 User / Tenant 단위 이동이 쉽다.

한계: Mapping 관리가 필요하고 Routing Logic이 증가하며, Mapping 자체가 새로운 장애 지점이 될 수 있다.

6.7.5 Geographic / Zone-based Sharding

지역이나 업무 영역을 기준으로 나눈다.

한국 사용자
→ Seoul Shard

미국 사용자
→ US Shard

Need U V2는 현재 국내 중심 서비스이므로 우선순위가 낮다.

6.7.6 Composite Sharding

여러 Key를 조합한다.

(region, user_id)

또는

(region, hash(user_id))

단일 Key로 해결하기 어려운 경우 사용할 수 있지만 설계와 운영 복잡도가 증가한다.

6.7.7 Sharding 방식 비교

방식 기준 장점 주요 한계 Need U V2 우선순위
Range-based Key 범위 Range Query에 유리 Hot Shard 가능 검토 가능
Hash-based Key Hash 균등 분산, Write 분산 Range Query 불리 주요 후보
Directory-based Mapping Table 유연한 Data 배치 Routing 관리 복잡 낮음
Geographic / Zone 지역 / 영역 지역성 및 규제 대응 구조 복잡 낮음
Composite 복수 Key 여러 요구사항 결합 설계 복잡 추후 검토

Need U에서 Sharding이 필요해질 경우 user_id 기반 Hash Sharding을 주요 후보로 검토할 수 있다. 다만 Sharding 적용 여부가 먼저 결정되어야 하며, 현재 단계에서 Sharding을 확정하지 않는다.

6.7.8 Need U에서 Sharding이 해결할 문제

Sharding은 복잡성을 감수하더라도 단일 Database가 감당하기 어려운 Data와 Traffic을 여러 Database로 분산하여 수평 확장해야 할 때 가치가 있는 기술이다.

따라서 Need U에서는 다음 질문을 기준으로 판단한다.

현재 Need U가 Sharding을 통해 해결해야 할 단일 DB의 한계에 도달했는가?

현재 Backend DB Capacity Scenario는 다음과 같다.

Peak DB QPS ≈ 243 QPS
Read        ≈ 194 QPS
Write       ≈  49 QPS

주요 Data + Index ≈ 13GB+

현재 큰 Table은 다음과 같다.

Table 예상 Row
friends 약 20.85M
personal_recommendations 최대 69.5M
gift_recommendations 최대 69.5M
Recommendation 합계 최대 139M
ai_messages 약 15.71M
notifications 약 6.55M

Row 수가 많다는 사실만으로 Sharding이 필요한 것은 아니다. Sharding의 주요 효과는 다음과 같다.

Write Traffic 분산
CPU / I/O 부하 분산
Storage 분산
Hotspot 완화
단일 DB의 물리적 한계를 넘어선 Scale-out

따라서 Need U에서는 현재 이 효과들이 실제로 필요한지를 확인해야 한다.

Write Traffic

현재 보수적 Peak Write는 약 49 QPS이다. Sharding을 적용하면 Write를 여러 Primary Database로 분산할 수 있다는 장점이 있지만, 현재 Capacity Scenario에서는 단일 Primary의 Write 처리량이 이미 한계에 도달했다고 판단할 근거가 없다.

따라서 현재 단계에서 여러 Primary로 Write를 나누기 위해 Sharding을 도입해야 할 필요성이 확인되지 않았다.

Read Traffic

현재 Peak Read는 약 194 QPS이다. AWS의 공개 RDS for MySQL 예시에서는 단일 Primary가 단순 SELECT 중심 Workload에서 약 8,000 Read QPS를 처리한 사례가 있으므로, 현재 예상 Read Traffic만으로 Read Replica나 Sharding을 선제 적용할 근거는 부족하다.

따라서 Query / Index 최적화 → RDS Scale-Up → 실제 Read 병목 시 Read Replica → 단일 DB 한계 시 Sharding 순서로 확장한다.

Storage

현재 계산 가능한 주요 Data + Index Payload는 약 13GB 이상이다.

Recommendation Table의 Row 수는 최대 약 139M으로 크지만, Sharding 필요성은 Row 개수 자체보다 실제 Storage, Index, Query Latency, I/O 및 운영 한계를 기준으로 판단해야 한다.

현재 계산에서는 단일 Database Storage를 여러 Database로 분리해야 할 정도의 Storage 한계가 확인되지 않았다.

6.7.9 실제 Sharding 사례 — Slack

Sharding이 어떤 규모와 문제에서 실제로 가치가 있었는지 확인하기 위해 Slack의 MySQL 사례를 참고한다.

Slack은 서비스 초기부터 MySQL을 사용했으며, 고객의 Workspace를 기준으로 Data를 여러 MySQL Shard에 분산했다. 이 구조에서는 하나의 Workspace에 속한 Message, Channel 등의 Data가 같은 Shard에 저장되어 Application이 해당 Workspace의 Shard를 찾아 Query를 전달했다.

이후 Slack의 서비스 규모가 커지면서 기존 Sharding 구조에서도 다음 문제가 발생했다.

  • 대형 고객의 단일 Shard가 가장 큰 Hardware의 처리 한계에 도달
  • 특정 Shard에 Traffic이 집중되는 Hotspot 발생
  • 수천 개의 Sharded MySQL Host 운영
  • 새로운 기능이 기존 Workspace 중심 Sharding 구조에 맞지 않는 문제

Slack은 2016년 무렵 이미 수십만 QPS 규모의 MySQL Query와 수천 개의 Sharded MySQL Host를 운영하고 있었다.

이후 Vitess 기반의 새로운 Horizontal Scaling 구조로 이전했고, 2020년 공개 자료 기준 Peak Query Load는 다음과 같다.

전체 ≈ 2.3M QPS
Read ≈ 2.0M QPS
Write ≈ 300K QPS

Slack은 이런 규모에서 단일 Host 한계, Hotspot, Shard 운영 문제를 실제로 경험했고, 더 유연하게 Data와 Load를 분산하기 위해 Vitess 기반 Sharding을 사용했다.

참고: Slack Engineering - Scaling Datastores at Slack with Vitess

6.7.10 Slack과 Need U 규모 비교

항목 Need U V2 Slack 공개 사례
전체 DB Query 약 243 QPS 약 2.3M QPS
Read 약 194 QPS 약 2.0M QPS
Write 약 49 QPS 약 300K QPS
DB 구조 초기 단일 RDS MySQL 대규모 Sharded MySQL / Vitess
주요 문제 아직 Load Test 전 Capacity Scenario Host 한계, Hotspot, 대규모 Shard 운영

단순 QPS 비율로 보면 Slack 공개 사례는 Need U V2 Capacity Scenario보다 전체 Query 약 9,500배, Write Query 약 6,100배 큰 수준이다.

물론 서비스별 Query Complexity, Hardware, Schema, Index, Transaction Pattern이 다르므로 QPS 숫자만으로 Sharding 도입 임계점을 정할 수는 없다. Slack이 300K Write QPS였으므로 그보다 낮으면 Sharding이 필요 없다는 의미도 아니다.

이 비교에서 확인하려는 것은 실제 Sharding 도입 사례에서는 단일 DB 또는 기존 Shard의 물리적 한계와 Hotspot이라는 구체적인 문제가 발생했고, 이를 해결하기 위해 Horizontal Scaling이 필요했다는 점이다.

반면 Need U의 현재 Scenario에서는 다음과 같다.

단일 Primary Write 한계
→ 확인되지 않음

CPU / I/O 포화
→ 확인되지 않음

Storage 한계
→ 확인되지 않음

Hot Shard
→ 아직 Sharding 자체를 적용하지 않았으므로 해당 없음

따라서 현재 Need U에는 Slack이 Sharding을 통해 해결해야 했던 수준의 확인된 Scale-out 문제 자체가 아직 존재하지 않는다.

6.7.11 Need U에 Sharding을 적용했을 때의 장점

현재 적용하지 않는다고 해서 Sharding이 Need U에 적합하지 않은 기술이라는 의미는 아니다. 서비스가 크게 성장한다면 다음 효과를 얻을 수 있다.

Write 분산

예를 들어 user_id를 Shard Key로 사용하면 다음과 같이 사용자별 Write를 서로 다른 Primary Database로 분산할 수 있다.

User Group A → Shard A
User Group B → Shard B
User Group C → Shard C

단일 Primary의 Write 처리량을 넘어서는 시점에는 큰 장점이 된다.

Storage 분산

Recommendation, Notification, Feedback 등의 누적 Data를 여러 Database로 분산해 단일 DB의 Storage 부담을 줄일 수 있다.

Hotspot 격리

특정 사용자 집단 또는 Data 영역의 Traffic이 증가하더라도 전체 Database가 동일한 영향을 받지 않도록 분리할 수 있다.

장애 영향 범위 축소

Shard별 장애가 독립적으로 격리되는 Architecture를 구성한다면 하나의 Shard 장애가 전체 사용자에게 영향을 주는 것을 줄일 수 있다.

6.7.12 Sharding 적용 시 추가되는 비용

Sharding의 장점을 얻는 대신 Need U Application과 운영 구조에는 새로운 복잡성이 생긴다. 중요한 것은 이러한 단점 때문에 Sharding을 배제하는 것이 아니라, 현재 Need U에서 이 비용을 감수할 만큼 Scale-out 효과가 필요한지 비교하는 것이다.

Query Routing

현재는 Backend가 하나의 MySQL Endpoint로 Query를 보내면 된다.

Backend
↓
RDS MySQL

Sharding 이후에는 요청의 Shard Key를 확인해 적절한 Database로 Routing해야 한다.

Backend
↓
Shard Key 확인
↓
Routing
├─ Shard A
├─ Shard B
└─ Shard C

JOIN 구조 변화

Need U의 대표 Query에는 다음과 같은 관계가 존재한다.

friends ↔ users
gift_recommendations ↔ products
personal_recommendations ↔ products
notifications ↔ notification_events

연관된 Data가 동일한 Shard에 위치하지 않으면 기존 Database JOIN을 그대로 수행하지 못하고 Application에서 여러 DB를 조회하거나 Data 배치를 다시 설계해야 할 수 있다.

Recommendation Shard
↓
Product ID 조회
↓
다른 DB에서 Product 조회

따라서 Shard Key를 선정할 때 함께 조회되는 Data를 가능한 한 같은 Shard에 배치하는 Data Locality가 중요하다.

Transaction 범위

단일 Database에서는 관련 변경을 하나의 Transaction으로 묶는 것이 상대적으로 단순하다.

BEGIN
↓
Data A 변경
Data B 변경
↓
COMMIT

Sharding 이후 서로 다른 Shard의 Data를 동시에 변경해야 한다면 Distributed Transaction, Event 기반 처리, 보상 처리 등 추가적인 설계가 필요할 수 있다.

Rebalancing

Shard별 Data 양이나 Traffic이 달라지면 Data를 새로운 Shard로 이동시키는 Rebalancing이 필요하다. 따라서 다음 운영 기능까지 고려해야 한다.

  • Shard Mapping
  • Data Migration
  • Dual Write / Migration 검증
  • Monitoring
  • 장애 처리
  • Shard 추가 및 분할

AWS 역시 Sharding은 Vertical Scaling이나 Read Replica 같은 기존 확장 방법의 물리적 한계를 넘어설 수 있는 방법이지만, Routing과 Data 분산 관리 등 Architecture 복잡성이 증가한다고 설명한다.

근거: AWS Database Blog - Scale your relational database for SaaS, Part 2: Sharding and routing

6.7.13 Need U의 Sharding 도입 판단

Need U V2의 Sharding 도입 여부는 Sharding 자체의 기술적 단점 때문이 아니라, 현재 Workload에서 Sharding이 제공하는 Write / Storage Scale-out 효과가 필요한지를 기준으로 판단한다.

현재 Capacity Scenario에서는 아직 다음 문제가 확인되지 않았다.

  • 단일 Primary Write 처리량 한계
  • Scale-up으로 해결할 수 없는 CPU / Memory 병목
  • Read Replica로 완화할 수 없는 Read 병목
  • 단일 DB Storage 한계
  • I/O 포화
  • 특정 Data 영역의 지속적인 Hotspot

반면 지금 Sharding을 적용하면 Query Routing, Shard Key 설계, Cross-Shard Query, Transaction, Rebalancing 및 Monitoring을 추가로 설계해야 한다.

따라서 V2 초기에는 현재 예상 부하에서 Sharding으로 해결해야 할 Scale-out 문제가 아직 확인되지 않았기 때문에 적용하지 않는다. 먼저 다음 순서로 검증한다.

Query / Index 최적화
↓
RDS Instance Scale-up
↓
Read 병목이면 Read Replica
↓
Storage / IOPS 조정
↓
Primary Write 병목 확인
↓
단일 DB의 물리적 한계 또는 Hotspot 확인
↓
Sharding 재검토

향후 단일 RDS의 Scale-up이나 Read Replica로 해결하기 어려운 Primary Write, CPU, I/O, Storage 또는 Hotspot 병목이 확인되면 Sharding을 다시 검토한다. 그 시점에는 Need U의 Data Access Pattern을 기준으로 user_id 등 실제 Shard Key 후보와 Data Locality를 별도로 검증한다.

6.8 AI Vector DB 설계

Need U V2에서는 Backend Transaction DB와 AI 검색용 Data Store를 분리한다.

Backend
↓
RDS MySQL
→ 사용자 / 친구 / 추천 결과 / 알림 / Feedback 등 서비스 Transaction Data
AI Server
↓
PostgreSQL + pgvector
→ AI Catalog Data
→ Embedding 저장
→ Vector Similarity Search

PostgreSQL에 pgvector Extension을 적용해 Vector DB 역할로 사용한다.

6.8.1 Data 소유 범위

사용자 Profile, 추천 결과와 같은 사용자 서비스 Data는 Backend DB가 소유한다. AI Vector DB에는 사용자 Embedding을 저장하지 않는다.

사용자 1명당 저장 Embedding = 0개

AI DB에는 상품 검색에 필요한 Catalog Data와 Embedding을 저장한다. 현재 AI팀 설계 기준 주요 Table은 다음과 같다.

Table 내용 Vector 여부
catalog_product AI Catalog에서 사용하는 상품 정보 X
product_semantics 상품 의미 속성, 관심사, 사용 상황, 리뷰 요약 X
product_filter_feature 알레르기, 소재, 주류 여부 등 Hard Filter 속성 X
taste_node 취향 분류 Tree X
product_taste_node 상품과 취향 Node Mapping X
embedding_space Embedding Model / Version / Dimension 정보 X
product_search_document 검색용 자연어 원문 + Embedding O

6.8.2 Product Embedding 생성

Embedding Model은 **Upstage Embed 2 (solar-embedding-2)**를 사용한다. Embedding Model은 자체 GPU Server에서 운영하지 않고 외부 API로 호출한다.

  • Embedding Dimension: 1024
  • Vector Type: float32

상품마다 검색 목적에 따라 최대 세 종류의 Search Document를 만든다.

v_content
→ 상품 자체 내용
→ 필수

v_usage
→ 어떤 상황에 적합한지
→ 선택

v_gift
→ 어떤 선물 대상 / 상황에 적합한지
→ 선택

따라서 상품 하나당 Embedding은 최소 1개, 최대 3개이다. 전체 흐름은 다음과 같다.

상품 Catalog Data
↓
상품 의미 속성 / 사용 상황 / 선물 특성 구성
↓
자연어 Search Document 생성
↓
solar-embedding-2 API 호출
↓
1024 Dimension Vector 생성
↓
product_search_document 저장
↓
pgvector Similarity Search

가격이나 재고만 변경되는 경우에는 상품의 의미 자체가 바뀌는 것이 아니므로 재Embedding하지 않는다. 주로 다음 상황에서 Embedding을 새로 생성한다.

  • 신규 상품 추가
  • Search Document의 의미가 변경되는 경우
  • 상품 의미 속성이 변경되는 경우
  • Embedding Model / Version을 변경하는 경우

6.8.3 Vector 규모 산정

현재 초기 상품 수는 약 10K로 설정한다. (K = 1,000)

사용자 Embedding은 저장하지 않으므로 Vector Row 수는 상품 수 × 상품당 Search Document 수로 결정된다.

최소 Scenario — 모든 상품에 필수 v_content만 존재하는 경우

10K Product × 1 Vector = 10K Vector

최대 Scenario — 모든 상품에 v_content, v_usage, v_gift가 존재하는 경우

10K Product × 3 Vector = 30K Vector

v_usage, v_gift의 실제 생성률은 아직 운영 Data가 없으므로 임의 평균값을 추가하지 않는다. Capacity Planning에서는 보수적으로 정상 운영 Upper Scenario = 30K Vector를 사용한다.

Vector 수가 증가하면 Exact Search의 Query Latency도 증가할 수 있으므로, 실제 성능 측정 결과에 따라 ANN Index 적용 여부를 검토한다.

  • HNSW: Query 성능과 Recall이 좋은 편이지만 Build 시간과 Memory 사용량이 더 큼
  • IVFFlat: 상대적으로 Build가 빠르고 Memory 사용량이 작지만 Recall / Query 성능 간 Trade-off가 있음

초기에는 Exact Search로 시작하고, 검색 Latency가 문제가 될 경우 HNSW 또는 IVFFlat 적용을 검토한다.

6.8.4 Vector Storage 계산

pgvector 공식 문서에서는 vector Type의 Storage를 4 × dimensions + 8 Byte로 정의한다.

근거: pgvector 공식 Repository

Need U는 1024 Dimension이므로 다음과 같다.

4 × 1024 + 8
= 4,104 Byte

따라서 Vector 하나의 크기는 4,104 Byte ≈ 4.1KB이다. 정상 운영 시 Storage는 다음과 같다.

10K Vector ≈  41MB
30K Vector ≈ 123MB

따라서 정상 운영 순수 Vector Storage ≈ 41~123MB로 산정한다.

6.8.5 Embedding Model 전환 시 Upper Scenario

Model 또는 Version을 변경할 때 기존 Vector를 바로 덮어쓰지 않는다. 전환 중에는 기존 Vector와 신규 Vector가 함께 존재한다.

Old Embedding Space
+
New Embedding Space

정상 운영 최대 30K Vector 기준으로 계산하면 다음과 같다.

30K × 2 Version = 60K Vector

60,000 × 4,104 Byte ≈ 246MB

따라서 Model 전환 Upper Scenario = 최대 약 60K Vector / 약 246MB로 산정한다. 새 Version 검증 후 Old Space를 삭제하면 다시 정상 운영 규모로 감소한다.

6.8.6 Vector 외 Storage와 Index

PostgreSQL + pgvector에는 Vector 외에도 다음 Data가 존재한다.

catalog_product
product_semantics
product_filter_feature
taste_node
product_taste_node
embedding_space
product_search_document 원문

따라서 실제 DB Size에는 Search Document Text, AI Catalog Table, 일반 Index, Vector Index, PostgreSQL Storage Overhead가 추가된다. 현재 Search Document 평균 길이와 실제 Index 구성이 확정되지 않았으므로 전체 DB Size를 임의로 계산하지 않는다.

Vector Index Size는 실제 Index 생성 후 확인한다.

SELECT pg_size_pretty(pg_relation_size('index_name'));

현재 직접 계산 가능한 규모는 다음과 같다.

항목 정상 운영 Model 전환
Product 약 10K 약 10K
Vector / Product 1~3개 1~3개 × 2 Version
Vector 수 10K~30K 최대 60K
Dimension 1024 1024
Vector 1개 4,104 Byte 4,104 Byte
순수 Vector Storage 41~123MB 최대 246MB

6.8.7 PostgreSQL 실행환경 비교

현재 Vector 규모에서는 Storage 자체보다 PostgreSQL 운영 방식이 더 중요한 선택 기준이다.

항목 EC2 + PostgreSQL RDS for PostgreSQL
PostgreSQL 설치 직접 AWS 관리
pgvector 직접 설치 지원 Extension 사용
OS 관리 직접 불필요
PostgreSQL 설정 자유도 높음 일부 제한
pgvector Version 자유도 높음 AWS 지원 범위
Backup / Restore 직접 구성 관리 기능 제공
PITR WAL 기반 직접 구성 필요 지원
Monitoring 직접 구성 RDS / CloudWatch 활용
HA 구성 직접 설계 Multi-AZ 지원
운영 복잡도 높음 낮음
비용 낮음 높음

Amazon RDS for PostgreSQL은 pgvector Extension을 지원하며, Automated Backup, Snapshot, Point-in-Time Recovery 등의 관리 기능을 제공한다.

EC2는 PostgreSQL과 pgvector Version, OS 및 설정을 자유롭게 제어할 수 있다는 장점이 있지만 PostgreSQL 설치, Patch, Backup, Monitoring, 장애 복구를 직접 관리해야 한다.

Need U에서는 PostgreSQL 자체 운영보다 AI Catalog와 Vector Search 기능 운영에 집중하기 위해 Amazon RDS for PostgreSQL을 선택한다.

근거:

6.8.8 AI Vector DB Single-AZ vs Multi-AZ

RDS 사용 시 초기 Deployment는 Single-AZ로 설정한다.

Single-AZ

AI Server
↓
RDS PostgreSQL
↓
AZ-A

하나의 Availability Zone에 DB Instance를 배치한다.

Multi-AZ DB Instance

Primary
AZ-A
  │
  │ Synchronous Replication
  ▼
Standby
AZ-B

AWS RDS Multi-AZ DB Instance는 다른 AZ에 동기식 Standby를 유지하고 Primary 장애 시 자동 Failover를 제공한다. Single Standby 방식의 Standby는 일반 Read Traffic을 처리하는 Read Replica가 아니다.

AWS에 따르면 Failover는 일반적으로 약 60~120초가 걸리며 당시 DB 상태에 따라 더 길어질 수 있다.

현재 Need U AI Vector DB는 다음과 같으며 높은 Vector Search Traffic이나 매우 짧은 장애 허용 시간이 확인된 상태가 아니다.

정상 Vector      ≈ 10K~30K
Model 전환 중    ≤ 60K
Availability 목표 = 99%+

따라서 초기에는 Single-AZ로 시작한다. 다음 조건에서는 Multi-AZ 전환을 검토한다.

  • AI 추천 기능 장애의 서비스 영향도가 커지는 경우
  • Availability 목표가 높아지는 경우
  • DB 장애 허용 시간이 줄어드는 경우
  • 실제 운영에서 RDS 장애나 Maintenance 영향이 문제가 되는 경우

Single-AZ가 99% Availability를 자동으로 보장한다는 의미는 아니며, 현재 목표와 초기 서비스 규모에서 해당 Risk를 수용하는 선택이다.

근거:

6.8.9 AI Vector DB RDS Instance 후보

초기에는 T4g Burstable 계열을 비교한다.

Instance vCPU Memory 역할
db.t4g.micro 2 1GiB 최소 사양 후보
db.t4g.small 2 2GiB 초기 후보
db.t4g.medium 2 4GiB Memory 확장 후보

참고: RDS DB Instance Class 사양

db.t4g.micro는 1GiB로 PostgreSQL Process, Connection, Query Working Memory, Vector Index 등을 고려하면 운영 Margin이 작을 수 있다. 반면 현재 Dataset 규모에서 처음부터 4GiB가 필요하다는 근거도 부족하다.

따라서 db.t4g.small / 2 vCPU / 2GiB를 최초 운영 및 성능 검증 후보로 선정한다.

6.8.10 AWS Pricing Calculator 비교

서울 Region, gp3 20GB, RDS Single-AZ 조건으로 AWS Pricing Calculator에서 직접 계산한 결과이다.

Size EC2 RDS for PostgreSQL
micro $9.41 / month $65.40 / month
small $17.00 / month $84.38 / month
medium $32.19 / month $121.61 / month

현재 선택 후보인 small에서는 EC2가 RDS보다 저렴하지만, RDS 선택은 Compute 비용보다 Backup / Restore, Monitoring, Maintenance, 장애 복구 등 Database 운영 부담을 줄이는 것을 우선한 결정이다.

비용은 Architecture 변경 시 AWS Pricing Calculator에서 다시 산정한다.

6.8.11 1차 AI Vector DB 구성

AI Server
↓
Amazon RDS for PostgreSQL
↓
pgvector
항목 선택
DB Engine Amazon RDS for PostgreSQL
Vector Extension pgvector
Deployment Single-AZ
Instance db.t4g.small
vCPU 2
Memory 2GiB
Storage gp3 20GB
정상 Vector 10K~30K
Model 전환 Upper 60K
AWS Calculator 예상 비용 $84.38 / month

따라서 V2 초기 AI Vector DB는 RDS for PostgreSQL + pgvector / Single-AZ / db.t4g.small / gp3 20GB로 설정한다.

6.8.12 AI Vector DB 확장 기준

초기 Instance는 실제 Vector Search Workload를 기준으로 검증한다. 확인 Metric은 다음과 같다.

  • CPUUtilization
  • CPU Credit 사용량
  • FreeableMemory
  • DatabaseConnections
  • ReadLatency / WriteLatency
  • DiskQueueDepth
  • Vector Search Latency
  • Query p95 / p99

병목 원인별 확장 방향은 다음과 같다.

Memory 부족
→ db.t4g.medium

지속 CPU 병목
→ General Purpose 계열 검토

Availability 요구 증가
→ Multi-AZ

db.t4g.small과 db.t4g.medium은 모두 2 vCPU Burstable 계열이므로 지속적인 CPU 병목이라면 Memory만 늘리는 것보다 General Purpose 계열을 별도로 검토한다.

7. AI

V1에서도 Backend와 AI Server를 별도의 Container로 구성하여 운영하고 있다. 따라서 V2에서는 Backend와 AI Server를 새롭게 분리하는 것이 아니라, 증가한 AI 요청을 독립적으로 확장하고 운영할 수 있도록 AI Server의 실행환경을 변경한다.

V2에서는 MAU 증가와 AI 기능 확장으로 대표 AI Inference Traffic이 약 4.87 RPS까지 증가할 것으로 예상한다.

또한 Need U의 AI 챗봇은 SSE를 사용한다.

FE ───── SSE ─────► BE ───── SSE ─────► AI

Frontend와 Backend 사이, Backend와 AI Server 사이에 각각 스트리밍 연결이 생성되며, AI Server는 Redis, PostgreSQL + pgvector, Embedding API, GPU Model Server와 통신하면서 응답을 생성한다.

따라서 AI Server는 외부 시스템의 응답을 기다리는 동안에도 HTTP 연결을 유지하면서 응답을 스트리밍할 수 있어야 한다.

이에 따라 V2에서는 다음 조건을 만족하는 AI Server 실행환경을 선택한다.

  • AI Server의 SSE 연결을 안정적으로 유지할 수 있어야 한다.
  • AI 요청 증가에 따라 AI Server만 독립적으로 Scale-out할 수 있어야 한다.
  • AI Model을 직접 실행하지 않는 AI Server의 특성에 맞게 CPU/Memory 기반 Container를 운영할 수 있어야 한다.
  • GPU Model Server와 AI Server의 실행환경과 생명주기를 분리할 수 있어야 한다.

7.1 AI Server 실행환경 후보 비교

V2에서 고려하는 실행환경은 EC2 + Docker, ECS + EC2, ECS + Fargate, Lambda이다.

실행환경 Need U 적용 방식 SSE AI Server 독립 확장 운영 특성
EC2 + Docker EC2에서 Container 직접 운영 가능 Container 추가 방식으로 가능 V1과 동일한 운영 방식
ECS + EC2 EC2 위에서 ECS Task 운영 가능 AI Service 단위 Scale-out 가능 Task 증가 시 EC2 자원도 함께 고려
ECS + Fargate Fargate에서 ECS Task 운영 가능 AI Service의 Task 단위로 Scale-out AI Server Task와 Host 관리 분리
Lambda 요청 단위 Function 실행 제약 존재 요청 수에 따라 자동 확장 지속적인 SSE 연결을 사용하는 AI Server와 운영 방식이 다름

EC2 + Docker

V1에서 이미 사용하고 있는 방식으로, EC2에서 AI Server Container를 실행할 수 있다. Container를 추가하여 AI Server를 여러 개 실행하는 것도 가능하므로 Scale-out 자체가 불가능한 것은 아니다.

그러나 V2에서는 AI 요청 증가에 따라 AI Server의 Container 수를 Backend와 별도로 조정해야 한다. 기존 Docker Compose 방식에서는 이러한 Container 실행과 확장에 필요한 EC2 자원을 직접 관리해야 하므로, V2에서 요구하는 AI Server 중심의 독립적인 확장 구조를 구성하려면 운영 관리 범위가 커진다.

따라서 V1의 빠른 검증에는 적합하지만, V2의 AI Server 운영환경으로는 제외한다.

ECS + EC2

ECS + EC2에서는 AI Server를 ECS Task로 실행하고 Service 단위로 Scale-out할 수 있다. 따라서 Need U의 AI Server 독립 확장 요구 자체는 충족할 수 있다.

다만 AI 요청 증가에 따라 AI Server Task를 추가하려면 해당 Task가 실행될 EC2 Host에 CPU와 Memory 여유가 있어야 한다. 만약 Task를 추가할 시점에 Host 자원이 부족하다면 AI Server Task 확장과 함께 EC2 자원도 조정해야 한다.

Need U의 AI Server는 GPU를 직접 사용하지 않으며, Application 실행에 필요한 CPU와 Memory를 중심으로 자원을 사용한다. 따라서 AI Server Task의 확장과 별도로 EC2 Host의 용량을 함께 관리해야 한다는 점이 운영상의 부담이 될 수 있다.

이러한 특성 때문에 ECS + EC2보다 Task 자체를 AI Server의 확장 단위로 가져가는 Fargate를 선택한다.

ECS + Fargate

ECS + Fargate에서는 AI Server를 하나의 ECS Service로 구성하고 여러 Fargate Task로 실행한다.

AI Server Service
       │
 ┌─────┼─────┐
 ▼     ▼     ▼
Task 1 Task 2 Task 3

Need U에서는 AI 요청 증가에 따라 Task의 CPU와 Memory 사용량이 증가할 수 있다. 따라서 부하테스트를 통해 Task당 안정적인 처리 범위를 확인한 후, 해당 범위를 초과하는 경우 AI Server Task를 추가하는 방식으로 Scale-out할 수 있다.

또한 AI Server는 GPU Model을 직접 실행하지 않으므로 GPU가 필요한 실행환경을 별도로 확보할 필요가 없다. AI Server에는 FastAPI Application을 실행하고 Redis, PostgreSQL + pgvector, Embedding API, GPU Model Server와 통신할 수 있는 CPU와 Memory만 필요하다.

따라서 AI Server Task와 Task에 필요한 CPU/Memory를 기준으로 실행환경을 구성하고, Host 관리와 Task 확장을 분리할 수 있다는 점에서 Fargate를 사용한다.

Lambda

Lambda는 요청 단위로 실행되는 Serverless 환경이므로 일반적인 API 처리에는 사용할 수 있지만, Need U의 AI Server는 AI 요청을 처리하는 동안 SSE 연결을 유지하면서 외부 시스템의 응답을 기다리는 구조이다.

또한 AI Server는 단순히 하나의 요청을 처리하고 종료하는 Function이 아니라 Redis, PostgreSQL + pgvector, Embedding API, GPU Model Server를 호출하고 결과를 스트리밍하는 상시 HTTP Application Server 형태이다.

따라서 Lambda의 요청 단위 실행 모델보다는 Container 기반으로 AI Server를 지속적으로 실행하면서 SSE 연결을 관리하는 ECS가 Need U의 구조에 적합하다.

7.2 AI Server 실행환경 선택

각 후보를 Need U의 요구사항에 적용하면 다음과 같다.

기준 EC2 + Docker ECS + EC2 ECS + Fargate Lambda
SSE 기반 AI 요청 가능 가능 가능 제약
AI Server 독립 Scale-out 가능 가능 가능 가능
V1 구조와의 연속성 높음 중간 낮음 낮음
AI Task 확장 시 Host 자원 고려 필요 필요 불필요 불필요
GPU Server와 분리 가능 가능 가능 가능
Need U AI Server 운영 방식 V1과 동일 가능 적합 구조 차이

V1의 EC2 + Docker 방식에서도 AI Server를 실행하고 확장하는 것은 가능하다. 그러나 V2에서는 AI 요청 증가에 따라 AI Server의 자원 사용량이 증가할 수 있으므로, Backend와 별도로 AI Server의 Task 수를 조정할 수 있는 구조가 필요하다.

ECS + EC2 역시 이러한 독립적인 Scale-out이 가능하지만, AI Server Task를 추가할 때 해당 Task가 실행될 EC2 Host의 CPU와 Memory 여유까지 함께 고려해야 한다.

반면 ECS + Fargate에서는 AI Server를 Container 단위의 Task로 실행하고, AI 요청 증가에 따라 AI Server Task의 자원 사용량을 기준으로 Task 수를 조정하면서 Host 자원 관리는 Fargate에 맡길 수 있다.

따라서 V2의 AI Server 실행환경은 ECS + Fargate로 선정한다.

7.3 AI Server Scale-up과 Scale-out

AI Server의 실행환경을 ECS + Fargate로 선정한 후, Task의 확장 방식은 Scale-out을 기본으로 한다.

Need U의 AI Server는 AI Model 자체를 실행하지 않고 Redis, PostgreSQL + pgvector, Embedding API, GPU Model Server와 통신한다.

AI Request
   │
   ├──► Redis
   ├──► PostgreSQL + pgvector
   ├──► Embedding API
   └──► GPU Model Server

또한 외부 시스템의 응답을 기다리는 동안 SSE 연결이 유지될 수 있다.

따라서 CPU와 Memory를 증가시키는 Scale-up은 하나의 Task가 사용할 수 있는 CPU와 Memory 자원을 증가시킬 수 있지만, 외부 시스템의 응답시간이나 외부 시스템의 처리 한계 자체를 개선하는 것은 아니다.

반면 여러 Task로 요청을 분산하면 하나의 Task에 집중되는 AI 요청과 외부 시스템 대기 작업을 분산할 수 있다. 이를 통해 Task별 CPU와 Memory 사용량을 낮추고, 전체적으로 처리할 수 있는 AI 요청량을 증가시킬 수 있다.

따라서 Need U에서는 AI 요청 증가에 따라 Task의 부하가 증가하는 상황에 대응하기 위해 CPU Utilization을 기준으로 AI Server Task를 추가하는 Scale-out 방식을 적용한다.

7.4 AI Server와 GPU Model Server 분리

AI Server는 실제 AI Model을 실행하지 않고 GPU Model Server에 Inference를 요청한다.

AI Server
    │
    │ HTTP Request
    ▼
GPU Model Server
    │
    ▼
   vLLM
  ┌──┴──┐
  ▼     ▼
 8B    1.3B

AI Server는 Redis, PostgreSQL + pgvector, Embedding API, GPU Model Server와 통신하고 SSE 응답을 관리하는 Application Server이므로 Fargate Task로 실행한다.

반면 GPU Model Server는 GPU와 AI Model을 직접 사용하는 별도의 실행환경이며, Need U에서는 RunPod Serverless GPU를 사용한다.

따라서 두 서버는 역할과 필요한 자원이 다르므로 별도의 실행환경으로 구성한다.

7.5 AI Server 내부 통신 및 장애 대응

AI Server는 사용자에게 직접 노출되는 서비스가 아니며 Backend만 내부적으로 호출한다. 따라서 Internet-facing ALB의 Target Group에 AI Server를 등록하지 않고, Backend와 AI Server 간 내부 통신에는 Amazon ECS Service Connect를 사용한다.

Backend ECS Service
↓
ECS Service Connect
↓
AI ECS Service
├── AI Task 1
└── AI Task 2

Fargate Task는 배포, 장애 복구, Scale-out 과정에서 Private IP가 변경될 수 있으므로 Backend가 특정 AI Task IP를 직접 참조하지 않는다. Backend는 ai-server와 같은 Service Connect의 논리적 Service Name으로 AI Server를 호출한다.

AI Server는 초기 2개 Fargate Task를 유지한다. 하나의 AI Task에 Process Crash, OOM, Container Health Check 실패 등의 장애가 발생하면 ECS Service가 해당 Task를 교체하고, 다른 정상 Task가 새로운 AI 요청을 계속 처리한다.

다만 이미 특정 AI Task에서 처리 중인 SSE Streaming Connection은 해당 Task 장애 시 다른 Task로 자동 이전되지 않는다. 따라서 SSE Retry / Resume, Graceful Shutdown, Timeout 정책을 별도로 적용한다.

근거:

7.6 AI Server Scale-out 기준

V2의 대표 AI Inference Traffic은 약 4.87 RPS이다. AI Server는 AI 요청을 처리하는 동안 Redis, PostgreSQL + pgvector, Embedding API, GPU Model Server 등의 외부 시스템과 통신하며 SSE 연결을 유지할 수 있으므로, RPS만으로 Task 부하를 판단하기 어렵다.

AI 요청의 예상 응답시간은 요청 유형에 따라 다음과 같다.

요청 유형 예상 응답시간
대화 응답(챗봇) 평균 0.5초
취향 분석 3초 이내
벡터DB 상품 추출 6초 이내

현재 요청 유형별 실제 트래픽 비율이 확인되지 않았으므로, 동시 SSE 연결 수를 보수적으로 산정하기 위해 가장 긴 응답시간인 6초를 W(SSE 연결 유지시간)의 초기값으로 사용한다.

단, 위 응답시간은 실제 측정값이 아니라 AI 개발 담당자가 제공한 예상 응답시간이며, GPU Model Server가 Warm 상태인 경우를 기준으로 한다. RunPod Serverless GPU에서 Cold Start가 발생할 경우 추가 지연이 발생할 수 있으므로 실제 운영 환경에서는 별도로 검증한다.

평균 동시 SSE 연결 수

Little's Law L = λ × W를 적용하면 대표 Peak 시점의 평균 동시 SSE 연결 수를 다음과 같이 추정할 수 있다.

λ = 4.87 RPS
W = 6초

L = λ × W
  = 4.87 × 6
  ≈ 29.2개

따라서 대표 Peak 시점에는 평균적으로 약 29.2개의 AI 요청이 동시에 처리 중일 것으로 예상한다. 29.2개는 AI Server의 처리 한계가 아니라 대표 Peak에서 예상되는 평균 부하이다.

동시 SSE 연결 수는 현재 AI Server가 처리 중인 요청 규모를 파악하는 지표로 활용할 수 있지만, SSE 연결 수 자체가 Task의 CPU 및 Memory 사용량을 직접 나타내는 지표는 아니다. 요청 유형에 따라 처리 과정과 외부 시스템 호출이 달라질 수 있으므로, 동시 연결 수만으로 Task의 실제 자원 사용량을 판단하기는 어렵다.

따라서 V2 초기 AI Server의 주요 Scale-out 기준은 CPU Utilization 60%로 설정한다. CPU Utilization이 Target인 60% 수준으로 증가하면 Task 수를 증가시켜 AI 요청 증가에 대응한다.

근거: AWS ECS - Capacity and Availability Best Practices

또한 CPU Utilization과 함께 Memory Utilization, Response Latency, P95/P99 Latency, Error Rate를 모니터링하여 실제 AI 요청이 안정적으로 처리되는지 확인한다.

Scale-out 기준 검증

초기 AI Server Task는 1 vCPU / 2 GB Memory를 기준으로 구성한다.

설정한 CPU Utilization 60%가 실제 AI 요청 처리에 적절한지 확인하기 위해 Load Test에서는 AI 요청량을 단계적으로 증가시키면서 다음 지표를 측정한다.

  • CPU Utilization
  • Memory Utilization
  • Response Latency
  • P95 / P99 Latency
  • Error Rate
  • 동시 처리 중인 AI 요청 수

대표 Peak인 4.87 RPS를 포함하여 Peak 이상의 부하까지 요청량을 단계적으로 증가시키면서 CPU Utilization과 Response Latency, Error Rate의 변화를 확인한다.

이를 통해 대표 Peak 트래픽에서 AI Server가 안정적으로 요청을 처리할 수 있는지 확인하고, CPU Utilization 60%를 기준으로 Task를 추가하는 현재 Scale-out 설계가 적절한지 검증한다.

7.7 AI Server ECS Service Auto Scaling

AI Server의 Task 수는 ECS Service Auto Scaling을 통해 조절한다. 주요 Scaling Metric은 CPU Utilization으로 설정하고, V2 초기 Scale-out Target은 60%로 설정한다.

AI Server는 SSE를 사용하여 AI 요청을 처리하는 동안 HTTP 연결을 유지한다. 또한 Redis, PostgreSQL + pgvector, Embedding API, GPU Model Server 등 외부 시스템과 통신하면서 응답을 생성한다.

이러한 특성 때문에 AI Server의 부하는 단순한 요청 수뿐만 아니라 외부 시스템의 응답시간과 요청 처리에 필요한 CPU 및 Memory 사용량에 따라 달라질 수 있다. 따라서 V2에서는 AI Server Task의 실제 자원 사용량을 나타내는 CPU Utilization을 주요 Scaling Metric으로 사용한다.

CPU Utilization이 Target인 60% 수준으로 증가하면 ECS Service Auto Scaling이 ECS Service의 Desired Task Count를 증가시키고, Fargate에서 새로운 AI Server Task를 실행한다.

새로운 Task가 정상적으로 실행되고 Container Health Check를 통과하면 ECS Service의 정상 Task로 유지되며, Service Connect를 통해 새로운 AI 요청을 처리할 수 있다.

초기 Service 설정

항목 설정
Minimum Tasks 2개
Desired Tasks 2개
Maximum Tasks 부하테스트 후 결정
주요 Scaling Metric CPU Utilization
Scale-out Target 60%

Minimum Tasks를 2개로 설정하여 하나의 Task에 장애가 발생하더라도 다른 Task에서 새로운 AI 요청을 처리할 수 있도록 한다.

Scale-out Target 검증

7.6에서 계산한 대표 Peak AI Inference Traffic은 약 4.87 RPS이다.

그러나 4.87 RPS만으로 AI Server Task의 실제 처리 한계를 결정하기는 어렵다. AI Server는 요청을 처리하는 동안 Redis, PostgreSQL + pgvector, Embedding API, GPU Model Server 등 외부 시스템과 통신하기 때문에 요청량과 CPU 사용량이 단순히 비례한다고 보기 어렵기 때문이다.

따라서 V2에서는 CPU Utilization 60%를 초기 Scale-out Target으로 설정하고, 부하테스트를 통해 해당 Target이 대표 Peak 트래픽에서 적절하게 동작하는지 검증한다.

부하테스트에서는 대표 Peak인 4.87 RPS를 포함하여 그 이상의 요청량까지 단계적으로 증가시키면서 CPU Utilization, Memory Utilization, Response Latency, P95 / P99 Latency, Error Rate를 측정한다.

이를 통해 CPU Utilization이 60%에 도달했을 때 Task가 추가되면서 Response Latency와 Error Rate가 안정적으로 유지되는지 확인한다. 또한 부하 증가에 따라 Task 수가 증가했을 때 AI Server가 안정적으로 요청을 처리하는지 확인하고, 필요할 경우 Task의 CPU/Memory 사양과 Maximum Tasks를 조정한다.

ECS Service Auto Scaling 적용

Load Test를 통해 결정한 Scale-out Threshold를 ECS Service Auto Scaling의 Target으로 사용한다.

AI Server Service
        │
        ├── Task 1
        └── Task 2
              │
              ▼
       CPU Utilization
              │
              ▼
    ECS Service Auto Scaling
              │
       ┌──────┴──────┐
       ▼             ▼
   Scale-out      Scale-in
       │             │
 Desired 증가    Desired 감소
       │             │
       ▼             ▼
 Fargate Task 추가  Task 감소

Scale-out이 발생하면 ECS Service가 Desired Task Count를 증가시키고, Fargate에서 새로운 AI Server Task를 실행한다. 새로운 Task가 정상적으로 실행되고 Container Health Check를 통과하면 Service Connect를 통해 AI 요청을 처리할 수 있다.

반대로 AI 요청이 감소하여 CPU Utilization이 낮아지면 ECS Service Auto Scaling을 통해 Desired Task Count를 감소시킨다.

Maximum Tasks는 사전에 임의의 값을 설정하지 않고, 부하테스트를 통해 대표 Peak 및 그 이상의 부하를 처리하는 데 필요한 Task 수를 확인한 후 결정한다.

7.8 AI Server Task 자원

AI Server는 AI Model을 직접 실행하지 않고 PostgreSQL + pgvector 조회, Redis 통신, Embedding API 및 GPU Model Server 호출, SSE 응답 전달을 담당한다.

실제 AI Model Inference는 별도의 GPU Model Server에서 수행하므로, AI Server Task에는 GPU가 필요하지 않으며 CPU와 Memory 중심의 Container 자원을 구성한다. 따라서 초기 Task는 1 vCPU / 2 GB Memory를 기준으로 구성한다.

항목 초기 설정 선정 근거
CPU 1 vCPU AI Model을 직접 실행하지 않고 외부 시스템 호출 및 응답 처리가 중심
Memory 2 GB FastAPI Application과 SSE 연결을 처리하기 위한 초기 운영값

참고: FastAPI vs Django Benchmark

1 vCPU / 2 GB는 확정된 처리 용량이 아니라 V2 초기 운영을 위한 시작점이다. 실제 Load Test에서 동시 SSE 연결을 증가시키면서 CPU, Memory, Response Latency, Error Rate를 측정하고 최종 Task 사양을 결정한다.

7.9 AI Server ECS Service 배포 전략

AI Server 역시 여러 Fargate Task로 구성되므로, 새로운 AI Server 버전을 배포할 때 기존 Task를 한 번에 종료하지 않고 새로운 버전의 Task로 점진적으로 교체한다.

다만 Backend와 달리 AI Server는 AI 요청 처리 과정에서 SSE 연결을 유지할 수 있다. 따라서 배포 과정에서 기존 AI Server Task를 즉시 종료하면 해당 Task에서 처리 중인 SSE 요청이 중단될 수 있다. 이를 방지하기 위해 기존 Task를 유지하면서 새로운 버전의 Task를 추가하고, 새로운 Task가 정상적으로 동작하는 것을 확인한 후 기존 Task를 단계적으로 종료하는 방식을 적용한다.

Rolling Update와 Blue/Green 비교

항목 Rolling Update Blue/Green
배포 방식 기존 Task와 새 Task를 점진적으로 교체 기존 환경과 신규 환경을 별도로 구성한 후 전환
추가 실행 환경 필요하지 않음 기존 환경과 신규 환경을 동시에 운영
배포 중 자원 사용량 기존 Task + 일부 신규 Task 기존 환경 + 신규 환경
트래픽 전환 Task 단위로 점진적 교체 신규 환경으로 일괄 전환 가능
Rollback 이전 Task를 다시 실행 기존 환경으로 트래픽 전환
SSE 연결 고려 기존 연결이 종료될 때까지 기존 Task 종료를 유예할 수 있음 기존 환경을 유지한 상태에서 신규 환경으로 전환 가능
Need U 적용 시 적합 가능하지만 추가 운영 자원 필요

Blue/Green Deployment는 기존 환경과 새로운 환경을 동시에 유지한 상태에서 새로운 버전을 검증한 후 트래픽을 새로운 환경으로 전환할 수 있다. 따라서 배포 중 기존 환경을 유지하면서 빠르게 Rollback할 수 있다는 장점이 있다.

그러나 Need U의 AI Server는 이미 ECS Service에서 여러 Fargate Task로 구성되어 있으며, 새로운 버전을 배포하기 위해 별도의 전체 실행 환경을 추가로 유지해야 하는 요구사항은 없다. 또한 AI Server는 SSE 연결을 유지할 수 있으므로, Blue/Green을 사용하더라도 기존 환경에서 이미 연결된 SSE 요청이 처리되는 동안 기존 환경을 일정 시간 유지해야 한다.

반면 Rolling Update는 기존 Task와 새로운 Task를 함께 운영하면서 새로운 Task의 Health Check를 확인한 후 기존 Task를 단계적으로 교체할 수 있다.

따라서 Need U에서는 별도의 전체 실행 환경을 추가로 유지하는 Blue/Green보다, 기존 Fargate Task와 새로운 Fargate Task를 점진적으로 교체하면서 SSE 연결을 고려할 수 있는 Rolling Update를 초기 배포 전략으로 선택한다.

8. 장애 대응

8.1 공통 장애 대응

Need U V2에서는 Frontend, Backend, AI Server, Database 등 각 Infrastructure Component에서 장애가 발생했을 때 다음 공통 흐름으로 대응한다.

장애 / 이상 징후 발생
↓
CloudWatch Monitoring
↓
CloudWatch Alarm
↓
Amazon SNS
↓
운영자 Email 알림
↓
자동 복구 또는 수동 복구
↓
Service Health 확인
↓
Grafana / CloudWatch Log 분석
↓
장애 원인 분석 및 재발 방지

CloudWatch Alarm은 Metric이 설정된 조건을 만족해 ALARM 상태로 변경되면 Amazon SNS Topic으로 Notification을 전달할 수 있으며, SNS Email Subscription을 통해 운영자에게 Email을 전송할 수 있다.

8.1.1 장애 감지

각 Infrastructure Component의 주요 Metric과 Health 상태를 CloudWatch에서 Monitoring한다.

대상 주요 감지 항목
Frontend ECS CPUUtilization, MemoryUtilization, Task 상태, Application Health
Backend ECS CPUUtilization, MemoryUtilization, Task 상태
AI Server ECS CPU, Memory, Task 상태, SSE 처리 상태
ALB UnHealthyHostCount, TargetResponseTime, Target 5XX
Backend RDS MySQL CPUUtilization, FreeableMemory, DatabaseConnections, Read/Write Latency, FreeStorageSpace
AI RDS PostgreSQL CPUUtilization, FreeableMemory, DatabaseConnections, Read/Write Latency, Vector Search Latency
Redis CPU, Memory, Connection 및 Application 접근 상태

ECS는 Service 단위의 CPUUtilization, MemoryUtilization Metric을 CloudWatch에서 제공하며 Fargate Task에도 Service-level Metric을 사용할 수 있다.

RDS는 CPUUtilization, FreeableMemory, DatabaseConnections, ReadLatency, WriteLatency, DiskQueueDepth, FreeStorageSpace 등의 Metric을 CloudWatch로 제공한다.

모든 순간적인 Metric 변화에 Alarm을 발생시키기보다는 일정 시간 이상 임계값을 초과하는 경우 Alarm이 발생하도록 설정하여 불필요한 알림을 줄인다.

CPUUtilization
≥ 설정 Threshold

일정 시간 지속
↓
CloudWatch Alarm

반면 ECS Service의 정상 Task 수 부족처럼 서비스 중단과 직접 연결될 가능성이 높은 장애는 빠르게 감지하도록 구성한다.

8.1.2 Email 장애 알림

CloudWatch Alarm이 ALARM 상태가 되면 Amazon SNS Topic으로 Event를 전달하고, 해당 Topic에 등록된 운영자 Email로 알림을 발송한다.

CloudWatch Alarm
↓
Amazon SNS Topic
↓
Email Subscription
↓
Cloud Team

Email에는 가능한 한 다음 정보를 포함한다.

Service
Environment
발생 시간
Alarm 이름
문제가 발생한 Resource
현재 Alarm 상태
감지된 Metric
자동 복구 수행 여부
확인이 필요한 항목

예시는 다음과 같다.

[CRITICAL] Need U Frontend 장애

Environment: Production
Resource: needu-frontend
Alarm: UnHealthyHostCount
State: ALARM

자동 대응:
ECS Replacement Task 실행

확인:
Frontend Health Check 및 Grafana Dashboard 확인

SNS의 Email Subscription은 수신자가 최초 Subscription 확인을 완료해야 실제 Alarm Email을 받을 수 있다.

8.1.3 자동 복구

장애 발생 시 모든 상황을 운영자가 직접 처리하지 않고, 자동 복구가 가능한 영역은 AWS 또는 Application 기능을 이용한다.

Frontend / Backend / AI Task 장애
→ ECS Service가 Desired Task 수 유지

일시적 Traffic 증가
→ ECS Service Auto Scaling

배포 Version 장애
→ 이전 정상 Image로 Rollback

Target 장애
→ ALB가 Unhealthy Target을 신규 요청에서 제외

자동 복구가 수행되더라도 장애가 해결되었다고 바로 종료하지 않고 이후 Health Check를 통해 실제 Service가 정상적으로 복구되었는지 확인한다.

자동 복구
↓
Health Check
↓
정상
→ Service 복구 확인

비정상
→ 운영자 수동 대응

8.1.4 복구 후 원인 분석

자동 복구를 통해 Service가 정상화되었더라도 장애 발생 원인을 별도로 확인한다.

복구 후에는 Grafana Dashboard와 CloudWatch Log / Metric을 확인하여 장애 전후의 상태를 분석한다.

Service 복구
↓
Grafana Dashboard 확인
↓
CloudWatch Metric / Log 확인
↓
장애 발생 시점 전후 비교
↓
원인 분석

확인할 수 있는 예시는 다음과 같다.

CPU가 장애 직전에 급격히 증가했는가?
Memory가 지속적으로 감소했는가?
Request Traffic이 갑자기 증가했는가?
ALB 5XX Error가 증가했는가?
ECS Task Restart가 반복되었는가?
Database Connection이 급증했는가?
DB Read / Write Latency가 증가했는가?
Storage가 부족해졌는가?
배포 직후 장애가 발생했는가?

Grafana는 여러 Infrastructure Metric을 한 화면에서 비교하여 장애 발생 시점 전후의 변화를 확인하는 분석 Dashboard로 사용한다. 따라서 Grafana의 역할은 장애 자체를 직접 복구하는 것이 아니라 장애 발생 이후 Metric을 종합하여 Root Cause를 분석하는 것으로 정의한다.

8.1.5 장애 대응 후 재발 방지

장애 원인을 확인한 뒤 동일한 문제가 반복되지 않도록 Architecture나 운영 설정을 조정한다.

장애 발생
↓
자동 / 수동 복구
↓
Grafana / Log 분석
↓
Root Cause 확인
↓
재발 방지 조치

예를 들어 다음과 같이 조치한다.

CPU 지속 포화
→ Instance / Task 사양 또는 Scale-out 기준 조정

Memory 부족
→ Memory 확장 또는 Application Memory 분석

Traffic 급증
→ Auto Scaling Threshold 조정

DB Connection 증가
→ Connection Pool 설정 검토

DB Query Latency 증가
→ Query / Index 분석

Storage 부족
→ Storage 확장 및 Alarm Threshold 조정

배포 후 장애
→ Health Check / Rollback 절차 개선

즉 Need U의 장애 대응은 단순히 서비스를 다시 실행하는 것에서 끝나지 않고 Detection → Notification → Recovery → Verification → Root Cause Analysis → Prevention의 전체 흐름으로 관리한다.

8.1.6 공통 장애 대응 구조

Frontend / Backend / AI / DB
            │
            ▼
      CloudWatch Metric
            │
            ▼
      CloudWatch Alarm
            │
      ┌─────┴─────┐
      │           │
      ▼           ▼
자동 복구      Amazon SNS
      │           │
      │           ▼
      │      운영자 Email
      │
      ▼
 Service Health Check
      │
      ├─ 정상
      │    ↓
      │  Service 복구
      │    ↓
      │  Grafana / CloudWatch 분석
      │    ↓
      │  Root Cause 확인
      │    ↓
      │  재발 방지
      │
      └─ 실패
           ↓
        수동 대응

8.2 Backend 장애 대응

V2 Backend는 ECS + Fargate 2개 Task를 기본 운영값으로 설정한다. 대표 Peak Traffic인 101~122 RPS를 2개 Task로 처리하며, 한 Task에 장애가 발생하면 ALB가 해당 Task를 새로운 요청의 트래픽 분배 대상에서 제외하고 ECS Service가 Replacement Task를 실행하여 Desired Count 2개를 회복한다.

장애 유형 주요 영향 감지 1차 대응
Process / Container 장애 해당 Task 요청 처리 불가 ALB Health Check / ECS Task 상태 트래픽 제외 + Task 교체
Task Infrastructure 장애 해당 Task 요청 처리 불가 ALB Health Check / ECS Task 상태 트래픽 제외 + Task 교체
CPU / Memory 과부하 응답시간 증가 / 5XX 증가 CloudWatch Metric Auto Scaling / 원인 분석
DB Dependency 장애 API 지연 / DB 관련 5XX Application Log / DB Metric DB 상태 확인
배포 장애 신규 Version 요청 실패 Health Check / Smoke Test 이전 Image Rollback
다중 Task 장애 처리 용량 부족 / 요청 실패 UnHealthyHostCount 장애 원인 확인 + Task 복구

공통 장애 알림은 CloudWatch Alarm → SNS → Email로 전달하여 운영자가 장애 발생 사실을 확인할 수 있도록 한다.

8.2.1 Backend Task 장애 판단

Backend Task의 정상 여부는 ALB Target Group Health Check를 통해 판단한다.

항목 설정
Endpoint /actuator/health
Interval 5초
Timeout 5초
Healthy Threshold 2회
Unhealthy Threshold 2회
Success Code 200

Health Check가 설정된 조건에 따라 연속으로 실패하면 해당 Task를 Unhealthy로 판단한다.

Backend Task
     ↓
/actuator/health
     ↓
Health Check 실패
     ↓
Unhealthy Threshold 충족
     ↓
Task를 Unhealthy로 판단

Interval 5초, Unhealthy Threshold 2회는 AWS가 특정 값을 필수로 요구하기 때문에 사용하는 것이 아니라, V2에서 장애 감지 시간을 짧게 유지하기 위한 초기 운영값이다.

실제 배포 후에는 Backend Task의 시작 및 안정화 시간을 측정하고, 장애 감지 시간과 오탐 여부를 확인하여 최종값을 조정한다.

8.2.2 장애 Backend Task 트래픽 제외

ALB Target Group은 Health Check 결과가 Unhealthy인 Task를 새로운 요청의 트래픽 분배 대상에서 제외한다.

                    ALB
                     │
          ┌──────────┴──────────┐
          ↓                     ↓
      Backend Task 1        Backend Task 2
         정상                  장애
          ↓                     ×
      요청 처리             트래픽 제외

따라서 한 Task에 장애가 발생하더라도 Health Check를 통해 장애가 확인된 이후에는 해당 Task로 새로운 요청이 전달되지 않고 정상 Task가 요청을 처리한다.

다만 Health Check가 장애를 감지하기 전에 이미 전달된 요청이나 기존 연결이 즉시 종료되는 것은 아니므로, 실제 장애 상황에서 HTTP 5XX, Response Time, 연결 종료 여부를 Load Test로 확인한다.

8.2.3 Backend ECS Task 자동 복구

ECS Service의 Desired Count는 2개 Task로 설정한다.

Task가 장애로 종료되거나 정상 상태를 유지하지 못해 교체가 필요한 경우, ECS Service는 Desired Count를 유지하기 위해 Replacement Task를 실행한다.

Task 1 정상
Task 2 장애
    ↓
ALB Health Check 실패
    ↓
Task 2 트래픽 분배 대상 제외
    ↓
ECS Service가 Desired Count 유지
    ↓
Replacement Task 실행
    ↓
Container 실행
    ↓
Health Check 통과
    ↓
정상 Task 2개 복구

Replacement Task가 정상 상태가 되기 전까지는 기존의 정상 Task가 요청을 처리하고, 신규 Task가 Health Check를 통과한 이후 해당 Task가 다시 트래픽 분배에 참여한다.

이때 Auto Scaling에 의한 Task 증가와 장애 복구는 목적이 다르다.

  • Auto Scaling: Traffic 증가에 따라 Desired Count 자체를 증가
  • 장애 복구: 장애로 감소한 실제 Task 수를 Desired Count까지 복구

두 경우 모두 ECS Service가 Task 수를 관리한다는 공통점이 있다.

8.2.4 Backend 장애 중 처리 용량 검증

V2의 대표 Peak Traffic은 101~122 RPS이다.

따라서 2개 Task 중 하나가 장애로 트래픽 분배 대상에서 제외되었을 때, 남은 정상 Task 1개가 Replacement Task가 정상 상태로 복귀하기 전까지 대표 Peak Traffic을 처리할 수 있는지를 검증해야 한다.

정상 상태
101~122 RPS
      ↓
Task 2개로 요청 처리

Task 1 장애
      ↓
Task 1 트래픽 제외
      ↓
Task 2만 요청 처리
      ↓
101~122 RPS
처리 가능 여부 검증
      ↓
Replacement Task 복구
      ↓
Task 2개로 요청 분산

Load Test에서는 다음 항목을 측정한다.

  • 정상 Task 1개가 101~122 RPS를 처리할 수 있는지
  • 장애 발생 후 HTTP 5XX 증가 여부
  • 장애 구간의 Response Time p95 / p99
  • Replacement Task 생성까지 걸리는 시간
  • Replacement Task가 Health Check를 통과하는 시간
  • 복구 후 요청이 다시 여러 Task로 분산되는지

여기서 101122 RPS ÷ 2 = 약 50.561 RPS는 정상 상태에서 두 Task에 요청이 균등하게 분산된다고 가정했을 때의 단순 분배값이다. 이는 Task 1개의 실제 처리 한계를 의미하지 않는다.

따라서 단일 Task의 실제 처리 가능 RPS는 장애 상황을 재현한 Load Test 결과로 판단한다.

8.2.5 Backend CPU / Memory 과부하

Task가 종료되지 않더라도 CPU 또는 Memory 사용량이 지속적으로 증가하면 Backend Response Time이 증가하거나 HTTP 5XX가 발생할 수 있다.

Peak Traffic 증가
      ↓
Task CPU / Memory 증가
      ↓
Request Processing Time 증가
      ↓
Response Time 증가
      ↓
Timeout / 5XX 증가

주요 Monitoring Metric은 다음과 같다.

  • ECS CPUUtilization
  • ECS MemoryUtilization
  • ALB TargetResponseTime
  • ALB Target 5XX
  • Request Count
  • Task 수
  • Application Error Log

일시적인 Spike인지 지속적인 Resource 부족인지 구분한다.

일시적인 Spike
     ↓
Monitoring

지속적인 Resource 부족
     ↓
ECS Auto Scaling
     ↓
Task 증가
     ↓
동일 부하 재검증

Task 수를 증가시킨 이후에도 지속적으로 Resource가 부족하면 Task의 CPU / Memory 사양 Scale-up을 검토한다.

8.2.6 Backend Dependency 장애

Backend 자체가 정상적으로 실행되고 있어도 MySQL과 같은 Dependency에 장애가 발생하면 API 요청이 실패할 수 있다.

Backend Task 정상
      ↓
MySQL Query 요청
      ↓
DB Connection / Query 실패
      ↓
Backend 처리시간 증가
      ↓
Timeout / 5XX 증가

이 경우 Backend Task를 반복적으로 재시작하지 않고 다음 항목을 확인한다.

  • Backend Application Log
  • DB Connection Pool 상태
  • RDS MySQL 상태
  • DB Connection 수
  • Query Latency
  • DB CPU / Memory / I/O

즉, Application 장애와 Dependency 장애를 구분하여 대응한다.

8.2.7 Backend 배포 장애 및 Rollback

신규 Backend Image 배포 후 Application이 정상적으로 실행되지 않거나 Health Check에 실패할 수 있다.

Backend Image는 Commit SHA 기반 Tag를 사용한다.

needu-backend:<Git SHA>

예:
needu-backend:a82f3c1
needu-backend:d91bc21

배포 과정은 다음과 같다.

신규 Image 배포
      ↓
Replacement Task 실행
      ↓
Application Start
      ↓
ALB Health Check
      ↓
Health Check 성공
      ↓
신규 Task Traffic 참여

신규 Task의 Health Check가 실패하거나 배포 후 HTTP 5XX 및 Response Time이 비정상적으로 증가하면 이전 정상 Image로 Rollback한다.

신규 Version
     ↓
Health Check / Smoke Test
     ↓
실패 또는 오류율 증가
     ↓
이전 정상 Commit SHA Image
     ↓
Task 복구
     ↓
Health Check 통과

배포 전후의 Response Time, HTTP 5XX, CPU, Memory를 비교하여 배포로 인한 성능 저하 여부도 확인한다.

8.2.8 Backend 장애 알림

Backend 장애는 ALB Target Group과 ECS의 Monitoring Metric을 이용하여 감지한다. 주요 Alarm 후보는 다음과 같다.

Alarm 대상 목적 대응
UnHealthyHostCount 비정상 Task 감지 ECS Task 상태 확인
높은 TargetResponseTime 응답시간 증가 감지 CPU / Memory / DB 확인
Target 5XX 증가 Backend 요청 실패 감지 Application / Dependency 확인
높은 CPUUtilization CPU 병목 감지 Task Scale-out / Scale-up
높은 MemoryUtilization Memory 부족 감지 Task Scale-up / 원인 분석
Task 수 감소 처리 용량 감소 감지 ECS Service 확인
ALB / ECS Metric
      ↓
CloudWatch Alarm
      ↓
SNS
      ↓
Email
      ↓
운영자 장애 확인

여기서 Monitoring Metric과 Alarm Metric을 동일하게 사용할 필요는 없으며, 실제 운영에서는 장애 영향이 큰 지표부터 Alarm을 적용한다.

8.2.9 Backend 복구 후 확인

ECS가 Replacement Task를 실행했다고 해서 장애 대응을 즉시 종료하지 않는다.

Replacement Task 실행
      ↓
Health Check 통과
      ↓
요청 정상 처리 확인
      ↓
HTTP 5XX 확인
      ↓
Response Time 확인
      ↓
Task 2개 정상 운영 확인
      ↓
장애 전후 Metric 비교
      ↓
Root Cause 분석

장애 발생 시 다음 항목을 비교한다.

  • 장애 발생 시점
  • UnHealthyHostCount
  • Task CPU / Memory
  • Target Response Time
  • HTTP 5XX
  • Task Replacement 여부
  • DB Connection / Query Latency
  • 직전 배포 여부

이를 통해 단순히 Task가 복구되었는지뿐 아니라 장애 원인이 Application, Resource, Dependency, Deployment 중 무엇이었는지 확인한다.

8.2.10 Backend 다중 Task 장애 및 Task 수 조정

초기 운영값은 2개 Task로 설정하지만, 장애 상황에서 남은 Task의 처리 능력을 검증한 결과에 따라 Task 수와 Task 사양을 조정한다.

한 Task 장애
    ↓
남은 1개 Task가
101~122 RPS 처리
    ↓
Response Time / Error Rate 확인
    ↓
Load Test 통과 여부 확인

남은 Task 1개가 대표 Peak Traffic을 안정적으로 처리하지 못하는 경우 다음을 검토한다.

  • Task CPU / Memory Scale-up
  • Minimum Task 수 증가
  • Auto Scaling 정책 조정

따라서 "2개 Task면 충분하다"를 고정된 전제로 두지 않고, 장애 상황에서의 단일 Task 처리 능력과 Replacement Task 복구 시간을 Load Test로 검증한 뒤 최종 운영값을 확정한다.

8.3 AI 장애 대응

V2 AI Server는 ECS + Fargate 2개 Task를 기본으로 운영한다. 대표 Peak 기준 AI 요청은 약 4.87 inference RPS이며, AI Server는 SSE 연결을 유지하면서 PostgreSQL + pgvector, Redis, Embedding API, GPU Model Server와 통신한다.

AI Server의 처리 상태는 단순 Request RPS만으로 판단하기 어렵기 때문에 CPU Utilization을 주요 자원 지표로 사용하고, 동시 SSE 연결 수와 Dependency 응답시간, Response Latency, Error Rate를 보조 지표로 함께 확인한다.

장애 유형 주요 영향 감지 1차 대응
AI Task / Container 장애 AI 요청 처리 불가 Container Health Check / ECS Task 상태 ECS Replacement Task 실행 + Service Connect 경로 유지
CPU / Memory 부족 AI 응답 지연 또는 Task 장애 ECS CloudWatch Metric ECS Scale-out / Task 자원 조정
SSE 연결 증가 동시 처리 요청 증가 동시 SSE Connection, Response Latency CPU / Memory / Latency 확인 후 Scale-out 검토
PostgreSQL 장애 Vector Search 실패 AI Log / DB Metric DB 상태 확인 및 Dependency 대응
Redis 장애 Cache / 상태 조회 실패 AI Log / Redis Metric Dependency 상태 확인
Embedding API 장애 Embedding 처리 지연 Application Log / Latency Timeout / 오류 처리
GPU Model Server 장애 Model Inference 실패 Application Log / Response Timeout / 오류 처리
배포 장애 신규 AI Version 실패 Health Check / Smoke Test 이전 Image Rollback

8.3.1 AI Task 장애 판단

AI Server는 Container Health Check와 ECS Task 상태를 이용하여 Task의 정상 여부를 확인한다.

AI Task
   ↓
/health
   ↓
Health Check 실패
   ↓
Unhealthy 판단

초기 V2에서는 /health에서 PostgreSQL, Redis, Embedding API, GPU Model Server 등의 외부 Dependency 상태를 필수 Health Check 조건으로 포함하지 않는다.

Health Endpoint에서는 AI Server Process 자체가 요청을 받을 수 있는 상태인지를 확인하고, 외부 Dependency의 상태는 Application Log와 각 Dependency의 Metric을 통해 별도로 확인한다.

8.3.2 장애 AI Task 처리

AI Server는 Internet-facing ALB의 Target이 아니며 Backend와 ECS Service Connect로 통신한다. AI Task의 Process 또는 Container 상태가 비정상이 되면 ECS Service가 해당 Task를 교체하여 Desired Count를 유지한다.

Backend
   │
   ▼
ECS Service Connect
   │
   ├── AI Task 1 정상
   └── AI Task 2 장애
            ↓
       ECS Replacement

한 AI Task에 장애가 발생하더라도 다른 정상 Task가 새로운 AI 요청을 처리할 수 있다.

다만 AI Server는 SSE를 사용하기 때문에 이미 장애 Task에서 처리 중이던 Streaming Connection은 다른 Task로 자동 이전되지 않는다.

진행 중인 SSE Connection
↓
AI Task 장애
↓
Streaming Connection 종료 가능
↓
Backend / Client가 오류 감지
↓
정책에 따라 Retry / Resume

재시도는 무한 반복하지 않고 제한된 횟수와 Timeout을 적용한다.

8.3.3 AI ECS Task 자동 복구

AI Service의 Desired Count는 2개 Task로 설정한다. 한 Task가 장애로 종료되면 ECS Service가 Replacement Task를 실행한다.

AI Task 1 정상
AI Task 2 장애
      ↓
Container / Task 상태 이상 감지
      ↓
ECS Replacement Task 실행
      ↓
AI Application Start
      ↓
Health Check 통과
      ↓
AI Task 2개 복구
      ↓
Service Connect를 통해 요청 처리

이 구조는 AI Task 하나의 장애가 AI 기능 전체 중단으로 이어지지 않도록 하기 위한 것이다.

8.3.4 AI 장애 중 처리 용량 검증

대표 Peak에서 AI 요청은 약 4.87 inference RPS이다. 그러나 AI Server는 SSE 연결을 유지하므로 RPS만으로 Task 처리 능력을 판단하지 않는다.

AI Peak
4.87 inference RPS
       +
동시 SSE Connection
       +
AI Response Time
       ↓
AI Task 처리 용량

따라서 한 Task가 장애 난 상황에서 남은 Task 1개가 다음 부하를 처리할 수 있는지 검증한다.

  • 4.87 inference RPS
  • 예상 동시 SSE Connection
  • AI Response Time
  • SSE Connection 유지시간
  • HTTP 오류율
  • AI 요청 Timeout 비율

Load Test는 단순히 초당 요청 수만 증가시키지 않고 SSE 연결을 실제로 유지하면서 AI 요청을 발생시키는 방식으로 수행한다.

8.3.5 AI Dependency 장애

AI Server는 여러 Dependency와 통신하므로 AI Server 자체가 정상이어도 특정 Dependency 장애가 AI 요청 실패로 이어질 수 있다.

                    AI Server
                       │
        ┌──────────────┼──────────────┐
        ↓              ↓              ↓
 PostgreSQL          Redis       GPU Model Server
        │              │              │
        └──────────────┼──────────────┘
                       ↓
                  AI Response

예를 들어 PostgreSQL Vector Search가 지연되면 다음과 같은 흐름이 발생한다.

AI Request
    ↓
Vector Search
    ↓
PostgreSQL 응답 지연
    ↓
AI 처리시간 증가
    ↓
SSE 응답 지연
    ↓
Timeout / 오류

따라서 AI Task 장애와 Dependency 장애를 구분한다. 주요 확인 항목은 다음과 같다.

  • AI Server Application Log
  • PostgreSQL Query Latency
  • Vector Search Latency
  • Redis Response Time
  • Embedding API Response Time
  • GPU Model Server Response Time
  • AI 전체 Response Time
  • HTTP Error Rate

8.3.6 AI Dependency Timeout

Dependency가 응답하지 않을 때 AI Server의 모든 SSE Connection이 Dependency 응답을 기다리면서 계속 점유되지 않도록 Timeout을 설정한다.

Dependency 요청
      ↓
정상 응답
→ 다음 처리

응답 지연
      ↓
Timeout
      ↓
AI 요청 종료
      ↓
오류 응답

특히 다음 Dependency에 대한 응답시간을 별도로 측정한다.

AI Request
    │
    ├── PostgreSQL
    │
    ├── Redis
    │
    ├── Embedding API
    │
    └── GPU Model Server

이를 통해 AI 전체 응답시간이 증가했을 때 어느 Dependency에서 지연이 발생했는지 확인할 수 있도록 한다.

8.3.7 AI CPU / Memory / SSE Connection 과부하

AI Server의 부하 상태를 판단하기 위해 CPU와 Memory를 주요 자원 지표로 사용하고, SSE Connection 수와 Response Latency를 보조 지표로 확인한다. 다음 Metric을 함께 확인한다.

  • ECS CPUUtilization
  • ECS MemoryUtilization
  • 동시 SSE Connection 수
  • AI Request Count
  • AI Response Time p95 / p99
  • HTTP Error Rate
  • PostgreSQL Response Time
  • Redis Response Time
  • Embedding API Response Time
  • GPU Model Server Response Time

예를 들어 다음과 같이 판단한다.

SSE Connection 증가
      ↓
Task Memory / Connection 부담 증가
      ↓
Response Time 증가
      ↓
Task 처리 한계 확인
      ↓
ECS Task Scale-out

SSE Connection이 증가했다고 해서 즉시 Scale-out하는 것이 아니라 CPU Utilization, Memory Utilization, Response Latency, Error Rate를 함께 확인하여 Task의 실제 부하를 판단한다.

반대로 SSE Connection은 낮지만 PostgreSQL 또는 GPU Model Server 응답시간 때문에 AI Response Time이 증가한다면 Task를 추가하기보다 해당 Dependency의 병목을 먼저 확인한다.

AI Server의 주요 Scale-out 기준은 7.6에서 정의한 것처럼 CPU Utilization 60%로 유지한다.

8.3.8 AI 배포 장애 및 Rollback

AI Server Image 역시 Commit SHA 기반으로 관리한다.

needu-ai:<Git SHA>

예:
needu-ai:a82f3c1
needu-ai:d91bc21

AI Server는 7.9에서 정의한 Rolling Update 방식으로 배포한다. 배포 후 신규 Task는 다음 순서로 확인한다.

신규 AI Image
      ↓
Task 실행
      ↓
Application Start
      ↓
/health Check
      ↓
Smoke Test
      ↓
정상
→ Traffic 참여

Health Check 또는 Smoke Test가 실패하면 신규 Version을 트래픽에 참여시키지 않고 이전 정상 Image로 Rollback한다.

배포 전후에는 다음 Metric을 비교한다.

  • AI Response Time
  • HTTP Error Rate
  • 동시 SSE Connection
  • PostgreSQL Response Time
  • Redis Response Time
  • Embedding API Response Time
  • GPU Model Server Response Time
  • CPU / Memory

이를 통해 신규 Model 호출 로직이나 Prompt 처리 변경으로 인해 전체 AI 응답시간이 증가했는지도 확인한다.

8.3.9 AI 장애 알림

AI Server 역시 주요 장애 Metric을 CloudWatch Alarm으로 모니터링하고 SNS Email로 운영자에게 알린다.

동시 SSE Connection 수는 AI Server의 처리 상태를 확인하기 위한 보조 Monitoring Metric으로 사용하며, 별도의 Alarm Threshold는 Load Test와 실제 운영 데이터를 바탕으로 결정한다.

Alarm 대상 목적 대응
UnHealthyHostCount AI Task 장애 감지 ECS Task 상태 확인
높은 TargetResponseTime AI 응답 지연 감지 Dependency / Resource 확인
높은 HTTP 5XX AI 요청 실패 감지 Application / Dependency 확인
높은 CPUUtilization CPU 병목 감지 ECS Scale-out 검토
높은 MemoryUtilization Memory 부족 감지 Task 자원 / Application 확인
PostgreSQL Latency 증가 Vector Search 지연 감지 AI DB 확인
Redis Latency 증가 Cache / 상태 조회 지연 감지 Redis 확인
Embedding API Latency 증가 Embedding 처리 지연 감지 API 상태 / Timeout 확인
GPU Model Server Latency 증가 Model Inference 지연 감지 GPU Server 확인
AI / ECS / Service Connect Metric
          ↓
CloudWatch Alarm
          ↓
SNS
          ↓
Email
          ↓
운영자 장애 확인

8.3.10 AI 복구 후 확인

Replacement Task가 Health Check를 통과한 이후에도 AI 요청이 실제로 정상 처리되는지 확인한다.

Replacement Task 실행
      ↓
Health Check 통과
      ↓
AI Request 정상 처리
      ↓
SSE Connection 정상 유지
      ↓
AI Response Time 확인
      ↓
HTTP Error Rate 확인
      ↓
Dependency Latency 확인
      ↓
Task 2개 정상 운영

특히 AI는 Health Check가 정상이어도 PostgreSQL, Redis, Embedding API, GPU Model Server 중 하나가 느려지면 실제 AI 응답에는 문제가 발생할 수 있다. 따라서 복구 후에는 다음을 함께 확인한다.

  • AI Request 성공 여부
  • SSE Connection 생성 / 유지 / 종료
  • AI Response Time
  • HTTP Error Rate
  • PostgreSQL Vector Search Latency
  • Redis Latency
  • Embedding API Latency
  • GPU Model Server Latency
  • Task CPU / Memory

8.3.11 AI Task 수 조정 기준

초기 AI Server는 2개 Task로 시작하지만 4.87 inference RPS만으로 Task 수를 고정하지 않는다. AI Server의 실제 처리 용량은 다음 요소의 영향을 함께 받기 때문이다.

Inference RPS
+
동시 SSE Connection
+
SSE 유지시간
+
Dependency Response Time
+
AI Response Time
      ↓
AI Task 실제 처리 용량

Load Test에서 Task 1개가 대표 Peak를 안정적으로 처리하지 못하거나 SSE Connection 증가에 따라 Response Time이 급격하게 증가한다면 다음을 검토한다.

  • Task Scale-out
  • Task CPU / Memory Scale-up
  • SSE Connection 처리 구조 개선
  • PostgreSQL Query / Vector Search 최적화
  • Redis 처리 성능 확인
  • Embedding API Timeout / 재시도 정책 조정
  • GPU Model Server Dependency 병목 확인

따라서 AI 역시 "2개 Task면 충분하다"를 고정값으로 두지 않고, 실제 SSE 및 Dependency 부하를 포함한 Load Test 결과를 기준으로 최종 Task 수를 결정한다.

8.4 Frontend 장애 대응

V2 Frontend는 ECS + Fargate에서 Minimum / Desired 2개 Task로 운영한다. Frontend의 Task 수를 2개로 유지하는 목적은 처리량보다 단일 Task 장애가 사용자 접근 전체 중단으로 이어지지 않도록 하기 위해서다.

장애 유형 감지 1차 대응
Frontend Process / Container 장애 ALB Health Check / ECS Task 상태 비정상 Target 제외 + Replacement Task
Task Infrastructure 장애 ECS Task 상태 / ALB Health Check 정상 Task가 요청 유지 + Replacement Task
CPU / Memory 과부하 ECS CloudWatch Metric Task 사양 또는 Task 수 조정
배포 장애 Health Check / Error Rate 자동 Rollback 또는 이전 Revision 복구

8.4.1 Frontend Task 장애

Frontend Task 1 장애
↓
ALB Health Check 실패
↓
Task 1을 신규 요청 대상에서 제외
↓
Frontend Task 2가 요청 처리 유지
↓
ECS Service가 Desired Count 2 유지
↓
Replacement Task 실행
↓
Health Check 통과
↓
다시 2개 Task 상태로 복구

하나의 Frontend Task 장애만으로 Frontend 전체가 중단되지 않도록 하며, Replacement Task가 정상화되는 동안 다른 정상 Task가 요청을 처리한다.

8.4.2 Frontend Task 수 조정 기준

Minimum Task는 가용성을 위해 2개로 유지한다. 다음 조건에서는 처리량 관점에서 Maximum Task와 Auto Scaling 정책을 조정한다.

  • Frontend Origin Traffic 증가
  • CPU / Memory 지속 고사용
  • SSR Response Time 증가
  • Error Rate 증가
  • 2개 Task가 Peak Traffic을 안정적으로 처리하지 못함

8.4.3 Frontend 배포 장애 및 Rollback

Frontend Image는 ECR에 저장하고 ECS Task Definition Revision을 통해 Rolling Deployment한다.

신규 Frontend Image
↓
ECS Service Update
↓
신규 Task 실행
↓
ALB Health Check
↓
정상 → 순차 Traffic 참여
실패 → Deployment 실패 처리 / Rollback

배포 전후 다음 항목을 비교한다.

  • HTTP Error Rate
  • Response Time
  • CPU / Memory
  • ALB Target Health
  • Task Restart / Replacement 여부

근거:

8.5 DB 장애 대응

Need U V2에서는 Backend Transaction DB와 AI Vector DB를 모두 Amazon RDS 기반으로 운영한다.

Backend
↓
RDS MySQL
AI Server
↓
RDS PostgreSQL + pgvector

두 Database는 Engine과 저장 Data가 다르지만, 장애 대응의 기본 흐름은 동일하게 적용한다.

DB 장애 / 이상 징후
↓
CloudWatch Monitoring
↓
CloudWatch Alarm
↓
Amazon SNS
↓
운영자 Email
↓
자동 또는 수동 복구
↓
Application 정상 연결 확인
↓
Grafana / CloudWatch Metric / Log 분석
↓
Root Cause 확인
↓
재발 방지

공통 장애 알림 방식은 8.1 공통 장애 대응에서 정의한 CloudWatch → SNS → Email 구조를 사용한다.

8.5.1 RDS Instance 장애

RDS Instance 자체에 장애가 발생하면 Backend 또는 AI Server가 Database에 정상적으로 접근하지 못할 수 있다.

RDS Instance 장애
↓
DB Connection 실패
↓
Application 요청 실패 또는 기능 제한
↓
CloudWatch Alarm / RDS Event
↓
SNS
↓
운영자 Email

현재 Backend DB와 AI Vector DB는 초기에는 Single-AZ로 운영한다. 따라서 Instance 또는 해당 Availability Zone에 문제가 발생했을 때 Multi-AZ와 같이 다른 Standby Instance로 자동 Failover되는 구조는 아니다.

장애 발생 시 RDS 상태와 Event를 확인하고 AWS에서 Instance가 정상 상태로 복구되는지 확인한다.

RDS Event는 Amazon SNS와 연동하여 Availability, Failure, Maintenance 등 주요 Event를 Email로 전달하도록 구성할 수 있다.

참고:

8.5.2 DB 성능 저하

Database가 완전히 중단되지 않아도 CPU, Memory, Connection 또는 Disk I/O가 병목이 되면 Application 응답시간이 증가할 수 있다.

Query 증가
↓
CPU / Memory / Connection / I/O 증가
↓
DB Query Latency 증가
↓
Application Response Time 증가
↓
Timeout / 5XX 증가

주요 Monitoring Metric은 다음과 같다.

Metric 확인 목적
CPUUtilization CPU 병목 확인
FreeableMemory 사용 가능한 Memory 확인
DatabaseConnections Connection 증가 및 Connection Pool 상태 확인
ReadLatency Read Query 지연 확인
WriteLatency Write Query 지연 확인
DiskQueueDepth Disk I/O 요청 적체 확인
FreeStorageSpace Storage 부족 확인

참고: Amazon RDS CloudWatch Metrics

성능 장애가 발생하면 Instance 사양을 바로 높이기보다 먼저 병목 원인을 구분한다.

Query Latency 증가
↓
Query / Index 확인
↓
CPU / Memory / Connection / I/O 확인
↓
병목 원인 판단
↓
필요 시 Instance Scale-up

Read 부하가 주요 원인이라면 다음 순서로 검토한다.

Query / Index 최적화
↓
Memory / Buffer 확보
↓
Instance Scale-up
↓
필요 시 Read Replica

8.5.3 Storage 부족

Database Data와 Index가 증가하면 Storage 여유 공간이 감소할 수 있다.

Backend DB에서는 Recommendation, Friend, Notification, AI Chat 등의 Data가 지속적으로 누적되고, AI Vector DB에서는 Catalog, Search Document, Embedding, Vector Index가 증가한다.

Data / Index 누적
↓
FreeStorageSpace 감소
↓
Threshold 이하
↓
CloudWatch Alarm
↓
SNS Email
↓
Storage 확장

Storage가 완전히 소진된 뒤 대응하는 것이 아니라 FreeStorageSpace를 Monitoring하여 사전에 운영자에게 알림이 전달되도록 구성한다.

참고:

8.5.4 Data 손상 및 Point-in-Time Recovery

Database Instance가 정상 동작하더라도 잘못된 Query나 Batch 작업으로 Data가 논리적으로 손상될 수 있다.

잘못된 DELETE
잘못된 UPDATE
잘못된 Batch 실행
Application Bug에 의한 대량 Data 변경

이 경우 단순 Instance 재시작으로 복구할 수 없으므로 **Point-in-Time Recovery(PITR)**를 사용한다.

14:00
정상 상태

14:03
잘못된 UPDATE 실행

14:05
문제 발견

복구 절차는 다음과 같다.

문제 발생 시각 확인
↓
14:02 시점으로 PITR
↓
새 RDS Instance 생성
↓
Data 검증
↓
Application 연결 전환

Amazon RDS의 Point-in-Time Recovery는 기존 DB를 직접 과거 상태로 되돌리는 방식이 아니라, 지정한 시점의 상태를 기준으로 새로운 DB Instance를 생성하는 방식이다.

참고: Amazon RDS Point-in-Time Recovery

따라서 논리적 Data 장애 대응은 다음과 같이 정리한다.

Data 손상 발견
↓
문제 발생 시간 확인
↓
PITR
↓
새 DB 생성
↓
Data 정합성 검증
↓
Application 연결 전환

8.5.5 Backup 및 Snapshot

DB 장애에 대비하여 RDS Automated Backup을 활성화한다.

RDS Automated Backup
↓
Backup Retention 기간 동안 Backup 유지
↓
PITR 사용 가능

Manual Snapshot은 주요 Schema 변경이나 Migration처럼 복구 Point를 명확하게 남길 필요가 있는 작업 전에 사용한다.

주요 DB Migration
↓
Manual Snapshot
↓
Schema / Data 변경

정리하면 다음과 같다.

목적 방식
정기 Backup RDS Automated Backup
특정 시점 복구 Point-in-Time Recovery
Migration 전 복구 지점 확보 Manual Snapshot

참고:

8.5.6 Single-AZ 장애와 Multi-AZ 전환 기준

현재 Backend DB와 AI Vector DB는 모두 초기에는 Single-AZ로 운영한다.

Application Layer
→ Backend / AI Server 다중 Task

Database Layer
→ 초기 Single-AZ

Application Layer는 단일 Task 장애를 격리하도록 다중화하지만, Database는 초기 99%+ Availability 목표와 현재 서비스 규모를 기준으로 Single-AZ의 Risk를 수용한다.

따라서 다음 상황에서는 Multi-AZ 전환을 검토한다.

  • DB 장애가 반복적으로 발생하는 경우
  • DB 장애로 인한 서비스 중단 시간이 운영상 문제가 되는 경우
  • Availability 목표가 높아지는 경우
  • DB 장애 허용 시간이 짧아지는 경우
  • 핵심 사용자 Flow가 DB 장애에 의해 장시간 중단되는 경우

전환 구조는 다음과 같다.

현재

RDS
Single-AZ
향후

Primary RDS
AZ-A
  │
  │ Synchronous Replication
  ▼
Standby RDS
AZ-B

Multi-AZ DB Instance의 Standby는 일반 Application Read Traffic을 처리하는 Read Replica가 아니라 Availability와 Failover를 위한 Standby이다.

참고:

8.5.7 Backend RDS MySQL 장애 대응

Backend RDS MySQL에는 다음과 같은 서비스 Data가 저장된다.

User
Friend
Taste Profile
Recommendation
Notification
AI Chat
Feedback
Click
Product

이 Data는 서비스의 Transaction Data이므로 Data 유실 및 잘못된 변경에 대한 복구가 중요하다. 주요 대응은 다음과 같다.

장애 대응
RDS Instance 장애 RDS 상태 확인 및 복구
CPU / Memory / I/O 병목 Metric 분석 후 Query 최적화 또는 Scale-up
Connection 증가 Connection Pool 및 DB Connection 확인
Storage 부족 Storage 확장
잘못된 DELETE / UPDATE PITR
Migration 실패 Snapshot 또는 PITR을 이용한 복구

Backend DB 장애가 발생하면 복구 이후 반드시 Application의 주요 기능을 확인한다.

Login
Friend 조회
Recommendation 조회
Notification
Feedback / Click 저장
AI Chat History 저장

8.5.8 AI Vector DB 추가 장애 대응

AI Vector DB는 RDS PostgreSQL + pgvector로 운영한다.

AI Server
↓
RDS PostgreSQL
↓
pgvector

Backend DB와 동일한 RDS 장애 대응 방식을 적용하지만, Vector DB에는 추가로 Vector Index와 Embedding Version에 대한 장애 대응이 필요하다.

Vector Index 문제

Embedding Data 자체는 정상인데 HNSW 또는 IVFFlat Index에 문제가 발생한 경우 Index를 다시 생성할 수 있다.

Vector Data
→ 정상

Vector Index
→ 문제 발생
↓
Index Rebuild

Index 문제와 원본 Vector Data 손상을 구분하여 대응한다.

Embedding Version 장애

Need U는 Embedding Model Version을 변경할 때 기존 Vector를 즉시 삭제하지 않고 Old Space와 New Space를 동시에 유지한다.

Old Embedding Space
+
New Embedding Space

신규 Embedding Version 적용 후 검색 품질이나 서비스 문제가 발견되면 Active Space를 이전 Version으로 되돌린다.

New Embedding Space 적용
↓
검색 품질 / 기능 이상
↓
Old Embedding Space로 Rollback
↓
정상 Search 확인

따라서 Embedding Model 전환은 기존 Vector를 즉시 덮어쓰는 방식보다 Rollback 가능한 형태로 운영한다. AI Vector DB 장애 대응은 다음과 같이 정리한다.

RDS 장애
→ RDS 복구

Data 손상
→ PITR / Backup Restore

Vector Index 문제
→ Index Rebuild

Embedding Version 문제
→ Old Embedding Space Rollback

8.5.9 DB 복구 후 원인 분석

DB가 정상 상태로 복구된 이후에도 장애 대응을 종료하지 않는다.

DB 복구
↓
Application Connection 확인
↓
주요 기능 정상 동작 확인
↓
Grafana Dashboard 확인
↓
CloudWatch Metric / DB Log 확인
↓
Root Cause 분석
↓
재발 방지

Grafana에서는 장애 발생 시점 전후의 Metric을 비교한다. 주요 확인 항목은 다음과 같다.

CPUUtilization 변화
FreeableMemory 변화
DatabaseConnections 증가 여부
ReadLatency / WriteLatency 증가 여부
DiskQueueDepth 증가 여부
FreeStorageSpace 감소 여부
Application 5XX 증가 여부
특정 Batch 실행 여부
배포 또는 Migration 직후 발생 여부

필요한 경우 Slow Query 또는 실행 계획을 추가로 확인하여 Database 병목이 Query에서 발생했는지 검증한다. 원인에 따라 다음과 같이 후속 조치를 수행한다.

Query 병목
→ Query / Index 개선

Memory 부족
→ Instance Memory 확장

CPU 지속 포화
→ Instance Scale-up

Read 병목
→ Read Replica 검토

Storage 부족
→ Storage 확장

Connection 증가
→ Connection Pool 설정 검토

잘못된 Data 변경
→ 권한 / Migration / Batch 절차 개선

반복적인 Instance / AZ 장애
→ Multi-AZ 검토

8.5.10 DB 장애 대응 정리

RDS Instance 장애
→ CloudWatch / RDS Event
→ SNS Email
→ RDS 상태 확인 및 복구

DB 성능 저하
→ Metric 분석
→ Query / Index 최적화
→ 필요 시 Scale-up

Storage 부족
→ FreeStorageSpace Alarm
→ Storage 확장

Data 손상
→ PITR
→ 새 RDS 생성
→ Data 검증 후 연결 전환

Migration 위험
→ Manual Snapshot

Single-AZ Availability 한계
→ 필요 시 Multi-AZ

Vector Index 장애
→ Index Rebuild

Embedding Version 장애
→ Old Embedding Space Rollback

복구 완료
→ Grafana + CloudWatch + DB Log 분석
→ Root Cause 확인
→ 재발 방지

9. VPC 및 네트워크 구성

V2에서는 V1에서 사용한 AWS VPC를 그대로 사용한다.

V2 초기 운영은 비용과 구조 단순성을 우선하여 Application과 Database를 AZ-A 한 곳에 배치하는 Single-AZ 구조로 구성한다.

다만 Application Load Balancer(ALB)는 AWS 제약상 서로 다른 Availability Zone의 Subnet을 최소 2개 지정해야 한다. 따라서 실제 Application과 Database는 AZ-A에만 배치하지만, ALB 연결용 Public Subnet은 AZ-A와 AZ-C 두 곳을 유지한다.

즉 다음처럼 구분한다.

실제 Service / DB
→ AZ-A에서 운영

ALB Network Mapping
→ Public Subnet A + Public Subnet C
  (ALB 요구사항 때문에 유지)

9.1 VPC 구성

새 VPC를 만들지 않고 V1의 VPC를 유지한다.

Backend, Frontend, AI Server, RDS가 모두 같은 서비스에 속하고 있어 별도의 VPC 경계가 필요한 요구사항이 없으며, 새 VPC를 추가하면 Peering 등 Network 구성이 불필요하게 복잡해진다.

따라서 V2에서는 기존 VPC 내부에서 Subnet과 Security Group으로 접근 범위를 나눈다.

VPC CIDR은 예시로 10.0.0.0/16과 같이 둘 수 있다. 실제 CIDR은 V1에서 사용 중인 VPC와 Subnet 범위를 확인한 뒤 확정한다.

9.2 Availability Zone 구성

V2 초기 Application / Database는 AZ-A 하나만 사용한다.

AZ-A
├── Public Subnet A
│   ├── Frontend Fargate
│   ├── Backend Fargate
│   └── AI Server Fargate
│
└── Private DB Subnet A
    ├── RDS MySQL
    └── RDS PostgreSQL + pgvector

Backend와 AI Server가 각각 여러 Task를 유지하더라도 초기에는 모두 AZ-A에 배치한다.

Backend Service
├── Task 1 ─ AZ-A
└── Task 2 ─ AZ-A

AI Service
├── Task 1 ─ AZ-A
└── Task 2 ─ AZ-A

Frontend Service
├── Task 1 ─ AZ-A
└── Task 2 ─ AZ-A

이 구조는 Task Process 장애는 격리할 수 있지만 AZ-A 자체에 장애가 발생하면 Application 전체가 영향을 받는다는 한계가 있다.

V2 초기에는 이 Risk를 수용하고, Availability 요구가 높아지면 AZ-C까지 Application Task를 분산하는 Multi-AZ 구조로 확장한다.

9.3 ALB가 Public Subnet A / C를 유지하는 이유

Application Load Balancer는 Availability Zone Subnet을 사용할 경우 서로 다른 AZ의 Subnet을 최소 2개 선택해야 한다. 따라서 ALB를 유지하면서 Public Subnet A 하나만 사용하는 구성은 불가능하다.

따라서 V2에서는 다음 두 Subnet을 ALB에 연결한다.

Public Subnet A
└── ALB Network Mapping

Public Subnet C
└── ALB Network Mapping

하지만 Target인 Frontend / Backend Task는 AZ-A에만 배치한다.

ALB의 Cross-Zone Load Balancing은 기본적으로 활성화되어 있으므로 ALB가 연결된 다른 AZ의 Node에서도 AZ-A의 Healthy Target으로 요청을 전달할 수 있다.

따라서 V2의 의미는 다음과 같다.

ALB는 AWS 요구사항 때문에 2개 AZ Subnet에 연결하지만, 실제 Application과 Database는 AZ-A에서 Single-AZ로 운영한다.

9.4 NAT Gateway 미사용

V2 초기에는 NAT Gateway를 사용하지 않는다.

Frontend, Backend, AI Server Fargate Task를 AZ-A의 Public Subnet에 배치하고 Public IP를 할당하여 Internet Gateway를 통해 Outbound 통신한다.

Fargate Task
↓
Public IP
↓
Internet Gateway
↓
External API / Runpod GPU Model Server

이를 통해 NAT Gateway의 고정비와 Data Processing 비용을 제거한다.

Application Port는 Internet 전체에 공개하지 않고 Security Group으로 접근 경로를 제한한다.

9.5 Public Subnet

Public Subnet A에는 실제 Application Task를 배치한다.

Public Subnet A
├── ALB
├── Frontend Fargate Task 1 + Public IP
├── Frontend Fargate Task 2 + Public IP
├── Backend Fargate + Public IP
└── AI Server Fargate + Public IP

Public Subnet C는 ALB Network Mapping을 충족하기 위한 Subnet으로만 사용한다.

Public Subnet C
└── ALB

Application Fargate Task는 Public Subnet C에 배치하지 않는다.

9.6 ALB Routing

Frontend와 Backend는 동일한 Internet-facing ALB를 공유한다.

Internet
↓
ALB :443
├── Default /* → Frontend Target Group
└── /api/*    → Backend Target Group

Frontend와 Backend는 각각 별도의 Target Group을 사용한다.

AI Server는 사용자에게 직접 노출하지 않고 Backend에서 ECS Service Connect를 통해 VPC 내부 통신으로 호출한다.

Backend ECS Service
↓
ECS Service Connect
↓
AI ECS Service

9.7 Private DB Subnet

RDS MySQL과 RDS PostgreSQL + pgvector는 AZ-A의 Private DB Subnet에 배치한다.

Private DB Subnet A
├── RDS MySQL
└── RDS PostgreSQL + pgvector

두 DB 모두 Single-AZ로 운영하며 Public Access는 허용하지 않는다. Backend와 AI Server는 VPC 내부 통신으로 각 DB에 접근한다.

Backend   → MySQL      :3306
AI Server → PostgreSQL :5432

현재 설계에서는 Multi-AZ를 사용하지 않으므로 AZ-A 장애 시 DB 자동 Failover는 제공되지 않는다.

단, RDS의 DB Subnet Group 구성상 향후 확장을 위해 다른 AZ의 DB Subnet을 추가로 준비할 수는 있지만, V2 초기 운영 Resource는 AZ-A에만 배치한다.

9.8 Redis 배치

Redis는 Internet에 공개하지 않고 VPC 내부 Shared Cache로 사용한다.

Backend ─┐
         ├── Redis :6379
AI Server┘

현재 문서에서는 Redis를 단일 Container로 유지하고 있으며 실제 실행 위치는 별도로 확정한다. 초기에는 AZ-A 내부에서 운영하고, Redis 장애 영향이 커지면 Managed Redis 또는 HA 구조를 검토한다.

9.9 Backend → AI Server 내부 통신

Backend와 AI Server는 모두 ECS + Fargate에서 실행된다. Fargate Task는 배포, 장애 복구, Scale-out 과정에서 Private IP가 변경될 수 있으므로 Backend가 AI Task의 IP를 직접 참조하는 방식은 사용하지 않는다.

내부 통신 후보는 다음과 같다.

방식 특징 비용 관점
ECS Service Connect ECS Service Name 기반 통신, Task 변경을 ECS가 관리 Service Connect 자체 추가 요금 없음
AWS Cloud Map Service Discovery DNS 기반으로 Task를 탐색 Resource / DNS Query에 소액 과금 가능
Internal ALB 별도 Load Balancer로 AI Task에 분산 ALB 고정비 + LCU 비용 추가

Need U에서는 Backend와 AI Server가 모두 ECS Service이며 AI Server는 외부 사용자에게 직접 노출할 필요가 없다. 따라서 별도의 Internal ALB를 추가하지 않고 ECS Service Connect를 사용한다.

Backend ECS Service
↓
http://ai-server:<port>
↓
ECS Service Connect
↓
AI ECS Service
├── AI Task 1
└── AI Task 2

Backend는 ai-server와 같은 논리적 Service Name으로 AI Server를 호출한다. AI Task가 배포, 장애 복구 또는 Scale-out으로 교체되어 IP가 변경되더라도 Backend의 호출 주소는 유지된다.

Service Connect 자체에는 추가 요금이 없으며, Service Connect Agent는 각 ECS Task 안에서 실행되어 Task에 할당된 CPU와 Memory를 Application Container와 공유한다. 따라서 별도의 Load Balancer 고정비 없이 ECS Service 간 내부 연결을 구성할 수 있다.

근거:

9.10 AWS → Runpod GPU Model Server

AI Server는 Public IP를 사용하여 Runpod GPU Model Server의 HTTPS Endpoint를 호출한다.

AI Fargate
↓
Public IP
↓
Internet Gateway
↓ HTTPS
Runpod GPU Model Server

Task 교체 시 Public IP가 달라질 수 있으므로 Runpod에서 고정 Source IP Allowlist가 필수라면 NAT Gateway, NAT Instance, Proxy 또는 Private Connectivity 등 별도 Egress 구조가 필요하다.

9.11 Security Group

Application Task가 Public IP를 가지더라도 Application Port를 Internet 전체에 공개하지 않는다.

ALB SG
Inbound
443 ← Internet
Frontend SG
Frontend Port ← ALB SG only
Backend SG
Backend Port ← ALB SG only
AI SG
AI Port ← Backend SG only
MySQL SG
3306 ← Backend SG only
PostgreSQL SG
5432 ← AI SG only
Redis SG
6379 ← Backend SG
6379 ← AI SG

9.12 최종 Network 배치

AWS VPC
│
├── Internet Gateway
│
├── AZ-A
│   ├── Public Subnet A
│   │   ├── ALB
│   │   ├── Frontend Fargate + Public IP
│   │   ├── Backend Fargate Task 1 + Public IP
│   │   ├── Backend Fargate Task 2 + Public IP
│   │   ├── AI Fargate Task 1 + Public IP
│   │   └── AI Fargate Task 2 + Public IP
│   │
│   └── Private DB Subnet A
│       ├── RDS MySQL
│       └── RDS PostgreSQL + pgvector
│
└── AZ-C
    └── Public Subnet C
        └── ALB
        (ALB 최소 2 AZ 요구사항 충족용)
Resource 초기 위치
ALB Public Subnet A + C
Frontend Fargate AZ-A / Public Subnet A
Backend Fargate AZ-A / Public Subnet A
AI Server Fargate AZ-A / Public Subnet A
RDS MySQL AZ-A / Private DB Subnet A / Single-AZ
RDS PostgreSQL + pgvector AZ-A / Private DB Subnet A / Single-AZ
Redis AZ-A / Private 영역 / 실행환경 별도 확정
NAT Gateway 사용하지 않음

실제 Application과 Database는 AZ-A 한 곳에서 운영하며, AZ-C는 ALB가 요구하는 두 번째 Public Subnet 용도로만 사용한다.

10. CI/CD

V2에서도 V1과 동일하게 GitHub Actions 기반 CI/CD를 사용한다.

V1에서 사용한 Branch 전략과 검증 원칙은 유지하되, V2에서는 Frontend / Backend / AI Server가 모두 ECS + Fargate에서 Container로 실행되므로 배포 단위와 CD 방식이 변경된다.

10.1 Branch 및 Merge 정책

Branch 전략은 V1과 동일하게 유지한다.

feature/*
↓ PR
dev
↓ PR
main
  • feature/* → dev
  • dev → main
  • PR 생성 또는 PR 대상 Branch에 Commit이 추가되면 CI를 다시 실행한다.
  • dev, main에는 Branch Protection과 Required Status Check를 적용한다.
  • CI가 실패한 PR은 Merge하지 않는다.

V2에서도 main에는 배포 가능한 상태의 코드만 유지한다.

10.2 CI 도구 및 AWS 인증

CI/CD 도구는 GitHub Actions를 유지한다. AWS 인증은 V1과 동일하게 GitHub Actions OIDC → AWS IAM Role 구조를 사용한다.

GitHub Actions
↓ OIDC
AWS IAM Role
↓
ECR / ECS

장기 Access Key를 GitHub Secrets에 저장하지 않고, Workflow 실행 시 임시 자격증명을 발급받는다.

CI Role에는 ECR Push 등 Build Artifact 생성에 필요한 최소 권한을 부여하고, CD Role에는 ECS Service Update와 Task Definition 등록에 필요한 권한을 분리한다.

근거:

10.3 Frontend CI

Frontend는 Next.js Container로 실행하므로 Docker Image를 Build하고 ECR에 저장한다.

PR CI

Checkout
↓
Dependency Install
↓
Type Check
↓
Lint / Format
↓
Next.js Build
↓
Docker Image Build Check

PR 단계에서는 ECR Push를 하지 않고 Docker Image가 정상적으로 생성되는지만 확인한다.

main CI

main Merge
↓
Type Check
↓
Lint / Format
↓
Next.js Build
↓
Playwright E2E
↓
Docker Image Build
↓
Git Commit SHA Tag
↓
ECR Push

E2E 범위는 V1에서 사용한 주요 사용자 Flow를 유지한다.

카카오 로그인
↓
친구 조회
↓
AI 대화
↓
추천 결과
↓
외부 상품 이동

Frontend Image Tag는 latest를 사용하지 않고 Git Commit SHA를 사용한다.

needu-frontend:<commit-sha>

V1과의 가장 큰 차이는 다음과 같다.

V1
Frontend Build
→ 정적 Artifact
→ EC2 / Nginx 배포

V2
Frontend Docker Image
→ ECR
→ ECS Fargate

10.4 Backend CI

Backend CI의 기본 검증 방식은 V1을 유지한다.

PR CI

Checkout
↓
Compile / Build
↓
Unit Test
↓
MySQL Integration Test
↓
AI API Contract / Mock Test
↓
Format / Static Check
↓
Docker Image Build Check

외부 AI 호출은 실제 Production API를 호출하지 않고 Mock 또는 Contract Test로 검증한다.

main CI

main Merge
↓
Build
↓
Unit / Integration Test
↓
Docker Image Build
↓
Git Commit SHA Tag
↓
ECR Push

Image 예: needu-backend:a82f3c1

한 번 Build한 Image를 이후 CD에서 그대로 사용하여 Build Once, Deploy Many 원칙을 유지한다.

10.5 AI Server CI

AI Server는 FastAPI 기반 Container로 운영하며 V2에서도 Backend와 독립적으로 Build / Deploy한다.

PR CI

Checkout
↓
Unit Test
↓
Ruff / Static Check
↓
API Contract Test
↓
Docker Image Build Check

main CI

main Merge
↓
Unit / Contract Test
↓
Docker Image Build
↓
Git Commit SHA Tag
↓
ECR Push

Image 예: needu-ai:<commit-sha>

AI Server CI에서는 Runpod GPU Model Server를 실제로 호출하는 Test를 기본 CI에 포함하지 않고 Mock / Contract Test를 사용한다.

Runpod GPU Model 자체의 배포 Pipeline은 AWS AI Server 배포와 분리한다.

AWS
AI Server
→ ECS + Fargate

Runpod
GPU Model Server
→ 별도 Model Deployment

즉 AI Server Application 변경과 GPU Model 변경을 서로 독립적으로 배포할 수 있도록 한다.

10.6 Spring Batch CI

Spring Batch는 상시 API를 처리하는 Backend Service와 별도 ECS Task로 실행한다.

초기 V2에서는 Backend와 동일한 Spring Boot Codebase와 Docker Image를 사용하고, 실행 Profile 또는 Command를 분리하는 방식으로 운영한다.

Backend Image
├── Backend ECS Service
└── Spring Batch Task

따라서 별도의 Batch Image를 중복 Build하지 않고 Backend main CI에서 생성한 Commit SHA Image를 재사용한다. Batch 로직도 Backend Unit / Integration Test 범위에 포함한다.

10.7 ECR Repository 및 Image 관리

V2에서는 Frontend까지 Container 기반으로 변경되므로 ECR Repository는 다음과 같이 구성한다.

Amazon ECR
├── needu-frontend
├── needu-backend
└── needu-ai

Image Tag는 모두 Git Commit SHA를 사용한다.

needu-frontend:<commit-sha>
needu-backend:<commit-sha>
needu-ai:<commit-sha>

latest Tag를 Production 배포 기준으로 사용하지 않는다.

PR 단계의 임시 Image는 ECR에 저장하지 않고, main Merge가 완료된 Image만 ECR에 저장한다.

초기에는 Rollback을 위해 main Image를 일정 기간 유지하고, Image 누적량이 증가하면 ECR Lifecycle Policy를 적용한다.

10.8 Production CD

Production 배포는 V1과 동일하게 Continuous Deployment가 아니라 Continuous Delivery 방식으로 운영한다.

즉 main에 Merge되었다고 바로 Production에 반영하지 않고, CI가 성공한 Commit SHA를 선택하여 수동으로 배포한다.

Trigger는 workflow_dispatch이며 입력값은 다음과 같다.

배포 대상 Commit SHA
배포 대상 Service

배포 대상은 다음과 같이 분리한다.

  • Frontend
  • Backend
  • AI Server
  • 필요 시 Spring Batch Task Definition

10.9 V2 CD 방식

V1에서는 EC2에 접속해 Docker Compose Container를 교체하는 Recreate 방식이었다.

V2에서는 Application이 ECS + Fargate로 이동했으므로 EC2 / Docker Compose / SSM 기반 Application 배포를 사용하지 않는다.

V1

GitHub Actions
↓
SSM
↓
EC2
↓
Docker Compose Recreate
V2

GitHub Actions
↓
ECS Task Definition Revision 등록
↓
ECS Service Update
↓
Fargate Rolling Deployment
↓
ALB Health Check

즉 V2에서는 ECS Service 자체가 Application 배포와 Task 교체를 담당한다.

10.10 Frontend CD

workflow_dispatch
↓
배포 Commit SHA 선택
↓
ECR Image 확인
↓
Frontend Task Definition Revision 생성
↓
ECS Frontend Service Update
↓
새 Task 실행
↓
ALB Health Check
↓
정상 → Traffic 참여
↓
기존 Task 종료

Frontend는 Minimum / Desired 2개 Task를 유지하며 Rolling Deployment 과정에서 신규 Task가 정상 상태가 된 뒤 기존 Task를 순차적으로 교체하도록 Deployment 설정을 구성한다.

배포 성공 여부는 다음을 확인한다.

  • ECS Task 상태
  • ALB Target Health
  • HTTP Error Rate
  • Frontend Response Time
  • 주요 E2E / Smoke Test

10.11 Backend CD

Backend는 최소 2개 Task를 유지하므로 Rolling Deployment를 적용한다.

Backend Task 1
Backend Task 2
↓
신규 Revision 배포
↓
새 Task 실행
↓
ALB Health Check
↓
Healthy
↓
Traffic 참여
↓
기존 Task 순차 종료

한 번에 모든 기존 Task를 종료하지 않고, 정상 Task가 요청을 처리하는 동안 새 Version으로 순차 교체한다. 배포 후 다음 항목을 확인한다.

  • /actuator/health
  • ALB Healthy Target 수
  • HTTP Error Rate
  • Response Time
  • DB Connection Error
  • 주요 API Smoke Test

10.12 AI Server CD

AI Server 역시 ECS Service Rolling Deployment를 사용한다.

AI Task 1
AI Task 2
↓
신규 Revision 배포
↓
새 Task 실행
↓
Health Check
↓
기존 Task 순차 종료

AI Server는 SSE 기반 Streaming Connection을 유지하므로 일반적인 단기 HTTP Request보다 Task 종료 시 영향이 크다.

배포 중 새 Task가 Healthy 상태가 되더라도 기존 Task에 연결된 SSE Connection은 새 Task로 자동 이전되지 않는다.

따라서 AI Server 배포 시 다음 정책을 적용한다.

  • 새 Task가 Healthy 상태가 된 이후 기존 Task를 Target에서 제외한다.
  • 기존 Task는 신규 Request를 받지 않도록 Deregistration하고, 진행 중인 SSE Connection이 종료될 시간을 부여한다.
  • Task 종료 시 Graceful Shutdown을 적용하여 진행 중인 Streaming Response를 가능한 범위에서 완료한다.
  • Streaming이 중간에 종료되는 경우 Backend / Client에서 Retry 또는 재요청할 수 있도록 오류 처리 정책을 둔다.
  • 최대 Streaming 시간과 ECS Task 종료 대기 시간은 실제 운영 Data를 기준으로 조정한다.

즉 SSE에서는 새 Task로 Traffic을 전환하는 것뿐 아니라, 기존 Task에 남아 있는 장기 Connection을 안전하게 종료하는 과정까지 고려한다.

기존 AI Task
└── SSE Connection 진행 중

새 AI Task 실행
↓
Health Check 통과
↓
기존 Task 신규 요청 차단
↓
기존 SSE Connection 일정 시간 유지
↓
Streaming 완료
↓
기존 Task 종료

배포 후 다음 항목을 확인한다.

  • AI Health Check
  • Error Rate
  • AI Response Time
  • SSE Connection Error
  • Redis Connection
  • PostgreSQL + pgvector Connection
  • Embedding API / Runpod GPU Model Server 호출 상태

10.13 Rollback

Production 배포 자체는 workflow_dispatch를 통해 수동으로 시작한다.

다만 배포 실패 대응은 ECS Deployment Circuit Breaker와 CloudWatch Alarm을 함께 사용하여 자동화한다.

ECS Deployment Circuit Breaker

새 Task가 정상적으로 시작되지 못하거나 Health Check에 반복적으로 실패하여 ECS Service가 안정 상태에 도달하지 못하면 Deployment를 실패로 판단한다.

신규 Task Definition Revision 배포
↓
Task 시작 실패
또는
Health Check 반복 실패
↓
Deployment 실패 판단
↓
마지막 정상 Deployment로 자동 Rollback

Circuit Breaker는 Container 시작 실패, Task 안정화 실패, Health Check 실패처럼 배포 자체가 정상적으로 완료되지 못하는 문제를 기본적으로 감지한다.

CloudWatch Alarm Rollback

Task 자체는 실행 중이더라도 실제 서비스에서 오류가 발생할 수 있다.

따라서 배포 중 주요 운영 Metric을 CloudWatch Alarm과 연결하여 이상이 확인되면 Deployment를 실패로 처리하고 이전 정상 Revision으로 Rollback한다.

초기에는 배포 문제와 직접 연관성이 높은 핵심 Alarm만 사용한다.

Backend
→ ALB 5xx 증가

AI Server
→ Application Error Rate 증가

Latency는 Runpod, Embedding API 등 외부 의존성의 일시적인 영향으로도 증가할 수 있으므로 초기 자동 Rollback 조건에는 바로 포함하지 않고 Monitoring 지표로 우선 사용한다.

자동 Rollback과 수동 Rollback

자동 Rollback은 명확하게 감지할 수 있는 기술적 장애를 대상으로 한다.

Task 시작 실패
Health Check 실패
ALB 5xx 증가
Application Error Rate 증가
↓
자동 Rollback

반면 다음과 같이 Health Check와 Metric만으로 판단하기 어려운 문제는 운영자가 확인 후 수동 Rollback한다.

추천 결과 이상
Business Logic 오류
잘못된 응답 내용
↓
운영자 판단
↓
이전 정상 Task Definition Revision 선택
↓
ECS Service Update

따라서 V2의 Rollback 구조는 다음과 같다.

Production 배포
→ workflow_dispatch 수동 실행

배포
→ ECS Rolling Deployment

배포 실패 감지
├── ECS Deployment Circuit Breaker
└── CloudWatch Alarm

명확한 기술적 장애
→ 자동 Rollback

Business Logic 문제
→ 수동 Rollback

Rollback 시에는 ECR에 보관된 이전 정상 Commit SHA Image와 이전 Task Definition Revision을 재사용하므로 Image를 다시 Build하지 않는다.

10.14 CI/CD 실패 처리

PR CI 실패

CI 실패
↓
Required Status Check 실패
↓
Merge 차단

main CI 실패

main CI 실패
↓
ECR Production Image 생성 안 함
↓
CD 대상에서 제외

CD 실패 - Task / Health Check 문제

신규 Task 시작 실패
또는
Health Check 실패
↓
ECS Deployment Circuit Breaker
↓
Deployment 실패
↓
이전 정상 Revision 자동 Rollback

CD 실패 - 운영 Metric 이상

신규 Version 배포
↓
ALB 5xx / Application Error Rate 증가
↓
CloudWatch Alarm
↓
Deployment 실패
↓
이전 정상 Revision 자동 Rollback

자동 판단이 어려운 문제

Business Logic 이상
↓
운영자 확인
↓
이전 정상 Revision 선택
↓
수동 Rollback

10.15 V1 → V2 CI/CD 변경점

항목 V1 V2
CI Tool GitHub Actions 유지
Branch feature → dev → main 유지
AWS 인증 OIDC → IAM Role 유지
Image Tag Git Commit SHA 유지
Production Trigger workflow_dispatch 유지
Frontend Artifact 정적 Build Artifact Docker Image → ECR
Frontend Runtime EC2 / Nginx ECS + Fargate
Backend Runtime EC2 + Docker Compose ECS + Fargate
AI Runtime EC2 + Docker Compose ECS + Fargate
Application CD SSM → EC2 → Compose ECS Service Update
Deployment Strategy Recreate Rolling Deployment
Backend Task 단일 Container 최소 2 Tasks
AI Task 단일 Container 최소 2 Tasks
Spring Batch Backend 내부 별도 Fargate Task
Rollback 이전 Image Tag로 Compose 재기동 Circuit Breaker / CloudWatch Alarm 자동 Rollback + 필요 시 수동 Revision Rollback

10.16 최종 CI/CD 구조

Developer
   │
   ▼
GitHub
   │
   ├── feature/* → dev PR
   │        │
   │        └── CI
   │
   └── dev → main PR
            │
            └── CI
                 │
                 ▼
             main Merge
                 │
        ┌────────┼────────┐
        ▼        ▼        ▼
    Frontend   Backend    AI
     Image      Image    Image
        │        │        │
        └────────┼────────┘
                 ▼
                ECR
                 │
                 ▼
       workflow_dispatch
                 │
                 ▼
        ECS Task Definition
                 │
        ┌────────┼────────┐
        ▼        ▼        ▼
    Frontend   Backend    AI
     Service    Service   Service
        │        │        │
        ▼        ▼        ▼
      Fargate  Fargate  Fargate
                 │
                 ▼
          Health / Smoke Check
                 │
        ┌────────┴────────┐
        ▼                 ▼
Circuit Breaker      CloudWatch Alarm
        │                 │
        └────────┬────────┘
                 ▼
       기술적 장애 시 자동 Rollback
                 │
                 └── Business Logic 이상은 수동 Rollback

V2 CI/CD의 핵심은 V1에서 검증한 GitHub Actions + OIDC + Commit SHA 기반 Version 관리 방식은 유지하고, EC2 / Docker Compose 중심의 배포를 ECS + Fargate Rolling Deployment로 전환하며, 배포 실패 시 Circuit Breaker와 CloudWatch Alarm을 이용해 기술적 장애를 자동 Rollback하는 것이다.

11. V2 한계점

V2는 V1보다 확장성과 장애 대응 능력을 크게 높였지만, 비용과 운영 복잡도를 고려해 모든 계층을 고가용성으로 구성하지는 않았다. 따라서 다음 한계가 남아 있다.

한계 현재 영향 향후 보완 방향
Application Fargate가 Public Subnet + Public IP를 사용 NAT Gateway 비용은 줄였지만 Application Task가 Public IP를 가지므로 Security Group 설정에 대한 의존도가 높다. 또한 Task 교체 시 Public IP가 변경될 수 있다. 보안 요구가 높아지거나 고정 Egress IP가 필요해지면 Private Subnet + NAT Gateway, NAT Instance 또는 별도 Egress 구조를 재검토한다.
Backend DB와 Vector DB가 Single-AZ DB Instance 또는 해당 AZ 장애 시 Multi-AZ와 같은 자동 Failover가 없다. Application Task가 살아 있어도 DB 접근이 불가능하면 핵심 기능이 중단될 수 있다. DB 장애 시간이 99% Availability 목표를 위협하거나 장애 허용 시간이 짧아지면 Multi-AZ로 전환한다.
Redis가 단일 Shared Container Backend와 AI Server가 함께 의존하므로 Redis 장애가 공통 장애 지점이 될 수 있다. 현재 실행 위치도 별도 확정이 필요하다. Redis 실행환경을 확정하고, Availability 요구가 커지면 Amazon ElastiCache 등 Managed Redis 구조를 검토한다.
Read Replica 미적용 현재 예상 Peak Read는 약 194 QPS로 초기에는 단일 Primary로 운영하지만, 사용자 증가 시 Read Query가 Primary의 CPU / I/O 병목이 될 수 있다. Query / Index 최적화와 RDS Scale-Up 이후에도 Read 병목이 지속되면 Read Replica를 추가한다.
AWS와 Runpod를 함께 사용하는 Cross-Cloud 구조 AI Server는 AWS에 있고 GPU Model Server는 Runpod에 있으므로 Internet 경로의 Network Latency, 외부 장애, Data Transfer 비용에 영향을 받을 수 있다. 실제 Latency와 비용을 Monitoring하고 필요 시 VPN, Private Connectivity 또는 AI 실행환경 통합을 검토한다.
SSE Connection은 Task 간 자동 이전되지 않음 Rolling Deployment나 Task 장애 시 기존 Streaming Connection이 새 Task로 이동하지 않으므로 진행 중인 AI 응답이 끊길 수 있다. Deregistration Delay, Graceful Shutdown, Client Retry / Reconnect 정책을 적용한다.
고정 Egress IP 부재 Public IP 기반 Fargate Task는 Task가 교체될 때 Source IP가 변경될 수 있다. Runpod 또는 외부 API에서 Source IP Allowlist가 필요하면 현재 구조로 대응하기 어렵다. 고정 Egress IP가 필요해지면 NAT Gateway, NAT Instance, Proxy 또는 Private Network 구성을 다시 설계한다.
Fargate 비용 증가 가능성 EC2 Host 운영 부담은 줄어들지만 Task가 상시 여러 개 실행되면 동일한 Compute를 EC2로 직접 운영하는 것보다 비용이 높아질 수 있다. 실제 Task Utilization과 월 비용을 Monitoring하고, 장기적으로 비용이 커지면 ECS + EC2 또는 Savings Plan 등 대안을 비교한다.
Application / DB가 AZ-A에 집중 Frontend, Backend, AI 모두 여러 Task를 운영하지만 같은 AZ-A에 있으므로 Task 단위 장애는 격리해도 AZ 장애는 격리할 수 없다. Single-AZ DB와 단일 Redis의 Risk도 함께 존재한다. 더 높은 Availability가 필요해지면 Application Task를 여러 AZ로 분산하고, Multi-AZ RDS와 Redis HA를 적용한다.

11.1 핵심 한계 요약

현재 V2는 Backend / AI Application Layer의 Scale-Out과 장애 격리에 우선 투자한 구조다. 반면 Database, Redis, Network Egress는 비용을 고려해 상대적으로 단순하게 구성하였다.

현재 V2

Application
→ ECS + Fargate
→ AZ-A Single-AZ 배치

Frontend
→ ECS + Fargate 2 Tasks / AZ-A

Backend / AI
→ 여러 Task지만 AZ-A에 집중

Database
→ RDS Single-AZ / AZ-A

Redis
→ 단일 Shared Container

Network Egress
→ Public IP + Internet Gateway

AI Inference
→ AWS → Runpod Cross-Cloud

따라서 향후 가장 먼저 확장 가능성이 높은 영역은 다음과 같다.

  1. AZ 장애까지 격리해야 할 경우 Application Multi-AZ + RDS Multi-AZ
  2. Redis 장애 영향 증가 시 Managed Redis / HA
  3. 보안 또는 고정 Egress IP 요구 증가 시 Private Subnet 기반 Egress 구조
  4. AI Traffic / Latency 증가 시 AWS ↔ Runpod Network 구조 재검토

V2는 완전한 High Availability Architecture가 아니라, Application과 Database를 AZ-A 한 곳에 집중하여 비용과 운영 복잡도를 줄이고, 필요 시 Multi-AZ로 확장하는 단계적 구조이다.

12. 최종 아키텍처

Internet
   │
   ▼
Internet Gateway
   │
   ▼
AWS VPC
│
├── AZ-A
│   │
│   ├── Public Subnet A
│   │   ├── Internet-facing ALB
│   │   │   ├── Default /* → Frontend Target Group
│   │   │   └── /api/* → Backend Target Group
│   │   │
│   │   ├── Frontend ECS Service
│   │   │   ├── Fargate Task 1 + Public IP
│   │   │   └── Fargate Task 2 + Public IP
│   │   │
│   │   ├── Backend ECS Service
│   │   │   ├── Fargate Task 1 + Public IP
│   │   │   └── Fargate Task 2 + Public IP
│   │   │
│   │   └── AI Server ECS Service
│   │       ├── Fargate Task 1 + Public IP
│   │       └── Fargate Task 2 + Public IP
│   │
│   └── Private DB Subnet A
│       ├── RDS MySQL
│       │   └── Single-AZ / db.t4g.medium
│       │
│       └── RDS PostgreSQL + pgvector
│           └── Single-AZ / db.t4g.small
│
├── AZ-C
│   └── Public Subnet C
│       └── ALB
│           (ALB 최소 2 AZ 요구사항 충족용)
│
├── Redis
│   └── AZ-A Private 영역 / 실행환경 별도 확정
│
└── External
    ├── Embedding API
    └── Runpod GPU Model Server

12.1 주요 통신 경로

User
↓ HTTPS
ALB
├── Frontend Fargate
└── Backend Fargate
      │
      ├── RDS MySQL
      ├── Redis
      └── ECS Service Connect
            ↓
          AI Server Fargate
            │
            ├── RDS PostgreSQL + pgvector
            ├── Redis
            ├── Embedding API
            └── Runpod GPU Model Server

Frontend / Backend / AI Server와 두 RDS는 실제 운영 Resource 기준으로 AZ-A에 집중한다.

AZ-C에는 Application Task나 DB를 두지 않고, Application Load Balancer의 최소 2개 AZ Subnet 요구사항을 충족하기 위한 Public Subnet만 둔다.

12.2 V2 최종 구성 요약

영역 V2 구성
VPC V1 VPC 유지
실제 Application AZ AZ-A 단일 AZ
ALB Network Mapping Public Subnet A + C
AZ-C 역할 ALB 연결용 Public Subnet만 사용
Frontend ECS + Fargate / 최소 2 Tasks / 모두 AZ-A
Backend ECS + Fargate / 최소 2 Tasks / 모두 AZ-A
AI Server ECS + Fargate / 최소 2 Tasks / 모두 AZ-A
Backend DB RDS MySQL / Single-AZ / AZ-A
AI Vector DB RDS PostgreSQL + pgvector / Single-AZ / AZ-A
Frontend / Backend ALB 1개 Internet-facing ALB 공유 / Target Group 분리
Backend → AI ECS Service Connect / 내부 Service Name 기반 통신
NAT Gateway 사용하지 않음
Redis AZ-A Private Shared Cache / 실행환경 별도 확정
AWS → Runpod HTTPS Public Endpoint

V2 초기에는 Application과 Database를 AZ-A에서 Single-AZ로 운영한다. 다만 ALB 자체는 AWS 요구사항 때문에 Public Subnet A와 C 두 개를 반드시 사용한다.

⚠️ **GitHub.com Fallback** ⚠️