한줄 결론: Web·Application·DB의 역할을 분리하고, 병목 계층만 수평 확장하며, 상태와 DB를 서버 밖에 두어야 장애와 확장에 대응할 수 있다.
상황
사용자 로그인, 상품 조회·주문, 회원·주문 정보 저장 기능이 있는 이커머스를 단계적으로 개선한다. 실제 AWS 리소스를 새로 만들기보다 왜 구조가 바뀌는지 이해한다.
Step 1 · 한 서버에서 역할 분리
문제: Web·주문 로직·DB가 한 서버에 섞여 병목과 장애 원인을 구분하기 어렵다.
답: 요청을 받는 Presentation, 업무 로직의 Application, 데이터를 저장하는 Data 계층으로 나눈다.
- Presentation: Nginx 같은 Web Server
- Application: 로그인·상품·주문 로직
- Data: 회원·상품·주문 데이터
구조 변경: 단일 서버 → Web / App / DB
효과: 계층별 로그·자원 사용량을 확인하고 필요한 부분만 배포·확장할 수 있다.
Step 2 · Scale Up과 Scale Out
App CPU만 100%라면 App이 병목이다.
- Scale Up: CPU·메모리를 키운다. 간단하지만 사양 한계와 교체 중 중단 가능성이 있다.
- Scale Out: App 수를 늘린다. 확장과 장애 대응에 유리하지만 Load Balancing과 상태 관리가 필요하다.
선택: 계속 성장하는 서비스는 App을 두 대 이상으로 수평 확장한다.
현재 실습 구조
Step 3 · Load Balancing
App 2대를 만들기만 해서는 요청이 자동 분산되지 않는다. Load Balancer가 하나의 주소로 요청을 받고 여러 App에 나눈다.
- Round Robin: 순서대로 분배
- Least Connections: 연결이 적은 서버에 분배
- Health 기반: 정상 서버에만 전달
구조 변경: App 1대 → Load Balancer + App 1·2
#ALB(Application Load Balancer)는 사용자 요청을 여러 Web/App 서버에 나눠주는 교통정리 장
Step 4 · Stateful과 Stateless
Session을 App 1 메모리에만 저장하면 다음 요청이 App 2로 갈 때 로그인 정보가 없다.
해결: App은 Stateless하게 만들고 Session은 ElastiCache·DynamoDB 같은 공용 저장소에 둔다.
- Stateful: 서버 자체가 사용자 상태를 기억
- Stateless: 상태를 외부 저장소에서 읽음
Step 5 · High Availability와 Health Check
Load Balancer는 Health Check URL을 주기적으로 확인해 실패한 App을 Target Group에서 제외한다.
- 한 대가 죽어도 유지하려면 App이 최소 2대 필요하다.
- 실제 가용성을 높이려면 서로 다른 Availability Zone에 둔다.
- Redundancy와 Health Check가 함께 필요하다.
Step 6 · Failure Domain과 DB 분리
Failure Domain은 하나가 고장 났을 때 함께 영향을 받는 범위다. App과 DB를 같은 EC2에 두면 App 배포·재시작이 DB 장애로 이어진다.
해결:
- DB를 App EC2와 분리한다.
- RDS를 사용하고 필요하면 Multi-AZ 대기 DB를 둔다.
- App 장애와 DB 장애의 범위를 분리한다.
DB를 별도 서버에 두는 것만으로 이중화되는 것은 아니다. 단일 DB라면 여전히 Single Point of Failure다.
Step 7 · AWS 서비스 매핑
역할 | AWS 서비스 | 선택 이유 |
가상 네트워크 | VPC | 서비스 네트워크 경계 |
DNS | Route 53 | 도메인을 진입점에 연결 |
요청 분산·Health Check | ALB | HTTP 요청 분산과 비정상 Target 제외 |
Web·App 실행 | EC2 | 애플리케이션 실행 |
자동 증감 | Auto Scaling Group | 부하·장애에 따라 인스턴스 수 조정 |
공용 Session | ElastiCache | 여러 App이 같은 Session 사용 |
관계형 DB | RDS Multi-AZ | 관리형 DB와 장애 조치 |
최종 구조도 — 이해용 단순 버전
flowchart TD
U["사용자"] --> DNS["Route 53<br>사이트 주소 안내"]
DNS --> ALB["ALB<br>정상인 App 서버로 요청 분배"]
ALB --> A1["App 1<br>로그인·조회·주문 처리"]
ALB --> A2["App 2<br>로그인·조회·주문 처리"]
A1 --> S["ElastiCache<br>로그인 상태 저장"]
A2 --> S
A1 --> DB["RDS<br>회원·상품·주문 데이터"]
A2 --> DB읽는 순서: 사용자 → Route 53 → ALB → App 1 또는 App 2 → ElastiCache·RDS
#ElastiCache는 빠른 임시 메모장, RDS는 안전한 영구 장부입니다.
구성 요소 설명
구성 요소 | 왜 필요한가 | 없으면 | 장애 시 |
Web Tier | 외부 요청 수신·전달 | App이 외부 처리까지 담당 | 다른 Web이 있으면 유지 |
App Tier | 로그인·상품·주문 로직 | 기능 처리 불가 | 정상 App으로 우회 |
ALB | 요청 분산·Health Check | 다중 서버 활용 어려움 | 관리형 다중화 범위에서 처리 |
Session 저장소 | 로그인 상태 공유 | 서버 변경 시 상태 유실 | 로그인 기능 영향 |
RDS | 관계형 데이터 저장 | 회원·주문 정보 유지 불가 | Multi-AZ면 장애 조치 |

