Scala 미드레벨 시스템 설계 기술면접
새 면접Q. 대규모 전자상거래 플랫폼에서 상품 검색 API를 설계해야 합니다. 검색 조건(카테고리, 가격범위, 브랜드 등)이 동적으로 조합될 수 있고, 페이징과 정렬을 지원해야 합니다. Scala를 사용하여 이 API의 엔드포인트 구조와 요청/응답 모델을 어떻게 설계하시겠습니까? 타입 안정성과 확장성을 고려하여 설명해주세요.
케이스 클래스와 sealed trait를 활용한 타입 안전한 필터 표현을 생각해보세요.
GET /api/v1/products/search 엔드포인트를 설계하고, 쿼리 파라미터는 sealed trait로 정의된 SearchFilter를 사용합니다. case class SearchRequest(filters: List[SearchFilter], page: Page, sort: SortOption)로 요청을 모델링하고, SearchFilter는 CategoryFilter, PriceRangeFilter 등의 케이스 클래스로 구현합니다. 응답은 case class SearchResponse[T](items: List[T], pagination: PaginationInfo, facets: Map[String, List[FacetValue]])로 제네릭하게 설계하여 재사용성을 높입니다. Option 타입을 활용해 선택적 필드를 명시적으로 표현하고, Circe나 Play JSON으로 자동 직렬화를 구성합니다. 필터 조합 로직은 Filter 패턴과 함수 합성을 통해 구현하여 새로운 필터 추가 시 확장이 용이하도록 합니다.
- • sealed trait와 case class로 타입 안전한 필터 모델링
- • 제네릭을 활용한 재사용 가능한 응답 구조
- • Option 타입으로 선택적 필드 명시적 표현
- • 함수 합성을 통한 확장 가능한 필터 조합 로직
복잡한 검색 조건을 타입 안전하게 처리하고 런타임 에러를 컴파일 타임에 잡아내는 API 설계에 활용됩니다.
검색 필터가 10개 이상으로 늘어나고 복잡한 AND/OR 조건을 지원해야 한다면 어떻게 설계를 개선하시겠습니까?
Q. 금융 거래 시스템에서 Akka Actor 모델을 사용하여 계좌 간 송금 기능을 구현하려고 합니다. 동시성 제어, 트랜잭션 일관성, 장애 복구를 모두 고려해야 합니다. Actor 기반 아키텍처를 어떻게 설계하고, 분산 트랜잭션 문제를 어떻게 해결하시겠습니까?
Saga 패턴과 Actor의 메시지 기반 상태 관리를 결합하여 생각해보세요.
각 계좌를 독립적인 AccountActor로 모델링하여 단일 계좌에 대한 동시성을 Actor의 메시지 큐로 자동 처리합니다. 송금은 TransferCoordinator Actor가 Saga 패턴으로 조율하며, 출금 요청 -> 입금 요청 -> 완료 확인의 단계를 거칩니다. 각 단계마다 보상 트랜잭션(롤백 로직)을 정의하고, 실패 시 이미 실행된 작업을 되돌립니다. Actor의 상태는 Akka Persistence로 이벤트 소싱하여 장애 복구 시 상태를 재구성합니다. At-least-once 전달 보장을 위해 메시지에 idempotency key를 포함하고, AccountActor는 중복 처리를 감지합니다. Cluster Sharding으로 계좌를 분산 배치하고, 특정 노드 장애 시 다른 노드에서 Actor를 재시작하여 고가용성을 확보합니다.
- • AccountActor로 계좌별 동시성 자동 제어
- • Saga 패턴으로 분산 트랜잭션 조율 및 보상 트랜잭션 구현
- • Akka Persistence와 이벤트 소싱으로 장애 복구
- • Idempotency key와 Cluster Sharding으로 고가용성 확보
높은 동시성과 강한 일관성이 요구되는 금융, 예약 시스템에서 Actor 모델로 안전하게 상태를 관리합니다.
이벤트 소싱을 사용할 때 스냅샷 전략을 어떻게 설계하고, 스냅샷과 이벤트 간의 일관성을 어떻게 보장하시겠습니까?
Q. 실시간 대시보드 서비스에서 사용자별 맞춤 데이터를 제공합니다. 데이터 소스는 여러 마이크로서비스에서 집계되며, 일부는 빠르게 변경되고 일부는 상대적으로 정적입니다. Scala 기반 서비스에서 다층 캐싱 전략을 어떻게 설계하고, 캐시 무효화는 어떻게 처리하시겠습니까?
데이터 변경 빈도에 따라 캐시 계층을 분리하고, 이벤트 기반 무효화를 고려해보세요.
로컬 메모리 캐시(Caffeine)와 분산 캐시(Redis)의 2계층 구조로 설계합니다. 정적 데이터(사용자 프로필, 설정)는 Caffeine에 TTL 1시간으로 캐싱하고, 동적 데이터(실시간 통계)는 Redis에 TTL 5분으로 캐싱합니다. 캐시 키는 사용자ID와 데이터 타입을 조합하여 네임스페이스를 구분합니다. 데이터 변경 시 Kafka 이벤트를 발행하고, 각 서비스는 해당 이벤트를 구독하여 관련 캐시를 선제적으로 무효화합니다. Cats Effect의 Memoize를 활용해 동일 요청의 중복 호출을 방지하고, Cache-Aside 패턴으로 캐시 미스 시 원본 데이터를 조회 후 캐싱합니다. 캐시 스탬피드를 방지하기 위해 Semaphore로 동시 로딩을 제어합니다.
- • Caffeine과 Redis의 2계층 캐싱으로 데이터 특성별 최적화
- • Kafka 이벤트 기반 선제적 캐시 무효화
- • Cache-Aside 패턴과 Cats Effect Memoize 활용
- • Semaphore로 캐시 스탬피드 방지
대시보드나 추천 시스템에서 응답 속도를 개선하면서도 데이터 신선도를 유지하는 데 사용됩니다.
캐시 워밍 전략이 필요한 상황에서 애플리케이션 시작 시 어떤 데이터를 우선적으로 캐싱하고, 이를 어떻게 구현하시겠습니까?
Q. 월간 활성 사용자 1억 명 규모의 소셜 미디어 피드 시스템을 설계합니다. 사용자가 팔로우하는 수천 명의 게시물을 실시간으로 집계하여 타임라인을 구성해야 합니다. Scala 기반으로 읽기/쓰기 확장성을 모두 고려한 아키텍처를 제시하고, Fan-out 전략의 트레이드오프를 설명해주세요.
Fan-out on write와 Fan-out on read의 하이브리드 접근을 생각해보세요.
하이브리드 Fan-out 전략을 사용합니다. 일반 사용자(팔로워 < 10만)는 Fan-out on write로 게시 시점에 모든 팔로워의 타임라인에 미리 저장하여 읽기 성능을 최적화합니다. 셀럽(팔로워 > 10만)은 Fan-out on read로 읽기 시점에 동적 집계하여 쓰기 부하를 줄입니다. Akka Streams로 Fan-out 작업을 백프레셔 제어하며 비동기 처리하고, Kafka를 통해 게시 이벤트를 분산 처리합니다. 타임라인 데이터는 Cassandra에 사용자ID를 파티션 키로 저장하여 수평 확장하고, 최근 1주일 데이터만 유지합니다. Redis Sorted Set으로 최신 피드를 캐싱하고, GraphQL로 클라이언트가 필요한 필드만 선택적으로 조회하게 하여 네트워크 비용을 줄입니다. Reactive Streams로 무한 스크롤을 구현하여 페이지네이션 부하를 분산합니다.
- • 하이브리드 Fan-out으로 사용자 특성별 최적화
- • Akka Streams와 Kafka로 비동기 분산 처리
- • Cassandra 파티셔닝으로 수평 확장
- • GraphQL과 Reactive Streams로 클라이언트 최적화
Twitter, Instagram 같은 대규모 소셜 미디어에서 수억 사용자의 피드를 실시간으로 제공하는 데 사용됩니다.
셀럽 사용자의 팔로워 수가 1천만 명을 넘어가면 Fan-out on read도 성능 문제가 발생할 수 있습니다. 이를 어떻게 해결하시겠습니까?
Q. RESTful API에서 에러 처리를 일관되게 설계하려고 합니다. Scala의 Either, Try, Option을 활용하여 비즈니스 로직에서 발생하는 다양한 에러(검증 실패, 리소스 없음, 외부 API 장애 등)를 타입 안전하게 처리하고, HTTP 응답으로 변환하는 전략을 설계해주세요.
ADT로 에러를 도메인 모델링하고, 함수형 에러 처리 체인을 구성해보세요.
sealed trait AppError를 정의하고 ValidationError, NotFoundError, ExternalServiceError 등을 케이스 클래스로 구현하여 에러를 도메인 모델링합니다. 비즈니스 로직은 Either[AppError, Result] 타입을 반환하도록 통일하고, for-comprehension으로 에러 전파를 자연스럽게 처리합니다. Controller 레이어에서 implicit class를 사용해 Either를 HTTP 응답으로 변환하는 확장 메서드를 제공합니다. AppError의 각 타입마다 toHttpResponse 메서드를 구현하여 적절한 상태 코드(400, 404, 503 등)와 에러 메시지 구조를 매핑합니다. 외부 API 호출은 Try로 감싸고 recover로 AppError로 변환합니다. Cats의 EitherT를 사용하여 Future와 Either를 합성하고, 비동기 에러 처리 체인을 구성합니다.
- • sealed trait ADT로 에러 도메인 모델링
- • Either를 통한 타입 안전한 에러 전파
- • implicit class로 HTTP 응답 변환 추상화
- • EitherT로 Future와 Either 합성
마이크로서비스 API에서 일관된 에러 응답 형식을 제공하고, 클라이언트가 에러를 예측 가능하게 처리하도록 돕습니다.
여러 검증 에러를 동시에 수집하여 한 번에 반환하려면 Either 대신 어떤 타입을 사용하는 것이 적절할까요?
Q. 이벤트 기반 마이크로서비스 아키텍처에서 주문 서비스, 재고 서비스, 배송 서비스가 있습니다. 주문 생성 시 재고 확인과 배송 준비가 순차적으로 이루어져야 하며, 각 단계의 실패를 처리해야 합니다. Scala와 Kafka를 사용하여 이벤트 기반 워크플로우를 어떻게 설계하시겠습니까?
이벤트 체인과 상태 기계를 결합하여 각 서비스의 독립성을 유지하세요.
각 서비스는 독립적인 Kafka 토픽을 구독하고 이벤트를 발행합니다. 주문 서비스는 OrderCreated 이벤트를 발행하고, 재고 서비스가 이를 구독하여 재고를 확인한 후 InventoryReserved 또는 InventoryInsufficient 이벤트를 발행합니다. 배송 서비스는 InventoryReserved를 구독하여 ShipmentPrepared 이벤트를 발행합니다. 각 서비스는 상태 머신(OrderStatus, InventoryStatus)을 유지하며 이벤트에 따라 상태를 전이합니다. 실패 이벤트(InventoryInsufficient)는 주문 서비스가 구독하여 보상 처리(주문 취소)를 수행합니다. fs2-kafka로 Kafka 스트림을 함수형으로 처리하고, 정확히 한 번(exactly-once) 시맨틱을 위해 트랜잭션 프로듀서를 사용합니다. 이벤트 스키마는 Avro로 정의하고 Schema Registry로 버전 관리하여 호환성을 보장합니다.
- • 이벤트 체인으로 서비스 간 느슨한 결합
- • 상태 머신으로 각 서비스의 상태 관리
- • 실패 이벤트와 보상 처리로 일관성 유지
- • fs2-kafka와 트랜잭션 프로듀서로 정확히 한 번 처리
전자상거래나 물류 시스템에서 여러 도메인 간 협업이 필요한 복잡한 비즈니스 프로세스를 구현합니다.
이벤트 순서가 보장되어야 하는 경우 Kafka 파티셔닝 전략을 어떻게 설계하시겠습니까?
Q. 실시간 분석 서비스에서 초당 10만 건의 이벤트를 수집하고 처리합니다. 이벤트는 집계되어 여러 시간 윈도우(1분, 5분, 1시간)로 계산되어야 하며, 결과는 지연 없이 대시보드에 표시되어야 합니다. Scala와 Akka Streams 또는 Spark Streaming을 활용한 스트림 처리 아키텍처를 설계하고, 백프레셔와 장애 복구 전략을 설명해주세요.
윈도우 집계와 상태 관리, 그리고 체크포인팅을 중심으로 생각해보세요.
Akka Streams로 초기 수집 레이어를 구성하고 백프레셔로 업스트림 속도를 제어합니다. 이벤트를 Kafka에 버퍼링하고, Spark Structured Streaming으로 윈도우 집계를 수행합니다. Tumbling Window와 Sliding Window를 조합하여 1분, 5분, 1시간 윈도우를 동시에 계산하고, Watermark를 설정하여 지연 도착 데이터를 일정 시간까지 허용합니다. 상태는 Spark의 체크포인트로 HDFS나 S3에 주기적으로 저장하여 장애 복구 시 마지막 상태부터 재개합니다. 집계 결과는 Redis에 쓰고 WebSocket으로 대시보드에 푸시합니다. 처리량이 급증할 때는 Kafka 파티션 수와 Spark executor 수를 동적으로 조정하는 오토스케일링을 구성합니다. 메트릭(지연 시간, 처리량)을 Prometheus로 수집하고 Grafana로 모니터링하여 병목 구간을 식별합니다.
- • Akka Streams 백프레셔와 Kafka 버퍼링으로 속도 제어
- • Spark Structured Streaming으로 다중 윈도우 집계
- • Watermark와 체크포인트로 지연 데이터 처리 및 장애 복구
- • 오토스케일링과 메트릭 모니터링으로 확장성 확보
IoT 센서 데이터 분석, 실시간 광고 효과 측정, 네트워크 모니터링 등 대용량 스트림 데이터를 실시간으로 집계하는 시스템에 사용됩니다.
정확히 한 번(exactly-once) 시맨틱을 보장하기 위해 Spark Streaming에서 어떤 설정과 패턴을 사용해야 합니까?
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!