EC2 Docker Compose 기반 초기 배포 설계 - 100-hours-a-week/KTB4-4th-wiki GitHub Wiki

목차

  1. 설계 목표
  2. 초기 서비스 규모
  3. 예상 Peak Traffic
  4. AI / LLM Traffic
  5. V1 최종 아키텍처
  6. 구성요소별 역할
  7. EC2 구성 및 용량
  8. Container / Host 구성
  9. Network 및 Security Group
  10. 데이터 저장 및 보호
  11. 배포 운영 기준
  12. 장애 허용 수준
  13. 예상 비용 및 운영 전략
  14. V1에서 적용하지 않는 구성
  15. ECR / IAM 및 Secret 운영
  16. 현재 구조의 한계
  17. V2 확장 기준
  18. V1 최종 기준

1. 설계 목표

Need U V1은 초기 사용자를 대상으로 서비스 가설을 검증하는 단계다.

핵심 사용자 흐름은 다음과 같다.

카카오 로그인
↓
친구 조회
↓
AI 대화 / 취향 분석
↓
선물 추천
↓
외부 상품 페이지 이동

V1 인프라는 이 흐름을 낮은 비용과 낮은 운영 복잡도로 안정적으로 제공하는 것을 목표로 한다.

처음부터 고가용성이나 대규모 확장을 전제로 하지 않고, 실제 운영 데이터를 확보한 뒤 병목이 확인된 구성요소부터 확장한다.

1.1 왜 AWS를 사용하는가

AWS, GCP, Azure 모두 V1 요구사항을 구현할 수 있다. Need U V1에서는 다음 이유로 AWS EC2 기반 구조를 선택한다.

  1. 팀이 EC2, Nginx, Security Group, Docker Compose 기반 배포 경험을 활용할 수 있다.
  2. Spring Boot와 Docker 기반의 현재 Application 구조를 크게 변경하지 않고 배포할 수 있다.
  3. V1 규모에서는 RDS, ElastiCache, ECS 같은 Managed Service 없이도 필요한 기능을 구성할 수 있다.
  4. V1의 우선순위는 장기적인 최적화보다 빠른 출시, 낮은 비용, 낮은 운영 복잡도다.

즉 AWS가 다른 Cloud보다 절대적으로 우수해서 선택한 것이 아니라, 현재 팀의 경험과 V1 구현 방식에 가장 자연스럽게 연결되는 선택지이기 때문에 사용한다.

2. 초기 서비스 규모

아래 값은 실제 운영 데이터가 아니라 V1 인프라 설계를 위한 가정치다.

지표 V1 예상 규모
MAU 약 1,000명
WAU 약 400~500명
DAU 약 80~140명
일반 Peak Traffic 약 0.16 RPS

2.1 MAU 약 1,000명

초기 사용자는 다음 경로를 통해 유입된다고 가정한다.

  • 카카오테크 부트캠프 교육생
  • 팀원 및 지인
  • 디스콰이엇 홍보
  • 기존 사용자의 친구 공유

MAU 1,000명은 실제 가입자를 예측한 수치라기보다 초기 인프라 규모를 정하기 위한 설계 기준값이다.

2.2 WAU 약 400~500명

Need U는 매일 사용하는 서비스보다 친구의 생일이나 선물이 필요한 시점에 다시 방문하는 서비스에 가깝다.

선물을 고려할 가능성이 높은 관계를 약 30명으로 가정하고, 친구 30명 중 최소 한 명이 일주일 안에 생일일 확률을 계산하면 약 44%다.

참고:

P = 1 - (358 / 365)^30
≈ 44%

MAU 1,000명에 적용하면 약 440명이다.

따라서 WAU를 약 400~500명으로 설정한다.

2.3 DAU 약 80~140명

친구 30명 중 최소 한 명이 하루 안에 생일일 확률은 약 7.9%다.

P = 1 - (364 / 365)^30
≈ 7.9%

MAU 1,000명 기준 약 79명이다.

Need U에서는 생일 당일 외에도 다음 행동이 발생할 수 있다.

  • 생일 이전 선물 탐색
  • 친구 취향 확인
  • 기존 추천 결과 조회
  • AI 취향 대화

따라서 약 80명을 하한으로 두고, 생활형 AI 서비스의 DAU/MAU 비율을 참고하여 DAU 80~140명으로 설정한다.

출시 이후에는 실제 MAU / WAU / DAU와 사용자 행동 데이터를 기준으로 수정한다.

2.4 참고 지표

외부 서비스 수치는 Need U 규모를 직접 환산하기 위한 값이 아니라, 사용자 활성도와 Peak 가정을 참고하기 위한 자료다.

서비스 MAU DAU 참고
카카오 선물하기 약 2,900만 비공개 일 거래 건수 공개 사례 존재
카카오톡 약 4,963만 비공개 대규모 메신저 서비스
쿠팡 약 3,357만 약 1,500만대 고빈도 커머스
컬리 약 450만 약 80만 커머스
뤼튼 약 119만 약 17만 생활형 AI
에이닷 약 186만 약 27만 AI 비서
ChatGPT 앱 약 1,204만 약 316만 범용 AI

근거:

외부 수치는 시점과 조사기관에 따라 달라질 수 있으므로 출시 후 Need U 실측치를 우선한다.

외부 서비스의 Peak 값은 공개 Peak RPS가 아니라 비교를 위한 단순 환산값이다.

Peak RPS = DAU × 0.20 × 20 / 3600

컬리
= 800,000 × 0.20 × 20 / 3600
≈ 889 RPS

쿠팡
= 15,700,000 × 0.20 × 20 / 3600
≈ 17,444 RPS

이 계산은 Need U와 동일한 사용 패턴을 의미하지 않으며 규모 감각을 잡기 위한 참고치로만 사용한다.

3. 예상 Peak Traffic

V1에는 대규모 요청이 특정 순간에 몰리는 이벤트성 기능이 없다.

다만 다음 시점에는 상대적인 Peak가 발생할 수 있다.

3.1 출시 초기

  • 부트캠프 내부 공유
  • 지인 공유
  • 디스콰이엇 홍보

3.2 일상 사용

Need U의 주요 행동은 저녁 시간에 집중될 가능성이 있다.

  • 친구 생일 확인
  • 친구 정보 및 취향 조회
  • AI 대화
  • 선물 추천 탐색

