Node.js 리드·아키텍트 CS 기초 면접
새 면접Q. B-Tree와 B+Tree의 구조적 차이를 설명하고, 왜 대부분의 관계형 데이터베이스 인덱스는 B+Tree를 선택하는지 설명해주세요. 특히 범위 검색(range scan)과 랜덤 액세스 패턴에서 각각의 성능 특성과, 디스크 I/O 관점에서 B+Tree가 유리한 이유를 설명해주세요.
리프 노드의 연결 구조와 데이터 저장 위치에 집중해보세요.
B-Tree는 모든 노드에 키와 데이터를 저장하지만, B+Tree는 리프 노드에만 데이터를 저장하고 내부 노드는 키만 저장합니다. B+Tree의 리프 노드는 연결 리스트로 연결되어 있어 범위 검색 시 순차 스캔이 가능하며, 내부 노드가 더 많은 키를 저장할 수 있어 트리의 높이가 낮아져 디스크 I/O 횟수가 줄어듭니다. 데이터베이스는 디스크 기반 저장소이므로 페이지 단위 읽기에 최적화되어 있고, B+Tree의 순차 접근 특성은 프리페칭과 캐싱에 유리합니다. 또한 풀 스캔이 필요한 경우 리프 노드만 순회하면 되어 효율적입니다. Node.js에서 대용량 데이터 처리 시 이러한 인덱스 구조를 이해하면 쿼리 최적화와 페이지네이션 전략 수립에 도움이 됩니다.
- • B+Tree는 리프 노드에만 데이터 저장, 내부 노드는 키만 저장
- • 리프 노드가 연결 리스트로 연결되어 범위 검색에 유리
- • 트리 높이가 낮아져 디스크 I/O 횟수 감소
대용량 로그 데이터에서 시간 범위 기반 조회 쿼리 성능을 최적화할 때 인덱스 구조 이해가 필수입니다.
MySQL InnoDB의 클러스터 인덱스와 세컨더리 인덱스에서 B+Tree가 어떻게 다르게 활용되는지 설명해주세요.
Q. 일관된 해싱(Consistent Hashing) 알고리즘의 동작 원리와 일반 해싱 대비 장점을 설명하고, 가상 노드(Virtual Node)를 사용하는 이유를 설명해주세요. 또한 Node.js 기반 분산 캐시 시스템에서 노드 추가/제거 시 데이터 재배치 비용을 최소화하기 위한 전략을 제시해주세요.
노드 추가/제거 시 영향받는 키의 범위를 생각해보세요.
일반 해싱은 노드 개수 변경 시 거의 모든 키가 재배치되지만, 일관된 해싱은 해시 링 구조를 사용해 노드 추가/제거 시 인접한 노드의 키만 재배치됩니다. 가상 노드는 하나의 물리 노드를 여러 개의 논리 노드로 분산 배치하여 데이터 분포의 불균형을 해소하고, 노드 장애 시 부하가 여러 노드에 분산되도록 합니다. Node.js 분산 캐시에서는 각 노드당 100~200개의 가상 노드를 할당하고, 노드 추가 시 점진적 웜업 전략을 사용하며, 제거 시에는 graceful shutdown으로 데이터를 미리 이관합니다. Redis Cluster나 Memcached 클러스터링 시 이 알고리즘을 이해하면 효율적인 샤딩 전략을 수립할 수 있습니다.
- • 해시 링 구조로 노드 변경 시 최소한의 키만 재배치
- • 가상 노드로 데이터 분포 균등화 및 장애 영향 분산
- • 점진적 웜업과 graceful shutdown으로 재배치 비용 최소화
Redis 클러스터 확장 시 서비스 중단 없이 노드를 추가하고 캐시 미스를 최소화하는 데 사용됩니다.
Rendezvous Hashing(HRW)과 비교했을 때 일관된 해싱의 장단점은 무엇인가요?
Q. TCP의 흐름 제어(Flow Control)와 혼잡 제어(Congestion Control)의 차이를 설명하고, Slow Start, Congestion Avoidance, Fast Retransmit, Fast Recovery 각 단계의 동작 원리를 설명해주세요. 또한 Node.js HTTP 서버에서 대용량 파일 전송 시 TCP 혼잡 제어가 성능에 미치는 영향과 최적화 방안을 제시해주세요.
수신자의 처리 능력과 네트워크 전체의 혼잡도는 다른 문제입니다.
흐름 제어는 수신자의 버퍼 오버플로우를 방지하기 위해 송신자의 전송 속도를 조절하는 것이고, 혼잡 제어는 네트워크 전체의 혼잡을 방지하기 위한 것입니다. Slow Start는 cwnd를 1에서 시작해 지수적으로 증가시키고, Congestion Avoidance는 임계값 이후 선형 증가하며, Fast Retransmit은 3개의 중복 ACK 수신 시 즉시 재전송하고, Fast Recovery는 혼잡 발생 시 cwnd를 절반으로 줄여 빠르게 복구합니다. Node.js에서는 TCP_NODELAY 옵션으로 Nagle 알고리즘을 비활성화하고, SO_SNDBUF/SO_RCVBUF로 버퍼 크기를 조정하며, HTTP/2나 멀티플렉싱을 활용해 연결 재사용을 최적화할 수 있습니다. CDN이나 엣지 캐싱을 통해 RTT를 줄이는 것도 효과적입니다.
- • 흐름 제어는 수신자 기준, 혼잡 제어는 네트워크 전체 기준
- • cwnd가 지수 증가 후 선형 증가하며 손실 시 감소하는 메커니즘
- • TCP 소켓 옵션 튜닝과 프로토콜 선택으로 최적화
글로벌 서비스에서 높은 RTT 환경의 사용자에게 대용량 미디어 파일을 전송할 때 TCP 튜닝이 필수입니다.
BBR(Bottleneck Bandwidth and RTT) 혼잡 제어 알고리즘이 기존 CUBIC과 어떻게 다른지 설명해주세요.
Q. HTTP/1.1의 Keep-Alive, HTTP/2의 멀티플렉싱, HTTP/3의 QUIC 프로토콜이 각각 어떤 문제를 해결하기 위해 등장했는지 설명하고, Head-of-Line Blocking 문제가 각 버전에서 어떻게 다르게 나타나는지 설명해주세요. Node.js 서버에서 HTTP/2를 도입할 때 고려해야 할 사항은 무엇인가요?
연결 재사용과 요청 동시 처리, 그리고 전송 계층의 차이를 생각해보세요.
HTTP/1.1 Keep-Alive는 연결 재사용으로 핸드셰이크 오버헤드를 줄이지만 HOL Blocking이 발생하고, HTTP/2는 하나의 TCP 연결에서 멀티플렉싱으로 여러 스트림을 동시 처리하지만 TCP 레벨의 HOL Blocking은 여전히 존재합니다. HTTP/3는 UDP 기반 QUIC를 사용해 스트림 독립성을 보장하고 패킷 손실 시 다른 스트림에 영향을 주지 않습니다. Node.js에서 HTTP/2 도입 시 서버 푸시 전략, 스트림 우선순위 설정, 클라이언트 호환성 확인이 필요하며, TLS 필수이므로 인증서 관리와 성능 오버헤드를 고려해야 합니다. 또한 기존 미들웨어나 프록시가 HTTP/2를 지원하는지 확인이 필요합니다.
- • HTTP/1.1은 연결 레벨, HTTP/2는 애플리케이션 레벨, HTTP/3는 전송 레벨 HOL Blocking 해결
- • HTTP/2 멀티플렉싱은 TCP 레벨 HOL Blocking 한계 존재
- • QUIC는 스트림 독립성으로 완전한 HOL Blocking 해결
모바일 환경에서 패킷 손실이 빈번한 상황에서 HTTP/3 도입으로 사용자 경험을 개선할 수 있습니다.
Node.js에서 HTTP/2 서버 푸시를 구현할 때 어떤 리소스를 푸시 대상으로 선택해야 효과적인가요?
Q. 프로세스 컨텍스트 스위칭(Context Switching)과 스레드 컨텍스트 스위칭의 비용 차이를 메모리, 레지스터, 캐시 관점에서 설명하고, Node.js가 싱글 스레드 이벤트 루프 모델을 채택한 이유를 컨텍스트 스위칭 비용과 연관지어 설명해주세요. 또한 Worker Threads 도입 시 어떤 상황에서 성능 이득이 있는지 판단 기준을 제시해주세요.
주소 공간 전환과 TLB, CPU 캐시 무효화를 고려해보세요.
프로세스 컨텍스트 스위칭은 가상 메모리 주소 공간 전환으로 TLB와 CPU 캐시가 무효화되어 비용이 크지만, 스레드는 같은 주소 공간을 공유하므로 레지스터와 스택만 교체하여 비용이 상대적으로 낮습니다. Node.js는 컨텍스트 스위칭 오버헤드를 최소화하고 비동기 I/O로 대기 시간을 효율적으로 활용하기 위해 싱글 스레드 모델을 채택했습니다. Worker Threads는 CPU 집약적 작업(암호화, 이미지 처리, 데이터 압축)에서 이득이 있으며, I/O 바운드 작업은 오히려 오버헤드만 증가합니다. 도입 기준은 작업이 10ms 이상 CPU를 점유하고, 병렬 처리로 인한 속도 향상이 스레드 생성 및 통신 비용을 상회하는 경우입니다.
- • 프로세스 전환은 주소 공간 변경으로 TLB/캐시 무효화, 스레드는 레지스터만 교체
- • Node.js는 컨텍스트 스위칭 최소화와 비동기 I/O 효율성을 위해 싱글 스레드 채택
- • Worker Threads는 CPU 집약적이고 10ms 이상 소요되는 작업에 효과적
대용량 이미지 리사이징 API에서 Worker Threads를 사용해 메인 스레드 블로킹 없이 처리량을 증가시킬 수 있습니다.
Node.js에서 Child Process와 Worker Threads 중 어떤 것을 선택해야 하는지 판단 기준을 제시해주세요.
Q. 가상 메모리(Virtual Memory)의 페이징(Paging) 기법과 페이지 폴트(Page Fault)가 발생하는 원리를 설명하고, 스래싱(Thrashing) 현상이 발생하는 조건과 Node.js 애플리케이션에서 이를 방지하기 위한 메모리 관리 전략을 제시해주세요.
물리 메모리보다 큰 프로세스를 실행할 때 페이지 교체가 빈번해지는 상황을 생각해보세요.
가상 메모리는 프로세스마다 독립된 주소 공간을 제공하고, 페이징은 메모리를 고정 크기 페이지로 나누어 관리합니다. 페이지 폴트는 접근하려는 페이지가 물리 메모리에 없을 때 발생하며, 디스크에서 로드하는 과정에서 큰 지연이 발생합니다. 스래싱은 페이지 폴트가 너무 빈번해져 CPU가 실제 작업보다 페이지 교체에 대부분의 시간을 소비하는 현상입니다. Node.js에서는 힙 크기 제한 설정, 스트림 기반 처리로 메모리 사용량 제한, 캐시 크기 제한과 LRU 정책 적용, 프로세스 모니터링으로 메모리 임계값 초과 시 graceful restart 전략을 사용합니다.
- • 페이지 폴트는 필요한 페이지가 물리 메모리에 없을 때 발생
- • 스래싱은 페이지 교체가 과도하게 발생해 CPU가 실제 작업을 못하는 현상
- • 스트림 처리, 메모리 제한, 캐시 정책으로 예방
컨테이너 환경에서 메모리 제한을 초과하면 OOM Killer가 동작하기 전에 스래싱으로 성능이 급격히 저하됩니다.
페이지 교체 알고리즘 중 LRU, LFU, FIFO의 차이와 각각 어떤 상황에 적합한지 설명해주세요.
Q. 해시 테이블에서 충돌(Collision) 해결 방법인 체이닝(Chaining)과 개방 주소법(Open Addressing)의 차이를 설명하고, 각각의 장단점과 적합한 사용 사례를 제시해주세요. 또한 해시 테이블의 로드 팩터(Load Factor)가 성능에 미치는 영향과 리사이징 전략을 설명해주세요.
메모리 구조와 캐시 지역성, 삭제 연산의 복잡도를 고려해보세요.
체이닝은 각 버킷에 연결 리스트를 사용해 충돌을 해결하며 삭제가 간단하지만 추가 메모리가 필요하고 캐시 지역성이 낮습니다. 개방 주소법은 빈 슬롯을 찾아 저장하므로 메모리 효율적이고 캐시 친화적이지만 삭제가 복잡하고 클러스터링 문제가 있습니다. 로드 팩터가 높아지면 충돌이 증가해 성능이 저하되므로, 일반적으로 0.75를 임계값으로 설정하고 초과 시 테이블 크기를 2배로 늘려 리해싱합니다. Node.js에서 Map 객체는 내부적으로 해시 테이블을 사용하며, 대용량 데이터 처리 시 적절한 초기 크기 설정과 리사이징 비용을 고려해야 합니다. 체이닝은 삭제가 빈번한 캐시에, 개방 주소법은 읽기 중심의 룩업 테이블에 적합합니다.
- • 체이닝은 연결 리스트 사용, 개방 주소법은 빈 슬롯 탐색
- • 로드 팩터 0.75 초과 시 리사이징으로 성능 유지
- • 사용 패턴에 따라 적합한 충돌 해결 방법 선택
인메모리 세션 스토어 구현 시 해시 테이블 설계가 조회 성능에 직접적인 영향을 미칩니다.
선형 탐사, 이차 탐사, 이중 해싱의 차이와 클러스터링 문제를 어떻게 완화하는지 설명해주세요.
Q. 분산 시스템에서 리더 선출(Leader Election) 문제를 해결하기 위한 알고리즘으로 Raft와 Paxos가 있습니다. 두 알고리즘의 핵심 차이와 Raft가 이해하기 쉽다고 평가받는 이유를 설명하고, Node.js 기반 마이크로서비스 아키텍처에서 서비스 디스커버리나 분산 락 구현 시 이러한 합의 알고리즘을 어떻게 활용할 수 있는지 설명해주세요.
로그 복제와 상태 머신, 그리고 term 개념을 중심으로 생각해보세요.
Paxos는 이론적으로 정확하지만 복잡하고 구현이 어려운 반면, Raft는 문제를 리더 선출, 로그 복제, 안전성으로 분리하고 term 개념으로 시간을 구분해 이해하기 쉽습니다. Raft는 강한 리더 모델을 사용하고 로그 엔트리가 순차적으로만 커밋되어 일관성 추론이 간단합니다. Node.js 마이크로서비스에서는 etcd나 Consul 같은 Raft 기반 시스템을 서비스 레지스트리로 활용하거나, Redis를 통한 분산 락 구현 시 Redlock 알고리즘과 함께 사용합니다. 직접 구현보다는 검증된 라이브러리를 사용하되, 네트워크 파티션 시나리오와 split-brain 방지 전략을 이해하고 설계해야 합니다. CAP 이론에서 CP를 선택하는 경우 Raft 기반 시스템이 적합합니다.
- • Raft는 문제 분리와 term 개념으로 Paxos보다 이해하기 쉬움
- • 강한 리더 모델과 순차적 로그 커밋으로 일관성 보장
- • etcd, Consul 등 검증된 시스템 활용이 직접 구현보다 안전
마이크로서비스 환경에서 설정 정보를 일관되게 관리하기 위해 etcd 같은 Raft 기반 저장소를 사용합니다.
네트워크 파티션 발생 시 Raft 클러스터에서 split-brain을 어떻게 방지하나요?
Q. MVCC(Multi-Version Concurrency Control) 메커니즘의 동작 원리를 설명하고, PostgreSQL과 MySQL InnoDB에서 MVCC 구현 방식의 차이를 언두 로그와 가시성 규칙 관점에서 설명해주세요. 또한 장기 실행 트랜잭션이 MVCC 시스템에 미치는 영향과 Node.js 애플리케이션에서 이를 관리하는 전략을 제시해주세요.
각 트랜잭션이 보는 데이터 버전과 가비지 컬렉션을 생각해보세요.
MVCC는 읽기와 쓰기가 서로 블로킹하지 않도록 각 행의 여러 버전을 유지하고, 트랜잭션 시작 시점의 스냅샷을 기준으로 데이터를 읽습니다. PostgreSQL은 테이블에 직접 여러 버전을 저장하고 VACUUM으로 정리하며, MySQL InnoDB는 언두 로그에 이전 버전을 저장하고 purge 스레드가 정리합니다. 장기 실행 트랜잭션은 오래된 버전을 계속 참조하므로 가비지 컬렉션이 지연되어 테이블 블로트와 성능 저하를 유발합니다. Node.js에서는 트랜잭션 타임아웃 설정, 긴 작업은 배치로 분리, 읽기 전용 트랜잭션은 Read Committed 사용, 커넥션 풀 관리로 유휴 트랜잭션 방지, 모니터링으로 장기 트랜잭션 조기 감지 전략이 필요합니다.
- • MVCC는 여러 버전 유지로 읽기-쓰기 블로킹 방지
- • PostgreSQL은 테이블 내 저장, MySQL은 언두 로그 활용
- • 장기 트랜잭션은 가비지 컬렉션 지연으로 성능 저하 유발
분석 쿼리가 오래 실행되는 동안 OLTP 성능이 저하되는 문제를 MVCC 이해로 해결할 수 있습니다.
PostgreSQL에서 테이블 블로트가 심각해졌을 때 VACUUM FULL과 일반 VACUUM의 차이와 운영 전략을 설명해주세요.
Q. DNS 조회 과정에서 재귀적 질의(Recursive Query)와 반복적 질의(Iterative Query)의 차이를 설명하고, DNS 캐싱이 여러 레벨(브라우저, OS, 리졸버, 네임서버)에서 어떻게 동작하는지 설명해주세요. Node.js 애플리케이스에서 DNS 조회 실패나 지연이 성능에 미치는 영향과 최적화 방안을 제시해주세요.
DNS 리졸버의 역할과 TTL, 그리고 Node.js의 dns 모듈 특성을 고려해보세요.
재귀적 질의는 DNS 리졸버가 최종 답을 찾을 때까지 다른 네임서버에 질의하여 클라이언트에게 완전한 응답을 제공하고, 반복적 질의는 각 네임서버가 다음 네임서버 주소만 알려주어 클라이언트가 직접 추가 질의를 수행합니다. DNS 캐싱은 브라우저, OS resolver, 재귀 리졸버, 권한 네임서버 각 레벨에서 TTL 기반으로 동작하며 조회 속도를 크게 향상시킵니다. Node.js는 기본적으로 OS의 getaddrinfo를 사용하므로 DNS 조회가 블로킹될 수 있고, dns.resolve()로 비블로킹 조회를 사용하거나, 커넥션 풀의 keepAlive 옵션으로 재사용을 늘리고, 로컬 DNS 캐시 데몬(dnsmasq) 도입, 서비스 메시에서 사이드카 기반 로컬 리졸빙을 활용할 수 있습니다.
- • 재귀적 질의는 리졸버가 완전한 답 제공, 반복적 질의는 클라이언트가 직접 추가 질의
- • 다단계 캐싱으로 DNS 조회 성능 향상
- • Node.js는 dns.resolve()와 keepAlive로 DNS 병목 최소화
컨테이너 환경에서 DNS 조회 지연이 누적되어 API 응답 시간이 증가하는 문제를 최적화해야 합니다.
마이크로서비스 환경에서 서비스 디스커버리를 DNS 기반으로 구현할 때의 한계와 대안을 설명해주세요.
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!