C++ 미드레벨 시스템 설계 면접
새 면접Q. 대용량 파일 처리 시스템을 설계한다고 가정합니다. 여러 스레드가 동시에 파일을 읽고 처리해야 할 때, 어떤 아키텍처 패턴을 사용하시겠습니까? Producer-Consumer 패턴과 Thread Pool 패턴 중 선택하고 그 이유를 설명해주세요.
작업 큐의 관리 방식과 스레드 생성 비용을 고려해보세요.
Thread Pool 패턴을 선택하겠습니다. Thread Pool은 미리 생성된 워커 스레드들이 작업 큐에서 태스크를 가져가 처리하므로, 스레드 생성/소멸 오버헤드를 줄일 수 있습니다. 대용량 파일 처리는 작업 단위가 명확하고 반복적이므로 고정된 수의 워커 스레드로 효율적으로 처리 가능합니다. 또한 동시성 제어가 용이하여 시스템 리소스를 예측 가능하게 관리할 수 있습니다. std::async나 C++의 thread pool 라이브러리를 활용하면 구현이 간단하며, 작업 큐는 lock-free queue나 condition variable을 사용해 동기화할 수 있습니다.
- • Thread Pool 패턴 선택과 스레드 재사용을 통한 오버헤드 감소
- • 작업 큐를 통한 태스크 분배 메커니즘
- • 동시성 제어와 리소스 관리의 예측 가능성
로그 분석 시스템이나 이미지 배치 처리 서버에서 안정적인 처리량을 유지하기 위해 사용됩니다.
Thread Pool의 적정 스레드 개수는 어떻게 결정하시겠습니까? CPU 코어 수와의 관계를 설명해주세요.
Q. 게임 엔진에서 빈번한 객체 생성/소멸로 인한 메모리 단편화 문제가 발생하고 있습니다. Object Pool과 Custom Allocator 중 어떤 전략을 선택하시겠으며, 각각의 트레이드오프를 설명해주세요.
객체 크기의 균일성과 할당 패턴의 예측 가능성을 고려해보세요.
두 전략을 혼합 사용하되, 주로 Object Pool을 우선 적용하겠습니다. Object Pool은 동일한 타입의 객체를 재사용하므로 메모리 단편화를 근본적으로 방지하며, 할당/해제 속도가 O(1)로 매우 빠릅니다. 게임 엔진의 총알, 파티클 같은 동일 타입 객체에 적합합니다. 다만 Pool 크기 예측이 어렵고 메모리 낭비 가능성이 있습니다. Custom Allocator(예: Arena Allocator)는 다양한 크기의 객체를 효율적으로 관리할 수 있지만 구현 복잡도가 높고 해제 시점 관리가 까다롭습니다. 따라서 빈번히 생성되는 동일 타입 객체는 Object Pool로, 프레임 단위 임시 객체는 Arena Allocator로 관리하는 것이 최적입니다.
- • Object Pool의 메모리 재사용을 통한 단편화 방지
- • 할당/해제 성능과 메모리 낭비 간의 트레이드오프
- • 객체 타입과 생명주기에 따른 전략 분리
게임 엔진의 파티클 시스템이나 네트워크 패킷 처리에서 일정한 성능을 보장하기 위해 사용됩니다.
Object Pool에서 풀 크기가 부족할 때 동적 확장을 구현한다면 어떤 전략을 사용하시겠습니까?
Q. 실시간 주식 거래 시스템에서 시세 데이터를 캐싱해야 합니다. LRU 캐시와 TTL 기반 캐시 중 어떤 전략이 적합하며, 캐시 무효화 정책은 어떻게 설계하시겠습니까?
데이터의 시간 민감성과 접근 패턴의 특성을 고려해보세요.
TTL 기반 캐시가 더 적합합니다. 주식 시세 데이터는 시간에 따라 가치가 변하는 시계열 데이터이므로, 접근 빈도보다는 데이터의 신선도가 중요합니다. TTL을 1~5초 정도로 설정하여 오래된 데이터가 자동으로 만료되도록 합니다. 캐시 무효화는 Push 방식과 Pull 방식을 혼합하여, 거래소에서 가격 변동 이벤트가 발생하면 즉시 해당 종목 캐시를 무효화(Push)하고, TTL 만료 시 자동 갱신(Pull)하도록 설계합니다. 또한 std::unordered_map과 타임스탬프를 조합하여 O(1) 조회 성능을 유지하면서도 만료 검사를 효율적으로 수행할 수 있습니다.
- • 시계열 데이터 특성에 따른 TTL 기반 캐시 선택
- • Push와 Pull 방식을 혼합한 캐시 무효화 전략
- • 데이터 신선도와 조회 성능의 균형
실시간 주식 거래 플랫폼이나 암호화폐 거래소에서 빠른 시세 조회와 데이터 정합성을 동시에 보장하기 위해 사용됩니다.
캐시 히트율을 모니터링하고 TTL 값을 동적으로 조정하는 메커니즘을 어떻게 구현하시겠습니까?
Q. 대규모 데이터 처리를 위한 C++ 라이브러리 API를 설계합니다. 사용자가 데이터를 처리하는 방식으로 동기 API와 비동기 API 중 무엇을 제공하시겠으며, 각각의 인터페이스 설계 원칙을 설명해주세요.
사용자의 제어권과 코드 복잡도, 그리고 성능 요구사항을 고려해보세요.
두 가지 API를 모두 제공하되, 기본은 동기 API로 하고 비동기는 옵션으로 제공하겠습니다. 동기 API는 processData() 형태로 직관적이며 에러 처리가 간단하고 디버깅이 쉬워 대부분의 사용 사례에 적합합니다. 비동기 API는 processDataAsync()로 std::future나 콜백을 반환하도록 설계하여, I/O 대기 시간이 긴 경우나 병렬 처리가 필요한 경우에 사용할 수 있게 합니다. RAII 원칙을 적용해 리소스 관리를 자동화하고, const-correctness를 보장하며, 예외 안전성을 guarantee합니다. 또한 템플릿을 활용해 타입 안전성을 제공하되, 컴파일 타임을 고려해 명시적 인스턴스화도 함께 제공합니다.
- • 동기 API를 기본으로 하고 비동기 API를 선택적으로 제공
- • std::future나 콜백 기반 비동기 인터페이스 설계
- • RAII, const-correctness, 예외 안전성 원칙 적용
데이터베이스 클라이언트 라이브러리나 파일 처리 SDK에서 다양한 사용 패턴을 지원하기 위해 사용됩니다.
비동기 API에서 작업 취소 기능을 제공한다면 어떻게 설계하시겠습니까?
Q. 로그 수집 시스템이 초당 10만 건의 로그를 처리해야 하는데, 현재 단일 스레드 구조로는 한계에 도달했습니다. 수평 확장과 수직 확장 전략을 각각 설명하고, 어떤 방식을 선택하시겠습니까?
시스템의 병목 지점과 확장 비용, 그리고 장애 격리를 고려해보세요.
수평 확장을 우선 선택하되, 수직 확장과 혼합하는 전략을 사용하겠습니다. 수평 확장은 로그 수집 노드를 여러 대로 분산하고, 로드 밸런서나 메시지 큐(Kafka, RabbitMQ)를 통해 로그를 분배합니다. 장점은 선형적 확장이 가능하고 단일 장애점이 없으며, 특정 노드 장애 시에도 다른 노드가 처리를 이어받을 수 있다는 점입니다. 수직 확장은 단일 노드의 CPU 코어와 메모리를 증가시키고 멀티스레드 처리를 최적화하는 방식으로, 구현이 간단하지만 물리적 한계가 있습니다. 실제로는 각 노드에서 멀티스레드로 처리하되(수직), 노드 수를 늘려 전체 처리량을 확장하는(수평) 하이브리드 접근이 최적입니다. 로그는 해시 기반 샤딩으로 분배하여 순서 보장이 필요한 경우 동일 소스의 로그는 같은 노드로 라우팅합니다.
- • 메시지 큐 기반 수평 확장으로 선형적 처리량 증가
- • 각 노드 내 멀티스레드 처리를 통한 수직 확장 병행
- • 해시 기반 샤딩과 장애 격리 전략
대규모 마이크로서비스 환경의 중앙 로그 수집 시스템에서 안정적인 처리량을 보장하기 위해 사용됩니다.
특정 노드가 다운되었을 때 해당 노드가 처리하던 로그를 다른 노드로 재분배하는 메커니즘을 어떻게 설계하시겠습니까?
Q. 실시간 알림 시스템을 설계합니다. Observer 패턴과 Event Queue 패턴 중 어떤 것을 선택하시겠으며, 각각의 장단점과 C++에서의 구현 방식을 설명해주세요.
이벤트 발행자와 구독자 간의 결합도, 그리고 비동기 처리 필요성을 고려해보세요.
Event Queue 패턴을 선택하겠습니다. Observer 패턴은 Subject와 Observer가 직접 연결되어 동기적으로 통지하므로 간단하지만, Observer 처리 시간이 길면 Subject가 블로킹되고 결합도가 높아집니다. Event Queue는 이벤트를 큐에 넣고 별도 스레드가 비동기로 처리하므로, 발행자와 구독자가 완전히 분리되고 처리 속도 차이를 흡수할 수 있습니다. C++에서는 std::queue와 std::mutex, std::condition_variable을 사용해 스레드 안전한 큐를 구현하거나, lock-free queue를 사용할 수 있습니다. 이벤트는 std::variant나 상속 기반 다형성으로 타입을 관리하고, 구독자는 이벤트 타입별로 핸들러를 등록합니다. 또한 배압(backpressure) 처리를 위해 큐 크기 제한과 드롭 정책을 설정합니다.
- • Event Queue를 통한 비동기 처리와 발행자-구독자 분리
- • 스레드 안전한 큐 구현과 condition variable 활용
- • 배압 처리와 큐 크기 제한 정책
모바일 앱의 푸시 알림 서버나 IoT 디바이스 이벤트 처리 시스템에서 안정적인 이벤트 전달을 보장하기 위해 사용됩니다.
이벤트 처리 순서가 중요한 경우와 순서가 중요하지 않은 경우의 큐 구현을 어떻게 다르게 설계하시겠습니까?
Q. C++ 백엔드 서버에서 데이터베이스 연결을 관리하는 Connection Pool을 설계합니다. 커넥션 획득/반환 메커니즘, 유휴 커넥션 관리, 그리고 장애 상황 처리 전략을 설명해주세요.
커넥션 재사용과 리소스 누수 방지, 그리고 데이터베이스 장애 시 복구 메커니즘을 고려해보세요.
RAII 기반 Connection Pool을 설계하겠습니다. std::queue로 유휴 커넥션을 관리하고, acquire() 메서드는 큐에서 커넥션을 가져오거나 없으면 새로 생성합니다. 반환은 std::unique_ptr의 custom deleter를 활용해 자동으로 풀에 반환되도록 하여 리소스 누수를 방지합니다. 유휴 커넥션은 백그라운드 스레드가 주기적으로 health check를 수행하고, 일정 시간 이상 미사용 시 종료하여 최소/최대 커넥션 수를 유지합니다. 장애 상황에서는 커넥션 획득 시 타임아웃을 설정하고, 실패 시 지수 백오프로 재시도합니다. 데이터베이스가 다운된 경우 Circuit Breaker 패턴을 적용해 일정 시간 동안 요청을 차단하고, 주기적으로 재연결을 시도하여 복구를 감지합니다. 모든 동기화는 std::mutex와 condition_variable로 처리합니다.
- • RAII와 custom deleter를 활용한 자동 커넥션 반환
- • 백그라운드 스레드 기반 유휴 커넥션 health check
- • Circuit Breaker 패턴을 통한 장애 격리 및 복구
고성능 웹 서버나 API 게이트웨이에서 데이터베이스 연결 비용을 최소화하고 안정적인 서비스를 제공하기 위해 사용됩니다.
Connection Pool의 최대 크기를 동적으로 조정하는 알고리즘을 어떻게 설계하시겠습니까? 어떤 메트릭을 기준으로 하시겠습니까?
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!