확장성 패턴 기본기
처리량·지연·팬아웃으로 확장 문제를 분해하고 LB, 파이프/필터, 스캐터-개더, 오케스트레이션/코레오그래피 선택 기준 정리
한 문단 요약 — 확장성 문제는 하나가 아니라 세 가지다
결론: 확장성 문제는 처리량(throughput), 지연(latency), 팬아웃(fan-out) 세 축으로 분해해야 판단이 가능하다.
처리량은 “얼마나 많이 동시에 처리할 수 있는가”의 문제다.
지연은 평균이 아니라 꼬리 지연(tail latency)이 SLA를 깨뜨리는 문제다.
팬아웃은 하나의 요청이 몇 개의 의존 호출로 분해되는가의 문제다.
확장성 패턴은 이 세 문제 중 무엇을 희생하고 무엇을 지킬지 선택하는 도구다.
공통 예시 시나리오
결론: 모든 패턴은 같은 요청을 어떻게 나누고 다시 모으는가의 차이다.
하나의 요청:
“상품 검색 API”
→ 개인화 추천 조회
→ 재고 확인
→ 결과 조합 후 응답
이 단일 흐름을 끝까지 재사용한다.
Load Balancing
결론: Load Balancing은 처리량 확장의 출발점이지, 지연 문제의 해답은 아니다.
정의
여러 인스턴스로 들어오는 요청을 분산하는 패턴.
직관
여러 계산대에 줄을 나누어 세운다.
동작 흐름
Client
→ Load Balancer
→ Search API 인스턴스 N개 중 하나
→ 응답
장점 / 단점
| 장점 | 단점 |
|---|---|
| 처리량 수평 확장 | 지연 감소 보장 안 됨 |
| 단순한 구조 | 느린 인스턴스에 발목 잡힘 |
| 장애 격리 일부 가능 | 상태 공유 시 효과 감소 |
실패 모드
- 느린 인스턴스로 인한 tail latency
- 세션/캐시 상태 공유로 인한 병목
언제 쓰는가
- 요청이 독립적이고 균질할 때
- 상태를 외부로 분리했을 때
언제 쓰지 말아야 하는가
- 요청별 처리 시간이 크게 다를 때
- 팬아웃 문제를 해결하려 할 때
적용 체크리스트
- 상태는 외부 저장소에 있는가?
- 헬스 체크 기준이 명확한가?
- 느린 인스턴스를 배제할 수 있는가?
- 오토스케일 조건이 명확한가?
- LB 자체가 병목이 아닌가?
Pipes & Filters
결론: Pipes & Filters는 단계별 책임 분리와 병렬 처리를 가능하게 하지만, 상태 공유 순간 무너진다.
정의
요청 처리를 여러 단계(filter)로 분리해 파이프라인으로 연결하는 패턴.
직관
조립 라인에서 부품이 단계별로 이동한다.
동작 흐름
Request
→ 검색 Filter
→ 추천 Filter
→ 재고 Filter
→ 응답
장점 / 단점
| 장점 | 단점 |
|---|---|
| 단계별 확장/교체 용이 | 전체 지연 증가 |
| 병렬 처리 가능 | 상태 공유 시 결합도 폭발 |
| 테스트 용이 | 디버깅 난이도 상승 |
실패 모드
- 파이프 중간 지연 누적
- 필터 간 암묵적 상태 의존
언제 쓰는가
- 단계별 책임이 명확할 때
- 각 단계가 독립적으로 확장 가능할 때
언제 쓰지 말아야 하는가
- 필터 간 강한 상태 공유가 필요할 때
적용 체크리스트
- 각 필터가 순수 함수에 가까운가?
- 실패 시 중단/폴백 전략이 있는가?
- 병렬화 가능한 단계는 무엇인가?
- 관측 지점이 명확한가?
- 필터 추가 비용을 예측했는가?
Scatter-Gather
결론: Scatter-Gather는 팬아웃 문제를 해결하지만, 지연과 실패를 증폭시킨다.
정의
요청을 여러 하위 요청으로 분산(scatter)하고 결과를 모아(gather) 응답하는 패턴.
직관
여러 팀에 동시에 질문하고 답을 취합한다.
동작 흐름
Request
→ 검색 서비스
→ 추천 서비스
→ 재고 서비스 (병렬)
→ 결과 조합
→ 응답
장점 / 단점
| 장점 | 단점 |
|---|---|
| 응답 병렬화 | tail latency 증가 |
| 기능 분리 | 부분 실패 처리 복잡 |
| 유연한 조합 | 타임아웃 설계 필수 |
실패 모드
- 하나의 느린 호출로 전체 지연
- 일부 실패로 전체 실패 전파
언제 쓰는가
- 하위 호출들이 독립적일 때
- 일부 결과 누락을 허용할 때
언제 쓰지 말아야 하는가
- 모든 하위 결과가 필수일 때
- 타임아웃/폴백 설계가 없을 때
적용 체크리스트
- 타임아웃 기준이 명확한가?
- 부분 성공을 허용하는가?
- 결과 조합 로직이 단순한가?
- 호출 수 상한이 있는가?
- 장애 격리가 가능한가?
Execution Orchestrator
결론: Orchestrator는 흐름 제어를 중앙집중화하는 대신, 책임과 위험도 함께 모은다.
정의
중앙 컴포넌트가 전체 호출 순서와 분기를 제어하는 패턴.
직관
지휘자가 각 연주 타이밍을 통제한다.
동작 흐름
Request
→ Orchestrator
→ 검색
→ 추천
→ 재고
→ 응답
장점 / 단점
| 장점 | 단점 |
|---|---|
| 흐름 가시성 높음 | SPOF 위험 |
| 복잡한 분기 처리 용이 | 확장 부담 집중 |
| 디버깅 쉬움 | 변경 시 영향 큼 |
실패 모드
- Orchestrator 장애로 전체 중단
- 과도한 책임 집중
언제 쓰는가
- 흐름이 복잡하고 조건 분기가 많을 때
- 중앙 관측이 중요한 경우
언제 쓰지 말아야 하는가
- 단순 병렬 호출만 필요한 경우
적용 체크리스트
- Orchestrator 이중화가 되어 있는가?
- 타임아웃/재시도 정책이 있는가?
- 책임 범위가 명확한가?
- 상태 관리 전략이 있는가?
- 부하 집중을 감당할 수 있는가?
Choreography
결론: Choreography는 결합도를 낮추는 대신, 흐름 가시성을 포기한다.
정의
각 서비스가 이벤트에 반응하며 자율적으로 다음 단계를 수행하는 패턴.
직관
약속된 신호에 맞춰 각자가 움직인다.
동작 흐름
검색 완료 이벤트
→ 추천 서비스 반응
→ 재고 서비스 반응
→ 결과 이벤트
장점 / 단점
| 장점 | 단점 |
|---|---|
| 결합도 최소화 | 흐름 파악 어려움 |
| 확장 용이 | 디버깅 난이도 높음 |
| SPOF 감소 | 일관성 관리 어려움 |
실패 모드
- 이벤트 누락/중복
- 흐름 추적 불가
언제 쓰는가
- 서비스 간 독립성이 중요할 때
- 비동기 처리 허용 시
언제 쓰지 말아야 하는가
- 강한 순서 보장이 필요할 때
적용 체크리스트
- 이벤트 계약이 명확한가?
- 중복 처리 대비가 되어 있는가?
- 흐름 추적 도구가 있는가?
- 장애 시 복구 전략이 있는가?
- 운영 팀이 이해 가능한가?
Orchestrator vs Choreography
결론: 선택 기준은 흐름 복잡도 vs 결합도 허용치다.
| 기준 | Orchestrator | Choreography |
|---|---|---|
| 흐름 가시성 | 높음 | 낮음 |
| 결합도 | 높음 | 낮음 |
| 장애 반경 | 큼 | 분산 |
| 운영 난이도 | 중 | 높음 |
| 변경 비용 | 큼 | 분산 |
흔한 설계 실수
결론: 확장성 실패는 대부분 보조 설계 부재에서 발생한다.
- Scatter-Gather 후 타임아웃/폴백 없음
- Pipes & Filters에서 상태 공유로 결합도 폭발
- Orchestrator를 단일 장애점으로 방치
패턴 선택 질문 7개
- 동기인가 비동기인가?
- 팬아웃 호출 수는 몇 개인가?
- 일부 실패를 허용하는가?
- SLA는 평균인가 최악인가?
- 관측 가능성이 중요한가?
- 변경 빈도는 높은가?
- 운영 팀 역량은 충분한가?
재학습 체크리스트
- 확장 문제를 세 가지로 분해했는가?
- 팬아웃 수를 수치로 말할 수 있는가?
- tail latency 대응이 있는가?
- 타임아웃 기준이 명확한가?
- 폴백 전략이 있는가?
- SPOF를 인지했는가?
- 운영 부담을 고려했는가?
- 패턴 조합 가능성을 검토했는가?
- 트래픽 성장 시나리오가 있는가?
- “왜 이 패턴인가”를 설명할 수 있는가?