국내 모바일 쇼핑 사례에서는 저녁 시간대에 사용이 집중되는 경향이 있고, 한 사례에서는 22~23시 한 시간의 결제 비중이 약 17.3%로 나타났다.

Need U도 친구 생일 확인, 취향 조회, AI 대화, 선물 탐색이 저녁에 집중될 가능성이 있다고 보고, 이를 올림하여 V1 설계에서는 Peak 1시간 집중률을 DAU의 20%로 사용한다.

이 값은 Need U의 실측값이 아니라 인프라 산정을 위한 보수적 가정이다.

근거:

운영 후 실제 시간대별 Traffic 분포가 확보되면 20% 가정을 실측치로 교체한다.

3.3 대표 사용자 여정별 API 호출

기능정의서와 화면설계서를 기준으로 대표적인 정상 사용자 여정에서 발생하는 호출을 정리한다.

화면 / 흐름 API 목적 호출 시점 1회 정상 여정 호출 수 R/W
NU-01 로그인 카카오 OAuth 시작 카카오로 시작하기 클릭 1 외부
NU-01 로그인 서비스 로그인 / 회원 확인 카카오 인증 성공 후 1 W 1
NU-01 로그인 회원 / 동의 / 온보딩 상태 확인 로그인 성공 직후 1 R 1
NU-01 로그인, 신규 개인정보 수집 동의 저장 동의 모달 확인 클릭 1 W 1
NU-03-A 메인, 취향 미수집 홈 상태 / 안내 메시지 / 취향 상태 조회 메인 진입 1 R 1
NU-03-A 메인 AI 대화 페이지 이동 CTA 클릭 0 -
NU-03-B 메인, 취향 수집 완료 홈 추천 상품 조회 메인 진입 1 R 1
NU-04 AI 대화 전 대화 내역 조회 AI 화면 진입 1 R 1
NU-04 AI 대화 메시지 전송 + AI 답변 요청 전송 버튼 클릭 1/턴 W 1
NU-04 AI 대화 취향 분석 진행률 조회 답변 완료 후 1/턴 R 1
NU-04 분석 중 분석 상태 조회 분석 지연 후 재확인 / 재진입 0~1 R 0~1
NU-04 분석 완료 분석 결과 저장 “맞습니다” 선택 1 W 1
NU-05 친구 목록 친구 목록 조회 친구 탭 진입 1 R 1
NU-05 친구 목록 추가 친구 목록 조회 스크롤 하단 도달 0~1 R 0~1
NU-06 친구 상세 친구 상세 / 취향 키워드 조회 친구 카드 클릭 1 R 1
NU-06 친구 상세 선물 추천 페이지 이동 “선물 하기” 클릭 0 -
NU-07 추천 결과 추천 대상 친구 정보 조회 화면 진입 1 R 1
NU-07 추천 결과 추천 상품 목록 조회 화면 진입 1 R 1
NU-07 추천 결과 상품 이미지 조회 카드가 화면 근처에 올 때 3 R 3
NU-07 추천 결과 예산 변경 후 추천 재조회 예산 조절 완료 후 0~1 R 0~1
NU-07 추천 결과 외부 상품 페이지 이동 버튼 클릭 1 외부
NU-09 에러 직전 요청 재시도 재시도 버튼 클릭 0~1 원요청 동일
NU-09 에러 피드백 전송 피드백 제출 0~1 W 0~1

대표 정상 여정의 내부 호출 합계는 다음과 같이 본다.

구분 Read Write 외부
로그인 1 1 1
메인 1 0 0
AI 대화 6턴 + 분석 저장 7 7 0
친구 목록 / 상세 2 0 0
추천 결과 5 0 1
합계 16 5 2
내부 API 호출
= Read 16 + Write 5
= 약 21회

재시도, 추가 스크롤, 분석 지연 등은 정상 대표 여정의 21회에는 포함하지 않고 변동 요소로 본다.

3.4 Peak RPS 계산

DAU 상한 140명의 20%가 Peak 1시간에 접속하면:

140 × 20%
= 28명

Peak RPS:

28명 × 21회 / 3600초
≈ 0.16 RPS

따라서 V1의 일반적인 Peak Traffic은 **약 0.16 RPS**로 본다.

이 값은 평균적인 일반 Peak를 위한 설계값이며 출시 직후 홍보 유입처럼 짧은 Burst는 별도로 발생할 수 있다.

4. AI / LLM Traffic

AI 기능은 모든 화면에서 호출되는 것이 아니라 AI 대화 및 추천 흐름에서 사용한다.

대표 사용자가 AI 대화를 약 6턴 진행한다고 가정한다.

Peak 사용자 28명
× 사용자당 6회
= 168 LLM 요청/hour

RPS로 변환하면:

168 / 3600
≈ 0.047 RPS

평균 LLM 응답 시간을 약 3초로 두면 Little's Law 기준 평균 동시 요청은 다음과 같다.

L = λ × W

0.047 × 3
≈ 0.14
LLM RPS 평균 응답 시간 평균 동시 요청
약 0.047 약 3초 약 0.14

V1에서는 AI Scale-out보다 다음 운영 제어를 우선한다.

  • Timeout
  • Rate Limit
  • 중복 요청 방지
  • Retry
  • 외부 API 실패 처리

5. V1 최종 아키텍처

User
  │ HTTPS
  ▼
Route 53
  │
  ▼
Elastic IP
  │
  ▼
EC2 t3.medium
│
├─ Nginx (Host)
│  ├─ /     → React/Vite dist
│  └─ /api  → Spring Boot Backend :8080
│
├─ Docker Compose
│  ├─ Backend (Spring Boot)
│  ├─ AI Server (FastAPI)
│  └─ Redis
│
├─ MySQL (Host Process)
│  └─ Data Directory → EBS
│
└─ PostgreSQL (Host Process)
   ├─ pgvector Extension
   └─ Data Directory → EBS

AI Server는 Backend와 별도 Repository로 관리한다.

Frontend
↓
Backend
↓
AI Server
├─ External LLM API
├─ Embedding API
├─ Redis
└─ PostgreSQL + pgvector

Backend는 AI Server API를 호출하고, AI Server가 AI 관련 처리와 Vector Search를 담당한다.

5.1 왜 AI Server를 별도 Application으로 두는가

AI 모델 자체는 외부 API를 사용하지만, Need U의 AI 기능 전체가 단순 API 호출만으로 끝나는 것은 아니다.

