Vue.js 리드·아키텍트 CS 기초 면접
새 면접Q. 대규모 실시간 대시보드에서 최근 1시간 동안의 이벤트 스트림을 시간 윈도우별로 집계해야 합니다. 매초 수천 건의 이벤트가 들어오고, 오래된 데이터는 자동으로 제거되어야 하며, O(1)에 가까운 삽입/삭제/조회가 필요합니다. 이런 요구사항을 만족하는 자료구조 조합을 설계하고, 시간 복잡도와 공간 복잡도 측면에서 트레이드오프를 설명해주세요.
시간 기반 만료와 빠른 접근을 동시에 지원하려면 해시와 순서 있는 자료구조의 조합을 고려해보세요.
원형 버퍼(Circular Buffer)와 해시맵을 조합한 구조가 적합합니다. 원형 버퍼는 고정 크기 배열로 시간 윈도우를 슬롯으로 나누어 O(1) 삽입과 자동 만료를 제공하며, 해시맵은 이벤트 ID별 빠른 조회를 지원합니다. 추가로 Double-ended Queue(Deque)를 사용하면 양쪽 끝에서 O(1) 삽입/삭제가 가능해 슬라이딩 윈도우 구현에 유리합니다. 공간 복잡도는 O(n)이지만 윈도우 크기로 상한이 정해지므로 메모리 사용량이 예측 가능합니다. 실시간성이 중요하다면 LRU 캐시 패턴을 적용해 자주 조회되는 집계 결과를 캐싱할 수 있습니다. 분산 환경에서는 Redis의 Sorted Set이나 Time Series 자료구조를 활용하는 것도 고려해야 합니다.
- • 원형 버퍼와 해시맵 조합으로 O(1) 성능 달성
- • Deque를 활용한 슬라이딩 윈도우 구현
- • 고정 윈도우 크기로 메모리 사용량 예측 가능
- • 분산 환경에서는 Redis Sorted Set 활용
실시간 모니터링 대시보드에서 초당 메트릭 집계와 오래된 데이터 자동 제거에 활용됩니다.
이벤트가 순서대로 도착하지 않고 지연되거나 중복될 수 있다면 어떤 추가 메커니즘이 필요할까요?
Q. 권한 시스템에서 사용자-역할-권한이 다대다 관계를 가지며, 역할은 계층 구조(부모 역할의 권한을 상속)를 이룹니다. 특정 사용자가 특정 리소스에 대한 권한을 가지는지 판단하는 알고리즘을 설계할 때, 순환 참조 방지, 캐싱 전략, 그리고 권한 변경 시 캐시 무효화를 어떻게 처리하시겠습니까? 시간 복잡도와 함께 설명해주세요.
그래프 순회와 메모이제이션을 결합하되, 변경 시 영향받는 노드만 무효화하는 전략을 생각해보세요.
위상 정렬(Topological Sort)로 역할 계층의 순환 참조를 사전에 검증하고, DFS나 BFS로 권한을 상향식으로 집계합니다. 메모이제이션을 통해 각 역할별 권한 집합을 캐싱하면 후속 조회는 O(1)이 되며, 초기 계산은 O(V+E)입니다(V는 역할 수, E는 관계 수). 권한 변경 시에는 변경된 노드와 그 하위 노드만 선택적으로 무효화하는 Dirty Flag 패턴을 사용합니다. 대규모 시스템에서는 Bloom Filter로 권한 존재 여부를 빠르게 사전 체크하고, 실제 권한 검증은 캐시에서 수행합니다. 분산 환경에서는 버전 번호나 타임스탬프를 활용해 캐시 일관성을 유지하며, 권한 변경 이벤트를 pub/sub으로 전파합니다.
- • 위상 정렬로 순환 참조 검증
- • DFS/BFS와 메모이제이션으로 O(1) 조회 달성
- • Dirty Flag 패턴으로 선택적 캐시 무효화
- • Bloom Filter로 사전 필터링
엔터프라이즈 어드민 시스템에서 복잡한 조직도 기반 권한 체크에 사용됩니다.
동적으로 권한 규칙이 추가되거나 시간 기반 권한(특정 기간에만 유효)이 필요하다면 어떻게 확장하시겠습니까?
Q. TCP의 흐름 제어(Flow Control)와 혼잡 제어(Congestion Control)의 차이를 설명하고, Slow Start, Congestion Avoidance, Fast Retransmit, Fast Recovery 알고리즘이 실제 웹 애플리케이션 성능에 미치는 영향을 분석해주세요. 특히 HTTP/2 멀티플렉싱 환경에서 TCP Head-of-Line Blocking이 발생하는 원리와 HTTP/3(QUIC)가 이를 어떻게 해결하는지 설명해주세요.
흐름 제어는 수신자 중심, 혼잡 제어는 네트워크 중심이며, TCP는 단일 스트림 순서 보장이 멀티플렉싱과 충돌합니다.
흐름 제어는 수신자의 버퍼 오버플로우를 방지하기 위해 윈도우 크기를 조절하는 End-to-End 메커니즘이고, 혼잡 제어는 네트워크 전체의 혼잡을 감지해 전송률을 조절하는 메커니즘입니다. Slow Start는 지수적으로 윈도우를 증가시켜 초기 연결에서 대역폭을 빠르게 활용하지만, 첫 페이지 로드 시 여러 RTT가 필요해 지연이 발생합니다. HTTP/2는 하나의 TCP 연결로 여러 스트림을 멀티플렉싱하지만, 패킷 손실 시 TCP의 순서 보장 때문에 모든 스트림이 블로킹되는 TCP Head-of-Line Blocking이 발생합니다. HTTP/3의 QUIC은 UDP 기반으로 스트림별 독립적인 순서 보장을 제공해 이 문제를 해결하며, 연결 수립도 0-RTT로 단축합니다. 실무에서는 CDN의 TCP 최적화(BBR 알고리즘 등)와 함께 프로토콜 선택이 중요합니다.
- • 흐름 제어는 수신자, 혼잡 제어는 네트워크 중심
- • Slow Start가 초기 연결 지연 유발
- • HTTP/2는 TCP Head-of-Line Blocking 존재
- • HTTP/3 QUIC은 스트림별 독립적 순서 보장
글로벌 서비스에서 프로토콜 선택과 CDN 설정 시 네트워크 특성을 고려해 최적화합니다.
모바일 네트워크처럼 패킷 손실률이 높은 환경에서 TCP와 QUIC의 성능 차이는 어떻게 나타날까요?
Q. ACID 트랜잭션의 격리 수준(Isolation Level) 4단계를 설명하고, Read Uncommitted, Read Committed, Repeatable Read, Serializable 각각에서 발생할 수 있는 Dirty Read, Non-Repeatable Read, Phantom Read 현상을 정리해주세요. 실무에서 기본값인 Read Committed를 사용하다가 Repeatable Read로 변경할 때 고려해야 할 데드락 위험과 성능 트레이드오프를 설명해주세요.
격리 수준이 높아질수록 동시성은 낮아지고 일관성은 높아지며, 락의 범위와 지속 시간이 달라집니다.
Read Uncommitted는 커밋되지 않은 데이터를 읽어 Dirty Read가 발생하고, Read Committed는 커밋된 데이터만 읽지만 같은 쿼리를 반복하면 다른 결과가 나오는 Non-Repeatable Read가 발생합니다. Repeatable Read는 트랜잭션 시작 시점의 스냅샷을 보장해 반복 읽기는 일관되지만, 범위 쿼리 시 새로운 행이 삽입되는 Phantom Read가 발생할 수 있습니다(MySQL InnoDB는 Next-Key Lock으로 방지). Serializable은 완전한 격리를 보장하지만 성능이 크게 저하됩니다. Read Committed에서 Repeatable Read로 변경 시 트랜잭션이 더 긴 시간 동안 락을 유지해 데드락 확률이 증가하고, 특히 긴 트랜잭션과 짧은 트랜잭션이 섞이면 대기 시간이 늘어납니다. 실무에서는 비즈니스 로직의 일관성 요구사항과 TPS를 고려해 선택하며, 필요한 쿼리에만 SELECT FOR UPDATE를 사용하는 것이 일반적입니다.
- • 격리 수준별 Dirty/Non-Repeatable/Phantom Read 발생 여부
- • Repeatable Read는 스냅샷 격리 제공
- • 격리 수준 상승 시 데드락과 성능 저하
- • 비즈니스 요구사항에 따른 선택적 적용
금융 거래나 재고 관리처럼 데이터 일관성이 중요한 비즈니스 로직에서 격리 수준을 선택합니다.
분산 데이터베이스 환경에서 ACID 대신 BASE 모델을 채택할 때 애플리케이션 레벨에서 어떤 보상 로직이 필요할까요?
Q. Node.js 기반 백엔드 서버에서 CPU 집약적 작업(예: 이미지 처리, 암호화)을 수행할 때 이벤트 루프가 블로킹되는 문제가 발생합니다. 프로세스, 스레드, Worker Thread, Child Process의 차이를 설명하고, 각각의 컨텍스트 스위칭 비용과 메모리 격리 수준을 비교한 후, Node.js에서 CPU 작업을 오프로드하는 최적의 전략을 제시해주세요.
프로세스는 완전히 격리되고, 스레드는 메모리를 공유하며, 각각 생성 비용과 통신 방식이 다릅니다.
프로세스는 독립적인 메모리 공간을 가져 완전히 격리되지만 생성 비용이 크고 IPC로 통신해야 하며, 스레드는 같은 프로세스 내에서 메모리를 공유해 생성 비용이 작고 통신이 빠르지만 동기화 문제가 발생합니다. 컨텍스트 스위칭은 프로세스가 스레드보다 비용이 높습니다(메모리 맵 전환 필요). Node.js의 Worker Thread는 V8 격리를 제공하면서도 SharedArrayBuffer로 효율적인 데이터 공유가 가능하고, Child Process는 완전히 독립적인 Node 인스턴스를 실행합니다. CPU 집약 작업은 Worker Thread Pool을 사용해 이벤트 루프를 블로킹하지 않고 처리하되, 작업 단위가 크고 격리가 중요하면 Child Process를 사용합니다. 실무에서는 Bull Queue 같은 작업 큐와 결합해 비동기로 처리하거나, 마이크로서비스로 분리하는 것도 고려합니다.
- • 프로세스는 메모리 격리, 스레드는 공유
- • 컨텍스트 스위칭 비용은 프로세스가 더 큼
- • Worker Thread로 CPU 작업 오프로드
- • 작업 큐와 결합한 비동기 처리
실시간 이미지 리사이징이나 PDF 생성 같은 CPU 집약 작업을 웹 서버에서 처리할 때 사용됩니다.
멀티코어 환경에서 Node.js 클러스터 모드를 사용할 때 로드 밸런싱과 세션 공유는 어떻게 처리하시겠습니까?
Q. B-Tree와 B+Tree의 구조적 차이를 설명하고, 왜 대부분의 관계형 데이터베이스가 인덱스로 B+Tree를 선택하는지 디스크 I/O 관점에서 설명해주세요. 또한 페이지 크기와 트리의 차수(fanout)가 성능에 미치는 영향, 그리고 범위 쿼리와 포인트 쿼리에서 각각의 성능 특성을 비교해주세요.
B+Tree는 리프 노드에만 데이터를 저장하고 리프가 연결 리스트로 연결되어 있다는 점이 핵심입니다.
B-Tree는 모든 노드에 키와 데이터를 저장하지만, B+Tree는 내부 노드에는 키만, 리프 노드에만 데이터를 저장하고 리프 노드끼리 연결 리스트로 연결됩니다. B+Tree는 내부 노드가 더 많은 키를 저장할 수 있어 트리 높이가 낮아지고, 디스크 I/O 횟수가 줄어듭니다. 범위 쿼리 시 B+Tree는 리프 노드를 순차 스캔하면 되지만 B-Tree는 트리 전체를 순회해야 해 비효율적입니다. 페이지 크기가 크면 fanout이 증가해 트리 높이가 낮아지지만, 캐시 효율이 떨어질 수 있습니다. 포인트 쿼리는 두 자료구조 모두 O(log n)이지만, B+Tree는 항상 리프까지 탐색해야 해 약간 느릴 수 있으나, 실무에서는 범위 쿼리 빈도가 높아 B+Tree가 선호됩니다.
- • B+Tree는 리프에만 데이터 저장
- • 내부 노드의 높은 fanout으로 디스크 I/O 감소
- • 리프 노드 연결 리스트로 범위 쿼리 최적화
- • 페이지 크기와 fanout의 트레이드오프
MySQL InnoDB의 클러스터드 인덱스와 세컨더리 인덱스 모두 B+Tree를 사용합니다.
SSD 환경에서는 랜덤 I/O 비용이 낮은데, 그럼에도 B+Tree가 여전히 최선의 선택일까요?
Q. 해시 테이블의 충돌 해결 방법인 Chaining과 Open Addressing(Linear Probing, Quadratic Probing, Double Hashing)의 장단점을 비교하고, 로드 팩터(Load Factor)가 성능에 미치는 영향을 설명해주세요. 실무에서 JavaScript의 Map이나 Python의 dict가 내부적으로 어떤 방식을 사용하며, 왜 그 방식을 선택했는지 추론해주세요.
Chaining은 메모리 오버헤드가 있고, Open Addressing은 클러스터링 문제가 있으며, 로드 팩터는 재해싱 시점을 결정합니다.
Chaining은 충돌 시 연결 리스트로 저장해 구현이 단순하고 로드 팩터가 1을 넘어도 동작하지만, 포인터 오버헤드와 캐시 지역성이 낮습니다. Open Addressing은 충돌 시 다른 슬롯을 탐색해 메모리 효율이 높고 캐시 친화적이지만, 로드 팩터가 높아지면 클러스터링으로 성능이 급격히 저하됩니다. Linear Probing은 간단하지만 1차 클러스터링이 발생하고, Quadratic Probing은 이를 완화하지만 2차 클러스터링이 있으며, Double Hashing은 두 번째 해시 함수로 균등 분포를 만듭니다. 로드 팩터 0.7~0.75를 넘으면 재해싱으로 테이블 크기를 늘립니다. JavaScript V8 엔진은 작은 맵에는 선형 탐색, 큰 맵에는 Open Addressing을 사용하며, Python dict는 Open Addressing을 사용해 메모리 효율과 속도를 모두 확보합니다.
- • Chaining은 단순하지만 포인터 오버헤드
- • Open Addressing은 캐시 친화적이나 클러스터링 발생
- • 로드 팩터 0.7 초과 시 재해싱 필요
- • 언어별로 크기와 상황에 따라 다른 전략 사용
인메모리 캐시나 세션 저장소에서 빠른 키-값 조회를 위해 해시 테이블을 사용합니다.
분산 시스템에서 Consistent Hashing이 필요한 이유와 일반 해싱과의 차이점은 무엇인가요?
Q. DNS 조회 과정을 Recursive Query와 Iterative Query로 구분해 설명하고, DNS 캐싱이 브라우저, OS, 로컬 DNS 서버, 권한 서버 각 레벨에서 어떻게 동작하는지 설명해주세요. 또한 TTL 설정이 서비스 배포와 장애 복구에 미치는 영향을 실무 관점에서 분석하고, DNS 기반 로드 밸런싱(Round Robin DNS)의 한계와 대안을 제시해주세요.
Recursive는 DNS 서버가 대신 찾아주고, Iterative는 클라이언트가 단계적으로 찾으며, TTL은 캐시 유효 시간을 결정합니다.
Recursive Query는 로컬 DNS 서버가 루트부터 권한 서버까지 재귀적으로 조회해 최종 IP를 반환하고, Iterative Query는 각 단계에서 다음 DNS 서버 주소만 알려주어 클라이언트가 반복 조회합니다. 브라우저는 자체 DNS 캐시를 가지며, OS는 hosts 파일과 DNS 캐시를 확인하고, 로컬 DNS 서버는 TTL 기반으로 캐싱합니다. TTL이 길면 DNS 변경 시 반영이 느려 장애 복구나 배포 시 문제가 되고, 짧으면 DNS 조회 빈도가 증가해 지연이 발생합니다. 실무에서는 평소 긴 TTL(3600초)을 사용하다가 배포 전 짧게(60초) 변경합니다. Round Robin DNS는 클라이언트 캐싱과 헬스체크 부재로 장애 서버로 트래픽이 가는 문제가 있어, GeoDNS, Anycast, 또는 AWS Route 53 같은 헬스체크 기반 DNS 솔루션을 사용합니다.
- • Recursive는 DNS 서버가, Iterative는 클라이언트가 조회
- • 다단계 캐싱으로 DNS 조회 지연 감소
- • TTL은 변경 반영 속도와 조회 빈도의 트레이드오프
- • Round Robin DNS는 헬스체크 부재로 한계
글로벌 서비스에서 리전별 트래픽 라우팅과 장애 복구 시 DNS TTL 관리가 중요합니다.
DNSSEC은 무엇이며, DNS 스푸핑 공격을 어떻게 방어하나요?
Q. 데이터베이스 복제(Replication)에서 Master-Slave 구조의 비동기 복제와 동기 복제의 트레이드오프를 설명하고, Replication Lag이 발생했을 때 애플리케이션에서 어떤 문제가 생기는지 구체적인 시나리오를 들어주세요. 또한 Read-Your-Writes Consistency를 보장하기 위한 전략과, Multi-Master 복제에서 발생하는 쓰기 충돌을 해결하는 방법을 설명해주세요.
동기 복제는 일관성을 보장하지만 지연이 증가하고, 비동기는 빠르지만 데이터 손실 가능성이 있습니다.
비동기 복제는 Master가 Slave 복제 완료를 기다리지 않아 쓰기 성능이 좋지만, Replication Lag으로 인해 방금 쓴 데이터를 읽지 못하거나 Master 장애 시 일부 데이터가 손실될 수 있습니다. 동기 복제는 모든 Slave가 확인할 때까지 대기해 강한 일관성을 보장하지만 지연이 증가하고 Slave 장애 시 전체 쓰기가 블로킹됩니다. Replication Lag 시나리오로는 사용자가 프로필을 업데이트한 직후 Slave에서 읽어 이전 정보가 보이는 경우가 있습니다. Read-Your-Writes를 보장하려면 같은 사용자의 읽기를 Master로 라우팅하거나, 마지막 쓰기 타임스탬프를 클라이언트가 전달해 그 시점 이후 데이터만 반환하도록 합니다. Multi-Master에서는 Last-Write-Wins(타임스탬프 기반), Conflict-Free Replicated Data Types(CRDT), 또는 애플리케이션 레벨 병합 로직으로 충돌을 해결합니다.
- • 비동기는 성능 우선, 동기는 일관성 우선
- • Replication Lag으로 Read-Your-Writes 위반 가능
- • Master 라우팅이나 타임스탬프로 일관성 보장
- • Multi-Master는 충돌 해결 전략 필요
대규모 서비스에서 읽기 부하 분산을 위해 Slave를 사용하며, Lag을 고려한 라우팅 전략이 필요합니다.
CAP 정리에서 Partition Tolerance가 필수인 이유와, CP와 AP 시스템의 실무 사례를 설명해주세요.
Q. 가상 메모리의 페이징(Paging) 시스템에서 페이지 폴트(Page Fault)가 발생하는 과정과 성능에 미치는 영향을 설명하고, LRU, LFU, Clock 알고리즘 등 페이지 교체 알고리즘의 동작 원리와 트레이드오프를 비교해주세요. 또한 Thrashing이 발생하는 원인과, Working Set 모델을 통한 해결 방법을 설명하고, 실무에서 애플리케이션 메모리 사용 패턴이 페이지 폴트에 미치는 영향을 분석해주세요.
페이지 폴트는 디스크 I/O를 유발해 매우 느리고, Thrashing은 페이지 교체가 과도하게 발생하는 상황입니다.
페이지 폴트는 접근하려는 페이지가 물리 메모리에 없을 때 발생하며, OS가 디스크에서 페이지를 로드하는 동안 프로세스가 블로킹되어 수백 마이크로초에서 밀리초의 지연이 발생합니다. LRU는 가장 오래 사용되지 않은 페이지를 교체해 시간 지역성을 활용하지만 구현 비용이 높고, LFU는 사용 빈도 기반이지만 초기 패턴에 민감하며, Clock 알고리즘은 Circular Buffer와 Reference Bit으로 LRU를 근사해 효율적입니다. Thrashing은 프로세스의 Working Set보다 할당된 메모리가 작아 페이지 폴트가 연속 발생해 CPU는 유휴하고 I/O만 증가하는 상태로, Working Set 모델은 프로세스가 자주 참조하는 페이지 집합을 분석해 충분한 메모리를 할당합니다. 실무에서는 큰 배열 순회나 랜덤 접근 패턴이 페이지 폴트를 증가시키므로, 메모리 지역성을 고려한 알고리즘 설계가 중요합니다.
- • 페이지 폴트는 디스크 I/O로 성능 저하 유발
- • LRU는 정확하지만 비용 높고, Clock은 근사로 효율적
- • Thrashing은 Working Set 부족으로 발생
- • 메모리 지역성을 고려한 알고리즘 설계 필요
대용량 데이터 처리 시 메모리 접근 패턴 최적화로 페이지 폴트를 줄여 성능을 개선합니다.
TLB(Translation Lookaside Buffer)의 역할과, TLB 미스가 성능에 미치는 영향을 설명해주세요.
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!