Java 시니어 시스템 설계 면접
새 면접Q. MSA 환경에서 주문-결제-재고 서비스 간 데이터 일관성을 보장해야 합니다. 2PC(Two-Phase Commit), Saga 패턴(Orchestration vs Choreography), Outbox 패턴의 동작 원리와 장단점을 비교하고, 각 패턴이 적합한 비즈니스 시나리오를 설명해주세요. 또한 보상 트랜잭션 설계 시 고려사항과 멱등성 보장 방법도 함께 설명해주세요.
2PC는 강한 일관성을 보장하지만 코디네이터 장애 시 블로킹 문제가 있어 MSA에서는 부적합합니다. Saga 패턴은 각 서비스가 로컬 트랜잭션을 수행하고 실패 시 보상 트랜잭션으로 롤백하며, Orchestration은 중앙 조율자가 관리하여 복잡한 비즈니스 로직에 적합하고 Choreography는 이벤트 기반으로 느슨한 결합을 제공합니다. Outbox 패턴은 DB 트랜잭션과 이벤트 발행의 원자성을 보장하기 위해 이벤트를 별도 테이블에 저장 후 CDC나 폴링으로 발행합니다. 보상 트랜잭션은 역순으로 실행되어야 하며, 멱등성 보장을 위해 고유 트랜잭션 ID와 중복 처리 체크가 필수입니다.
- • 2PC는 블로킹 문제로 MSA에 부적합, Saga는 최종 일관성 제공
- • Orchestration은 중앙 관리로 복잡한 흐름 제어, Choreography는 이벤트 기반 느슨한 결합
- • Outbox 패턴으로 DB 트랜잭션과 이벤트 발행의 원자성 보장
- • 보상 트랜잭션은 역순 실행, 멱등성 보장 필수
Saga 패턴에서 장시간 실행되는 트랜잭션(Long Running Transaction) 중 타임아웃이 발생했을 때 어떻게 처리하시겠습니까? 또한 보상 트랜잭션 자체가 실패하는 경우의 대응 전략은 무엇인가요?
Q. 월간 수억 건의 주문 데이터를 처리하는 시스템에서 데이터베이스 샤딩 전략을 설계해야 합니다. Range-based, Hash-based, Directory-based 샤딩의 장단점을 비교하고, 샤드 키 선정 기준과 리샤딩(Resharding) 전략을 설명해주세요. 또한 Cross-shard Join과 분산 트랜잭션 문제를 어떻게 해결하시겠습니까?
Range-based 샤딩은 날짜나 ID 범위로 분할하여 범위 쿼리에 유리하지만 핫스팟 문제가 발생할 수 있고, Hash-based는 균등 분산이 가능하나 범위 쿼리가 어렵고 리샤딩 시 모든 데이터 재배치가 필요합니다. Directory-based는 매핑 테이블로 유연성을 제공하지만 단일 장애점이 될 수 있습니다. 샤드 키는 쿼리 패턴, 데이터 분포, 확장성을 고려해 선정하며 주문 시스템은 user_id를 사용하는 것이 일반적입니다. 리샤딩은 Consistent Hashing이나 Virtual Bucket을 사용해 데이터 이동을 최소화하고, Cross-shard Join은 애플리케이션 레벨에서 처리하거나 데이터 중복(Denormalization)으로 회피하며, 분산 트랜잭션은 Saga 패턴으로 대체합니다.
- • Range-based는 범위 쿼리 유리하나 핫스팟, Hash-based는 균등 분산하나 리샤딩 비용 높음
- • 샤드 키는 쿼리 패턴과 데이터 분포 고려, user_id가 일반적
- • Consistent Hashing이나 Virtual Bucket으로 리샤딩 비용 최소화
- • Cross-shard Join은 애플리케이션 레벨 처리나 Denormalization으로 회피
특정 샤드에 데이터가 집중되는 핫스팟 문제가 발생했을 때, 운영 중인 시스템에서 다운타임 없이 해당 샤드를 분할하는 전략을 설명해주세요.
Q. CQRS와 Event Sourcing 패턴을 적용한 시스템 아키텍처를 설계할 때 고려해야 할 사항들을 설명해주세요. Event Store 설계 방법, 스냅샷 전략, 이벤트 버전 관리, Projection 업데이트 방식을 포함하여 설명하고, 기존 CRUD 방식 대비 장단점과 적합한 도메인을 제시해주세요.
Event Sourcing은 상태 변경을 이벤트로 저장하여 완전한 감사 로그와 시간 여행이 가능하며, CQRS는 읽기와 쓰기 모델을 분리하여 각각 최적화할 수 있습니다. Event Store는 Append-only로 설계하며 이벤트 순서 보장을 위해 Aggregate ID와 버전을 함께 저장하고, 성능을 위해 일정 주기로 스냅샷을 생성합니다. 이벤트 스키마 변경 시 Upcasting으로 구버전 이벤트를 변환하거나 다중 버전을 지원해야 하며, Projection은 이벤트를 구독하여 비동기로 읽기 모델을 업데이트합니다. 금융 거래, 주문 처리 등 감사가 중요하고 복잡한 비즈니스 로직이 있는 도메인에 적합하지만, 학습 곡선이 높고 이벤트 재생 시 성능 이슈가 있어 단순 CRUD에는 오버엔지니어링입니다.
- • Event Sourcing은 상태 변경을 이벤트로 저장, 완전한 감사 로그 제공
- • Event Store는 Append-only 설계, 스냅샷으로 성능 최적화
- • 이벤트 버전 관리는 Upcasting이나 다중 버전 지원
- • 금융, 주문 등 감사 중요한 도메인에 적합, 단순 CRUD는 오버엔지니어링
Event Sourcing 시스템에서 수백만 개의 이벤트가 쌓인 Aggregate를 재구성할 때 성능 문제를 어떻게 해결하시겠습니까? 스냅샷 생성 시점과 보관 정책은 어떻게 결정하시겠습니까?
Q. 대규모 MSA 환경에서 API Gateway를 설계할 때 고려해야 할 핵심 요소들을 설명해주세요. Rate Limiting, Request Routing, Service Discovery 통합, Response Aggregation, 인증/인가 처리 방식을 포함하여 설명하고, Spring Cloud Gateway와 Netflix Zuul의 차이점 및 성능 특성을 비교해주세요.
API Gateway는 클라이언트 요청의 단일 진입점으로 Rate Limiting은 Token Bucket이나 Sliding Window 알고리즘으로 구현하며 Redis를 사용해 분산 환경에서 동기화합니다. Request Routing은 경로 기반, 헤더 기반, 가중치 기반 라우팅을 지원하며 Service Discovery(Eureka, Consul)와 통합하여 동적으로 백엔드 서비스를 찾습니다. Response Aggregation은 여러 서비스 호출을 병렬 처리하여 결합하며, 인증은 JWT 검증을 Gateway에서 처리하고 인가는 각 서비스에서 처리하는 것이 일반적입니다. Spring Cloud Gateway는 비동기 논블로킹(Reactor 기반)으로 높은 처리량을 제공하며 Zuul 1.x는 동기 블로킹 방식으로 스레드 풀 제약이 있지만, Zuul 2.x는 비동기로 개선되었습니다.
- • Rate Limiting은 Token Bucket 알고리즘, Redis로 분산 동기화
- • Service Discovery 통합으로 동적 라우팅, Response Aggregation으로 병렬 처리
- • 인증은 Gateway에서 JWT 검증, 인가는 각 서비스에서 처리
- • Spring Cloud Gateway는 비동기 논블로킹으로 높은 처리량
API Gateway가 단일 장애점(SPOF)이 되는 것을 방지하기 위한 고가용성 전략과, Gateway 레벨에서 Circuit Breaker를 적용할 때의 장단점을 설명해주세요.
Q. 대규모 전자상거래 시스템에서 상품 정보 캐싱 전략을 설계해야 합니다. Cache-Aside, Read-Through, Write-Through, Write-Behind 패턴의 동작 원리와 장단점을 비교하고, 캐시 무효화(Invalidation) 전략과 Cache Stampede 문제 해결 방법을 설명해주세요. 또한 Redis Cluster와 Caffeine을 조합한 Multi-level Cache 아키텍처의 설계 방안을 제시해주세요.
Cache-Aside는 애플리케이션이 직접 캐시를 관리하여 유연하지만 코드 복잡도가 증가하고, Read-Through는 캐시가 DB 로딩을 담당하여 코드가 단순하지만 초기 로딩이 느립니다. Write-Through는 쓰기 시 캐시와 DB를 동시 갱신하여 일관성이 높지만 쓰기 지연이 발생하고, Write-Behind는 비동기 쓰기로 성능이 좋지만 데이터 유실 위험이 있습니다. 캐시 무효화는 TTL 기반, 이벤트 기반(CDC), 버전 관리 방식이 있으며, Cache Stampede는 만료 시 다수 요청이 동시에 DB를 조회하는 문제로 Lock을 사용한 Single Flight 패턴이나 확률적 조기 만료로 해결합니다. Multi-level Cache는 Caffeine(로컬)을 1차, Redis(분산)를 2차로 구성하여 네트워크 비용을 줄이고, 로컬 캐시는 Pub/Sub으로 무효화 이벤트를 전파합니다.
- • Cache-Aside는 유연하나 복잡, Write-Through는 일관성 높으나 쓰기 지연
- • 캐시 무효화는 TTL, 이벤트 기반, 버전 관리 방식
- • Cache Stampede는 Single Flight 패턴이나 확률적 조기 만료로 해결
- • Multi-level Cache는 Caffeine(로컬) + Redis(분산) 조합, Pub/Sub으로 무효화
캐시와 DB 간 데이터 불일치가 발생했을 때 이를 감지하고 복구하는 전략은 무엇인가요? 또한 Hot Key 문제로 특정 캐시 항목에 트래픽이 집중될 때 어떻게 대응하시겠습니까?
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!