백엔드 면접 질문 20선 (20 Critical Backend Interview Questions)
한 줄 정의
실제 운영(production) 환경의 백엔드 시스템에서 발생하는 성능 병목, 동시성 이슈, 분산 시스템의 트레이드오프를 깊이 있게 파악하기 위해 면접관이 질문하는 20가지 핵심 기술 항목 가이드다.
핵심 요지
- 데이터베이스 최적화: UUID 인덱스 단편화, 대용량 오프셋 페이징 회피, 복합 인덱스 설계, N+1 쿼리 최적화 등 데이터 접근 계층의 지연 요소를 해결한다.
- 동시성 및 트랜잭션: 분산 락, 낙관적/비관적 락, 트랜잭션 격리 수준, 중첩 트랜잭션 예외, 그리고 네트워크 재시도에 대응하는 API 멱등성 및 결제 복구를 보장한다.
- 캐시 설계와 부작용: 캐시 스탬피드(Stampede) 완화, 적합한 캐시 제거 정책(LRU 등) 선택, 무효화 전략 설계 및 과도한 네트워크 홉 추가로 인한 성능 저하 방지.
- API 및 대용량 트래픽: 슬라이딩 윈도우 기반 처리량 제한, 대용량 파일 스트리밍/Presigned URL 처리, 장시간 작업의 비동기 워커화.
- 메시징 아키텍처: 메시지 브로커(RabbitMQ vs Kafka)의 설계 철학, 포이즌 메시지 처리(DLQ), 오프셋 커밋 실패 대응 및 분산 환경 내 메시지 처리 순서 보장.
상세
데이터베이스와 쿼리 성능
- 무작위 UUID 컬럼 인덱스 생성 시 영향
- 문제점: 표준 무작위 UUID는 B-tree 인덱스에 삽입될 때 정렬되지 않은 임의의 지점에 들어가므로 심각한 페이지 단편화(page fragmentation)를 일으키며, 지속적인 디스크 쓰기를 강제한다.
- 대책: 순차형 ID(sequential ID)나 시간 정렬형 UUID(time-sorted UUID, 예: ULID, UUID v7)를 선택하여 삽입 지점을 마지막에 집중시킴으로써 인덱스 페이지 단편화를 완화한다.
- OFFSET 없는 5,000만 행 페이지네이션 (Pagination)
- 문제점:
OFFSET과LIMIT을 함께 쓸 경우 데이터베이스는 타겟 데이터를 반환하기 전에 오프셋 이전의 수많은 행을 전부 스캔하고 버려야 하므로 페이지 깊이가 깊어질수록 성능이 급격히 떨어진다. - 대책: 키셋 페이지네이션(keyset pagination) 혹은 커서 기반 페이지네이션을 적용한다. 마지막으로 본 항목의 ID(정렬 기준 값)를 쿼리 조건(
WHERE ID > last_id)에 넣어 탐색 범위를 즉시 좁힘으로써 조회 속도를 일정하게 유지한다.
- 문제점:
- 단일 인덱스 2개 vs 복합 인덱스
- 원리: 두 개 컬럼(A, B)을 함께 자주 조회할 때 각각 단일 인덱스를 걸면, 데이터베이스는 쿼리 수행 시 단 하나의 인덱스만 선택하여 스캔을 하고 다른 인덱스는 활용하지 않는 경우가 많다.
- 해결: 복합 인덱스(composite index,
(A, B))를 사용해야 한다. 이때 컬럼의 순서(가장 카디널리티가 높거나 자주 필터링에 걸리는 컬럼을 앞단에 배치)가 인덱스 스캔의 효율성을 결정한다.
- N+1 쿼리 문제
- 원리: ORM 사용 시 연관 객체를 지연 로딩(lazy loading)할 때 흔히 발생한다. 사용자 N명을 먼저 1회 쿼리한 후, 루프를 돌며 각 사용자의 연관 데이터(예: 프로필)를 조회하기 위해 추가로 N번 쿼리를 보낸다.
- 대책: 명시적 조인(explicit join)을 사용하거나 ORM의 즉시 로딩(eager loading / Fetch Join)을 활용하여 단일 쿼리로 한 번에 데이터를 가져온다.
동시성과 트랜잭션
- 분산 시스템 내 티켓 중복 예약 방지 및 트랜잭션 중첩의 함정
- 중복 예약 방지: 남은 좌석 확인 후 예약(check-then-act)은 분산 환경에서 경쟁 상태(race condition)를 일으킨다. 데이터베이스 수준의 비관적 락(
SELECT ... FOR UPDATE), 버전 번호를 대조하는 낙관적 락(optimistic locking), 혹은 Redis 분산 락을 활용한다. - 중첩 트랜잭션의 함정 (Transaction Nesting Pitfalls):
@Transactional설정 시 기본 전파 옵션(REQUIRED) 하에서 내부 메소드 예외 발생 시 전체 트랜잭션이 강제 롤백(UnexpectedRollbackException)되는 문제가 생긴다. 외부 API 호출이나 알림 발송 등 DB 커밋 이후 실행되어야 할 로직은@TransactionalEventListener등을 활용해 트랜잭션 범위 밖으로 완전히 분리하여 설계하는 것이 바람직하다.
- 중복 예약 방지: 남은 좌석 확인 후 예약(check-then-act)은 분산 환경에서 경쟁 상태(race condition)를 일으킨다. 데이터베이스 수준의 비관적 락(
- Repeatable Read vs Serializable 격리 수준 (Isolation Levels)
- 격리 수준 차이:
Repeatable Read는 트랜잭션 도중 조회 일관성을 보장하지만 다른 트랜잭션의 추가 데이터를 조회하는 팬텀 리드(Phantom Read)나 쓰기 스큐(Write Skew)가 여전히 발생할 수 있다. 완벽한 일관성이 요구된다면 격리 수준을Serializable로 높이거나 비관적 락을 적절히 저울질해야 한다. - 동시성 트레이드오프: 높은 격리 수준은 데이터 무결성을 보장하는 대가로 데이터베이스 락 획득 범위가 증가하여 트랜잭션 대기 시간 증가 및 처리량(Throughput) 저하를 수반한다.
- 격리 수준 차이:
- SPOF 없는 분산 락 (Distributed Lock Edge Cases)
- 원리: 단일 Redis 인스턴스에 걸어두는 분산 락은 해당 인스턴스 장애 시 단일 장애점(SPOF)이 된다. 이를 위해 Redlock 알고리즘이나 분산 코디네이터인 ZooKeeper를 활용한다.
- 분산 락의 에지 케이스: 작업 중인 노드에서 긴 GC Pause(Stop-the-world)나 네트워크 지연이 발생할 때, 락의 TTL이 먼저 만료되었는데도 작업 노드는 이를 모른 채 공유 자원을 덮어쓰는 문제가 생길 수 있다.
- 해결 대책: 락 획득 시마다 증가하는 유일 ID인 **펜싱 토큰(fencing token)**을 자원 저장소에 전달하여 만료된 락을 가진 작업자의 쓰기를 원천 차단해야 한다.
- 결제 재시도 엔드포인트의 멱등성 및 결제 예외 처리
- 결제 멱등성: 클라이언트 네트워크 연결 차단으로 결제 결과 응답을 받지 못해 재요청할 때의 이중 결제를 차단하기 위해 멱등성 키(idempotency key)를 Redis 등 고속 임시 저장소에 저장/대조하여 동일 요청 시 이전 처리 결과를 그대로 반환(exactly-once processing)한다.
- 결제 미캡처 예외 처리 (Payment Never Captured Exception Handling): 2단계 결제 방식(Authorize 후 Capture)을 사용하는 경우, 승인(Authorize)은 성공했으나 이후 통신 오류로 인해 매입(Capture)을 실행하지 못하는 예외가 발생할 수 있다.
- 해결 대책: 일정 시간 동안 Capture가 발생하지 않은 Authorize 트랜잭션을 주기적으로 검사하여 자동으로 취소(Void)시키거나, 관리자 승인을 유도하는 대조 스케줄러(Reconciliation Job)를 백엔드 아키텍처에 구현해 자금 불일치 리스크를 복구해야 한다.
캐싱 전략
- 캐시 스탬피드 (Cache Stampede)
- 원리: 고부하 환경에서 트래픽이 높은 캐시 키가 만료되는 순간, 수천 개의 스레드가 동시에 데이터베이스를 조회하고 데이터를 캐싱하려 시도하면서 데이터베이스에 병목이 발생해 다운되는 현상이다.
- 대책: 확률적 조기 만료(probabilistic early expiration)를 도입하거나, 캐시 미스 시 데이터베이스에 단 하나의 갱신 요청만 들어가도록 분산 락 또는 세마포어로 상호 배제를 적용한다.
- 사용자 프로필 캐시 무효화 (Cache Invalidation)
- 원리: 프로필 정보 변경 시 DB와 캐시 간의 정합성을 보장하는 방식이다.
- 장단점:
- Write-through: DB에 저장할 때 캐시도 같이 업데이트하여 정합성은 높으나 쓰기 지연 시간이 늘어난다.
- Cache-aside: 캐시 미스 시 DB를 조회하여 캐싱한다. 프로필이 수정될 때 캐시 키를 명시적으로 삭제(delete)하는 방식을 취하며, 적절한 TTL을 설정하여 정합성이 오래 어긋나지 않도록 가드레일을 둔다.
- 캐시 도입으로 인한 지연 시간 증가
- 원리: 캐시 미스가 잦은 환경(낮은 캐시 히트율, Cache Hit Ratio)에서는 캐시 조회 네트워크 홉(network hop)과 데이터베이스 조회 네트워크 홉이 이중으로 추가되어 오히려 응답 시간이 늘어난다.
- 대책: 캐시 히트율을 상시 모니터링하고 가치가 높은 핫 데이터 위주로 캐싱 윈도우를 조정해야 한다.
- 캐시 제거 정책 (Eviction Policy) 선택
- 비교:
- LRU (Least Recently Used): 가장 오랫동안 쓰이지 않은 캐시를 교체한다. 지속적 업데이트가 있는 콘텐츠 피드 등에 적합하다.
- 세션 저장소: 사용자의 접속 정보를 보관하므로 LRU를 적용하면 활동 중인 유저의 세션 캐시가 제거되어 비자발적 로그아웃이 발생한다. 세션은 만료 시간(TTL) 기반 또는 사용자 세션 만료 시 명시적 소멸 정책을 사용해야 한다.
- 비교:
API 및 네트워크 아키텍처
- 하위 호환성을 지닌 API 페이로드 변경
- 원리: 구버전 모바일 앱은 수년간 업데이트되지 않으므로, API 페이로드 구조를 깨거나 필터를 임의로 삭제하면 앱이 비정상 종료된다.
- 대책: 헤더나 URL을 이용한 명시적인 API 버전 관리(versioning)를 준수하고, 기존 클라이언트를 위한 하위 호환 필드를 삭제하지 않고 유지(backwards compatibility)해야 한다.
- 처리량 제한 (Rate Limiting) 알고리즘
- 비교:
- 고정 윈도우 카운터 (Fixed Window Counter): 구현은 단순하나 윈도우 경계점(예: 59분 59초와 00분 01초)에 한도의 2배에 달하는 요청이 유입될 수 있는 버스트(burst) 위험이 있다.
- 슬라이딩 윈도우 로그 (Sliding Window Log): 유입 시점을 저장하므로 정밀한 제한이 가능하지만 모든 요청 타임스탬프를 보관해야 하므로 메모리 사용량이 극대화되는 트레이드오프가 있다.
- 비교:
- 5GB 대용량 비디오 파일 업로드
- 원리: 대용량 파일을 한 번에 메모리에 적재해 업로드하면 힙 메모리가 가득 차 메모리 초과(OOM) 오류로 서버 프로세스가 다운된다.
- 대책: 파일 스트리밍(streaming) 또는 청크 단위로 나누어 업로드하는 멀티파트 업로드(multipart upload)를 사용하거나, 서버를 거치지 않고 저장소(S3 등)에 직접 쓰도록 Presigned URL을 발급해준다.
- 동기식 API 요청에서 40초 이상 소요 작업
- 원리: 동기 스레드로 긴 작업을 수행하면 네트워크 게이트웨이 및 클라이언트에서 커넥션 타임아웃이 나기 쉽다.
- 대책: 비동기 워커 패턴(asynchronous worker pattern)을 적용한다. 동기 API는 즉시 202 Accepted와 작업 식별자(Job ID)를 반환하고, 백그라운드 워커가 작업을 돌리는 동안 클라이언트가 주기적으로 상태 조회(polling) 또는 웹소켓 등으로 진행 상황을 전달받도록 설계한다.
메시지 브로커와 비동기 처리
- RabbitMQ vs Kafka 기술 선택 기준
- RabbitMQ: AMQP 기반의 지능형 라우팅과 작업 큐(task queue)에 최적화되어 복잡한 필터링 및 메시지 처리에 유리하다.
- Kafka: 고성능 처리에 유효한 분산 덤 로그(dumb log) 파일로 구현되어 메시지를 보존하고 재실행(replayability)해야 하는 대용량 스트리밍 처리에 유리하다.
- Kafka 컨슈머의 오프셋 커밋 실패
- 결과: 메시지를 처리 완료했음에도 오프셋 커밋을 하지 못하면 컨슈머 재시작 시 동일 메시지를 중복 읽게 된다.
- 대책: 이를 감안해 컨슈머가 처리하는 핵심 비즈니스 로직(예: DB 저장)은 항상 멱등성이 보장되거나 중복 제거를 하도록 설계해야 한다.
- 포이즌 메시지 (Poison Message) 대응
- 결과: 예외를 유발하는 잘못된 데이터가 든 메시지가 처리 실패 후 큐로 다시 유입되면, 여러 워커가 이를 반복해서 받아 에러를 터트리며 전체 노드를 강제 종료시키는 무한 루프가 발생한다.
- 대책: 최대 재시도 횟수를 명시하고 이를 초과한 메시지는 데드 레터 큐(Dead Letter Queue, DLQ)로 분리해 적재한 후 추후 일괄 처리하거나 알림을 보낸다.
- 메시지 송신 순서 보장
- 원리: 다중 컨슈머 분산 환경에서는 메시지 1을 가져간 워커 A가 메시지 2를 가져간 워커 B보다 늦게 연산을 끝낼 수 있어 순서가 무너진다.
- 대책: 관련된 메시지(예: 동일 유저의 동작들)는 같은 파티션 키(partitioning key) 혹은 라우팅 키(routing key)로 묶어 항상 동일한 단일 스레드/컨슈머에게만 할당되도록 구조화한다.
예시
- UUID vs Serial ID:
UUID생성 방식은 삽입(insert) 속도가 데이터가 쌓일수록 지수적으로 감소하지만, 시간 순 정렬이 적용된ULID나UUID v7은 B-Tree 리프 노드 우측 끝에 순차 삽입되므로 성능이 유지된다. - 5GB 비디오 처리:
Presigned URL을 클라이언트에 제공해 AWS S3로 다이렉트 스트리밍하게 설계하면, 백엔드 서버는 CPU/메모리 트래픽 프리로 동작한다.
충돌
아직 확인된 아키텍처 및 이론적 충돌 사안은 없습니다.