JavaScript 시니어 데이터베이스 기술면접

JavaScript 시니어 (7년+) 데이터베이스 5문항 조회수 29 · 2026-08-04 (화) 13:52:09
1 쿼리 최적화
Hard

Q. Node.js 애플리케이션에서 N+1 쿼리 문제가 발생하는 원인을 설명하고, ORM(Sequelize, TypeORM)을 사용할 때 이를 해결하기 위한 Eager Loading, Lazy Loading, DataLoader 패턴의 차이점과 각각의 적용 시나리오를 제시해주세요. 또한 GraphQL API에서 DataLoader를 활용한 배치 처리 및 캐싱 전략을 설명해주세요.

연관 데이터를 조회할 때 발생하는 반복 쿼리 문제와 배치 처리 방식을 생각해보세요.

A. 모범답안

N+1 문제는 메인 쿼리 1번 후 각 결과마다 추가 쿼리 N번이 발생하는 현상입니다. Eager Loading은 JOIN을 통해 한 번에 데이터를 가져오지만 불필요한 데이터까지 로드할 수 있고, Lazy Loading은 필요할 때만 조회하지만 N+1이 발생합니다. DataLoader는 같은 이벤트 루프 틱 내의 요청을 배치로 묶어 한 번에 처리하며, 캐싱 레이어를 제공해 중복 요청을 방지합니다. GraphQL에서는 리졸버마다 DataLoader 인스턴스를 컨텍스트에 주입하고, 요청 범위 캐싱으로 동일 요청 내에서만 캐시를 유지합니다. 배치 함수는 WHERE IN 절을 사용해 여러 ID를 한 번에 조회하고, 결과를 요청 순서대로 매핑해 반환합니다.

핵심 포인트
  • • N+1 문제는 반복 쿼리로 인한 성능 저하
  • • Eager Loading은 JOIN 사용, Lazy Loading은 지연 로딩
  • • DataLoader는 배치 처리와 요청 범위 캐싱 제공
  • • GraphQL 리졸버에서 컨텍스트 기반 DataLoader 활용
답변에 넣으면 좋은 키워드
N+1 쿼리 Eager Loading Lazy Loading DataLoader 배치 처리 WHERE IN
실무에서는

사용자 목록과 각 사용자의 프로필 이미지를 조회하는 API에서 수백 개의 추가 쿼리가 발생해 응답 시간이 수 초로 증가하는 상황

Follow-up 질문

DataLoader의 배치 윈도우 타이밍과 maxBatchSize 옵션을 어떻게 조정해야 최적의 성능을 얻을 수 있을까요?

2 인덱스 전략
Hard

Q. 복합 인덱스(Composite Index)의 컬럼 순서가 쿼리 성능에 미치는 영향을 설명하고, Cardinality와 Selectivity 개념을 바탕으로 최적의 인덱스 컬럼 순서를 결정하는 전략을 제시해주세요. 또한 Covering Index와 Index-Only Scan의 차이를 설명하고, Node.js에서 PostgreSQL의 EXPLAIN ANALYZE 결과를 분석해 인덱스 효율성을 판단하는 방법을 설명해주세요.

인덱스는 왼쪽부터 순차적으로 사용되며, 선택도가 높은 컬럼의 배치가 중요합니다.

A. 모범답안

복합 인덱스는 왼쪽 컬럼부터 순차적으로 사용되므로 WHERE 절에서 가장 자주 사용되고 Selectivity가 높은(고유값이 많은) 컬럼을 앞에 배치해야 합니다. Cardinality는 고유값의 개수를 의미하고, Selectivity는 전체 대비 고유값 비율로 높을수록 필터링 효율이 좋습니다. Covering Index는 쿼리에 필요한 모든 컬럼을 인덱스에 포함시켜 테이블 접근 없이 Index-Only Scan을 가능하게 합니다. EXPLAIN ANALYZE에서 Index Scan vs Seq Scan, 실제 실행 시간, rows 예측값 vs 실제값 차이를 확인하고, Index Cond와 Filter 차이를 분석해 인덱스가 제대로 활용되는지 판단합니다. Node.js에서는 pg 라이브러리의 쿼리 결과와 함께 EXPLAIN 결과를 로깅해 모니터링합니다.

