Vue.js 미드레벨 데이터베이스 기술면접
새 면접Q. Vue 애플리케이션에서 결제 프로세스를 구현하는데, 동시에 여러 사용자가 마지막 남은 재고 1개를 구매하려고 할 때 중복 판매가 발생했습니다. 데이터베이스의 트랜잭션 격리 수준(Isolation Level)이 무엇인지 설명하고, READ COMMITTED와 SERIALIZABLE의 차이점, 그리고 이 문제를 해결하기 위해 어떤 격리 수준과 락 전략을 사용해야 하는지 설명해주세요.
트랜잭션 격리 수준은 동시성과 일관성 사이의 트레이드오프를 다루며, 낙관적 락과 비관적 락도 함께 고려해야 합니다.
트랜잭션 격리 수준은 동시에 실행되는 트랜잭션들이 서로에게 미치는 영향을 제어하는 설정입니다. READ COMMITTED는 커밋된 데이터만 읽을 수 있지만 같은 트랜잭션 내에서 같은 쿼리가 다른 결과를 반환할 수 있는 반면, SERIALIZABLE은 완전한 격리를 보장하지만 동시성이 크게 떨어집니다. 재고 중복 판매 문제는 SELECT FOR UPDATE를 사용한 비관적 락으로 해결할 수 있으며, 재고 조회 시점에 행 단위 배타적 락을 걸어 다른 트랜잭션의 접근을 차단합니다. 또는 낙관적 락(version 컬럼)을 사용하여 UPDATE 시점에 버전을 체크하고 충돌 시 재시도하는 방식도 가능합니다. 일반적으로 READ COMMITTED 수준에서 명시적 락을 사용하는 것이 성능과 안정성의 균형이 좋습니다.
- • 트랜잭션 격리 수준의 개념과 종류 이해
- • READ COMMITTED와 SERIALIZABLE의 동시성/일관성 트레이드오프
- • SELECT FOR UPDATE를 통한 비관적 락 구현
- • 낙관적 락(version 컬럼) 전략의 이해
전자상거래 시스템에서 한정 수량 상품 판매, 좌석 예매, 포인트 차감 등 동시성 제어가 필요한 모든 상황에서 사용됩니다.
REPEATABLE READ 격리 수준에서 발생할 수 있는 Phantom Read 문제를 설명하고, MySQL InnoDB에서는 어떻게 이를 방지하는지 설명해주세요.
Q. Vue 애플리케이션의 사용자 주문 관리 시스템에서 하나의 orders 테이블에 주문 정보, 배송지 정보, 상품 정보가 모두 저장되어 있어 데이터 중복과 수정 이상이 발생하고 있습니다. 데이터베이스 정규화의 목적과 제1정규형, 제2정규형, 제3정규형의 조건을 설명하고, 이 테이블을 어떻게 분리해야 하는지 설명해주세요.
정규화는 데이터 중복을 최소화하고 이상 현상을 방지하기 위해 테이블을 분리하는 과정이며, 각 정규형은 특정 종속성 문제를 해결합니다.
데이터베이스 정규화는 데이터 중복을 제거하고 삽입/수정/삭제 이상을 방지하여 데이터 무결성을 보장하는 설계 기법입니다. 제1정규형(1NF)은 모든 속성이 원자값을 가져야 하며, 제2정규형(2NF)은 1NF를 만족하면서 부분 함수 종속을 제거해야 하고, 제3정규형(3NF)은 2NF를 만족하면서 이행적 함수 종속을 제거해야 합니다. 주문 테이블은 orders(주문 기본 정보), order_items(주문 상품 목록), shipping_addresses(배송지), products(상품 마스터)로 분리하여 각 테이블이 단일 주제를 다루도록 해야 합니다. 다만 과도한 정규화는 조인 성능 저하를 야기할 수 있으므로, 조회 빈도가 높은 경우 의도적 비정규화(반정규화)를 고려할 수 있습니다.
- • 정규화의 목적: 중복 제거와 이상 현상 방지
- • 1NF(원자값), 2NF(부분 함수 종속 제거), 3NF(이행적 종속 제거) 이해
- • 테이블 분리 전략과 외래키 관계 설정
- • 정규화와 성능 사이의 트레이드오프 이해
신규 서비스 설계 시 테이블 구조를 결정하거나, 레거시 시스템의 데이터 품질 문제를 해결할 때 정규화 원칙을 적용합니다.
반정규화를 적용하기 적합한 시나리오를 설명하고, 반정규화 시 데이터 일관성을 유지하기 위한 전략을 제시해주세요.
Q. Vue 애플리케이션에서 사용자가 포인트로 상품을 구매하는 기능을 구현했는데, 프로덕션 환경에서 간헐적으로 데드락(Deadlock) 에러가 발생합니다. 데드락이 발생하는 원리와 필요조건(4가지)을 설명하고, 두 개의 트랜잭션이 users 테이블과 point_history 테이블을 서로 다른 순서로 락을 획득할 때 데드락이 발생하는 시나리오, 그리고 이를 방지하기 위한 전략을 설명해주세요.
데드락은 두 개 이상의 트랜잭션이 서로가 보유한 락을 기다리며 무한 대기 상태에 빠지는 현상이며, 락 획득 순서를 일관되게 유지하는 것이 핵심입니다.
데드락은 두 개 이상의 트랜잭션이 서로가 점유한 자원을 기다리며 무한 대기 상태에 빠지는 현상입니다. 데드락 발생의 4가지 필요조건은 상호배제(Mutual Exclusion), 점유와 대기(Hold and Wait), 비선점(No Preemption), 순환대기(Circular Wait)입니다. 트랜잭션 A가 users 테이블을 먼저 락하고 point_history를 락하려 하고, 동시에 트랜잭션 B가 point_history를 먼저 락하고 users를 락하려 하면 순환대기가 발생하여 데드락이 됩니다. 이를 방지하려면 모든 트랜잭션에서 테이블 접근 순서를 일관되게 유지하고(예: 항상 users → point_history 순서), 트랜잭션 범위를 최소화하며, 인덱스를 적절히 설정하여 락 범위를 줄여야 합니다. 또한 데드락 발생 시 자동 재시도 로직을 구현하여 사용자 경험을 개선할 수 있습니다.
- • 데드락의 4가지 필요조건 이해
- • 순환대기로 인한 데드락 발생 시나리오 파악
- • 락 획득 순서 일관성 유지를 통한 예방
- • 트랜잭션 범위 최소화와 인덱스 최적화
결제, 포인트 적립/차감, 재고 관리 등 여러 테이블을 동시에 수정하는 복잡한 트랜잭션에서 데드락 모니터링과 예방이 필수적입니다.
MySQL에서 데드락이 발생했을 때 SHOW ENGINE INNODB STATUS 명령으로 확인할 수 있는 정보와 이를 분석하는 방법을 설명해주세요.
Q. Vue 애플리케이션에서 게시글 목록과 각 게시글의 작성자 정보를 표시하는 기능을 구현했는데, 100개의 게시글을 조회할 때 데이터베이스 쿼리가 101번 실행되는 N+1 쿼리 문제가 발생했습니다. N+1 문제가 무엇인지 설명하고, JOIN을 사용한 해결 방법과 애플리케이션 레벨에서 DataLoader 패턴을 사용한 해결 방법의 차이점을 설명해주세요.
N+1 문제는 연관된 데이터를 개별적으로 조회하면서 발생하며, Eager Loading이나 Batch Loading으로 해결할 수 있습니다.
N+1 쿼리 문제는 메인 쿼리로 N개의 레코드를 조회한 후, 각 레코드의 연관 데이터를 조회하기 위해 N번의 추가 쿼리가 발생하여 총 N+1번의 쿼리가 실행되는 성능 문제입니다. JOIN을 사용하면 한 번의 쿼리로 게시글과 작성자 정보를 함께 가져올 수 있지만, 1:N 관계에서는 중복 데이터가 발생할 수 있습니다. DataLoader 패턴은 애플리케이션 레벨에서 같은 요청 사이클 내의 개별 조회를 모아서 배치로 처리하는 방식으로, 여러 컴포넌트에서 독립적으로 데이터를 요청해도 자동으로 최적화됩니다. JOIN은 데이터베이스 레벨 최적화로 즉시 실행되지만 복잡한 관계에서는 쿼리가 복잡해지고, DataLoader는 유연하지만 추가적인 구현이 필요하며 캐싱 전략도 고려해야 합니다.
- • N+1 문제의 원인과 성능 영향 이해
- • JOIN을 통한 Eager Loading 방식
- • DataLoader 패턴의 배치 처리 메커니즘
- • 각 해결 방법의 트레이드오프 비교
목록 화면에서 연관 데이터를 함께 표시할 때 발생하는 가장 흔한 성능 문제로, API 응답 시간 개선의 핵심 포인트입니다.
ORM(예: TypeORM, Prisma)을 사용할 때 N+1 문제를 방지하기 위한 설정이나 쿼리 작성 방법을 설명해주세요.
Q. Vue 애플리케이션의 검색 기능이 느려져서 데이터베이스 쿼리를 최적화해야 합니다. EXPLAIN 명령어를 사용한 쿼리 실행 계획 분석 방법을 설명하고, type 컬럼의 값들(ALL, index, range, ref, eq_ref, const)이 의미하는 바와 성능 순서, 그리고 Extra 컬럼에서 'Using filesort'와 'Using temporary'가 나타날 때의 문제점과 해결 방법을 설명해주세요.
EXPLAIN은 MySQL이 쿼리를 어떻게 실행하는지 보여주며, type과 Extra 컬럼을 통해 인덱스 활용도와 임시 연산 여부를 파악할 수 있습니다.
EXPLAIN은 데이터베이스가 쿼리를 실행하기 위해 사용하는 전략을 보여주는 명령어로, 성능 병목 지점을 파악하는 핵심 도구입니다. type 컬럼은 테이블 접근 방식을 나타내며, 성능 순서는 const(PK/Unique 단일 조회) > eq_ref(조인에서 PK/Unique) > ref(일반 인덱스) > range(범위 검색) > index(인덱스 풀스캔) > ALL(테이블 풀스캔) 순입니다. 'Using filesort'는 ORDER BY를 위해 별도의 정렬 작업이 필요함을 의미하며, ORDER BY 절에 맞는 복합 인덱스를 생성하여 해결할 수 있습니다. 'Using temporary'는 GROUP BY나 DISTINCT 처리를 위해 임시 테이블이 생성됨을 의미하며, 적절한 인덱스 설계나 쿼리 재작성으로 개선할 수 있습니다. rows 컬럼이 실제 테이블 크기와 비슷하면 풀스캔을 의미하므로 인덱스 추가를 고려해야 합니다.
- • EXPLAIN을 통한 쿼리 실행 계획 분석
- • type 컬럼의 값별 성능 특성 이해
- • Using filesort와 Using temporary의 원인과 해결
- • 인덱스 설계를 통한 쿼리 최적화
프로덕션 환경에서 느린 쿼리를 발견했을 때 EXPLAIN 분석을 통해 인덱스 추가나 쿼리 재작성 방향을 결정합니다.
Covering Index가 무엇인지 설명하고, Extra 컬럼에 'Using index'가 표시될 때의 성능상 이점을 설명해주세요.
Q. Vue 애플리케이션의 로그 데이터가 수억 건으로 증가하면서 조회 성능이 급격히 저하되었습니다. 데이터베이스 파티셔닝(Partitioning)의 개념과 종류(Range, List, Hash, Key)를 설명하고, 날짜별로 조회되는 로그 테이블에 적합한 파티셔닝 전략과 파티션 프루닝(Partition Pruning)이 성능 향상에 기여하는 원리를 설명해주세요.
파티셔닝은 대용량 테이블을 논리적으로는 하나지만 물리적으로는 여러 개로 분할하여 관리하는 기법이며, 쿼리 조건에 따라 필요한 파티션만 스캔합니다.
파티셔닝은 대용량 테이블을 특정 기준으로 여러 개의 작은 파티션으로 분할하여 관리 효율성과 쿼리 성능을 개선하는 기법입니다. Range 파티셔닝은 연속된 값 범위로 분할(예: 날짜, 숫자), List는 특정 값 목록으로, Hash는 해시 함수로 균등 분배, Key는 MySQL 내부 해시 함수를 사용합니다. 로그 테이블은 날짜별 조회가 많으므로 Range 파티셔닝으로 월별 또는 일별로 분할하는 것이 적합하며, 오래된 파티션은 DROP으로 빠르게 삭제할 수 있습니다. 파티션 프루닝은 WHERE 절의 파티션 키 조건을 분석하여 필요한 파티션만 스캔하는 최적화 기법으로, 전체 테이블 대신 일부 파티션만 접근하여 I/O를 크게 줄입니다. 다만 파티션 키가 WHERE 절에 포함되지 않으면 모든 파티션을 스캔하므로 쿼리 패턴을 고려한 설계가 중요합니다.
- • 파티셔닝의 목적과 4가지 종류 이해
- • 로그성 데이터에 적합한 Range 파티셔닝 전략
- • 파티션 프루닝을 통한 I/O 감소 원리
- • 파티션 키 설계와 쿼리 패턴의 연관성
수억 건 이상의 로그, 이벤트, 히스토리 데이터를 관리할 때 파티셔닝을 통해 쿼리 성능과 데이터 관리 효율성을 동시에 개선합니다.
파티셔닝과 샤딩(Sharding)의 차이점을 설명하고, 각각이 적합한 시나리오를 비교해주세요.
Q. Vue 애플리케이션의 백엔드 API에서 주문 생성 로직 내부에서 포인트 적립 함수를 호출하는데, 포인트 적립이 실패해도 주문은 완료되어야 하는 요구사항이 있습니다. 트랜잭션 전파(Transaction Propagation)의 개념을 설명하고, REQUIRED, REQUIRES_NEW, NESTED의 차이점, 그리고 이 시나리오를 해결하기 위해 어떤 전파 속성을 사용해야 하는지 설명해주세요.
트랜잭션 전파는 이미 트랜잭션이 진행 중일 때 새로운 트랜잭션을 어떻게 처리할지 결정하는 정책입니다.
트랜잭션 전파는 이미 트랜잭션이 시작된 상태에서 다른 트랜잭션 메서드를 호출할 때 트랜잭션을 어떻게 관리할지 결정하는 설정입니다. REQUIRED는 기존 트랜잭션이 있으면 참여하고 없으면 새로 생성하며, REQUIRES_NEW는 항상 새로운 트랜잭션을 시작하고 기존 트랜잭션을 일시 중단시킵니다. NESTED는 기존 트랜잭션 내에서 중첩된 트랜잭션을 생성하여 부분 롤백이 가능합니다. 주문은 성공하고 포인트 적립만 실패할 수 있도록 하려면 포인트 적립 함수에 REQUIRES_NEW를 적용하여 독립적인 트랜잭션으로 분리하고, try-catch로 예외를 처리하여 주문 트랜잭션에 영향을 주지 않도록 해야 합니다. 또는 포인트 적립을 비동기 메시지 큐로 분리하여 eventual consistency 패턴을 적용할 수도 있습니다.
- • 트랜잭션 전파 속성의 개념과 종류
- • REQUIRED, REQUIRES_NEW, NESTED의 동작 차이
- • 독립적인 트랜잭션 분리를 위한 REQUIRES_NEW 활용
- • 부분 실패 처리와 eventual consistency 패턴
주문/결제 프로세스에서 부가 기능(알림, 포인트, 쿠폰 등)의 실패가 핵심 기능에 영향을 주지 않도록 트랜잭션을 분리할 때 사용합니다.
Spring Framework의 @Transactional 어노테이션에서 rollbackFor 속성의 역할과 체크 예외와 언체크 예외에 대한 기본 롤백 정책을 설명해주세요.
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!