시니어 백엔드 면접 질문 5편 - 분산/MSA 아키텍처 (5~10년차)
5편은 분산 시스템과 MSA 아키텍처입니다. 면접관은 패턴 이름을 아는지보다 언제 도입하고 어떤 비용을 감수했는지를 봅니다.
시리즈 구성:
- 1편: 인프라/스케일링
- 2편: 운영/안정성
- 3편: 설계/리더십
- 4편: 동시성/런타임
- 5편 (현재): 분산/MSA 아키텍처
- 6편 (예정): 가용성/안정성 패턴
1. SAGA 패턴
Q1. MSA에서 트랜잭션을 어떻게 처리하시나요?
기대 답변: 2PC는 락이 길어져 부적합하기 때문에 SAGA(보상 트랜잭션 릴레이)를 씁니다.
- Choreography: 각 서비스가 이벤트를 듣고 자기 단계 처리
- Orchestration: 중앙 조정자가 단계별 호출
선택 기준은 서비스 수와 흐름 복잡도입니다. 단순 흐름은 Choreography, 가시성·복구가 중요하면 Orchestration.
🔄 꼬리질문 1: 보상 트랜잭션 자체가 실패하면 어떻게 하나요?
기대 답변:
- 재시도 정책 (지수 백오프 + jitter, 최대 N회)
- **DLQ(Dead Letter Queue)**로 격리, 운영 알람
- 멱등키 기반 수동 재처리 콘솔
핵심은 eventual consistency 윈도우를 명시적으로 정의하는 것입니다.
🔄 꼬리질문 2: Choreography의 단점은?
기대 답변:
- 흐름이 코드 여러 곳에 흩어져 추적 어려움
- 새 단계 추가 시 의존 서비스 모두 인지해야
- 분산 트레이싱 없으면 디버깅 비용 폭증
→ 흐름이 5단계 넘으면 Orchestration으로 전환 권장.
🔄 꼬리질문 3: SAGA vs Outbox 패턴 차이는?
기대 답변:
- SAGA: 분산 트랜잭션 흐름 패턴 (보상)
- Outbox: 로컬 트랜잭션과 이벤트 발행을 원자적으로 묶는 패턴
둘은 보완 관계입니다. SAGA 각 단계에서 이벤트 발행의 신뢰성을 Outbox로 보장.
2. CQRS
Q2. CQRS는 언제 도입하시나요?
기대 답변: 명령(Command)과 조회(Query)의 데이터 모델·저장소 요구가 달라질 때입니다.
- 조회 쪽이 복잡 집계·검색이고 쓰기보다 빈도가 훨씬 높음
- 쓰기 모델이 도메인 규칙으로 무거운데 조회 응답속도 SLO가 엄격
도입 비용은 프로젝션 갱신 지연(eventual consistency)과 운영 복잡도입니다.
🔄 꼬리질문 1: 일관성 윈도우는 어떻게 다루나요?
기대 답변:
- 사용자 화면에서 자기 변경 사항은 즉시 보여야 함 → Read Your Writes: 쓰기 직후 잠시 마스터에서 조회
- 외부 알림에는 프로젝션 완료 후 전송
- 운영 메트릭으로 lag(p99) 지표화
🔄 꼬리질문 2: Event Sourcing과 같이 가야 하나요?
기대 답변: 아닙니다. CQRS는 모델 분리, Event Sourcing은 상태 표현입니다.
- CQRS만 도입: 일반 RDB + 별도 read DB
- ES까지 도입: 감사/재구성이 필요할 때만 (운영 부담 큼)
대부분은 CQRS만 도입해도 효과가 충분합니다.
🔄 꼬리질문 3: 프로젝션 갱신은 어떻게 안정화하나요?
기대 답변:
- CDC(Debezium 등)로 카프카에 변경 흘리고 컨슈머가 read DB 갱신
- 멱등 처리 + 순서 보장(파티션 키 = aggregate id)
- 실패 시 재처리 가능하도록 오프셋·DLQ 관리
3. API Gateway 패턴
Q3. API Gateway에 어디까지 책임을 두시겠어요?
기대 답변: 횡단 관심사만 둡니다.
- 인증·인가, 레이트 리밋, 라우팅, 응답 캐싱, 관측(trace ID 부여)
비즈니스 로직·집계는 두지 않습니다. BFF가 필요하면 별도 레이어로.
🔄 꼬리질문 1: 단일 장애점 위험은 어떻게 줄이나요?
기대 답변:
- 다중 인스턴스 + 무상태 + L4/L7 LB
- 게이트웨이 자체에 서킷 브레이커
- 인증 캐시(JWT 검증 결과)로 ID provider 의존도 축소
🔄 꼬리질문 2: 게이트웨이가 병목이 될 때 어떤 신호를 보나요?
기대 답변:
- 게이트웨이 p99 vs 백엔드 p99 격차
- 커넥션 풀 사용률, keep-alive 재사용률
- TLS 핸드셰이크 비율(0-RTT/session resumption 적용 효과)
🔄 꼬리질문 3: 게이트웨이 vs Service Mesh, 어떻게 나누나요?
기대 답변:
- 게이트웨이: 외부 트래픽 입구 (north-south)
- Service Mesh: 내부 서비스 간 (east-west) — mTLS, 재시도, 트래픽 분할
둘은 보완. 작은 조직은 게이트웨이만으로 시작, 서비스 50+ 넘으면 Mesh 검토.
4. DDD와 Bounded Context
Q4. Bounded Context를 코드·팀·DB에 어떻게 반영하시나요?
기대 답변:
- 코드: 모듈/패키지 경계 = 컨텍스트 경계. 다른 컨텍스트 객체 직접 import 금지
- 팀: 한 컨텍스트는 한 팀의 결정권
- DB: 스키마 분리 (가능하면 인스턴스도). 공유 DB는 컨텍스트 경계를 깨뜨림
🔄 꼬리질문 1: 컨텍스트 간 통신은 어떻게 하나요?
기대 답변:
- 비동기 도메인 이벤트 우선
- 동기 호출은 오픈 호스트 서비스 형태의 명시적 계약
- 직접 DB 조회는 금지 (Anti-Corruption Layer로 격리)
🔄 꼬리질문 2: 도메인 모델과 ORM 엔티티가 같아야 하나요?
기대 답변: 규모에 따라 다릅니다.
- 작은 도메인: 같아도 OK (
@Entity= 도메인 객체) - 복잡 도메인: 분리 (도메인 객체 + 영속 엔티티 + 매핑). 운영 부담은 늘지만 도메인 규칙이 ORM에 오염되지 않음
🔄 꼬리질문 3: 마이크로서비스로 분리하는 기준은?
기대 답변: 컨텍스트 경계가 분명하고 다음 중 둘 이상이면 분리:
- 배포 주기/팀이 다름
- 스케일 요구가 다름 (예: 결제는 5배, 카탈로그는 1배)
- 장애 격리 SLO가 다름
코드 모듈로 시작해 문제가 보일 때 분리하는 모듈러 모놀리스 → MSA 흐름이 안전합니다.
5. 모놀리스 내 도메인 이벤트와 아웃박스
Q5. 같은 모놀리스 안 두 모듈을 직접 호출 대신 도메인 이벤트로 묶을 때 트랜잭션 경계는 어떻게 설계하나요?
기대 답변: 3축으로 정리합니다.
- 이벤트 발행 시점 제어: 커밋 후 발행 (
@TransactionalEventListener AFTER_COMMIT) - 유실 방지: Outbox 패턴 — 같은 트랜잭션에 outbox 테이블 insert, 별도 릴레이가 발행
- 실패 처리: 재시도 + DLQ + 보상 트랜잭션
🔄 꼬리질문 1: 커밋 전 발행하면 어떤 일이 생기나요?
기대 답변:
- 커밋 실패 시 컨슈머가 이미 처리한 상태 → 정합성 깨짐
- 같은 트랜잭션 안 다른 작업의 lock과 경합
AFTER_COMMIT이 기본, 단 커밋 후 발행 실패는 별도로 잡아 outbox로 보완.
🔄 꼬리질문 2: Outbox 폴링의 DB 부하는 어떻게 제어하나요?
기대 답변:
- 인덱스(
status, created_at)와 적정 batch size - 발행 후 즉시 삭제 또는 status 업데이트
- 규모가 커지면 CDC(Debezium)로 전환해 폴링 자체를 제거
🔄 꼬리질문 3: 컨슈머 측 멱등성은 어떻게 보장하나요?
기대 답변:
- 이벤트 ID를 유니크 키로 두고 처리 이력 테이블에 insert
- 처리 완료 후 ack
- 멱등 키 보존 기간을 비즈니스 윈도우보다 길게
마무리: 5편 핵심 정리
- SAGA: 보상 트랜잭션과 DLQ까지 설계, Choreography vs Orchestration 판단
- CQRS: 일관성 윈도우 관리, Event Sourcing은 선택적
- API Gateway: 횡단 관심사만, 비즈니스 로직 금지
- DDD: 컨텍스트 경계가 코드·팀·DB까지 반영
- 도메인 이벤트 + 아웃박스: 커밋 후 발행 + 유실 방지 + 멱등성
다음 6편은 가용성/안정성 패턴 — Rate Limiting, Bulkhead, Circuit Breaker, Timeout Cascade, 멱등성과 재시도 jitter를 다룹니다.