AI Server에는 다음 책임이 존재한다.

  • 대화 Prompt 구성
  • 응답 Parsing
  • 취향 분석 Logic
  • Embedding 요청
  • Vector Search
  • 추천 / Ranking Logic
  • AI 관련 State 관리

이를 모두 Spring Boot에 넣으면 일반 서비스 Logic과 AI Logic의 책임이 섞인다.

따라서 V1에서도 Backend와 AI Server를 Application 수준에서 분리한다.

다만 V1 Traffic이 낮기 때문에 Application은 분리하되 EC2까지 분리하지는 않는다.

논리적 분리
Backend / AI Server
      O

물리적 분리
Backend EC2 / AI EC2
      X

이 방식으로 AI Repository와 배포 단위는 분리하면서, 별도 EC2 운영 비용과 Network 관리 복잡도는 추가하지 않는다.

6. 구성요소별 역할

6.1 Nginx

Nginx를 외부 요청의 단일 진입점으로 사용한다.

Internet
↓
HTTPS :443
↓
Nginx
├─ /     → Frontend
└─ /api  → Backend

Nginx를 사용하는 이유는 다음과 같다.

  • HTTPS 종료 지점을 하나로 유지할 수 있다.
  • React/Vite 정적 파일을 직접 제공할 수 있다.
  • /api 요청만 Backend로 전달할 수 있다.
  • Backend, AI Server, Redis, Database Port를 외부에 노출하지 않아도 된다.
  • Frontend와 Backend를 동일 Origin으로 제공할 수 있어 CORS와 Cookie 구성이 단순해진다.

AI Server는 사용자가 직접 호출하지 않는다.

사용자
↓
Nginx
↓
Backend
↓
AI Server

따라서 AI Server Port, Redis 6379, MySQL 3306, PostgreSQL 5432는 인터넷에 공개하지 않는다.

6.2 Frontend

React + Vite 기반이다.

Frontend는 Container로 실행하지 않고 Production Build 결과인 dist/를 Nginx Root에 배치한다.

6.3 Backend

Spring Boot 기반 API Server다.

주요 역할:

  • 카카오 로그인
  • 사용자 / 친구 관리
  • 생일 및 취향 데이터 관리
  • 일반 서비스 데이터 조회 / 저장
  • AI Server API 호출
  • 추천 결과 저장 및 조회
  • MySQL 접근

Backend가 외부 LLM API나 PostgreSQL + pgvector를 직접 처리하지 않는다.

6.4 AI Server

FastAPI 기반 별도 Application이다.

주요 역할:

  • AI 대화 처리
  • 취향 분석
  • Prompt / Parsing
  • 외부 LLM API 호출
  • Embedding API 호출
  • Vector Search
  • 추천 / Ranking Logic
  • Redis 기반 임시 AI State / Cache
  • PostgreSQL + pgvector 접근

AI 모델 자체는 EC2에서 실행하지 않는다.

따라서 V1에는 GPU Server나 vLLM이 필요하지 않다.

6.5 Redis

Redis는 장기 영속 데이터보다 짧게 유지되는 상태와 반복 접근 데이터를 처리한다.

예:

  • AI 대화 State
  • 분석 중간 상태
  • Cache
  • Rate Limit
  • 중복 요청 방지
  • TTL 데이터

이 데이터를 모두 Database에 저장하면 짧게 사용하고 버리는 상태까지 영구 저장소에서 관리해야 하므로 불필요한 Read / Write와 정리 작업이 늘어난다.

Redis는 Memory 기반으로 빠르게 읽고 쓸 수 있고 TTL을 통해 자동 만료시킬 수 있으므로 이 역할에 적합하다.

V1에서는 ElastiCache를 도입하지 않고 Docker Container 하나로 실행한다.

또한 V1의 초기 기준은 별도 Persistence Volume 없이 운영하는 것이다.

영구 데이터
→ MySQL / PostgreSQL

임시 State / Cache
→ Redis

따라서 Redis Container 재생성 시 Cache와 임시 State 초기화를 허용한다.

다만 운영 후 AI 대화 State 유실이 실제 사용자 경험에 큰 영향을 주거나 복구 비용이 커진다면 RDB/AOF Persistence 또는 ElastiCache를 재검토한다.

6.6 MySQL

MySQL은 서비스의 일반적인 정형 데이터를 저장한다.

  • 사용자
  • 친구 관계
  • 취향 결과
  • 상품 원본 정보
  • 가격 / 재고 / URL
  • 추천 결과

이 데이터는 Application 재배포와 관계없이 유지되어야 하는 서비스의 Source of Truth다.

따라서 MySQL은 Application Container와 수명주기를 분리하여 EC2 Host Process로 실행하고 데이터 디렉터리는 EBS에 저장한다.

V1에서 RDS를 바로 사용하지 않는 이유는 현재 Traffic이 낮고, DB를 Managed Service로 분리했을 때 얻는 가용성 이점보다 비용과 관리 대상 증가를 우선 줄이기 위해서다.

DB의 중요도가 낮다는 의미는 아니다. V1에서도 EBS, Backup, 외부 Port 차단을 적용하고, 운영 후 Failover와 자동 Backup 요구가 커지면 RDS를 검토한다.

6.7 PostgreSQL + pgvector

AI 상품 검색을 위한 Vector 데이터 저장소다.

저장 대상:

  • Product ID
  • 검색용 상품 설명
  • Embedding Vector
  • 검색 Filter / Metadata

AI 추천에서는 단순한 키워드 일치가 아니라 사용자의 취향과 의미적으로 유사한 상품을 찾을 필요가 있다.

pgvector를 사용하면 PostgreSQL 내부에 Embedding Vector를 저장하고 Vector Similarity Search를 수행할 수 있다.

AI Server
↓
Embedding 생성
↓
PostgreSQL + pgvector
↓
Vector Similarity Search
↓
Product ID
↓
Backend
↓
MySQL 상품 정보와 결합

왜 별도 Vector DB를 사용하지 않는가

Pinecone, Milvus와 같은 별도 Vector Database를 사용할 수도 있다.

하지만 V1에서는 상품 및 Vector 데이터 규모가 크지 않고, 이미 PostgreSQL에 pgvector Extension을 추가하면 필요한 Vector Search를 수행할 수 있다.

별도 제품을 추가하면 다음 관리 대상이 늘어난다.

  • 별도 Service
  • Credential
  • Network
  • 비용
  • Monitoring
  • Backup / 운영 정책