핵심 포인트
  • • 복합 인덱스는 컬럼 순서가 성능에 결정적 영향
  • • Selectivity 높은 컬럼을 앞에 배치
  • • Covering Index로 테이블 접근 제거
  • • EXPLAIN ANALYZE로 실제 실행 계획 분석
답변에 넣으면 좋은 키워드
복합 인덱스 Cardinality Selectivity Covering Index Index-Only Scan EXPLAIN ANALYZE
실무에서는

날짜 범위와 상태 조건으로 주문을 조회하는 쿼리가 느려서 복합 인덱스를 추가했지만 여전히 Seq Scan이 발생하는 상황

Follow-up 질문

Partial Index와 Expression Index는 각각 어떤 상황에서 사용하며, 유지보수 측면에서 어떤 트레이드오프가 있나요?

3 동시성 제어
Hard

Q. 데이터베이스의 Lock 메커니즘에서 Pessimistic Lock과 Optimistic Lock의 차이를 설명하고, 각각의 장단점과 적합한 사용 사례를 제시해주세요. 또한 Node.js에서 재고 차감 같은 동시성 문제를 해결하기 위해 SELECT FOR UPDATE, Version 컬럼, Redis 분산 락을 활용하는 방법과 각각의 트레이드오프를 비교해주세요.

비관적 락은 충돌을 가정하고 미리 잠그고, 낙관적 락은 충돌이 적다고 가정하고 검증합니다.

A. 모범답안

Pessimistic Lock은 트랜잭션 시작 시 행을 잠가 다른 트랜잭션의 접근을 차단하며, Deadlock 위험이 있지만 충돌이 많은 환경에서 확실한 일관성을 보장합니다. Optimistic Lock은 Version 컬럼으로 변경 감지 후 충돌 시 재시도하며, 성능은 좋지만 충돌 시 애플리케이션에서 재시도 로직이 필요합니다. SELECT FOR UPDATE는 DB 레벨 락으로 단일 DB에서 강력한 보장을 제공하지만 락 대기 시간이 발생합니다. Version 컬럼 방식은 UPDATE WHERE version = old_version으로 업데이트 성공 여부를 확인하며, 충돌 시 애플리케이션에서 재시도합니다. Redis 분산 락(Redlock)은 다중 인스턴스 환경에서 동시성을 제어하지만 네트워크 지연과 Redis 장애를 고려해야 합니다.

핵심 포인트
  • • Pessimistic Lock은 선제적 잠금, Optimistic Lock은 충돌 검증
  • • SELECT FOR UPDATE는 DB 행 레벨 락 제공
  • • Version 컬럼으로 낙관적 동시성 제어
  • • Redis 분산 락은 다중 인스턴스 환경에서 활용
답변에 넣으면 좋은 키워드
Pessimistic Lock Optimistic Lock SELECT FOR UPDATE Version 분산 락 Redlock
실무에서는

이커머스에서 동시에 여러 사용자가 같은 상품을 구매할 때 재고가 음수로 내려가는 문제가 발생하는 상황

Follow-up 질문

Deadlock이 발생했을 때 이를 감지하고 해결하기 위한 전략과 Node.js에서 이를 처리하는 방법은 무엇인가요?

4 트랜잭션 격리 수준
Hard

Q. 데이터베이스 트랜잭션 격리 수준(Read Uncommitted, Read Committed, Repeatable Read, Serializable)별로 발생할 수 있는 문제(Dirty Read, Non-Repeatable Read, Phantom Read)를 설명하고, PostgreSQL과 MySQL의 기본 격리 수준 차이와 그 이유를 설명해주세요. 또한 Node.js에서 결제 처리 같은 크리티컬한 트랜잭션을 구현할 때 어떤 격리 수준을 선택해야 하며, Sequelize나 Knex.js에서 이를 어떻게 설정하는지 제시해주세요.

격리 수준이 높아질수록 일관성은 강화되지만 동시성은 감소합니다.

A. 모범답안

