2.2. 요청 분산 구조의 한계와 개선 - 100-hours-a-week/KTB4-13th-wiki GitHub Wiki

2.2. 요청 분산 구조의 한계와 개선

1. V1의 Nginx 단일 운영

V1에서는 App EC2의 Nginx가 외부 요청을 받는 유일한 진입점입니다.

사용자
  ↓
Nginx
  ↓
Backend

Nginx와 Backend가 같은 EC2에 있으므로 EC2 장애, Nginx 재시작, 배포 오류가 로그인·검색·리뷰·장바구니·주문·결제 전체의 중단으로 이어집니다. Backend를 여러 대로 늘려도 요청을 나누고 비정상 서버를 제외할 진입점이 없습니다.

현재 구조의 한계는 다음과 같습니다.

  • 단일 장애 지점: Nginx 또는 App EC2 하나의 장애가 모든 API에 영향을 줍니다.
  • 확장 한계: 한 EC2의 CPU·메모리·네트워크가 전체 Backend 처리량의 상한입니다.
  • 요청 분산 부재: Backend를 추가해도 사용자를 여러 서버로 보낼 방법이 없습니다.
  • 장애 서버 제외 불가: Backend가 비정상이어도 Nginx가 이를 자동으로 판단해 요청을 차단하지 않습니다.
  • 배포 중 중단: Nginx 또는 Backend를 재시작하는 동안 요청을 처리할 대체 서버가 없습니다.
  • 운영 결합: HTTPS 인증서, Reverse Proxy 설정과 Backend 실행을 같은 EC2에서 관리해야 합니다.

이 구조에서는 검색·리뷰 조회뿐 아니라 장바구니와 결제 요청도 같은 진입점에 의존합니다. 따라서 장애가 발생하면 일부 기능만 느려지는 것이 아니라 사용자의 구매 흐름 전체가 중단될 수 있습니다.

V1의 Peak는 0.18 RPS였지만 V2의 평상시 Peak는 625 RPS입니다. 평상시 Peak가 T분 동안 중단되면 최대 625 × 60 × T건의 API 요청이 영향을 받을 수 있습니다. 단일 Nginx 장애가 발생하면 더 많은 사용자의 탐색과 구매 흐름이 동시에 중단될 수 있으므로 외부 진입점의 이중화가 필요합니다.

2. Nginx 다중 운영의 한계

서로 다른 AZ의 App EC2에서 Nginx를 실행하면 한 대의 EC2가 멈춰도 다른 EC2를 사용할 수 있습니다. 하지만 사용자의 요청을 어느 Nginx로 보낼지 정하고, 고장 난 Nginx를 다른 Nginx로 바꾸는 기능은 별도로 만들어야 합니다.

사용자
  ↓ 별도 진입점
  ├─ AZ A · Nginx · Backend
  └─ AZ C · Nginx · Backend

이 구성에서는 다음 항목을 직접 관리해야 합니다.

  • 사용자 요청을 Nginx 사이에 나누는 기능
  • 고장 난 Nginx를 찾아 다른 서버로 보내는 기능
  • 서버별 인증서와 설정을 같은 상태로 유지하는 작업
  • Backend 서버 주소와 배포 상태 관리

Nginx를 여러 대 실행하는 것만으로는 진입점 장애가 해결되지 않습니다. 요청을 나누고 고장 난 서버를 제외하는 앞단 기능까지 직접 운영해야 하므로, 서버 수와 관리 작업이 함께 늘어납니다.

3. Multi-AZ Nginx와 ALB 비교

두 방식 모두 App을 서로 다른 AZ에 배치해 한 AZ 장애 시 다른 AZ에서 요청을 처리합니다. 차이는 외부 요청 분산과 장애 Target 제외를 직접 운영할지, 관리형 서비스로 사용할지입니다.

항목 Multi-AZ Nginx ALB
장점 세부 Proxy·Rewrite 제어 요청 분산, Health Check, 장애 Target 제외, ECS·ASG 연동
단점 추가 EC2, 앞단 장애 전환, 설정·인증서 동기화 필요 시간당 요금·사용량 요금, AWS 서비스 의존
장애 시 위험 앞단 전환 실패 시 전체 API 중단 비정상 Target 제외 후 정상 Target으로 전달

Multi-AZ Nginx는 Nginx만 여러 대 실행한다고 자동 전환되지 않습니다. Nginx 앞단에 요청 분산과 장애 전환 구성을 추가하고 직접 운영해야 합니다. ALB는 이 기능을 관리형으로 제공합니다.

두 진입점의 비용은 다음과 같이 비교합니다.

비용 항목 Multi-AZ Nginx ALB
기본 실행 비용 추가 EC2 $18.72 + gp3 $2.74 = $21.46/월 ALB $16.20 + 사용량 1단위 $5.76 = $21.96/월
산정 가정 추가 EC2 1대·gp3 30GB 서울 리전, 720시간, 사용량 1단위
사용량 비용 별도 진입점 구성에 따라 발생 요청·연결·처리 데이터가 늘면 증가
운영 비용 장애 전환·설정 동기화·배포를 직접 운영 Listener·Target Group 관리
장애 손실 앞단 전환 실패 시 API 전체 중단 Health Check 실패 Target 자동 제외

서버 가격만 보지 않고, 장애가 났을 때 자동으로 다른 서버로 보낼 수 있는지와 이를 관리하는 데 드는 작업까지 함께 비교합니다.