따라서 V1에서는 PostgreSQL + pgvector로 시작한다.

Vector 데이터 규모나 검색 부하가 증가하여 실제 병목이 확인되면 별도 DB 또는 Vector 전용 구조를 재검토한다.

7. EC2 구성 및 용량

7.1 단일 EC2 선택

V1에서는 여러 배치 방식을 비교한 뒤 단일 EC2 구성을 채택한다.

대안 구조 장점 단점 V1 판단
1안 FE + Backend + AI + Redis + MySQL + PostgreSQL을 EC2 1대 가장 저렴하고 단순 장애 영향과 Resource 경쟁이 큼 채택
2안 PostgreSQL을 별도 EC2로 분리 Vector DB Resource 분리 EC2 / Network / Monitoring 증가 미채택
3안 AI Server를 별도 EC2로 분리 AI와 Backend Resource 분리 현재 AI Traffic 대비 운영 대상 증가 미채택
4안 Frontend를 S3 + CloudFront로 분리 정적 파일 배포 / CDN 확장 용이 Resource와 배포 구성 증가 미채택

채택 구조:

EC2
├─ Nginx
├─ Frontend dist
├─ Backend
├─ AI Server
├─ Redis
├─ MySQL
└─ PostgreSQL + pgvector

단일 EC2를 선택한 이유

  • 예상 일반 Peak가 약 0.16 RPS로 낮다.
  • 초기 인프라 비용을 최소화할 수 있다.
  • 관리해야 할 Server와 Network 구성이 적다.
  • Backend와 AI Server 간 별도 Server Network 구성이 필요 없다.
  • MySQL과 PostgreSQL도 별도 Server로 운영하지 않아 초기 운영 복잡도가 낮다.

감수하는 단점

  • EC2 한 대가 전체 서비스의 SPOF가 된다.
  • Spring Boot, FastAPI, Redis, MySQL, PostgreSQL이 CPU / Memory / Disk를 공유한다.
  • AI 또는 DB 부하가 다른 Application 성능에 영향을 줄 수 있다.

따라서 “단일 EC2가 이상적인 구조”라서 선택한 것이 아니라, 현재 Traffic에서 가장 단순한 구조로 시작하고 그 한계를 명시적으로 감수하는 선택이다.

7.2 왜 t3.medium으로 시작하는가

초기 Instance는 t3.medium을 사용한다.

V1에서는 모든 주요 구성요소를 하나의 EC2에서 실행하므로 Instance Type을 결정할 때 단순 Traffic뿐 아니라 Memory 여유와 월 비용을 함께 고려한다.

AWS Pricing Calculator에서 서울 리전 기준으로 비교한 월 예상 비용은 다음과 같다. image

Instance Type vCPU RAM 월 예상 비용 V1 판단
t3.small 2 2 GiB 21.72 USD 비용은 낮지만 Backend + AI Server + Redis + MySQL + PostgreSQL을 함께 운영하기에는 Memory 여유가 부족할 가능성이 높음
t3.medium 2 4 GiB 40.70 USD V1 초기 Instance로 채택
t3.large 2 8 GiB 78.66 USD Memory 여유는 크지만 V1 초기 규모에서 바로 사용하기에는 비용이 증가함

비용은 AWS Pricing Calculator에서 아시아 태평양(서울) 리전을 기준으로 계산한 값이며, 실제 청구 금액은 사용 시간과 추가 Resource에 따라 달라질 수 있다.

t3.small은 월 비용이 가장 낮지만 2 GiB RAM 안에서 다음 Process를 함께 운영해야 한다.

  • Spring Boot Backend
  • FastAPI AI Server
  • Redis
  • MySQL
  • PostgreSQL + pgvector
  • Nginx
  • Docker / OS

따라서 2 GiB에서는 Application과 Database가 동시에 Memory를 사용하면서 여유가 부족할 가능성이 높다.

반대로 t3.large는 8 GiB RAM을 제공하여 여유는 충분하지만, 월 예상 비용이 78.66 USD로 t3.medium의 약 2배 수준이다.

V1은 아직 실제 운영 Resource 사용량이 없는 단계이므로 처음부터 t3.large를 사용하는 것보다 4 GiB를 제공하는 t3.medium으로 시작하고 실제 사용량을 측정하는 방식을 선택한다.

따라서 V1의 초기 Instance는 다음과 같이 확정한다.

Instance Type
→ t3.medium

vCPU
→ 2

RAM
→ 4 GiB

월 예상 비용
→ 40.70 USD

### 7.3 초기 Resource Budget

V1에서는 `t3.medium`의 4 GiB RAM을 다음 기준으로 시작한다.

| 구성요소 | V1 초기 기준 |
| --- | ---: |
| Backend | 최대 `1536MB` |
| AI Server | 최대 `512MB` |
| Redis | 최대 `256MB` |
| MySQL + PostgreSQL + OS + Nginx + Docker + File Cache | 나머지 약 `1792MB` |

