Node.js 리드·아키텍트 데이터베이스 면접
새 면접Q. 월 1억 건 이상의 주문 데이터를 처리하는 전자상거래 시스템을 설계 중입니다. 주문 테이블이 3년 치 데이터로 5억 건을 넘어서면서 조회 성능이 급격히 저하되고 있습니다. 파티셔닝, 샤딩, 아카이빙 전략을 각각 어떤 상황에서 선택해야 하는지, 그리고 Node.js 애플리케이션 레벨에서 고려해야 할 사항은 무엇인지 설명해주세요.
데이터 접근 패턴과 비즈니스 요구사항에 따라 전략이 달라지며, 각 방식의 운영 복잡도와 애플리케이션 변경 범위를 고려해야 합니다.
파티셔닝은 단일 DB 내에서 테이블을 논리적으로 분할하는 방식으로, 주로 시계열 데이터(월별/연도별)나 지역별 분할에 적합하며 애플리케이션 코드 변경이 거의 없습니다. 샤딩은 데이터를 여러 DB 인스턴스로 물리적으로 분산하는 방식으로, 단일 DB 성능 한계를 넘어서거나 지리적 분산이 필요할 때 사용하며, Node.js에서는 샤드 키 기반 라우팅 로직과 분산 트랜잭션 처리를 구현해야 합니다. 아카이빙은 오래된 데이터를 별도 스토리지로 이관하는 방식으로, 최근 3-6개월 데이터만 주로 조회되는 패턴에 적합하며, 통합 조회가 필요한 경우 CQRS 패턴이나 데이터 레이크를 함께 고려해야 합니다. Node.js 레벨에서는 커넥션 풀 관리, 쿼리 라우팅 미들웨어, 읽기 전용 레플리카 활용, 그리고 Sequelize나 TypeORM 같은 ORM의 파티션/샤드 지원 여부를 검토해야 합니다. 비용 측면에서는 파티셔닝이 가장 경제적이고, 샤딩은 인프라 비용과 운영 복잡도가 높으며, 아카이빙은 스토리지 비용 최적화에 유리합니다.
- • 파티셔닝은 단일 DB 내 논리적 분할로 애플리케이션 변경 최소화
- • 샤딩은 물리적 분산으로 확장성 확보하지만 라우팅 로직과 분산 트랜잭션 처리 필요
- • 아카이빙은 핫/콜드 데이터 분리로 운영 데이터 최적화
- • Node.js 커넥션 풀, 쿼리 라우팅, ORM 지원 여부 고려
- • 각 전략의 비용, 운영 복잡도, 비즈니스 요구사항 트레이드오프 분석
대용량 트래픽 서비스에서 데이터 증가에 따른 성능 저하 시 데이터 분산 전략을 수립할 때 활용됩니다.
샤딩 전략을 선택했을 때, 샤드 재분배(re-sharding)가 필요한 상황이 발생하면 어떻게 무중단으로 마이그레이션하시겠습니까?
Q. 복합 검색 조건이 많은 상품 검색 API에서 WHERE 절에 카테고리, 가격 범위, 브랜드, 재고 여부 등 10개 이상의 필터 조건이 선택적으로 조합됩니다. 모든 조합에 대해 복합 인덱스를 생성할 수 없는 상황에서, 인덱스 전략을 어떻게 수립하고 Node.js 애플리케이션에서 쿼리를 어떻게 최적화하시겠습니까?
카디널리티, 선택도, 실제 쿼리 패턴 분석을 기반으로 핵심 인덱스를 선정하고, 커버링 인덱스와 인덱스 머지 전략을 고려해야 합니다.
먼저 실제 쿼리 로그를 분석하여 가장 빈번하게 사용되는 필터 조합 상위 5-10개를 파악하고, 카디널리티가 높고 선택도가 좋은 컬럼(카테고리, 브랜드 등)을 우선순위로 복합 인덱스를 생성합니다. 복합 인덱스 컬럼 순서는 등호 조건, 범위 조건, 정렬 조건 순으로 배치하며, 가격 범위 같은 범위 조건은 인덱스 마지막에 위치시킵니다. 자주 조회되는 컬럼들을 포함한 커버링 인덱스를 생성하여 테이블 액세스를 최소화하고, 단일 컬럼 인덱스들이 인덱스 머지로 활용될 수 있도록 구성합니다. Node.js 레벨에서는 쿼리 빌더 패턴을 활용해 조건 유무에 따라 동적으로 최적 쿼리를 생성하고, 복잡한 조합 검색은 Elasticsearch 같은 검색 엔진으로 오프로드하는 하이브리드 아키텍처를 고려합니다. 또한 Redis를 활용한 결과 캐싱과 쿼리 실행 계획 모니터링을 통해 지속적으로 인덱스 효율성을 검증하고, Slow Query Log 분석을 자동화하여 인덱스 전략을 개선합니다.
- • 실제 쿼리 패턴 분석으로 핵심 복합 인덱스 선정
- • 카디널리티와 선택도 기반 인덱스 컬럼 우선순위 결정
- • 커버링 인덱스로 테이블 액세스 최소화, 인덱스 머지 활용
- • 복잡한 검색은 Elasticsearch 등 검색 엔진으로 오프로드
- • 쿼리 빌더 패턴, 결과 캐싱, Slow Query 모니터링
전자상거래나 부동산 플랫폼처럼 다양한 필터 조합 검색이 필요한 서비스에서 인덱스 전략을 수립할 때 사용됩니다.
인덱스가 많아질수록 INSERT/UPDATE 성능이 저하되는데, 읽기와 쓰기 성능 사이의 트레이드오프를 어떻게 관리하시겠습니까?
Q. 금융 서비스에서 계좌 이체 트랜잭션을 구현할 때, Serializable 격리 수준은 안전하지만 동시성이 낮고, Read Committed는 동시성이 높지만 팬텀 리드나 Non-repeatable read 문제가 발생할 수 있습니다. 각 격리 수준에서 발생할 수 있는 문제와 Node.js에서 이를 해결하기 위한 낙관적 락, 비관적 락 전략을 설명하고, 실제 어떤 기준으로 선택하는지 말씀해주세요.
비즈니스 요구사항의 정합성 수준과 처리량 요구사항을 균형있게 고려하고, 애플리케이션 레벨 락 메커니즘으로 보완할 수 있습니다.
Read Committed에서는 같은 트랜잭션 내에서 같은 데이터를 두 번 읽을 때 다른 값이 나올 수 있는 Non-repeatable read와, 범위 쿼리 시 새로운 행이 추가되는 Phantom read가 발생할 수 있으며, Repeatable Read는 Phantom read만, Serializable은 모든 문제를 방지하지만 락 경합으로 처리량이 크게 감소합니다. 비관적 락은 SELECT FOR UPDATE를 사용하여 트랜잭션 시작 시 행을 잠그는 방식으로, Sequelize에서는 transaction.LOCK 옵션으로 구현하며, 충돌이 빈번한 경우 적합하지만 데드락 위험이 있습니다. 낙관적 락은 version 컬럼을 활용해 UPDATE 시 version을 비교하여 충돌을 감지하는 방식으로, 충돌이 드문 경우 높은 동시성을 제공하지만 재시도 로직이 필요합니다. 금융 이체처럼 정합성이 중요한 경우 Repeatable Read + 비관적 락을 사용하고, 재고 차감처럼 충돌이 적은 경우 Read Committed + 낙관적 락을 사용하며, 극도로 높은 동시성이 필요한 경우 Redis 분산 락이나 메시지 큐를 활용한 순차 처리를 고려합니다. Node.js에서는 트랜잭션 타임아웃 설정, 데드락 감지 및 재시도 로직, 그리고 APM 도구를 통한 락 대기 시간 모니터링이 필수적입니다.
- • 격리 수준별 발생 가능한 문제(Dirty read, Non-repeatable read, Phantom read) 이해
- • 비관적 락은 SELECT FOR UPDATE로 즉시 잠금, 충돌 빈번 시 적합
- • 낙관적 락은 version 컬럼으로 충돌 감지, 충돌 드물 시 높은 동시성
- • 정합성 vs 동시성 트레이드오프를 비즈니스 요구사항으로 판단
- • 데드락 처리, 타임아웃 설정, 분산 락 등 보완 전략 필요
금융 거래, 재고 관리, 예약 시스템 등 데이터 정합성이 중요한 동시성 제어 상황에서 활용됩니다.
MSA 환경에서 여러 서비스에 걸친 분산 트랜잭션이 필요한 경우, Saga 패턴과 2PC 중 어떤 것을 선택하시겠습니까?
Q. 대시보드 API에서 복잡한 집계 쿼리(여러 테이블 JOIN, GROUP BY, HAVING 포함)가 30초 이상 소요되어 타임아웃이 발생하고 있습니다. EXPLAIN 분석 결과 Full Table Scan과 Using temporary, Using filesort가 나타났습니다. 이 쿼리를 튜닝하기 위한 단계별 접근 방법과, Node.js 아키텍처 레벨에서 고려할 수 있는 대안을 제시해주세요.
쿼리 자체 최적화, 인덱스 전략, 데이터 구조 변경, 그리고 아키텍처 패턴 변경을 단계적으로 고려해야 합니다.
첫 번째로 EXPLAIN ANALYZE로 실행 계획을 분석하여 Full Table Scan 발생 원인을 파악하고, JOIN 조건과 WHERE 절에 적절한 인덱스를 추가하며, JOIN 순서를 작은 테이블부터 시작하도록 최적화합니다. Using filesort를 해결하기 위해 ORDER BY 컬럼에 인덱스를 생성하고, GROUP BY 컬럼 순서를 인덱스 컬럼 순서와 일치시키며, 필요시 커버링 인덱스를 구성합니다. 쿼리 자체를 여러 개의 작은 쿼리로 분할하거나, 서브쿼리를 JOIN으로 변경하고, 불필요한 컬럼 조회를 제거하여 결과셋 크기를 줄입니다. 아키텍처 레벨에서는 집계 결과를 Materialized View나 별도 집계 테이블로 미리 계산하여 저장하고, Node.js에서 node-cron으로 주기적으로 업데이트하는 방식을 고려합니다. CQRS 패턴을 적용하여 읽기 전용 복제본이나 Redis, MongoDB 같은 읽기 최적화 스토어로 데이터를 동기화하거나, 실시간성이 중요하지 않다면 비동기 처리 후 결과를 캐싱하는 전략을 사용합니다. 마지막으로 ClickHouse나 BigQuery 같은 분석 전용 DB로 데이터를 ETL하여 대시보드 쿼리를 오프로드하는 방안도 검토합니다.
- • EXPLAIN 분석으로 Full Table Scan, filesort 원인 파악 및 인덱스 최적화
- • 쿼리 분할, JOIN 순서 조정, 불필요한 컬럼 제거로 쿼리 자체 개선
- • Materialized View나 집계 테이블로 사전 계산 결과 저장
- • CQRS 패턴으로 읽기 전용 스토어 분리 또는 캐싱 전략
- • 분석 전용 DB(ClickHouse, BigQuery)로 오프로드
관리자 대시보드나 리포팅 시스템에서 복잡한 집계 쿼리 성능 문제를 해결할 때 적용됩니다.
Materialized View 업데이트 전략에서 실시간성과 일관성을 어떻게 균형있게 가져가시겠습니까?
Q. Node.js 기반 예약 시스템에서 동일한 좌석에 대해 동시에 여러 요청이 들어올 때 중복 예약이 발생했습니다. 데이터베이스 레벨의 락(행 락, 테이블 락, 갭 락)과 애플리케이션 레벨의 분산 락(Redis, DB named lock) 중 어떤 방식을 선택해야 하는지, 각각의 장단점과 Node.js 클러스터 환경에서의 고려사항을 설명해주세요.
락의 범위, 성능 영향, 장애 시나리오, 그리고 시스템 아키텍처(단일 인스턴스 vs 멀티 인스턴스)를 종합적으로 고려해야 합니다.
데이터베이스 행 락(SELECT FOR UPDATE)은 트랜잭션 범위 내에서 자동으로 관리되고 ACID를 보장하지만, 트랜잭션이 길어지면 락 대기 시간이 증가하고 데드락 위험이 있으며, 커넥션을 점유하여 커넥션 풀 고갈 문제가 발생할 수 있습니다. 갭 락은 Repeatable Read 이상에서 범위 조건 시 자동 적용되어 Phantom read를 방지하지만, 의도치 않은 넓은 범위 잠금으로 동시성이 크게 저하될 수 있습니다. Redis 분산 락(Redlock 알고리즘)은 DB 커넥션과 독립적으로 동작하여 타임아웃 제어가 명확하고, Node.js 멀티 인스턴스 환경에서도 일관된 락을 제공하지만, Redis 장애 시 락이 해제되지 않거나 중복 획득되는 문제가 있어 TTL 설정이 중요합니다. DB named lock(GET_LOCK)은 트랜잭션과 독립적이고 명시적 제어가 가능하지만, 커넥션을 별도로 사용해야 하고 애플리케이션에서 락 해제를 보장해야 합니다. 예약 시스템처럼 짧은 트랜잭션에서는 SELECT FOR UPDATE + Repeatable Read가 적합하고, 외부 API 호출이 포함된 긴 작업에서는 Redis 분산 락을 사용하며, Node.js에서는 ioredis의 Redlock 라이브러리나 node-redlock을 활용하고, 락 획득 실패 시 재시도 로직과 최대 대기 시간을 설정해야 합니다. 모니터링 측면에서는 락 대기 시간, 데드락 발생 빈도, Redis 락 TTL 만료 현황을 추적하고, Circuit Breaker 패턴으로 락 획득 실패 시 빠르게 실패 처리하는 것이 중요합니다.
- • 행 락(SELECT FOR UPDATE)은 ACID 보장하지만 커넥션 점유와 데드락 위험
- • 갭 락은 Phantom read 방지하지만 넓은 범위 잠금으로 동시성 저하
- • Redis 분산 락은 멀티 인스턴스 환경에 적합하나 TTL과 장애 처리 중요
- • 짧은 트랜잭션은 DB 락, 긴 작업은 분산 락 사용
- • 락 타임아웃, 재시도 로직, 모니터링 필수
예약 시스템, 티켓팅, 한정 수량 상품 구매 등 동시성 제어가 필요한 서비스에서 중복 처리를 방지할 때 사용됩니다.
Redis 분산 락을 사용할 때 Redis 마스터 장애 발생 시 락 정합성을 어떻게 보장하시겠습니까?
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!