Read Uncommitted는 커밋되지 않은 데이터를 읽어 Dirty Read가 발생하고, Read Committed는 커밋된 데이터만 읽지만 Non-Repeatable Read가 발생합니다. Repeatable Read는 트랜잭션 시작 시점의 스냅샷을 유지해 일관된 읽기를 보장하지만 Phantom Read가 발생할 수 있습니다. Serializable은 완전한 격리를 제공하지만 성능이 저하됩니다. PostgreSQL은 MVCC로 Read Committed를 기본값으로 사용하며, MySQL InnoDB는 Repeatable Read를 기본값으로 사용합니다. 결제 처리는 Read Committed 이상을 사용하고, 금액 계산 시 FOR UPDATE로 행을 잠가야 합니다. Sequelize에서는 transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED })로 설정하고, Knex.js에서는 trx.transacting 메서드와 함께 격리 수준을 명시합니다.

핵심 포인트
  • • 격리 수준에 따라 Dirty Read, Non-Repeatable Read, Phantom Read 발생
  • • PostgreSQL은 Read Committed, MySQL은 Repeatable Read 기본
  • • 크리티컬 트랜잭션은 적절한 격리 수준과 행 락 필요
  • • ORM에서 격리 수준을 명시적으로 설정
답변에 넣으면 좋은 키워드
트랜잭션 격리 수준 Dirty Read Non-Repeatable Read Phantom Read MVCC FOR UPDATE
실무에서는

결제 처리 중 동일 트랜잭션 내에서 잔액을 두 번 조회했는데 값이 달라져 이중 결제가 발생한 상황

Follow-up 질문

MVCC(Multi-Version Concurrency Control)가 어떻게 동작하며, 이로 인해 발생할 수 있는 Bloat 문제와 VACUUM의 역할은 무엇인가요?

5 데이터베이스 모델링
Medium

Q. 정규화(Normalization)의 1NF, 2NF, 3NF, BCNF를 각각 설명하고, 실무에서 과도한 정규화로 인해 발생하는 성능 문제와 이를 해결하기 위한 반정규화(Denormalization) 전략을 제시해주세요. 또한 Node.js 기반 소셜 미디어 서비스에서 게시글, 댓글, 좋아요 기능을 설계할 때 읽기 성능을 위해 어떤 부분을 반정규화할지, 그리고 데이터 일관성을 유지하기 위한 방안을 설명해주세요.

정규화는 중복 제거와 이상 현상 방지가 목적이지만, JOIN 비용과 읽기 성능을 고려해야 합니다.

A. 모범답안

1NF는 원자값 보장, 2NF는 부분 함수 종속 제거, 3NF는 이행적 함수 종속 제거, BCNF는 모든 결정자가 후보키인 상태입니다. 과도한 정규화는 다중 JOIN으로 쿼리 복잡도와 실행 시간을 증가시킵니다. 반정규화는 계산된 값 저장, 테이블 통합, 중복 컬럼 추가 등으로 읽기 성능을 개선합니다. 소셜 미디어에서는 게시글 테이블에 댓글 수, 좋아요 수를 컬럼으로 추가해 집계 쿼리를 제거하고, 작성자 이름을 중복 저장해 JOIN을 줄입니다. 데이터 일관성은 트랜잭션 내에서 원본과 중복 데이터를 함께 업데이트하고, 배치 작업으로 주기적으로 동기화하며, 애플리케이션 레벨 캐시를 활용해 보완합니다.

핵심 포인트
  • • 정규화는 1NF부터 BCNF까지 단계적으로 중복 제거
  • • 과도한 정규화는 JOIN 비용 증가
  • • 반정규화는 계산된 값 저장과 중복 컬럼으로 읽기 최적화
  • • 트랜잭션과 배치 작업으로 일관성 유지
답변에 넣으면 좋은 키워드
정규화 1NF 2NF 3NF BCNF 반정규화 중복 컬럼
실무에서는

게시글 목록 조회 시 작성자 정보를 가져오기 위해 매번 User 테이블을 JOIN하면서 응답 속도가 느려지는 상황

Follow-up 질문

이벤트 소싱(Event Sourcing) 패턴에서는 정규화 개념이 어떻게 적용되며, CQRS와 함께 사용할 때의 장점은 무엇인가요?

댓글 0

로그인 후 댓글을 작성할 수 있습니다.

아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!