```text
4096MB
- Backend 1536MB
- AI Server 512MB
- Redis 256MB
= 약 1792MB

이 값은 공식 권장값이 아니라 V1에서 먼저 적용할 초기 Resource Budget이다.

V1은 위 값으로 시작하고, 운영 후 실제 Memory 사용량과 OOM 여부를 확인하여 각 Limit을 조정한다. 전체 Memory 여유가 부족하면 개별 Limit만 계속 줄이기보다 t3.large Scale-Up을 우선 검토한다.

7.4 Scale-Up 기준

다음 현상이 반복되면 Scale-Up을 검토한다.

  • Memory 여유 부족
  • 지속적인 Swap 사용
  • OOM
  • CPU 지속 포화
  • MySQL Query Latency 증가
  • pgvector 검색 Latency 증가
  • AI Server Resource 사용량 증가
  • Disk I/O 병목

단순히 Resource 사용률 한 번만 보고 Scale-Up하지 않고 지속적인 병목 여부를 확인한다.

7.5 Frontend를 S3 + CloudFront로 분리하지 않는 이유

실서비스에서는 S3 + CloudFront가 일반적인 선택지가 될 수 있다.

장점은 다음과 같다.

  • Frontend와 Backend 배포 분리
  • CDN 활용
  • 정적 파일을 Application EC2 외부에서 제공
  • 이후 글로벌 배포 확장 용이

하지만 V1의 일반 Peak는 약 0.16 RPS이고, HTML / CSS / JS 정적 파일을 Nginx가 제공하는 부하는 현재 병목이 될 가능성이 낮다.

또한 Frontend가 별도로 살아 있더라도 Need U의 핵심 기능은 Backend와 AI Server에 의존한다.

따라서 V1에서는 S3 + CloudFront를 추가하지 않고 Nginx가 Frontend dist/를 제공한다.

정적 Traffic이나 CDN 요구가 증가하면 이후 분리한다.

7.6 Database를 별도 EC2로 분리하지 않는 이유

Database를 별도 EC2로 분리하면 다음 장점이 있다.

  • Application과 CPU / Memory / Disk 분리
  • Application 장애와 DB Process 분리
  • DB별 Resource 관리 용이

하지만 동시에 다음 관리 대상이 증가한다.

  • 추가 EC2
  • EBS
  • Security Group
  • Network
  • Monitoring
  • Backup 대상

현재 Peak 약 0.16 RPS만으로는 DB Resource 경쟁을 이유로 Server를 미리 분리할 근거가 부족하다.

따라서 MySQL과 PostgreSQL + pgvector를 같은 EC2에서 시작하고, 실제 CPU / Memory / Disk I/O / Query / Vector Search 병목이 확인된 Database부터 분리한다.

8. Container / Host 구성

8.1 Docker Compose 관리 대상

구성요소 Compose 비고
Backend O Spring Boot
AI Server O FastAPI
Redis O 공식 Redis Image
Nginx X Host
Frontend X Nginx 정적 파일
MySQL X Host Process
PostgreSQL + pgvector X Host Process

8.2 분리 기준

Application은 재배포 때 교체 가능한 Container로 운영한다.

Docker Compose
├─ Backend
├─ AI Server
└─ Redis

Database는 Application 배포와 수명주기를 분리한다.

Host
├─ MySQL
└─ PostgreSQL + pgvector

Nginx는 외부 진입점이므로 Host에 설치한다.

8.2.1 왜 Application은 Container이고 DB는 Host Process인가

Backend와 AI Server는 새로운 Image로 자주 교체되는 Application이다.

Docker Container로 관리하면 다음 이점이 있다.

  • 개발 / CI / Production 실행 환경을 맞추기 쉽다.
  • Image SHA 단위로 배포 버전을 추적할 수 있다.
  • 이전 Image로 Rollback하기 쉽다.

반면 MySQL과 PostgreSQL은 Application 배포마다 교체되는 대상이 아니라 영구 데이터 저장소다.

V1에서는 Database Container까지 Compose 배포 수명주기에 포함시키지 않고 Host Process로 분리한다.

자주 교체되는 것
→ Backend / AI Server
→ Docker Container

임시 State
→ Redis
→ Docker Container

영구 데이터
→ MySQL / PostgreSQL
→ Host Process + EBS

이는 Container에서 Database를 운영할 수 없다는 의미가 아니라, V1의 Application 배포와 Database 수명주기를 단순하게 분리하기 위한 선택이다.

8.3 Docker Compose 초기 설정

V1에서 Compose가 관리하는 서비스의 초기 기준은 다음과 같다.

구성요소 Compose 관리 초기 설정
Backend O network_mode: host, mem_limit: 1536m, restart: unless-stopped
AI Server O network_mode: host, mem_limit: 512m, restart: unless-stopped
Redis O network_mode: host, mem_limit: 256m, restart: unless-stopped, Persistence Volume 없음
Nginx X Host 설치
Frontend X dist/를 Nginx Root에 배치
MySQL X Host Process + EBS
PostgreSQL + pgvector X Host Process + EBS

mem_limit 값은 공식 권장값이 아니라 V1에서 실제 사용량을 측정하기 위한 초기 기준이다.

AI Server가 추가되었으므로 기존 Backend + Redis 기준만으로 Resource Budget을 잡지 않고 AI Server 512MB를 별도로 확보한다.

8.4 내부 연결

V1에서는 Backend, AI Server, Redis Container에 Host Network 방식을 사용한다.

따라서 Host Process로 실행되는 MySQL / PostgreSQL과 같은 EC2 내부 주소 공간에서 통신한다.

Backend
├─ MySQL        → localhost:3306
├─ Redis        → localhost:6379
└─ AI Server    → localhost:<AI_PORT>

AI Server
├─ Redis                  → localhost:6379
├─ PostgreSQL + pgvector  → localhost:5432
├─ External LLM API
└─ Embedding API

Backend는 PostgreSQL + pgvector를 직접 조회하지 않고 AI Server API를 통해 AI 기능을 사용한다.

Backend 환경변수 예시

SPRING_DATASOURCE_URL
→ jdbc:mysql://localhost:3306/${DB_NAME}

REDIS_HOST
→ localhost

REDIS_PORT
→ 6379

AI_SERVER_URL
→ http://localhost:<AI_PORT>

AI Server 환경변수 예시

POSTGRES_HOST
→ localhost

POSTGRES_PORT
→ 5432

REDIS_HOST
→ localhost

REDIS_PORT
→ 6379

LLM_API_KEY
→ EC2 환경변수 / env file에서 주입

EMBEDDING_API_KEY
→ EC2 환경변수 / env file에서 주입

Secret은 Git Repository나 Docker Image에 포함하지 않는다.

AI Server의 실제 Application Port는 AI Repository 실행 설정과 맞춰 확정하되, 외부에는 공개하지 않는다.

운영 후 Service 분리 또는 Multi-Host 구성이 필요해지면 Host Network 대신 Docker Bridge Network 또는 별도 Network 구성을 재검토한다.

9. Network 및 Security Group

V1에서는 Default VPC의 Public Subnet을 사용한다.

Internet
↓
Route 53
↓
Elastic IP
↓
EC2

9.1 Inbound

Port Source 정책
443 0.0.0.0/0 허용
80 0.0.0.0/0 HTTP → HTTPS Redirect 필요 시 허용
22 - 배포 / 운영에 사용하지 않음

외부에 공개하지 않는 Port:

  • Backend 8080
  • AI Server 내부 Port
  • Redis 6379
  • MySQL 3306
  • PostgreSQL 5432

관리 접속과 배포에는 SSM을 우선 사용한다.

9.2 Outbound

외부 API 호출이 필요하므로 기본 Outbound 허용을 사용한다.

Backend:

  • Kakao OAuth / API
  • 필요한 외부 서비스

AI Server:

  • LLM API
  • Embedding API
  • 필요한 AI 외부 API

10. 데이터 저장 및 보호

10.1 Database

MySQL과 PostgreSQL 데이터는 EBS에 저장한다.

Application Container가 재배포되어도 Database 데이터는 유지된다.

운영 항목:

  • EBS Snapshot
  • 논리 Backup
  • DB Port 외부 비공개
  • Disk 사용량 모니터링
  • MySQL Query 상태 확인
  • PostgreSQL / pgvector 검색 상태 확인

10.2 Redis

V1 Redis는 Persistence 없이 운영한다.

Redis에는 Cache와 TTL 기반 임시 State만 저장하고, 사용자·친구·취향 결과·상품 원본·추천 결과 등 영구 데이터는 Database에 저장한다.

따라서 Redis Container 재생성 시 임시 State 초기화는 허용한다.

운영 후 AI 대화 State 유실이 핵심 사용자 흐름에 반복적으로 영향을 주는 것이 확인되면 Persistence 또는 Managed Redis를 도입한다.

10.3 Backup 및 데이터 보호

V1에서도 Database는 영구 데이터이므로 Application보다 강하게 보호한다.

초기 운영 기준:

  • MySQL Data Directory → EBS
  • PostgreSQL Data Directory → EBS
  • EBS Snapshot
  • MySQL 논리 Backup
  • PostgreSQL 논리 Backup
  • Database Port 외부 비공개
  • Disk 사용량 확인
  • MySQL Query 및 Disk I/O 확인
  • PostgreSQL / pgvector Search Latency 확인

Snapshot과 논리 Backup은 목적이 다르다.

EBS Snapshot
→ Volume 단위 복구

Logical Backup
→ Database / Table 단위 복구 가능성 확보

백업 주기와 보존 기간은 실제 운영 일정에 맞춰 확정하되, V1에서도 Snapshot + 논리 Backup을 모두 사용하는 방향으로 시작한다.

11. 배포 운영 기준

V1에서는 무중단 배포보다 Recreate 방식의 단순한 배포를 사용한다.

기존 Container
↓
종료
↓
신규 Container
↓
Health Check
↓
서비스 재개

Backend와 AI Server는 각각 독립적으로 배포한다.

11.1 왜 Recreate 방식인가

무중단 배포를 하려면 동시에 구버전과 신버전 Application을 실행하고 Traffic을 전환할 수 있는 구조가 필요하다.

예:

  • Application Instance 다중화
  • ALB
  • Blue-Green
  • Rolling Deployment

하지만 V1은 Backend와 AI Server가 각각 하나의 Container로 동작하고, 일반 Peak Traffic도 낮다.

따라서 무중단 구조를 먼저 추가하기보다 짧은 계획 Downtime을 허용하는 Recreate 방식을 사용한다.

기존 Container 종료
↓
새 Image 실행
↓
Health Check
↓
성공 → 배포 완료
실패 → 이전 Image Rollback

배포 Downtime이 실제 사용자 문제로 커지면 Blue-Green 또는 Rolling 방식을 검토한다.

11.2 배포 빈도

항목 V1 기준
기본 빈도 주 2회
기본 시각 새벽 04:00
20~23시 배포 지양
Production Trigger workflow_dispatch 수동 배포
긴급 버그 예외 배포 허용

V1에서는 주 2회 새벽 04:00 배포를 기본 운영 기준으로 사용한다.

왜 새벽 04:00인가

Need U의 선물 탐색과 AI 대화는 저녁 시간대에 사용될 가능성이 높기 때문에 20~23시 계획 배포를 피한다.

낮은 Traffic이 예상되는 새벽에 계획 Downtime을 배치하여 사용자 영향을 줄인다.

왜 주 2회인가

배포가 너무 잦으면 Recreate 방식에서 계획 Downtime 횟수도 함께 증가한다.

반대로 배포 간격이 너무 길면 V1 단계에서 발생하는 사용자 피드백과 버그 수정 반영이 느려진다.

따라서 사용자 영향과 개발 속도의 절충안으로 주 2회를 초기 기준으로 사용한다.

Critical Bug는 정기 일정과 관계없이 긴급 배포할 수 있다.

운영 후 실제 시간대별 Traffic과 팀의 배포 패턴을 확인하여 빈도와 시간을 조정한다.

11.3 Rollback

  • Frontend → 이전 dist/
  • Backend → 이전 ECR Image SHA
  • AI Server → 이전 ECR Image SHA

Database Schema Rollback은 Application Rollback과 별개의 문제이므로 파괴적인 Migration은 피한다.

12. 장애 허용 수준

V1에서는 단순 서버 오류율보다 핵심 사용자 흐름의 성공 여부를 중요하게 본다.

그 이유는 Infrastructure 내부에서 짧은 오류가 발생하더라도 사용자가 정상 결과를 받을 수 있다면 실제 서비스 영향은 작기 때문이다.

반대로 Server 자체가 살아 있더라도 로그인, AI 대화, 추천 조회가 지속적으로 실패하면 사용자는 서비스를 정상적으로 사용할 수 없다.

따라서 Need U에서는 Infrastructure 상태 자체보다 사용자 핵심 동작이 성공하는지를 장애 판단의 중심에 둔다.

참고:

핵심 흐름:

  • 로그인
  • 친구 조회
  • AI 대화
  • 취향 저장
  • 추천 조회
  • 외부 상품 이동
장애 허용 수준
사용자에게 보이지 않는 일시 오류 상대적으로 허용
비핵심 기능 일부 장애 제한적으로 허용
로그인 / AI / 추천 등 핵심 흐름 장애 낮게 허용
핵심 데이터 손실 거의 허용하지 않음

가용성 99.0%는 V1 내부 참고값으로만 사용할 수 있다.

168시간 × 1%
≈ 100.8분 / 주

외부 SLA로 보장하는 값은 아니다.

사용자 경험 및 장애 영향 참고:

13. 예상 비용 및 운영 전략

V1 비용의 핵심은 Managed Service를 선제적으로 늘리지 않는 것이다.

주요 AWS Resource:

  • EC2 t3.medium 1대
  • EBS
  • Public IPv4 / Elastic IP
  • Route 53
  • Amazon ECR Private Repository
    • needu-backend
    • needu-ai

Frontend는 Host Nginx에서 제공하므로 S3 + CloudFront를 사용하지 않는다.

MySQL과 PostgreSQL + pgvector는 별도 RDS로 분리하지 않는다.

13.1 운영 전략

V1은 초기 사용자 검증 단계이므로 단일 EC2 기반의 최소 구성으로 시작하고, 실제 운영 데이터를 기준으로 필요한 구성요소만 확장한다.

V1
단일 EC2 기반 최소 구성
↓
실제 Resource / 비용 측정
↓
병목 확인
↓
필요한 부분만 조정

V2
↓
실제 병목이 확인된 구성요소부터 분리

V1은 위 구성으로 시작한다.

실제 운영 후 AWS Cost Explorer를 통해 비용을 확인하고, 예상보다 비용이 크거나 Resource 사용량이 낮다면 Instance / Storage 구성을 조정한다.

13.2 V1 Credit 사용 계획

Need U V1은 초기 사용자 검증 단계이므로, AWS에서 제공되는 Credit을 우선 활용하여 초기 운영 비용을 절감한다.

V1에서는 다음 비용을 Credit으로 우선 충당할 예정이다.

  • EC2 t3.medium
  • EBS
  • Public IPv4 / Elastic IP
  • Route 53
  • Amazon ECR

따라서 V1의 실제 현금 지출은 AWS Pricing Calculator에서 계산한 예상 비용보다 낮을 수 있다.

다만 Credit은 일시적으로 제공되는 비용 지원이므로, Credit 사용 여부와 별개로 실제 운영 비용을 지속적으로 확인한다.

AWS Credit 우선 사용
↓
V1 운영 비용 절감
↓
AWS Cost Explorer로 실제 비용 확인
↓
Credit 소진 이후에도 유지 가능한 비용 구조인지 검토

즉 V1에서는 Credit을 활용해 초기 비용 부담을 줄이되, Credit에 의존한 구조를 만드는 것이 아니라 Credit이 종료된 이후에도 감당 가능한 인프라 구성을 유지하는 것을 기준으로 한다.

14. V1에서 적용하지 않는 구성

아래 구성은 기술적으로 불필요하거나 나쁜 선택이라서 제외한 것이 아니다.

현재 V1 Traffic과 팀 규모에서 얻는 효과보다 비용과 운영 복잡도가 더 크다고 판단한 항목이다.

따라서 각 구성에는 다시 도입을 검토할 조건을 함께 정의한다.

구성 미적용 이유 재검토 시점
Custom VPC / Private Subnet 단일 EC2에서 Network 복잡도 증가 Server / DB 분리 시
ALB Traffic을 분산할 Application Server가 1대 Scale-out 시
S3 + CloudFront 낮은 정적 Traffic, Nginx로 충분 정적 Traffic 증가 시
별도 DB EC2 초기 Traffic 대비 비용 / 운영 증가 Resource 경쟁 발생 시
RDS Host DB로 빠르게 시작 Backup / Failover 중요도 증가 시
ElastiCache Redis 단일 Container로 시작 가능 Redis 중요도 / Scale 증가 시
별도 Vector DB SaaS pgvector로 초기 요구 처리 가능 Vector 규모 / 검색 부하 증가 시
별도 AI EC2 초기 AI Traffic이 낮음 AI Resource 경쟁 발생 시
GPU / vLLM 외부 모델 API 사용 자체 모델 운영 시
ECS / Kubernetes 단일 Host에서 필요성 낮음 Multi-Host 운영 시
Blue-Green / Rolling Downtime 영향 대비 복잡도 큼 무중단 요구 증가 시

15. ECR / IAM 및 Secret 운영

15.1 ECR

Backend와 AI Server는 각각 Private Amazon ECR Repository를 사용한다.

needu-backend:<Git Commit SHA>
needu-ai:<Git Commit SHA>

latest에 의존하지 않고 Commit SHA를 Image Tag로 사용한다.

이를 통해 다음을 보장한다.

  • 어떤 Commit이 배포되었는지 추적 가능
  • 동일 Image를 Production에 배포
  • 이전 SHA로 Rollback 가능
  • Backend와 AI Server 버전을 독립적으로 관리

Frontend는 Docker Image가 아니라 dist/ Artifact를 사용한다.

15.2 GitHub Actions → AWS 권한

CI에서는 장기 AWS Access Key를 GitHub Secret에 저장하는 방식보다 GitHub Actions OIDC → AWS IAM Role 방식을 사용한다.

GitHub Actions
↓ OIDC
AWS IAM Role
↓
ECR Push

IAM Role은 필요한 Repository Push 등 최소 권한으로 구성한다.

15.3 EC2 권한

Production EC2에는 Instance Role을 부여한다.

필요 권한 예:

  • ECR Image Pull
  • SSM Session Manager / Run Command
  • 운영에 필요한 최소 AWS Resource 조회

SSH 22는 기본 운영 경로로 사용하지 않는다.

15.4 Secret

다음 값은 Repository와 Docker Image에 포함하지 않는다.

Backend 예:

  • Kakao API Credential
  • DB Password
  • JWT Secret

AI Server 예:

  • LLM API Key
  • Embedding API Key
  • PostgreSQL Password

V1에서는 EC2의 환경변수 또는 권한이 제한된 env file로 주입한다.

파일을 사용하는 경우 Repository에 포함하지 않고 권한을 제한한다.

chmod 600

향후 Secret 수가 증가하거나 Rotation 요구가 커지면 AWS Secrets Manager 또는 Parameter Store를 검토한다.

16. 현재 구조의 한계

16.1 단일 EC2가 SPOF

현재 주요 구성요소가 하나의 EC2에 집중되어 있다.

EC2 장애
↓
Nginx / Backend / AI / Redis / DB
↓
전체 서비스 영향

restart: unless-stopped를 적용한 Container는 Process 종료 후 재기동할 수 있지만, Docker나 EC2 Host 장애 자체를 다른 Host로 복구하지는 못한다.

16.2 Docker Compose의 복구 범위

V1에서는 Backend, AI Server, Redis에 restart: unless-stopped를 적용한다.

Container Process 종료
↓
Docker Restart Policy
↓
Container 재실행

이 정책은 Container Process 장애에는 도움이 되지만 EC2 자체가 중단되면 Compose도 함께 멈춘다.

Docker Compose는 한 Host에서 Container 실행 상태를 관리하는 도구이며, 다른 EC2로 Workload를 자동 재배치하는 Orchestrator가 아니다.

여러 Host 간 자동 재배치가 필요해지는 시점에 ECS 같은 Orchestration 환경을 검토한다.

16.3 Resource 경쟁

동일 Host에서 다음 Resource를 공유한다.

CPU
Memory
Disk I/O
Network

Traffic 증가 시:

Backend 처리 증가
+
AI Server 처리 증가
+
MySQL Query 증가
+
pgvector 검색 증가
+
Redis 접근 증가
↓
동일 EC2 Resource 경쟁

16.4 Database 장애 분리 안 됨

Application과 Database의 배포 수명주기는 분리되어 있지만 물리적으로는 같은 EC2에 있다.

따라서 EC2 장애 시 Database도 함께 영향을 받는다.

16.5 Nginx 단일 진입점

Nginx 또는 EC2 장애 시 Frontend와 API 접근이 모두 불가능하다.

현재는 Backend Instance가 1대이므로 ALB를 추가해도 실제 분산 대상이 없다.

16.6 Redis 단일 Container

Redis Container 장애 또는 재생성 시 임시 데이터가 초기화될 수 있다.

Redis State의 중요도가 커지면 Persistence 또는 Managed Redis를 재검토해야 한다.

16.7 Recreate Downtime

Backend와 AI Server를 교체하는 동안 짧은 Downtime이 발생할 수 있다.

현재 Traffic과 배포 빈도를 고려해 이를 허용한다.

16.8 자동 복구 범위 제한

V1에서 자동화하는 범위:

CI
↓
Artifact 생성
↓
Production 배포
↓
Health Check
↓
배포 실패 시 Rollback

자동화하지 않는 범위:

  • EC2 장애 시 자동 Instance 생성
  • Database Failover
  • Multi-AZ
  • Database Schema 자동 Rollback
  • Error Rate 기반 자동 Rollback
  • Secret 자동 Rotation

16.9 Observability 초기 수준

V1에서는 복잡한 APM이나 Distributed Tracing보다 Host와 핵심 Application 상태를 먼저 확인한다.

확인 대상:

  • EC2 CPU
  • EC2 Memory
  • Disk 사용률 / 여유 공간
  • Backend Health Check
  • Backend Application Log
  • AI Server Health Check
  • AI Server API Latency
  • HTTP 4xx / 5xx
  • Redis Memory
  • MySQL 상태 / Query / Disk I/O
  • PostgreSQL 상태 / Disk I/O
  • pgvector Search Latency
  • 외부 LLM / Embedding API 오류율

V1에서는 Error Rate 기반 자동 Rollback, APM, Distributed Tracing을 기본 적용하지 않는다.

장애 원인 분석 시간이 반복적으로 길어지거나 Server가 분리되어 요청 흐름 추적이 어려워지면 APM, Trace, Dashboard, Alarm을 확장한다.

17. V2 확장 기준

V2에서는 “사용자가 늘었다”는 이유만으로 모든 구성요소를 한 번에 분리하지 않는다.

V1에서 단일 EC2를 선택한 이유가 비용과 운영 복잡도를 줄이기 위해서였던 만큼, V2에서도 측정 없이 구조를 복잡하게 만들지 않는 것을 원칙으로 한다.

실제 병목을 확인한 뒤 해당 구성부터 확장한다.

상황 검토 방향
Backend Traffic 증가 Application Scale-out + ALB
AI Server Resource 경쟁 AI Server 별도 EC2 또는 Scale-out
MySQL 병목 MySQL 분리 / RDS
pgvector 검색 병목 PostgreSQL 분리 / RDS 검토
Redis 중요도 / 부하 증가 ElastiCache
무중단 요구 증가 Blue-Green / Rolling
Multi-Host Container 관리 필요 ECS
자체 LLM 운영 필요 GPU Endpoint / vLLM
장애 분석 복잡도 증가 APM / Trace / Dashboard

17.1 확장 판단 지표

다음 실측값을 기준으로 판단한다.

  • MAU / WAU / DAU
  • Peak RPS
  • CPU / Memory
  • Disk I/O
  • MySQL Query Latency
  • PostgreSQL + pgvector Search Latency
  • AI Server API Latency
  • 외부 AI API 오류율
  • Redis Memory
  • 장애 빈도
  • 배포 빈도

18. V1 최종 기준

서비스 규모

MAU  약 1,000명
WAU  약 400~500명
DAU  약 80~140명
Peak 약 0.16 RPS
LLM  약 0.047 RPS

Infrastructure

AWS
↓
Default VPC / Public Subnet
↓
Elastic IP
↓
EC2 t3.medium
│
├─ Nginx
├─ Frontend dist
├─ Docker Compose
│  ├─ Backend
│  ├─ AI Server
│  └─ Redis
├─ MySQL
└─ PostgreSQL + pgvector

Application 책임

Backend
├─ 인증 / 사용자 / 친구
├─ 일반 서비스 Business Logic
├─ MySQL
└─ AI Server API 호출
AI Server
├─ LLM / Embedding API
├─ Redis AI State
├─ PostgreSQL + pgvector
└─ AI 추천 Logic

운영 원칙

V1에서 먼저 적용하는 값과 방식은 다음과 같다.

  • 단일 EC2로 시작한다.
  • Frontend는 Nginx 정적 파일로 제공한다.
  • Backend / AI Server / Redis는 Docker Compose로 관리한다.
  • MySQL / PostgreSQL은 Host Process로 운영하고 EBS에 데이터를 저장한다.
  • 외부에는 HTTPS 443만 기본 공개한다.
  • AI 모델 자체는 운영하지 않는다.
  • Backend와 AI Server는 각각 별도 CI/CD를 사용한다.
  • Recreate 배포와 SHA 기반 Rollback을 사용한다.
  • 초기 Instance는 t3.medium으로 시작한다.
  • Backend 1536MB, AI Server 512MB, Redis 256MB를 초기 Container Memory Limit으로 사용한다.
  • Redis는 V1에서 Persistence Volume 없이 운영한다.
  • 계획 배포는 주 2회 새벽 04:00을 기본으로 한다.
  • 실제 Resource 사용량을 측정한 뒤 필요하면 t3.large로 Scale-Up한다.
  • V2에서는 실제 병목이 확인된 구성요소부터 분리한다.
⚠️ **GitHub.com Fallback** ⚠️