ALB는 비용 외에도 진입점 운영을 단순하게 합니다.

  • ECS Service·ASG와 연동해 Target 등록·해제를 자동화할 수 있습니다.
  • Health Check에 실패한 Target을 자동으로 요청 대상에서 제외합니다.
  • HTTPS 인증서를 ALB Listener에서 관리해 EC2별 인증서 동기화가 필요하지 않습니다.
  • 배포나 EC2 교체 중에도 정상 Target으로 요청을 전달할 수 있습니다.
  • 접근 로그와 Target 응답시간을 한 곳에서 확인할 수 있습니다.

Multi-AZ Nginx는 이 기능을 서버 설정, 배포 스크립트와 별도 장애 전환 구성으로 직접 운영해야 합니다. 인프라 담당자가 개발을 병행하는 팀에서는 이 운영 작업이 추가 비용과 장애 위험이 됩니다.

ALB의 사용량 요금은 LCU(Load Balancer Capacity Unit)라는 단위로 계산합니다. LCU는 ALB가 처리한 요청·연결·데이터 양을 나타내는 사용량 단위입니다. 월 매출 1억 5천만 원을 기준으로 1시간 장애가 발생했을 때의 직접 매출 기회 손실은 약 208,320원입니다. 이는 평균 장애 시간이 아니라 비용 판단을 위한 시나리오입니다. ALB 비용을 지불하는 대신 장애 감지와 전환, 인증서 관리와 서버 등록을 관리형으로 사용합니다. 비용과 운영 부담을 함께 고려해 ALB를 선택합니다.

ALB는 외부 요청을 받아 여러 AZ의 App Target으로 분산하고, Health Check에 실패한 Target을 요청 대상에서 제외하는 관리형 진입점입니다. Application Load Balancer 개요 Target Health Check

사용자
  ↓ HTTPS
ALB
  ├─ AZ A · App EC2 · Backend Task
  └─ AZ C · App EC2 · Backend Task

따라서 V2의 외부 진입점은 ALB로 선택합니다. 기존 App EC2에서 실행하던 Nginx는 ALB가 HTTPS 종료와 요청 분산을 담당하므로 제거합니다. 다만 URL Rewrite, WebSocket, 세밀한 Rate Limit처럼 ALB로 대체할 수 없는 기능이 실제로 사용된다면 해당 기능에 한해 Nginx를 ALB 뒤에 유지합니다.

ALB만 배치한다고 가용성이 확보되는 것은 아닙니다. Target은 서로 다른 AZ에 배치해야 하며, ECS Service의 최소 Task 수는 한 AZ 장애 후에도 남은 Target이 요청을 처리할 수 있도록 설정해야 합니다.

ALB를 사용해도 다음 한계는 남습니다.

  • ALB 자체는 Backend 처리 용량을 늘리지 않으므로 ECS Task와 EC2 수를 별도로 확장해야 합니다.
  • Health Check는 비정상 Target을 제외할 뿐, 남은 Target의 처리 용량이나 응답시간을 보장하지 않습니다.
  • 세션·업로드 파일·캐시를 Task 내부에 저장하면 다른 Target으로 요청이 전환될 때 사용자 상태가 유지되지 않습니다.
  • MySQL이나 외부 API가 장애를 일으키면 ALB가 정상이어도 검색·주문·결제 요청은 실패할 수 있습니다.
  • ALB의 LCU 사용량과 요청 경로가 증가하면 예상보다 비용이 커질 수 있습니다.
  • AWS ALB에 의존하므로 Listener·Target Group 설정과 장애 상황을 AWS 방식에 맞게 관리해야 합니다.

따라서 ALB는 단일 진입점과 Target 장애 문제를 해결하는 구성이지, App·DB·외부 API 전체의 가용성을 보장하는 구성은 아닙니다. App 계층은 ECS Service와 ASG로, 데이터 계층과 외부 연동은 각각 별도의 장애 대응 방식을 적용해야 합니다.

4. Nginx 유지 여부 확인

Nginx 유지 여부는 다음 기능의 사용 여부로 결정합니다.

  • URL·Header 재작성
  • 이미지 업로드 크기 제한
  • WebSocket·장시간 연결
  • Rate Limit·IP 접근 제어
  • 정적 파일 제공·캐시

대체 가능한 기능은 ALB, CloudFront, WAF 또는 Backend로 이전하고, 대체할 수 없는 필수 기능이 있을 때만 ALB 뒤에 Nginx를 유지합니다.

북적북적의 Frontend는 S3·CloudFront에서 제공하고 Backend는 컨테이너로 실행합니다. 현재 요구사항에는 Nginx에서만 처리해야 하는 URL 재작성, WebSocket, 별도 정적 파일 제공이나 복잡한 캐시 정책이 없습니다. 이미지 업로드 제한과 접근 제어도 Backend·ALB·WAF에서 처리할 수 있으므로, V2에서는 Nginx를 제거합니다.

5. 최종 구성

사용자
  ↓ HTTPS
ALB
  ├─ AZ A · ECS on EC2 · Backend Task
  └─ AZ C · ECS on EC2 · Backend Task
  • ALB는 최소 2개 AZ에 연결합니다.
  • Backend Task는 서로 다른 AZ에 배치합니다.
  • Target Group Health Check는 실제 요청 처리 가능 상태를 확인합니다.
  • Health Check에 실패한 Target은 요청 대상에서 제외합니다.
  • ECS Service와 ASG가 부족한 Task·EC2를 보충합니다.
  • 평상시에는 Backend Task 4개를 실행하고 AZ별 2개씩 배치합니다.
  • 한 AZ 장애 후에는 정상 AZ의 Task 2개가 요청을 처리하며, 계산상 운영 기준 용량은 약 336 RPS입니다. 평상시 Peak 625 RPS를 모두 처리할 수 있는 용량은 아니므로, 장애 중에는 응답 지연이나 일부 기능 제한이 발생할 수 있습니다.