백엔드 면접 학습 노트 목차

채용공고 실전 면접 질문 — 90문항·모범답안 (백엔드 Java)

실제 채용공고를 분석해 도출된 예상 면접 질문 90개(10개사 × 9문항)와 각 문항 모범답안. 질문은 prime-career.com(p=13, 백엔드 Java) 공고 분석 원문 그대로이고, 모범답안은 소스가 미리보기로 gated되어 있어 1차자료(RFC·Kleppmann DDIA·Spring/PostgreSQL/Kafka/Redis 공식문서·microservices.io) 교차검증으로 직접 집필했다.

회사별로 어떤 역량을 어떤 프레이밍으로 묻는지 감을 잡는 용도. 개념 깊이는 앞의 01~12편 노트를, 원자료는 저장소 docs/04·05를 참고. 유형은 비교·트레이드오프·시나리오·장애진단이 중심(한국 시니어 백엔드 면접 결).


현대자동차 — 모빌리티 서비스 Backend Engineer (경력직)

키워드 1: 모빌리티 도메인 트랜잭션 설계

Q1. 예약-배차-탑승-종료-결제까지 하나의 라이드 라이프사이클을 상태 머신으로 설계해보세요. 네트워크 지연으로 탑승 시작 이벤트가 2번 들어오거나, 종료가 탑승보다 먼저 도착하거나, 결제 승인 후 정산 시스템 장애가 나는 경우에도 요금과 상태가 어긋나지 않게 하려면 어떤 불변식과 처리 규칙을 두겠습니까?

A. 라이드 상태를 REQUESTED→DISPATCHED→PICKED_UP→IN_PROGRESS→COMPLETED단조(monotonic) 전이로 정의하고, 각 전이에 허용 선행 상태를 명시한 상태 전이표를 둔다. 핵심 불변식 세 가지:

  • 멱등성: 모든 상태 변경 명령에 (ride_id, event_type, source_seq) 멱등 키 → 탑승 시작이 2번 와도 두 번째는 no-op. DB UNIQUE 제약을 최종 안전망으로.
  • 순서 무관성: 종료가 탑승보다 먼저 도착하면, 현재 상태(snapshot) 는 단조 규칙으로 거부/보류하되 이벤트 로그 에는 event_time 기준으로 모두 적재(늦게 온 이벤트는 재정렬 후 반영). 상태는 "이벤트 시각 최대값"으로 재계산 가능해야.
  • 금전 정합성: "결제 승인"과 "정산 반영"을 하나의 로컬 트랜잭션으로 묶지 말고 Saga + Outbox로. 결제 승인 후 정산 시스템 장애 시 결제는 유효, 정산은 Outbox 이벤트로 재시도(at-least-once) + 정산 소비자 멱등. 되돌릴 땐 보상 트랜잭션(환불). 상태와 금전은 각각 원장(ledger)에 append-only로 남겨 감사 가능.

Q2. 요금 계산(거리/시간/수요/할인/패널티)이 이벤트 스트림으로 계속 업데이트되는 구조에서, 최종 결제 금액과 정산/감사 가능한 원장 데이터를 어떻게 모델링하겠습니까? 특히 요금 규칙이 바뀌었을 때 과거 라이드를 재계산하면 안 되는 이유와 그 방지 설계를 설명해보세요.

A. 요금을 견적(Estimate)확정(Charge) 으로 분리한다. 주행 중엔 거리·시간·수요·할인·패널티 이벤트가 견적을 갱신하지만, 종료 시점에 그 순간의 규칙 엔진 버전과 입력 스냅샷(거리/시간/적용 프로모션 ID) 을 함께 불변 원장(charge ledger) 에 append한다. 최종 금액 = f(rule_version, inputs)결정론적 재현이 가능하되, 저장은 계산 결과 + 근거를 함께. 과거 라이드를 재계산하면 안 되는 이유: ① 이미 고객에게 청구·정산 완료된 금액이 사후 규칙 변경으로 바뀌면 회계·감사 정합성이 깨지고 법적 분쟁 소지, ② 프로모션·세금은 거래 시점 규칙 이 법적 기준. 방지 설계: 규칙을 버전드(rule_set v1, v2…)로 관리하고 라이드에 rule_version을 고정 저장, 계산은 항상 저장된 버전으로만. 규칙 변경은 신규 라이드부터 적용. 정정이 필요하면 원 레코드는 보존하고 조정 트랜잭션(adjustment) 을 추가로 기록(reversal + re-charge).

Q3. 배차/예약 API에서 피크 트래픽이 폭증할 때 중복 예약과 초과 배차를 막으면서도 지연을 최소화하려면 어떤 동시성 제어를 쓰겠습니까? DB 락, 분산 락, 낙관적 락, 토큰/쿼터 기반 중 무엇을 어떤 조건에서 선택하나요?

A. 계층별로 나눈다. ① 클라이언트 재시도로 인한 중복Idempotency-Key 헤더로 방지 — 동일 키는 같은 ride_id를 반환(RFC 초안의 멱등 키 패턴). ② 초과 배차(한 차량이 두 예약) 는 자원 경합이므로, 저경합이면 낙관적 락(@Version) + 실패 시 재시도, 고경합(특정 지역 피크)이면 원자 연산(UPDATE vehicle SET ride_id=? WHERE id=? AND ride_id IS NULL — 영향 행 0이면 이미 배차됨) 또는 Redis 분산락(Redisson, watchdog). ③ DB 비관락(SELECT FOR UPDATE)은 정확하지만 피크에 병목·데드락 위험이라 좁은 범위에만. 선택 기준: 정확성(초과 배차 0)이 지연보다 우선이므로 DB UNIQUE/원자 연산을 최종 진실로 두고, 그 앞단에 Redis로 후보 필터링해 DB 부하를 줄인다. 분산락은 fencing token으로 GC-pause 안전성 보강.

키워드 2: 차량 데이터 신뢰성·실시간성·품질

Q1. 차량에서 1초 단위 위치 이벤트를 받는데, 네트워크 품질에 따라 10~60초치가 한 번에 몰아서 오거나 순서가 뒤집혀 옵니다. 실시간 지도 관제와 사후 정산/분석 두 요구를 동시에 만족하는 저장/처리 구조를 제안하고, 지연/역전/중복을 어떻게 다룰지 설명해보세요.

A. 요구를 두 경로로 분리(CQRS 성격). ① 실시간 관제(낮은 지연·약한 정합성): 최신 위치만 필요하므로 event_time 기준 last-write-wins, 늦게 온 과거 위치는 관제 화면에선 무시. Redis/인메모리에 차량별 최신 좌표. ② 사후 정산·분석(높은 정합성): 원본 이벤트를 event_timeingest_time을 모두 붙여 append-only 로그(Kafka/시계열 DB) 에 전부 저장 → 배치에서 event_time으로 재정렬해 궤적 복원.

  • 지연: 실시간은 지연 이벤트 폐기, 정산은 워터마크(watermark)로 늦은 데이터 허용 창을 두고 창 지나면 late로 별도 처리.
  • 역전: 정산은 시퀀스/시각 기준 정렬로 흡수. 중복: (vehicle_id, event_time, seq) dedup. 차량 단말엔 단조 증가 시퀀스를 부여하면 역전·중복 판별이 쉽다.

Q2. GPS 튐/오차로 인해 차량이 순간이동한 것처럼 보이는 데이터가 들어오면, 안전/관제/요금 계산에 모두 영향을 줍니다. 위치 품질 점수를 설계한다면 어떤 특징량을 쓰고, 어떤 기준으로 필터/보정/격리할지 말해보세요.

A. 품질 점수 특징량: ① 물리적 타당성 — 직전 위치 대비 속도 = 거리/Δt, 가속도가 물리 상한(예: 차량 200km/h, 가속 한계) 초과 시 감점. ② 위성 지표 — HDOP/PDOP, 사용 위성 수, accuracy(m) 반경. ③ 도로 매칭 가능성 — map-matching으로 도로망과의 편차·헤딩 일치도. ④ 점프 연속성 — 튐 후 원위치 복귀 패턴. 처리: 점수 임계 이상은 채택, 경계값은 보정(칼만 필터/이동평균으로 스무딩·map-matching 스냅), 임계 미만은 격리(원본은 보존하되 관제·요금 계산에서 제외하고 별도 품질 큐로). 요금엔 보정 전 원본이 아니라 검증·보정된 궤적을 써 과금 분쟁을 줄인다. 격리 이벤트 비율을 모니터링해 단말·펌웨어 이상을 조기 탐지.

Q3. 차량 이벤트 스키마가 자주 바뀌고(펌웨어/게이트웨이 버전), 일부 필드가 누락되거나 의미가 변경됩니다. 생산 환경에서 스키마 진화를 안전하게 하려면 어떤 계약과 검증/배포 체계를 두겠습니까?

A. 이벤트에 명시적 스키마 버전(schema_version)을 포함하고, 스키마 레지스트리(Confluent Schema Registry 등)로 Avro/Protobuf 스키마를 중앙 관리한다. 호환성 규칙을 강제: 소비자를 먼저 배포할 수 있도록 backward compatible(새 필드는 optional+기본값, 필드 삭제 금지·deprecate만, 타입 변경 금지)을 기본으로, 필요 시 forward/full 호환. 의미 변경은 같은 필드 재활용 금지새 필드 추가 후 이전 필드 deprecate. 배포 체계: ① CI에서 스키마 등록 시 호환성 검사 통과해야 머지, ② 소비자 주도 계약 테스트(CDC, Pact) 로 프로듀서 변경이 소비자를 깨는지 사전 검출, ③ 파서는 관대하게(tolerant reader) — 모르는 필드는 무시, 누락은 기본값. 게이트웨이 버전별 트래픽 비율을 관측하고, 문제 버전은 롤백 가능하도록 버전 태깅.

키워드 3: 글로벌 제조사 수준의 운영

Q1. 배차 API 지연이 발생했는데, 원인은 DB 커넥션 고갈일 수도 있고 외부 지도/요금 서비스 지연일 수도 있습니다. 30분 안에 고객 영향도를 줄여야 할 때, 어떤 순서로 확인하고 어떤 임시 조치를 적용하겠습니까? 사후에는 무엇을 바꾸겠습니까?

A. 즉시(사용자 영향 차단): 외부 호출(지도/요금)에 타임아웃·서킷브레이커 적용 → OPEN 시 폴백(캐시된 ETA·기본 요금표)으로 degrade하되 서비스는 유지. 격리(원인 좁히기): APM 트레이스로 시간이 DB에서 소모되는지 외부 스팬에서 소모되는지 구분 — HikariCP 지표(active/pending)가 높고 Connection is not available면 커넥션 고갈, 외부 스팬 지연이면 서드파티 문제. 커넥션 고갈이면 트랜잭션 내 외부 호출 여부·OSIV 점검, 임시로 풀·타임아웃 조정보다 점유 시간 원인 제거. 사후: 외부 의존은 벌크헤드(격리 풀) 로 한 의존성 지연이 전체 스레드를 삼키지 않게, 요금/지도는 비동기·캐시 우선, SLO 알람(p99·에러율)·부하 테스트로 재발 방지. 포스트모템에 타임아웃·서킷 임계값을 데이터로 재조정.

Q2. 모빌리티 서비스에서 가장 위험한 데이터는 무엇이며(예: 위치/이동 이력/결제/차량 식별자), 로그/모니터링까지 포함해 데이터 최소화와 추적 가능성을 동시에 만족시키려면 어떻게 설계하나요?

A. 가장 민감한 건 위치·이동 이력 — 생활 패턴·거주지가 드러나 개인정보보호법·위치정보법 규제 대상. 원칙: ① 수집 최소화(목적에 필요한 해상도·보존기간만, 오래된 궤적은 집계 후 원본 파기·익명화), ② 저장 시 필드 레벨 암호화(위치·결제·식별자는 KMS 관리 키로, 차량 식별자는 토크나이즈), ③ 목적/역할 기반 접근통제(RBAC/ABAC) + 최소권한. 추적 가능성과의 균형: 로그·모니터링엔 원본 대신 가명화된 ID·좌표 반올림(geohash 저해상) 을 남기고, 원본 접근은 감사 로그(누가·언제·왜) 를 강제. 즉 "운영에 필요한 추적성"은 가명·집계 로 확보하고, 원본 열람은 승인·감사가 붙은 예외 경로로만. 개인정보 삭제 요청(파기권)에 대응할 수 있게 키 폐기(crypto-shredding) 설계.

Q3. 대규모 조직에서 배포가 잦을 때, 한 팀의 변경이 다른 팀의 장애로 번지는 것을 막는 기술적/프로세스적 장치를 무엇으로 두겠습니까? 실제로 당신이 리드한다면 어떤 표준을 강제하겠습니까?

A. 기술적으로 API/이벤트 계약을 명시적으로 관리하고 소비자 주도 계약 테스트(CDC) 를 CI에 강제해 배포 전 깨짐을 잡는다. 하위 호환 원칙(필드 추가 optional·삭제 금지·버전드 API), 점진 배포(카나리/블루그린 + 에러율·p99 기반 자동 롤백), 벌크헤드·서킷브레이커로 장애 전파 차단, 서비스 간 비동기(이벤트) 우선으로 시간 결합 제거. 관측성: 분산 트레이싱·SLO·에러버짓. 리드로서 강제할 표준: ① 계약 테스트 없는 API 변경 머지 금지, ② 모든 서비스에 타임아웃·재시도(멱등 전제)·서킷브레이커 기본 장착, ③ 배포는 카나리+자동 롤백 게이트 통과 필수, ④ 스키마 변경은 레지스트리 호환성 검사 통과, ⑤ 포스트모템·에러버짓으로 신뢰성을 팀 KPI화. 표준을 문서가 아니라 CI 게이트·템플릿(golden path) 으로 박아 우회 불가능하게.


주식회사 놀유니버스 — Backend Engineer, 커머스플랫폼 (경력직)

키워드 1: 커머스 도메인 모델링과 트랜잭션 설계

Q1. 주문 생성부터 결제 승인까지를 하나의 트랜잭션으로 묶지 않고도 정합성을 보장해야 합니다. 결제는 성공했는데 주문이 실패하거나, 주문은 생성됐는데 결제가 실패하는 케이스를 포함해, 데이터 모델과 상태 전이를 어떻게 설계하겠습니까?

A. 주문을 CREATED→PAYMENT_PENDING→PAID→CONFIRMED(실패 시 PAYMENT_FAILED/CANCELED)의 단조 전이 상태 머신으로 정의하고, 결제 승인과 주문 확정을 하나의 로컬 트랜잭션으로 묶지 않는다. 대신:

  • Saga + Outbox: 주문·결제·재고를 각각 로컬 트랜잭션으로 처리하고, 상태 변경과 이벤트 발행을 같은 트랜잭션의 outbox 테이블에 함께 기록해 원자성을 보장한다(dual-write 방지).
  • 결제 성공·주문 실패: 결제는 유효하므로 보상 트랜잭션(환불) 으로 되돌린다.
  • 주문 생성·결제 실패: 주문을 PAYMENT_FAILED로 확정하고 예약했던 재고를 해제한다. 재고는 reserve→commit 2단계로 다뤄 미결제 주문이 재고를 영구 점유하지 못하게 한다.

다층 방어로 설계하되 DB UNIQUE(주문 자연키/멱등 키)를 최종 안전망으로 두어, 상위 계층 중복이 새어 들어와도 두 번째 삽입은 실패하게 만든다. 상위 계층은 비용·UX 최적화 수단이고, 정합성의 진실은 항상 DB 제약이다.

Q2. 쿠폰과 프로모션이 중복 적용 가능, 불가능, 선착순, 최소결제금액, 카테고리 제한, 특정 결제수단 한정 같은 규칙을 가질 때, 규칙 엔진을 코드로 하드코딩하지 않고도 확장 가능하게 설계하는 방법을 설명해 주세요.

A. 할인 계산을 독립 도메인 서비스로 분리하고, 규칙을 코드가 아니라 데이터(스펙) 로 관리한다. 입력 컨텍스트는 장바구니/주문 초안, 고객·멤버십 등급, 결제수단, 채널, 시간대이고, 출력은 라인별 할인 배분 내역이다. 각 규칙은 조건(predicate) + 효과(effect) 구조로 표현한다.

  • 조건: 카테고리 제한, 최소결제금액, 특정 결제수단 한정 등을 predicate 조합으로. 새 조건 타입은 전략(Strategy) 플러그인으로 추가해 코어 변경 없이 확장한다.
  • 중복 적용: stackable 플래그 + 배타 그룹(exclusion group) + 우선순위로 조합 가능 여부와 순서를 데이터로 결정. 규칙 엔진이 적용 가능 규칙을 필터링한 뒤 고객 최적(또는 명시 우선순위) 조합을 계산한다.
  • 선착순: 수량 한정은 규칙 조건이 아니라 별도 발급 재고 카운터(원자 연산) 로 분리한다.

결과에 적용 규칙 ID·rule_version을 함께 남겨 정산·감사에서 근거를 재현할 수 있게 한다.

Q3. 정산 도메인을 처음 맡는다고 가정합니다. 주문, 환불, 부분취소, 쿠폰 부담 주체(자사, 파트너)까지 포함해 정산 원장을 어떻게 설계하고, 재처리와 감사 요구를 어떻게 만족시키겠습니까?

A. 최종 금액만 저장하지 않고 append-only 불변 원장으로 설계한다. 판매, 환불, 부분취소, 쿠폰·프로모션 비용을 각각 복식부기 성격의 원장 항목으로 기록하되, 각 항목에 amount, 차/대변 방향, 부담 주체(자사/파트너), order_id, 사유, occurred_at, rule_version을 남긴다. 정산액은 기간·주체별 원장 집계로 도출한다.

  • 정정 불가·역분개: 이미 기록한 항목은 수정·삭제하지 않고, 부분취소·환불은 원 항목을 보존한 채 reversal(역분개) 항목을 추가한다. 회계·감사 정합성과 시점 규칙이 보존된다.
  • 재처리: 각 원장 항목에 event_idUNIQUE로 두어 멱등하게 소비하고, 원장은 이벤트로부터 결정론적으로 재구성 가능하게 한다.
  • 감사: append-only 특성상 이력이 곧 감사 로그이며, 항목마다 근거(주문·규칙 버전)를 붙여 추적성을 확보한다.

키워드 2: 분산 시스템 안정성 설계

Q1. 동일 API 요청이 네트워크 타임아웃으로 재시도되어 주문이 두 번 생성될 수 있습니다. 클라이언트, API 게이트웨이, 애플리케이션, DB 레벨에서 각각 어떤 방어를 두고, 그중 무엇을 정답으로 삼겠습니까?

A. 계층별 역할을 분리하되, 정답은 DB의 유일성 보장을 최종 안전망으로 두는 다층 방어다.

  • 클라이언트: 요청마다 Idempotency-Key(UUID) 헤더를 부여하고 전송 중 버튼을 비활성화한다.
  • API 게이트웨이: 짧은 창의 중복을 best-effort로 스로틀·dedup한다(완벽하지 않음).
  • 애플리케이션: (idempotency_key, user)를 결과와 함께 저장해, 같은 키 재호출은 기존 주문을 반환하고 진행 중이면 대기/동일 응답 처리한다.
  • DB: 멱등 키 또는 비즈니스 자연키에 UNIQUE 제약을 걸어 두 번째 삽입을 물리적으로 차단한다.

정답을 DB에 두는 이유는, 게이트웨이·애플리케이션은 다중 인스턴스·경합·캐시 미스로 중복을 항상 막지는 못하기 때문이다. 상위 계층은 DB에 도달하는 요청 수와 사용자 마찰을 줄이는 최적화이고, 정합성의 최후 판정은 언제나 DB 제약이 내린다.

Q2. 이벤트 기반으로 주문 완료 후 메시징, 포인트 적립, 정산 반영 등이 이어진다고 할 때, 이벤트가 중복 소비되거나 순서가 뒤바뀌어도 안전하도록 설계하는 방법을 설명해 주세요.

A. 메시징·포인트 적립·정산 반영은 at-least-once를 전제로 설계한다.

  • 멱등 커맨드: 각 다운스트림 작업을 재실행해도 결과가 같도록 만든다(적립은 UPSERT, 정산은 원장 event_id 유일성).
  • 중복 소비 방어: 소비자마다 처리한 event_idprocessed_eventsUNIQUE로 저장해, 이미 처리한 이벤트는 no-op으로 흘려보낸다.
  • 순서 보장: Kafka 파티션 키를 order_id 로 잡아 한 주문의 이벤트는 같은 파티션에서 순서가 보존되게 한다. 파티션·토픽을 넘는 전역 순서는 보장되지 않으므로, 주문 상태에 version/state_rank를 두어 뒤늦게 온 과거 이벤트는 단조 규칙으로 무시한다(상태가 되돌아가지 않게).
  • 발행 원자성: 상태 변경과 이벤트 발행을 Outbox로 묶어 유실·이중발행을 막는다.

Q3. 장애로 인해 특정 시간대에 주문과 결제가 누락되었다는 신고가 들어왔습니다. 로그도 일부 유실됐고, 데이터가 여러 저장소에 분산돼 있습니다. 당신이 온콜 엔지니어라면 1시간 안에 무엇부터 확인하고, 어떻게 복구와 재발방지를 하겠습니까?

A. 첫 10분은 범위 확정이 핵심이다. CS 티켓·신고에서 공통 키(발생 시간대, 채널, 결제수단, 특정 API, 직전 배포 시각)를 뽑아 blast radius를 좁힌다. 배포 직후라면 롤백을 우선 후보로 둔다.

  • 고객 영향 차단: 위험한 진행 중 작업(추가 청구 등)을 중단하거나 degrade하고, 사용자에겐 상태를 보수적으로 노출한다.
  • 진실 소스 대조: 외부 진실인 PG(결제사) 승인 기록을 기준으로 우리 주문 DB·Kafka lag·Outbox 미발행분을 대사(reconciliation)한다. "결제는 승인됐는데 주문이 없는" 건이 가장 위험하므로 최우선 식별한다.
  • 복구: 누락 건은 결제사 승인 데이터를 원천으로 멱등 재처리(backfill) 한다. 멱등 키가 있으니 중복 생성 걱정 없이 재주입 가능하다.
  • 재발방지: Outbox·정기 대사 배치·DLQ·핵심 지표(승인율·lag·PAYING 체류시간) 알람을 붙이고 포스트모템으로 임계값을 재조정한다.

키워드 3: 성능, 확장성, 데이터 접근 최적화

Q1. 프로모션 선착순 쿠폰을 초당 수천 건 요청이 때릴 때, 단일 DB에서 행 잠금으로 처리하면 병목이 생깁니다. 공정성, 정확성, 성능을 동시에 만족시키는 설계를 제안해 주세요.

A. 공정성·정확성을 최우선으로 두고 성능은 그 범위 안에서 최적화한다. 단일 DB 행 잠금은 모든 요청을 한 행에 직렬화시켜 병목·데드락을 유발하므로 피한다.

  • 발급 재고를 사전 토큰화: 미리 N개의 발급 토큰을 만들어 Redis에 적재하고, Lua 스크립트로 재고 확인+차감을 원자적으로 수행한다(DECR가 음수면 소진). 원자성이 오버이슈(초과 발급)를 0으로 만든다.
  • 공정성: 요청을 FIFO 대기열/순번으로 처리해 먼저 온 요청이 먼저 발급받게 한다.
  • 정합성 최종 안전망: Redis에서 성공한 요청만 DB에 비동기로 확정하되, 발급 원장에 UNIQUE(coupon_code)와 발급 수 제약을 걸어 Redis 장애·재시도 상황에서도 총량이 초과되지 않게 한다.

즉 Redis 원자 연산으로 대량 트래픽을 앞단에서 걸러 DB 부하를 없애고, DB 제약을 정확성의 최후 보루로 둔다.

Q2. 주문 조회 API가 느립니다. 조인 테이블이 많고, 화면마다 필요한 필드가 달라서 N+1과 과도한 조인이 섞여 있습니다. 쿼리 최적화를 넘어, 데이터 모델과 읽기 모델을 어떻게 바꿔서 해결하겠습니까?

A. 즉효는 fetch join으로 N+1을 제거하고 화면별 DTO 프로젝션으로 필요한 컬럼만 조회하는 것이지만, 근본 해결은 읽기 요구를 화면별로 분리(CQRS) 하는 것이다. 단일 범용 API가 모든 화면을 커버하려다 조인 폭발이 생기므로, 목록·상세·백오피스를 별도 읽기 경로로 나눈다.

  • 읽기 모델 분리: 정규화된 쓰기 모델과 별개로, 주문 완료/변경 이벤트를 구독해 비정규화된 조회 전용 read model(구체화 뷰 또는 문서형 저장소)을 갱신한다. 조회 시엔 조인 없이 화면 형태 그대로 읽어 온다.
  • 화면별 프로젝션: 화면마다 필요한 필드만 담은 프로젝션을 두어 과도한 조인을 원천 제거한다.

읽기 모델은 최종 일관성을 허용하되(주문 이력 조회는 수 초 지연 허용), 결제 등 강한 일관성이 필요한 경로는 쓰기 모델을 직접 읽어 트레이드오프를 명시적으로 나눈다.

Q3. 결제 완료 직후 주문 상태를 바꾸는 업데이트가 동시에 여러 곳에서 발생해 데드락이 발생합니다. 재시도만으로는 해결이 안 됩니다. 데드락을 구조적으로 줄이는 방법을 데이터 접근 순서와 락 전략 관점에서 설명해 주세요.

A. 데드락은 두 트랜잭션이 같은 자원을 서로 다른 순서로 잠글 때 발생하므로, 구조적으로 순서를 통제한다.

  • 전역 일관 락 순서: 여러 행을 잠글 때 항상 order_id 오름차순 등 하나의 정렬 기준으로만 획득하게 강제해 순환 대기를 제거한다.
  • read-modify-write 제거: SELECT 후 UPDATE 대신 조건을 WHERE에 담은 단일 원자 UPDATE(UPDATE orders SET status=? WHERE id=? AND status=?)로 상태를 전이해 락 보유 구간을 최소화한다.
  • 낙관적 락: 상태 컬럼에 version을 두어 충돌 시 재시도로 풀면, 상호 대기(데드락) 대신 짧은 재시도로 수렴한다.
  • 단일 라이터 직렬화: 한 주문의 상태 변경을 order_id 기준 파티션/큐로 몰아 동시 갱신 자체를 없앤다.

추가로 트랜잭션을 짧게 유지하고, 인덱스로 잠기는 범위를 좁히며(불필요한 간극 락 회피), 격리 수준을 요구에 맞게 조정한다.


미소 — Back-end Developer (경력직)

키워드 1: 서비스 확장성과 MSA 전환 설계

Q1. 미소의 기존 모놀리식 백엔드에서 주문 처리와 알림 기능이 강하게 결합돼 있다고 가정해봅시다. 두 기능을 MSA로 분리하려면 어떤 순서로 접근하시겠습니까?

A. 알림을 먼저 떼낸다. 알림은 리프(leaf) 성격이라 주문 데이터를 소유하지 않고, 비동기·재시도 허용이라 분리 리스크가 가장 낮다. 순서는:

  • ① 경계 식별(seam): 주문 코드가 알림을 직접 호출하는 지점을 도메인 이벤트(order.completed, payment.approved)로 추상화. 데이터 경계도 함께 그어 알림이 주문 테이블을 직접 읽지 않게 한다.
  • ② Strangler Fig: 신규 알림 서비스를 세우고, 기존 주문 API 앞에 파사드/프록시를 둬 알림 트래픽만 점진적으로 신규로 우회시킨다. 기존 코드는 그대로 두고 한 경로씩 교살.
  • ③ 이벤트 발행: 주문은 상태 변경 시 Outbox에 이벤트를 적재, 알림 서비스가 구독해 발송(at-least-once + 멱등 소비).
  • ④ 컷오버·제거: 카나리로 신규 경로 비율을 올리고 지표 안정화 후 모놀리스의 알림 코드를 삭제한다. 롤백 가능하도록 구(舊) 경로를 마지막까지 살려둔다.

Q2. 미소처럼 빠르게 성장하는 O2O 서비스에서 '서비스 분리'와 '성능 최적화'가 동시에 필요할 때, 어떤 기준으로 분리 우선순위를 정하고 어떤 메트릭을 보고 판단하시겠습니까?

A. 우선순위는 네 축으로 점수화한다: ① 변경 빈도(자주 배포되는 도메인일수록 독립 배포 이득이 큼), ② 장애 전파 범위(결제·정산처럼 죽으면 전체가 멈추는 곳을 격리), ③ 스케일 특성(매칭·조회처럼 트래픽 곡선이 다른 부분을 떼야 개별 스케일아웃 가능), ④ 데이터 소유권 충돌(쓰기 주체가 명확한 경계부터). 판단 메트릭은 결합해서 본다:

  • 성능: 도메인별 p95/p99 latency, 처리량(RPS), 에러율, DB 커넥션 풀 대기(HikariCP active/pending).
  • 결합도: 배포 시 동반 변경되는 모듈 수, 서비스 간 동기 호출 깊이.
  • 비즈니스 영향: 예약 전환율·취소율에 직결되는 경로 우선.

원칙은 "측정 → 병목 도메인 분리 → 재측정". 성능 문제가 특정 쿼리·N+1이면 분리 없이 튜닝(인덱스·캐시)으로 먼저 잡고, 스케일·배포 병목이면 그때 분리한다. 분리 자체가 목적이 되지 않도록 메트릭으로 정당화한다.

Q3. 미소의 기존 시스템에서 도메인별 DB가 얽혀 있고, 서로 다른 테이블을 직접 참조하는 코드가 많습니다. 레거시 의존성을 깨지 않으면서 MSA로 전환하고 데이터 정합성을 유지하려면 어떤 설계를 제안하시겠습니까?

A. 핵심은 각 도메인이 자기 쓰기 모델(write model)을 단독 소유하게 만드는 것이다. 단계적으로:

  • ① 소유권 재정의: 테이블별 쓰기 주체를 하나로 확정. 다른 도메인의 직접 JOIN/참조를 API 또는 anti-corruption layer(ACL) 뒤로 숨겨 내부 스키마 변경이 외부로 새지 않게 한다.
  • ② 읽기 경로 분리: 참조하던 데이터는 CDC(Debezium 등)로 이벤트화해 소비 도메인이 자기 로컬 조회용 복제본(read model) 을 갖게 한다. 조인 대신 이벤트로 데이터를 흘려보낸다.
  • ③ 정합성: 도메인 간 쓰기는 단일 트랜잭션이 불가하므로 Saga + Outbox로 최종적 일관성 보장. 이벤트 스키마는 레지스트리로 버전 관리하고 backward-compatible 진화를 강제.
  • ④ 점진 전환: DB는 우선 스키마/서비스 단위로 논리 분리 → 트래픽 안정 후 물리 분리.

즉 레거시 참조를 한 번에 끊지 않고, ACL·CDC·이벤트로 감싸 의존성을 역전시킨 뒤 하나씩 소유권을 넘긴다.

키워드 2: 고트래픽 O2O 플랫폼의 데이터 모델링과 트랜잭션 설계

Q1. 사용자가 같은 예약 버튼을 네트워크 문제로 두 번 연속 누를 수 있습니다. 이런 중복 요청을 백엔드에서 어떻게 막으시겠습니까?

A. 멱등성(idempotency) 으로 방어한다. 클라이언트가 요청마다 고유 Idempotency-Key 를 헤더로 보내고, 서버는 이 키를 처리 결과와 함께 저장한다. 같은 키가 재호출되면 새로 처리하지 않고 저장된 기존 결과를 그대로 반환(같은 reservation_id)한다.

  • 최종 안전망: (user_id, slot_id) 또는 멱등 키에 DB UNIQUE 제약을 걸어, 경합으로 두 요청이 동시에 통과해도 두 번째 INSERT가 실패하게 만든다. 애플리케이션 락만 믿지 않는다.
  • 키 저장소: 짧은 TTL로 Redis에 처리 상태(진행중/완료)를 두면 재시도 폭주도 흡수. 진행중 상태면 409/기존 결과를 응답.
  • UX: 프런트의 버튼 debounce·비활성화는 보조 수단일 뿐, 신뢰 경계는 서버다.

이 조합은 네트워크 재시도, 더블클릭, 프록시 중복 전송을 모두 흡수하면서 정확히 한 건의 예약만 남긴다.

Q2. 미소 서비스에서 '예약 가능 시간'을 여러 사용자가 동시에 선점하려고 하는 상황을 가정해보세요. 비관적 락과 낙관적 락 중 무엇을 선택하고, 왜 그렇게 하시겠습니까?

A. 경쟁 정도와 실패 시 UX를 기준으로 나눈다.

  • 낙관적 락(@Version): 같은 슬롯 경쟁이 드물 때. 충돌 시 버전 불일치로 예외 → 재시도 또는 "방금 마감됐어요" 안내. 락 점유가 없어 처리량이 높지만, 충돌이 잦으면 재시도 폭주로 오히려 손해.
  • 비관적 락(SELECT ... FOR UPDATE): 미소처럼 인기 시간대에 선점 경쟁이 실재하는 도메인의 최종 확정 단계. 슬롯 행을 잠그고 확정해 초과 예약을 원천 차단한다. 락 범위를 슬롯 단위로 좁혀 데드락·병목을 줄인다.

실무 절충: 탐색·후보 노출은 락 없이 낙관적으로 빠르게, 최종 "예약 확정"만 비관적 락 또는 원자적 조건부 UPDATE(UPDATE slot SET taken=1 WHERE id=? AND taken=0, 영향 행 0이면 이미 선점)로 처리한다. 초고경합 구간은 Redis 분산락(Redisson watchdog, fencing token)으로 앞단 필터링해 DB 부하를 낮춘다. 정확성(중복 확정 0)이 지연보다 우선이라는 원칙을 지킨다.

Q3. 예약 생성, 결제 승인, 매니저 배정, 알림 발송이 모두 얽힌 상태에서 일부 단계만 성공했을 때, 데이터 정합성을 어떻게 설계하시겠습니까? 단일 트랜잭션은 사용할 수 없다고 가정하세요.

A. 로컬 ACID 트랜잭션을 쓸 수 없으므로 Saga + 보상 트랜잭션으로 최종적 일관성을 만든다.

  • 오케스트레이션 Saga: 예약→결제 승인→배정→알림을 조정자가 순차 실행. 중간 실패 시 역순 보상(결제 실패면 예약 취소, 배정 실패면 결제 취소/환불)으로 되돌린다. 알림은 마지막·비핵심이라 실패해도 재시도 큐로 뺀다.
  • 원자적 이벤트 발행: 각 단계에서 상태 변경과 이벤트를 Outbox 패턴으로 같은 로컬 트랜잭션에 기록 → 릴레이가 브로커로 발행. "DB는 커밋됐는데 이벤트는 유실"을 방지(dual write 문제 해소).
  • 멱등 처리: 모든 소비자는 이벤트 ID 기반 dedup으로 at-least-once 재전달에 안전. 결제처럼 되돌리기 어려운 단계는 뒤로 배치.
  • 가시성: 각 Saga 인스턴스의 상태를 원장에 남겨 stuck 트랜잭션을 모니터링·수동 개입 가능하게 한다.

키워드 3: 내부 운영 효율화와 데이터 기반 개선

Q1. 운영팀이 수동으로 처리하던 작업을 내부 툴로 자동화하려고 합니다. 백엔드 개발자로서 무엇부터 설계하시겠습니까?

A. 업무 관찰과 안전한 작업 단위 설계부터 시작한다. 코드가 아니라 프로세스 이해가 먼저다.

  • ① 흐름 관찰·분해: 운영팀의 실제 작업을 따라가 반복(정형) 단계와 예외(판단) 단계를 분리. 반복 단계만 자동화 후보로 삼고, 예외는 사람이 개입하는 반자동으로 남긴다.
  • ② 안전한 작업 단위화: 각 액션을 멱등하고 되돌릴 수 있는 단위로 정의하고, 수정 권한(RBAC)과 감사 로그(누가·언제·무엇을·왜) 를 필수로 박는다. 잘못 눌러도 복구 가능한 설계.
  • ③ 데이터 정합: 내부 툴이 운영 DB를 직접 쓰지 않게 API/서비스 경계를 통하게 해, 외부 서비스와 같은 검증·트랜잭션 규칙을 재사용한다.
  • ④ 측정 내장: 처리 건수·소요 시간·실패율을 처음부터 로깅해 개선 효과를 숫자로 증명.

가장 병목이고 위험이 낮은(되돌리기 쉬운) 작업부터 자동화해 신뢰를 쌓고 범위를 넓힌다.

Q2. 내부 툴 도입 후 업무 시간이 줄었는데도 운영 품질 지표가 개선되지 않는다면, 어떤 식으로 원인을 분석하시겠습니까?

A. '시간 절감'과 '품질 개선'은 별개 지표임을 전제로 분리 진단한다. 빨라졌다는 건 처리 속도일 뿐, 결과 정확도를 보장하지 않는다.

  • 품질 세부 지표 분해: 오탐/미탐률(자동화가 잘못 처리한 건), 예외 처리 비율(자동화가 못 받아 사람에게 넘긴 비율), 수동 재개입률(자동 처리 후 사람이 다시 고친 비율), SLA 초과 건수를 본다.
  • 가설 검증: 재개입률이 높으면 자동화 로직이 엣지 케이스를 못 잡는 것 → 규칙 정교화. 오탐이 많으면 임계값·검증 강화. 시간은 줄었는데 재작업이 늘었다면 속도가 품질을 갉아먹은 것.
  • 세그먼트 분석: 서비스 유형·지역·파트너 등급별로 쪼개 특정 세그먼트에서만 품질이 나쁜지 확인.
  • 선행/결과 지표 연결: 재개입률(선행)이 취소율·CS 문의(결과)로 어떻게 이어지는지 추적.

결론: 시간 지표에 만족하지 말고 품질을 독립적으로 계측해, 자동화가 만든 새로운 실패 모드를 찾아 로직을 보정한다.

Q3. 미소에서 비즈니스 지표를 개선하기 위한 백엔드 변경을 제안해야 한다면, 어떤 프레임워크로 '좋은 기술 과제'인지 판단하시겠습니까?

A. 네 가지 축으로 과제를 평가·우선순위화한다.

  • ① 지표 영향: 어떤 지표를 얼마나 움직이는가. 반드시 결과 지표(예약 전환율, 취소율, 재구매율)선행 지표(API 응답 시간, 매칭 성공률, 배정 리드타임) 를 연결해 인과 가설을 세운다. 선행 지표 개선이 결과로 이어질 근거가 있어야 좋은 과제.
  • ② 실행 가능성: 투입 대비 효과(ROI), 의존성, 기간. 작게 쪼개 빠르게 검증(A/B·피처 플래그) 가능한지.
  • ③ 운영 리스크: 장애 전파·롤백 난이도·데이터 정합성 영향. 핵심 결제·정산 경로를 건드리면 리스크 가중.
  • ④ 재사용성: 일회성 땜질인지, 공통 플랫폼·라이브러리로 여러 도메인이 재사용할 자산인지.

판단 순서: 가설(선행→결과)을 세우고 → 계측 가능하게 지표를 정의 → 리스크 대비 임팩트로 우선순위 → 실험으로 검증. "느낌"이 아니라 데이터로 정당화되고 되돌릴 수 있는 변경을 좋은 과제로 본다.


페이타랩 — Backend Developer (경력직)

키워드 1: 결제·주문 도메인의 트랜잭션 설계와 정합성 보장

Q1. 사용자가 결제 버튼을 두 번 눌렀거나 앱이 타임아웃 후 재시도해서 주문 생성과 결제 요청이 중복으로 들어왔습니다. 백엔드에서 중복 결제를 막고 주문을 하나만 만들려면 어떤 키로 멱등성을 보장하고, DB에는 어떤 제약을 두겠습니까?

A. 클라이언트가 요청을 시작하는 한 시점에 UUID로 Idempotency-Key를 1회 생성해 헤더로 보내고, 재시도는 같은 키를 재사용한다(IETF idempotency-key 헤더 초안 패턴). 서버는 (user_id, idempotency_key) 단위로 처리 기록을 남겨 첫 요청은 처리 후 결과(주문 ID·상태·응답 바디)를 저장하고, 동일 키 재요청은 저장된 응답을 그대로 돌려준다(같은 결과 재현). 처리 중이면 409/짧은 락으로 동시 중복을 막는다. DB 최종 안전망은 UNIQUE 제약이다. 멱등 레코드 테이블에 UNIQUE(idempotency_key), 결제엔 PG 주문번호(merchant_uid)로 UNIQUE를 걸어 이중 승인을 원천 차단하고, 주문엔 클라이언트 요청 식별자에 유니크를 둔다. 유니크 위반은 예외로 흘리지 말고 기존 주문을 조회해 정상 응답으로 흡수한다. PG 자체도 동일 merchant_uid 재승인을 거부하므로, 애플리케이션 멱등 + DB 제약 + PG 멱등의 3중 방어가 된다.

Q2. 주문 상태를 CREATED → PAYING → PAID → ACCEPTED → COMPLETED/REFUNDED처럼 운영할 때, 결제는 성공했는데 매장 접수가 실패하거나 알림 발송이 실패하는 부분 실패가 발생합니다. 정합성을 무엇으로 정의하고, 어떤 보상/재처리 전략을 택하겠습니까?

A. 정합성을 "최종적으로 돈의 흐름(결제·환불 원장)과 주문 상태가 모순되지 않는 것" 으로 정의한다. 강한 정합성이 필요한 구간과 결과적 정합성을 허용하는 구간을 분리하는 게 핵심이다.

  • 결제 승인 결과를 주문 PAID로 반영하는 것은 강한 정합성 구간 — 같은 로컬 DB 트랜잭션에서 커밋하고, 후속 이벤트는 Outbox 테이블에 함께 기록해 원자성을 보장(microservices.io Transactional Outbox).
  • 매장 접수·알림은 결과적 정합성 — Saga로 처리한다. 접수는 Outbox 이벤트를 at-least-once로 발행하고 소비자는 멱등 처리·재시도. 정해진 시간 안에 접수가 불가능하면 보상 트랜잭션(자동 환불) 으로 REFUNDED까지 몰아 돈과 상태를 다시 일치시킨다.
  • 알림 실패는 결제·주문 정합에 영향이 없는 부가 작업이므로 별도 재시도 큐로 분리해 핵심 흐름을 막지 않는다. 상태 전이는 허용 선행 상태를 명시한 단조 전이로만 허용한다.

Q3. Kafka 기반으로 결제완료 이벤트를 발행해 여러 마이크로서비스가 구독할 때, 이벤트가 중복되거나 순서가 뒤바뀌면서 주문 상태가 되돌아가거나 잘못 환불되는 사고가 발생합니다. DB·메시지·코드 레벨에서의 방어를 모두 설명해 주세요.

A. 세 레벨로 방어한다.

  • 코드 레벨: 주문 상태에 state_rank(결제승인=10, 접수=20, 완료=30…)를 부여하고, 이벤트에도 비즈니스 순서를 실어 들어온 rank가 현재보다 낮거나 같으면 무시한다. 이 단조 규칙이 순서 역전으로 인한 상태 되돌림을 흡수한다. 환불은 "결제 승인이 존재하고 아직 미환불"임을 확인한 뒤에만 수행한다.
  • 메시지 레벨: 파티션 키를 order_id 로 지정해 같은 주문의 이벤트가 한 파티션에서 순서를 유지하게 한다. 브로커는 at-least-once이므로 중복을 전제하고, 소비자는 처리한 event_id를 Inbox에 기록해 dedup한다.
  • DB 레벨: 조건부 업데이트(UPDATE ... WHERE state_rank < :incoming) 또는 낙관적 락(@Version)으로 경쟁 갱신을 막고, Inbox의 UNIQUE(event_id)를 최종 안전망으로 둔다. 세 레벨이 겹쳐야 재처리/재발행 상황에서도 안전하다.

키워드 2: 트래픽 피크 환경에서의 캐시·검색·성능 최적화

Q1. 매장 상세(메뉴·옵션·가격·품절여부)를 조회하는 API가 느립니다. Redis 캐시를 넣는다면 캐시 키, TTL, 무효화 전략을 어떻게 설계하겠습니까? 특히 품절은 거의 실시간에 가깝게 반영돼야 합니다.

A. 데이터를 정적(메뉴·옵션·가격)초고빈도 변동(품절·영업중) 으로 나눠 다르게 다룬다.

  • 정적: store:{storeId}:menu 키에 JSON으로 캐시하고 TTL은 길게(예: 30분) 잡되, 시간 만료에 의존하지 않는다. 상점이 메뉴·가격을 바꾸는 쓰기 시점에 이벤트로 명시적 무효화(캐시 삭제 후 재적재, 사실상 write-through)를 해 stale을 최소화한다.
  • 품절: TTL 몇 초로는 실시간성이 부족하므로 캐시에 얹지 않고, store:{storeId}:soldout 같은 별도 키(set/hash) 를 두어 품절 변경 이벤트로 즉시 갱신한다. 조회 시 정적 캐시 위에 품절 상태를 오버레이 합성해 응답한다.
  • 스탬피드 방지: TTL에 지터를 주고, 캐시 미스 시 뮤텍스/싱글플라이트로 한 요청만 DB를 재적재하게 해 재생성 폭주를 막는다.

Q2. Elasticsearch로 매장·메뉴 검색을 할 때, 검색 결과의 인기순(최근 주문수) 정렬과 영업중·거리·품절 제외 같은 필터를 동시에 만족해야 합니다. 어떤 인덱싱·집계·캐싱 전략을 택하겠습니까?

A. 정적 특성과 동적 특성을 분리해 설계한다.

  • 정적(매장명·카테고리·geo_point 좌표) 은 ES에 인덱싱하고, 거리는 geo_distance 필터·정렬로 처리한다. 검색어는 자동완성용 별도 인덱스(edge n-gram)를 둔다.
  • 동적: 영업중 여부는 update-by-query로 주기 반영하고, 품절처럼 초고빈도로 바뀌는 값은 ES에 넣지 않고 Redis/애플리케이션 후처리에서 최종 제외한다. ES는 후보 축소에만 쓰고 마지막 품절·영업 판정은 앱이 책임진다.
  • 인기순(최근 주문수): 실시간 정확 정렬은 부담이 크므로 배치/스트림으로 popularity_score 필드를 near-real-time 갱신하고, function_score로 관련도+인기+거리를 결합 스코어링한다.
  • 캐싱: 자주 나오는 인기 쿼리 결과는 짧은 TTL로 캐시해 ES 부하를 낮춘다.

Q3. 피크 타임에 특정 인기 매장 메뉴 조회가 폭주하면서 Redis도 핫키가 되고, DB는 커넥션이 고갈됩니다. 어떤 식으로 부하를 제한하고 우아하게 실패시키겠습니까? 구체적인 지표와 제어 지점을 말해 주세요.

A. 리소스 격리와 빠른 실패로 대응한다.

  • 제어 지점(계층): 엔드포인트별 동시 처리 상한(bulkhead), 매장ID별 토큰 버킷 레이트리밋, 다운스트림별 타임아웃 + 서킷브레이커로 자원을 나눈다.
  • Redis 핫키: 특정 매장에 요청이 쏠리므로 애플리케이션 로컬(near) 캐시를 앞단에 둬 대부분을 인메모리로 흡수하고, 캐시 미스 폭주는 싱글플라이트(request coalescing) 로 매장당 1건만 원본을 조회하게 한다. 필요하면 핫키 복제로 단일 샤드 쏠림을 분산한다.
  • DB 커넥션 고갈: HikariCP의 커넥션 대기 큐/획득 타임아웃을 짧게 둬 무한 대기 대신 빠르게 실패시킨다.
  • 우아한 실패: 서킷 OPEN이면 마지막 캐시·기본 응답으로 폴백하고, 초과분은 429 + Retry-After로 반환한다.
  • 지표: p99 지연, 에러율, HikariCP active/pending, 서킷 상태, 캐시·핫키 히트율.

키워드 3: Kubernetes 기반 MSA 운영과 장애 대응

Q1. MSA에서 주문 서비스가 매장 서비스를 호출할 때, 타임아웃·재시도·서킷브레이커를 어떤 기준으로 잡겠습니까?

A. 먼저 호출 성격을 분류한다(사용자 동기 대기 vs 백그라운드 비동기).

  • 타임아웃: 다운스트림의 p99(또는 p99.9)에 여유를 더해 설정하고, 상위 API의 전체 지연 예산에서 역산한다. 중첩 호출은 바깥 타임아웃 > 안쪽 타임아웃이 되도록 예산을 배분하고, 무한 대기는 절대 두지 않는다.
  • 재시도: 멱등 연산에만 허용한다(GET, 또는 멱등키가 있는 쓰기). 지수 백오프 + 지터를 쓰고, 재시도 예산(전체 트래픽의 일정 %) 으로 상한을 둬 다운스트림이 흔들릴 때 재시도 폭풍이 장애를 증폭하지 않게 한다.
  • 서킷브레이커: 에러율·느린 호출 비율이 임계를 넘으면 OPEN, half-open으로 탐색 복구한다. 다운스트림별로 격리(bulkhead) 해 한 의존성 지연이 전체 스레드를 삼키지 않게 하고, OPEN 시 폴백(캐시·기본값·큐잉)을 정의한다.

Q2. Kubernetes에서 주문 서비스의 HPA를 CPU 기준으로만 두었더니, CPU는 낮은데 응답 지연이 커집니다. 어떤 메트릭으로 오토스케일링을 바꾸고, 어떤 실험으로 검증하겠습니까?

A. CPU가 낮은데 지연이 크면 CPU 외 병목이 원인일 가능성이 높다 — DB/Redis 지연, 커넥션 풀 고갈, 스레드·락 경합, 다운스트림 지연. 이런 경우 CPU 기반 HPA는 아예 스케일하지 않는다.

  • 메트릭 변경: Prometheus Adapter로 커스텀/외부 메트릭 기반 HPA를 붙여 in-flight 요청 수, RPS 대비 동시성, p99 지연, 커넥션 풀 사용률, 큐 길이 같은 실제 포화 신호로 스케일한다. 단, 스케일아웃이 오히려 DB 병목을 악화시킬 수 있으므로, 병목이 앱 계층인지 공유 자원(DB)인지 먼저 구분한다. DB가 병목이면 스케일이 아니라 커넥션 풀·쿼리·캐시를 고쳐야 한다.
  • 검증 실험: k6 등으로 부하 → 지연 → 인스턴스 수 관계를 측정하고, 한 번에 한 변수만 바꾼다. 후보 임계값을 카나리로 적용해 스케일 시 p99가 실제로 개선되는지, DB 부하가 터지지 않는지 확인한 뒤 전면 적용한다.

Q3. 배포 직후 결제 승인 콜백 처리율이 떨어지고, 일부 주문이 PAYING에 멈춥니다. 로그는 분산돼 있고 원인은 불명확합니다. 온콜 상황에서 30분 안에 고객 영향을 최소화하는 관점으로 어떤 순서로 조치하겠습니까?

A. 영향 차단을 원인 규명보다 먼저 한다.

  1. 관찰: 대시보드에서 콜백 성공률, Kafka consumer lag, PAYING 체류 시간, 5xx·에러율을 최근 배포 시각과 상관시켜 본다.
  2. 즉시 완화: 증상 시작이 배포 시점과 일치하면 즉시 롤백/이전 버전으로 트래픽 전환(카나리면 카나리 비중 0으로). 이것이 고객 영향을 가장 빨리 끊는다.
  3. 정체 주문 구제: PAYING은 PG엔 이미 승인됐을 수 있으므로, 재조정 배치로 PG 상태를 조회해 승인=PAID 반영, 미승인=취소로 정리한다. 모든 조치는 멱등키로 이중청구를 막는다.
  4. 원인 규명(영향 차단 후): trace_id로 분산 로그를 상관 분석해 소비자 예외·역직렬화(스키마 변경) 오류·DB 락 여부를 확인한다.
  5. 커뮤니케이션·사후: 인시던트 채널로 상태를 공유하고, 포스트모템으로 타임아웃·롤백 게이트·알람 임계를 데이터 기반으로 재조정한다.

NHN 커머스 — 백엔드 개발자 (경력직)

키워드 1: 고도몰 기반 멀티테넌트 플랫폼 백엔드 설계 및 운영

Q1. 고도몰처럼 여러 상점이 하나의 백엔드를 공유하는 환경에서 특정 상점만 주문 생성이 느려지는 장애가 발생했습니다. 공통 코드 변경 없이도 원인을 좁히고, 테넌트 단위로 영향도를 격리하며, 재발 방지까지 포함해 어떤 순서로 대응하시겠습니까?

A. 관측→격리→수정 3단계로 진행한다. 관측: 주문 API의 p95/p99를 tenant_id 태그로 분해해 느린 테넌트를 특정하고, 분산 트레이싱으로 지연이 DB 쿼리인지 외부 연동인지 스팬 단위로 좁힌다. 한 테넌트만 느리면 대개 데이터 스큐(그 상점 주문·상품 로우가 비대해 인덱스 선택도·통계가 왜곡)이거나 그 테넌트만 타는 커스텀 설정/쿼리가 원인. 격리: 공통 코드는 그대로 두고 문제 테넌트에 레이트리밋·전용 커넥션 풀(벌크헤드)·별도 큐를 적용해 노이지 네이버가 다른 상점을 잠식하지 못하게 한다. 수정: EXPLAIN으로 실행계획을 확인해 통계 갱신·인덱스 보강, 필요 시 해당 테넌트만 샤드 분리. 재발 방지는 테넌트별 SLO 대시보드와 이상 탐지 알람, 그리고 부하 테스트에 데이터 스큐 시나리오를 포함하는 것으로 마무리한다.

Q2. 플랫폼 레거시에서 주문, 결제, 배송 같은 핵심 도메인의 테이블이 비대하고 결합도가 높을 때, 리스크를 최소화하면서 도메인 경계를 재정의하고 점진적으로 리팩터링하는 실행 계획을 제시해 주세요. 단, 기능 출시를 멈출 수 없습니다.

A. 스트랭글러 패턴이 기본이다. 먼저 트래픽 큰 API의 응답 스키마·에러코드·정렬·페이징 규약을 계약(contract)으로 고정하고, 소비자 주도 계약 테스트(CDC, Pact)를 CI 게이트로 걸어 리팩터링이 소비자를 깨지 않음을 보장한다. 이후 경계는 데이터가 아니라 트랜잭션·일관성 요구가 다른 지점을 기준으로 긋고 — 주문·결제·배송은 가용성/정합성 요구가 다르다 — 결합이 가장 약한 조각부터 추출한다. 이행 중에는 파사드를 앞에 두고 신규 경로를 피처 플래그로 소수 트래픽·특정 테넌트에만 켜 카나리한다. 데이터는 빅뱅 마이그레이션 대신 이중 쓰기나 CDC/Outbox로 신구 저장소를 병행하고, 대조·검증이 끝나면 읽기를 전환한다. 각 단계는 되돌릴 수 있는 작은 스텝으로 쪼개 롤백 가능성을 유지한다. 기능 출시는 트렁크에서 계속하되 미완성 경로는 플래그로 가려 배포와 릴리즈를 분리한다.

Q3. 고객사별 커스터마이징 요구가 강한 솔루션에서 코어 기능과 확장 기능(플러그인, 스크립트, 설정)의 경계를 어떻게 설계하시겠습니까? 잘못 설계하면 코어가 오염되는데, 이를 막는 구체적 장치를 말해 주세요.

A. 코어에는 절대 깨지면 안 되는 불변식만 둔다 — 주문 상태 머신의 허용 전이, 결제 승인 불변조건(중복 승인 금지·금액 일치), 재고 차감 규칙. 확장은 이 불변식을 우회하지 못하도록 명시적 확장 포인트(hook·이벤트·전략 인터페이스)로만 개방한다. 오염 방지 장치는 다음과 같다. ① 확장은 코어 내부 테이블·엔티티에 직접 쓰지 못하고 공개 API·도메인 이벤트로만 상호작용한다(안티부패 계층). ② 확장 실행은 샌드박스·타임아웃·리소스 쿼터로 격리해 한 스크립트가 코어 스레드를 삼키지 못하게 한다. ③ 확장 포인트마다 버전드 계약과 계약 테스트를 두고 코어 변경 시 하위 호환을 강제한다. ④ 확장이 던진 예외·부작용은 코어 트랜잭션과 분리해 실패해도 주문 정합성이 유지되게 한다. ⑤ 고객사 요구는 코어 코드 수정이 아니라 설정·정책 데이터로 흡수한다. 결국 확장은 코어의 손님이지 공동 소유자가 아니다.

키워드 2: 주문·결제·재고 정합성과 분산 트랜잭션 설계

Q1. 주문 생성 요청이 들어오면 재고 예약, 쿠폰 사용 처리, PG 결제 승인, 주문 확정이 필요합니다. PG 승인 성공 후 우리 서버가 타임아웃으로 응답을 못 한 경우, 중복 결제와 중복 주문을 막는 설계를 DB 스키마 수준과 API 수준에서 각각 어떻게 하시겠습니까?

A. API 수준: 클라이언트가 Idempotency-Key를 보내게 강제하고, "주문 시도(order attempt)" 리소스에 (tenant_id, idempotency_key) UNIQUE 제약을 둔다. 같은 키 재요청은 저장된 결과를 그대로 반환하고, 진행 중이면 409/대기로 처리한다. PG 승인 시 우리 주문번호를 함께 넘겨 PG가 중복 승인을 거부하게 한다. DB 스키마 수준: 결제 레코드에 (pg, pg_tx_id) UNIQUE, 주문에 (tenant_id, idempotency_key) UNIQUE를 최종 안전망으로 둔다. 타임아웃의 본질은 "우리가 모르는 성공"이므로, 승인 요청 전 PENDING 레코드로 의도를 먼저 남기고, 응답 유실 시 PG 조회 API로 실제 상태를 재확인(reconciliation)해 승인됐으면 확정, 아니면 취소한다. 결제와 주문 확정은 로컬 트랜잭션으로 묶지 말고 Outbox로 이벤트화해 at-least-once + 소비자 멱등으로 처리하고, 끝내 미확정된 승인은 배치로 자동 취소·환불해 이중 청구를 막는다.

Q2. 재고 차감은 경쟁이 심합니다. 한 상품 옵션에 초당 수백 건 주문이 몰릴 때, 재고를 정확히 지키면서도 성능을 확보하는 방법을 2가지 제시하고, 각 방법의 실패 모드와 운영상 주의점을 말해 주세요.

A. 방법 1 — 조건부 원자 UPDATE: UPDATE inventory SET qty = qty - :n WHERE sku = :sku AND qty >= :n, 영향 행이 0이면 재고 부족으로 거절한다. DB 행 락이 직렬화하므로 오버셀이 없다. 실패 모드는 인기 SKU 단일 행에 락 경합이 몰려 지연·데드락이 나고 커넥션 점유가 늘어나는 것. 주의점은 트랜잭션을 짧게, 재시도는 백오프로, 핫 SKU는 재고를 N개 버킷으로 쪼개 락을 분산하는 것이다. 방법 2 — Redis 선점: Lua로 원자 체크-차감(DECRBY)해 애플리케이션 앞단에서 재고를 잡고 DB엔 비동기 반영한다. 처리량이 크게 오른다. 실패 모드는 Redis-DB 정합성 이탈과 장애 시 유실·이중 차감. 주의점은 Lua로 원자성 보장, 확정 실패 시 보상 복원, DB를 최종 원장으로 두고 주기적 대조다. 정확성이 최우선이면 DB 원자 UPDATE를 진실로, 극한 성능이 필요하면 Redis를 앞단 필터로 두고 결국 DB로 정산한다.

Q3. 사가 기반으로 주문 파이프라인을 만들었는데, 보상 트랜잭션이 또 실패하는 보상 실패가 발생합니다. 이 상황에서 정합성과 운영 가능성을 동시에 만족시키는 설계를 어떻게 하시겠습니까?

A. 보상을 "즉시 성공"이 아니라 "최종적으로 수렴"하는 것으로 설계한다. 각 보상 액션을 커맨드로 영속화(command log)하고 지수 백오프 재시도 + 소비자 멱등으로 at-least-once 처리한다. 보상 스텝 자체가 멱등해야 재시도가 안전하다 — 예컨대 환불은 refund_id 기준으로 한 번만 반영한다. 재시도가 한계에 도달하면 dead-letter 큐로 보내고 운영자에게 알람을 띄운다. 자동 수렴 실패에는 반드시 사람이 개입할 경로를 남긴다. 상태는 Saga 오케스트레이터가 단계별로 영속화해 프로세스가 죽어도 마지막 지점부터 재개할 수 있게(crash-safe) 한다. 되돌릴 수 없는 스텝(외부 발송 등)은 pivot 이후로 배치하거나 실행 전 예약 방식으로 두어 보상 부담을 줄인다. 관측성 측면에서는 미결 보상 건수와 최고 지연을 대시보드·알람으로 노출해 "조용히 쌓이는 불일치"를 막고, 정합성은 최종적으로 원장 대조(reconciliation) 배치로 검증한다.

키워드 3: 트래픽 피크·성능·관측성 중심의 운영형 백엔드 역량

Q1. 갑자기 주문 조회 API p99가 300ms에서 3초로 튀었습니다. CPU는 여유인데 DB 커넥션 풀이 바닥나고 있습니다. 이때 단기 완화, 원인 분석, 구조적 개선을 각각 무엇으로 하시겠습니까?

A. 단기 완화: 문제 엔드포인트에 임시 레이트리밋·서킷브레이커로 폭주를 줄이고, 커넥션 획득 타임아웃을 짧게 잡아 스레드가 무한 대기하지 않고 빠르게 실패하게 한다. 풀 크기를 무작정 키우면 경합이 DB로 전가되므로 신중히 조정한다. 원인 분석: CPU 여유인데 풀이 바닥이면 커넥션을 오래 점유한다는 뜻이다. HikariCP의 active/pending 지표와 트레이스로 "느린 쿼리"인지 "커넥션 반납 지연"인지 구분한다. 흔한 범인은 인덱스 미스로 느려진 조회, 트랜잭션 안의 외부 API 호출, OSIV로 뷰 렌더링까지 커넥션을 붙잡는 경우, 혹은 특정 테넌트의 무거운 쿼리다. slow query log와 EXPLAIN으로 확정한다. 구조적 개선: 트랜잭션 경계 축소와 OSIV off, 조회는 읽기 복제본·캐시로 분리, 인덱스·쿼리 튜닝, 커넥션 풀을 도메인별 벌크헤드로 격리, 그리고 p99·풀 사용률 SLO 알람과 부하 테스트로 재발을 막는다.

Q2. 대규모 테넌트에서만 특정 배치(정산, 주문 마감)가 낮 시간 트래픽과 충돌해 지연을 유발합니다. 배치를 없앨 수 없을 때, 온라인 트랜잭션을 보호하는 설계를 제시해 주세요.

A. 자원 격리가 핵심이다. 배치는 전용 워커·큐로 분리해 온라인 API와 커넥션 풀·스레드 풀을 공유하지 않게 하고(벌크헤드), DB도 조회성 정산·집계는 리드 레플리카에서 돌려 주 DB의 쓰기 경합을 피한다. 배치는 큰 트랜잭션 하나가 아니라 청크 단위(keyset 페이징)로 쪼개 커밋해 락 보유 시간과 언두/리두 부담을 줄이고 중간 실패 시 재개할 수 있게 한다. 실행은 트래픽 저점으로 스케줄링하되, 불가피하게 낮에 돌면 레이트리밋·동시성 제한으로 배치가 쓰는 IOPS·커넥션에 상한을 둔다. 우선순위 큐로 온라인 요청이 배치보다 항상 먼저 자원을 얻게 하고, DB 레벨에선 배치 세션에 낮은 우선순위·리소스 그룹을 부여한다. 락 충돌을 피하려면 배치가 온라인과 같은 핫 로우를 동시에 갱신하지 않도록 파티션·시간을 분리한다. 마지막으로 배치 지연·랙과 온라인 p99를 한 대시보드에서 함께 보며 상호 영향을 관측한다.

Q3. 장애 대응 중 로그는 많은데 원인 추적이 안 되는 상황이 반복됩니다. 커머스 플랫폼에서 효과적인 관측성 표준을 새로 만든다면, 어떤 필드를 강제하고 어떤 대시보드를 기본으로 두시겠습니까?

A. 강제 필드: 모든 요청·로그·이벤트에 trace_id(+span_id)를 전파해 서비스·큐를 가로질러 상관관계를 잇고, tenant_id·mall_id·user_id·order_id 같은 도메인 키를 구조화 필드로 필수화한다. 여기에 서비스명·버전(배포 식별)·환경·결과 코드·지연(ms)을 표준 스키마로 통일한다. 로그는 사람이 읽는 문자열이 아니라 구조화(JSON) 로그로 남겨 tenant_id로 필터·집계할 수 있게 한다. 카디널리티가 큰 order_id는 로그·트레이스에만 싣고, 지표(metric) 라벨은 테넌트·엔드포인트·코드처럼 제한된 것만 붙여 카디널리티 폭발을 피한다. 기본 대시보드: ① 서비스·엔드포인트별 RED(Rate·Error·Duration, p50/p95/p99)와 SLO·에러버짓, ② 테넌트별 상위 지연·에러 랭킹(노이지 네이버 탐지), ③ DB 커넥션 풀·slow query·락 대기, ④ 큐/컨슈머 랙과 미결 보상·Outbox 적체, ⑤ 배포 마커를 지표에 오버레이한 릴리즈-회귀 상관. 원칙은 로그·지표·트레이스를 trace_id와 도메인 키로 서로 점프하게 엮는 것이다.


인크로스 — Back-end개발 (인턴)

키워드 1: 애드테크 백엔드: 이벤트 트래킹·어트리뷰션·부정 트래픽 대응

Q1. 광고 클릭/전환 이벤트를 수집할 때 중복 제거와 유실 방지를 동시에 만족시키려면 백엔드에서 어떤 키와 처리 방식을 쓰시겠어요?

A. 두 요구는 상충하는 듯 보이지만 "at-least-once 수집 + 멱등 저장 = effectively-once" 조합으로 함께 만족시킵니다. 먼저 유실 방지를 위해 클라이언트가 event_id(UUID)를 직접 생성하고, ack를 못 받으면 같은 event_id로 재전송합니다. Kafka로 넣는다면 프로듀서 acks=all + enable.idempotence=true로 브로커 유실·중복을 막습니다. 중복 제거는 event_id를 1차 멱등 키로 쓰되, 유실 대비 event_id가 없거나 신뢰가 낮은 소스는 (device_id, campaign_id, event_type, event_ts를 초 단위로 버킷팅) 조합 키를 보조로 씁니다. 저장 계층은 이 키에 DB UNIQUE 제약 + upsert로 두 번째 쓰기를 no-op 처리해 최종 안전망을 확보합니다. 최근 룩백 구간은 Redis SET에 TTL을 걸어 O(1)로 판별하고, 장기 보관은 DB 제약으로 이중화합니다. 재시도가 몇 번 와도 결과가 한 번 반영되는 것이 핵심입니다.

Q2. 라스트 클릭 어트리뷰션을 구현한다고 했을 때, 전환 이벤트가 들어오면 어떤 데이터 조회/저장 구조로 룩백 윈도우 7일 내 마지막 클릭을 빠르게 찾겠습니까? 트래픽이 커질수록 어떤 병목이 생기고 어떻게 완화하나요?

A. 핵심은 전환 조회를 전체 클릭 로그 스캔이 아니라 키 단위 조회로 만드는 것입니다. 클릭 이벤트를 conversion_key = (device_id/user_id, advertiser_id)로 묶어 저장하고, 전환이 오면 그 키로 최근 7일 내 event_ts가 가장 큰(마지막) 클릭 하나만 찾습니다. 핫 스토어로는 Redis Sorted Set(멤버=click_id, score=event_ts)을 키별로 두고 ZREVRANGEBYSCORE ... LIMIT 0 1로 윈도우 내 최신 클릭을 O(log N)에 조회하며, TTL 7일로 만료 데이터를 자동 정리합니다. 원본 클릭 로그는 별도 append-only 저장소에 남겨 감사·재집계에 씁니다. 병목: 트래픽이 커지면 키 카디널리티 폭증과 조회/쓰기 QPS가 문제입니다. 완화책은 ① 키 해시 기반 샤딩으로 부하 분산, ② TTL로 룩백 밖 데이터 자동 삭제해 메모리 상한 유지, ③ 굳이 전체 이력이 필요 없으면 키별 last-click만 upsert로 갱신해 데이터 크기를 상수로 억제하는 것입니다.

Q3. 봇/어뷰징 트래픽이 섞이면 광고 성과 지표가 무너집니다. 실시간 수집 파이프라인에서 IVT 의심 이벤트를 낮은 지연으로 표시/격리하려면 어떤 특징량과 아키텍처를 쓰시겠어요? 오탐/미탐을 어떻게 통제하나요?

A. 2계층 구조로 지연과 정확도를 분리합니다. ① 실시간 규칙 필터(저지연): 동일 IP·UA의 초당 반복 클릭, 데이터센터 IP 대역, referer/accept-language 헤더 이상, 노출→클릭 시간차가 비현실적으로 짧음 등 명백한 특징량을 Redis 슬라이딩 윈도우 카운터로 검사해 삭제가 아니라 suspected 플래그만 붙여 격리합니다(파이프라인을 막지 않음). ② 배치 통계 검증(후행·고정확): 디바이스별 CTR 분포 이상, 전환 패턴, IP 집중도 등을 사후에 재판정합니다. 이는 IAB/MRC의 GIVT(알려진 봇·크롤러·데이터센터, 공개 봇 리스트로 필터)와 SIVT(정교한 부정) 구분과 같은 맥락입니다. 오탐/미탐 통제: 실시간 단계는 오탐을 줄이려 임계값을 보수적으로 잡고 원본을 보존해 사후 복구가 가능하게 합니다. 미탐은 배치가 잡습니다. precision/recall과 격리율을 대시보드로 모니터링하며 임계값을 데이터로 튜닝하고, 알려진 정상 봇은 화이트리스트로 제외합니다.

키워드 2: 대용량 데이터 파이프라인: Kafka 기반 분산 처리·정합성·재처리 설계

Q1. Kafka 토픽을 설계할 때 파티션 키를 뭘로 잡겠습니까? 캠페인 기준 집계와 사용자 기준 어트리뷰션 요구가 동시에 있으면 어떻게 하죠?

A. Kafka는 파티션 내부에서만 순서를 보장하므로, 파티션 키는 "어떤 순서를 지켜야 하는가"로 결정합니다. 어트리뷰션은 한 사용자의 클릭→전환 이벤트 순서가 정확해야 하므로 원천 토픽은 user_id/device_id를 파티션 키로 잡습니다. 이러면 같은 사용자의 이벤트가 한 파티션에 모여 순서가 보장되고, 사용자 카디널리티가 높아 부하도 자연스럽게 분산됩니다. 캠페인 집계는 순서와 무관한 합산이라 파티션 키에 묶을 필요가 없습니다. 필요하면 스트림 처리 단계에서 campaign_id로 **재파티셔닝(repartition)**하거나 집계 전용 토픽으로 re-key해 캠페인별로 모읍니다. 이렇게 원천은 user 키, downstream은 campaign 키로 나누면 두 요구를 모두 만족합니다. 주의할 점은 대형 캠페인으로 인한 **핫 파티션(skew)**인데, user 키를 쓰면 특정 캠페인이 몰려도 여러 파티션에 흩어져 skew가 완화됩니다.

Q2. 컨슈머가 일시적으로 느려지면서 랙이 쌓일 때, 처리량을 올리는 방법 말고 데이터 정합성을 유지하면서 회복하는 운영 전략을 설명해 주세요.

A. 정합성의 핵심은 오프셋 커밋 시점입니다. enable.auto.commit=false로 자동 커밋을 끄고, DB write가 성공한 뒤에 수동으로 오프셋을 커밋합니다. 이러면 처리 전 크래시가 나도 재시작 시 미처리 메시지를 다시 읽어 유실이 없습니다(at-least-once). 대신 재처리로 인한 중복이 생기므로 저장 계층을 멱등하게 설계합니다 — 멱등 키에 upsert 또는 UNIQUE 제약을 걸어 같은 이벤트가 두 번 반영되지 않게 합니다. 이 조합이 사실상 exactly-once 효과를 냅니다. 운영 측면에서는 컨슈머를 수평 확장하되 한 컨슈머 그룹의 유효 병렬도는 파티션 수를 넘지 못한다는 제약을 인지하고, 계속 실패하는 poison 메시지는 **DLQ(dead-letter topic)**로 격리해 전체 랙을 막지 않게 합니다. max.poll.records·처리 배치 크기를 조정하고, 다운스트림 부하는 backpressure로 조절해 랙을 안정적으로 소진시킵니다.

Q3. 집계 로직에 버그가 있어 지난 3일치 데이터를 재처리해야 합니다. 운영 중인 시스템에서 안전하게 재처리하는 절차를 단계별로 말해보세요.

A. 결과를 덮어쓰지 않는 블루/그린 재처리로 합니다. ① 수정한 집계 로직에 rule_version을 부여합니다. ② Kafka 원천 토픽의 retention 안에 원본 이벤트가 append-only로 남아 있으므로, offsetsForTimes로 3일 전 타임스탬프 오프셋을 찾아 별도 컨슈머 그룹으로 재소비합니다(운영 중인 실시간 소비는 그대로 두어 영향 없음). ③ 결과 테이블에 rule_version 컬럼을 두고 새 버전 결과를 기존 데이터와 병렬로 백필합니다. ④ 두 버전을 diff·샘플 검증해 새 버전이 맞는지 확인한 뒤, 서빙 쿼리를 새 버전으로 cut-over합니다. 문제가 발견되면 이전 rule_version으로 즉시 롤백합니다. 이 방식의 전제는 원천 이벤트가 불변 로그로 보존되어 언제든 재현 가능하다는 점이며, 검증이 끝날 때까지 사용자에게는 항상 안정적인 이전 결과가 노출되어 무중단으로 안전하게 재처리됩니다.

키워드 3: Node.js 기반 API·BI·AI 연동: 내부 사용자 중심의 제품화

Q1. Node.js로 광고 리포트 API를 만든다면, 느린 쿼리가 발생할 때 API를 어떻게 보호하나요? 단순 캐시 말고도요.

A. Node.js는 이벤트 루프가 단일 스레드라, 무거운 집계가 동기 경로에 있으면 그 요청뿐 아니라 전체 서버가 블로킹됩니다. 따라서 ① 시간이 오래 걸리는 집계는 작업 큐(BullMQ 등)로 오프로드하고 202 Acceptedjob_id를 즉시 반환한 뒤, 완료 시 폴링/webhook으로 결과를 조회하는 비동기 구조로 분리합니다. ② 동기로 남는 쿼리에는 DB statement_timeout과 요청 타임아웃을 걸어 오래 매달리지 않게 하고, 반복 실패하는 의존성은 서킷브레이커로 차단합니다. ③ 동시 실행 수 제한(bulkhead)·rate limit으로 느린 쿼리가 커넥션 풀과 이벤트 루프를 고갈시키지 못하게 격리합니다. ④ 자주 쓰는 지표는 사전 집계(pre-aggregation) 테이블로 만들어 실시간 무거운 쿼리 자체를 없애고, 결과는 커서 기반 페이지네이션으로 잘라 응답 크기를 제한합니다. CPU 집약 로직이 불가피하면 worker_threads로 이벤트 루프에서 떼어냅니다.

Q2. Superset을 BI로 쓴다고 할 때, 광고주별로 자기 데이터만 보이게 하면서도 운영팀은 전체를 봐야 합니다. 데이터셋, 권한, 행 단위 보안을 어떻게 설계하실 건가요?

A. 기반은 공통 fact 테이블에 advertiser_id를 일관되게 포함시키는 것입니다. 그 위에 **Superset의 Row Level Security(RLS)**를 적용합니다. 광고주용 역할(role)에는 advertiser_id = <로그인 사용자에 매핑된 광고주> 조건이 자동 주입되는 RLS 필터를 걸어, 어떤 대시보드·차트를 열어도 SQL 단에서 자기 행만 필터링되게 합니다. 반면 운영팀 역할에는 RLS 필터를 적용하지 않아 전체를 조회합니다. 사용자↔광고주 매핑과 역할 부여로 접근을 통제하고, 대시보드·데이터셋 소유권도 역할 기준으로 나눕니다. 방어를 한 겹 더 두려면 **DB 자체의 Row Security Policy(예: PostgreSQL)**로 이중화해, BI 애플리케이션 설정 실수가 있어도 DB가 최종적으로 타 광고주 행을 막게 합니다. 원칙은 최소권한이며, 필터 조건을 화면단이 아니라 쿼리·DB단에 박아 우회를 불가능하게 하는 것이 핵심입니다.

Q3. LLM/Agent를 붙여 캠페인 성과 요약과 개선안 제안을 자동화한다면, 데이터 유출/환각/권한 문제를 어떻게 통제하실 건가요?

A. 원칙은 LLM을 신뢰 경계 밖에 두고, 원천 데이터에 직접 접근시키지 않는 것입니다. LLM은 백엔드가 정의한 Tool/API만 호출하게 하고(예: get_campaign_report(period, metrics)), 그 API 안에서 서버가 호출자 세션 기준으로 권한을 재확인합니다. 권한 통제: advertiser_id 같은 범위 파라미터를 LLM이 바꾸지 못하게 서버가 세션에서 강제 주입하고, RLS로 데이터를 필터링해 광고주가 남의 데이터를 요약하게 할 수 없게 합니다. 환각 통제: LLM이 수치를 지어내지 못하도록, 응답은 반드시 Tool이 반환한 실제 값을 근거(grounding)로만 서술하게 하고 출력의 수치가 원본과 일치하는지 검증합니다. 유출 통제: 툴 응답의 PII는 마스킹하고, 사용자 입력을 통한 프롬프트 인젝션에 대비해 입력·출력을 필터링합니다. 모든 호출은 감사 로그(누가·무엇을·언제)로 남겨 추적 가능하게 하며, 자동 제안은 참고용으로 표기해 최종 판단은 사람이 하도록 합니다.


넵튠(Neptune) — 솔루션개발실 백엔드개발자 (경력직)

키워드 1: 광고 수익화 DSP/SSP 백엔드 아키텍처와 트래픽 처리 설계

Q1. 광고 요청이 초당 수천 건 들어오는 상황에서 Go 기반 백엔드로 입찰 요청 처리 API를 설계해야 한다면 어떤 구성으로 만들겠습니까? 응답 지연시간과 장애 격리를 어떻게 확보할지 설명해 주세요.

A. RTB는 SSP가 주는 응답 예산(보통 수십~100ms대)을 초과하면 그 입찰은 경매에서 그냥 버려지므로, 전체 데드라인을 먼저 고정하고 하위 단계에 예산을 배분한다. 요청 경로를 수신·검증→타깃팅 판단→외부 연동(데이터 보강/DSP fan-out)→응답 조립으로 나누고, 각 단계에 개별 타임아웃을 건다. Go에서는 요청 진입 시 context.WithTimeout으로 데드라인을 만들고 모든 goroutine·다운스트림 호출에 context를 전파해, 데드라인이 지나면 즉시 취소·반환한다. 병렬 외부 호출은 errgroup으로 묶되, 요청당 goroutine 폭증을 막기 위해 세마포어/워커 풀로 동시성 상한을 둔다. 부분 실패는 허용 — 일부 응답이 늦으면 그때까지 모인 최선의 입찰만 반환하고, 실패한 의존성은 서킷브레이커로 차단해 no-bid로 폴백한다. 커넥션 풀 재사용, sync.Pool로 할당·GC 압력 감소, pprof로 상시 프로파일링을 병행한다.

Q2. 입찰 요청에서 내부 캠페인 필터링 결과는 1초 이내에 판단해야 하고, DB 조회가 병목이 됩니다. 이때 캐시를 어떻게 설계하고, 어떤 데이터는 캐시하면 안 되는지, 캐시 무효화는 어떻게 하겠습니까?

A. 조회 빈도는 높고 변경은 느린 데이터를 캐시 대상으로 삼는다. 캠페인 메타데이터, 정적 타깃팅 규칙, 크리에이티브 정보는 프로세스 로컬 캐시(TTL 있는 in-memory)를 L1로 두어 Redis 왕복조차 줄이고, 여러 인스턴스가 공유하는 L2로 Redis를 둔다. 캐시하면 안 되는 것은 초 단위로 변하고 부정확하면 손해가 나는 상태값 — 예산 소진량·페이싱 카운터, 노출/빈도 캡처럼 매 노출 갱신되는 값, PII다. 이런 값은 스냅샷 캐시 대신 Redis INCR 같은 원자 카운터로 실시간 반영해 예산 초과 집행을 막는다. 무효화는 두 겹으로 — 기본은 TTL로 eventual consistency를 두고, 캠페인 수정 시 pub/sub 이벤트로 즉시 무효화하거나 버전 스탬프를 갱신한다. 만료 순간 다수 요청이 동시에 DB로 몰리는 stampede는 singleflight와 TTL 지터, refresh-ahead로 막고, 미스가 잦은 키는 negative caching을 둔다.

Q3. 실시간 광고 수익화 시스템에서 장애가 발생했을 때, 응답 성공률은 유지되지만 수익이 줄어드는 장애와 응답은 느려지지만 수익은 유지되는 장애 중 무엇이 더 위험하다고 보십니까? 판단 기준과 대응 전략을 설명해 주세요.

A. 조용한 수익 손실 장애가 더 위험하다. 5xx나 지연 알람을 건드리지 않아 기술 대시보드는 정상으로 보이고, 그동안 매출이 지속적으로 새며, 발견이 늦어 손실이 누적되기 때문이다. 느린 장애는 오히려 자기 신고형이다 — p95 지연 알람이 뜨고 SSP 타임아웃으로 경매에서 탈락해 문제가 즉시 드러난다. 판단 기준은 매출 영향의 크기·탐지 가능성·되돌릴 수 있는지·영향 범위(blast radius) 네 가지다. 대응 전략의 핵심은 비즈니스 지표를 일급 SLI로 계측하는 것 — 입찰률·낙찰률(win rate)·필률(fill rate)·eCPM·분당 매출을 기술 지표와 나란히 두고, 지표 간 비율(예: QPS 대비 낙찰액)에 이상 탐지를 걸어 조용한 장애를 잡는다. 배포마다 카나리와 기준 버전을 비교하고, 매출·펀널 지표에 가드레일 임계치를 두어 자동 롤백으로 연결한다.

키워드 2: 다중 데이터베이스·레거시 혼재 환경에서의 스키마/쿼리/이관 설계

Q1. 광고 캠페인과 성과 데이터를 저장하는 테이블이 점점 커져 조회가 느려졌습니다. 어떤 기준으로 인덱스를 추가하고, 언제 테이블 분리나 파티셔닝을 고려하겠습니까?

A. 먼저 EXPLAIN ANALYZE로 실제 실행 계획을 보고, 어떤 WHERE 조건과 JOIN 키가 반복되는지, 어디서 풀스캔·정렬이 터지는지 확인한다. 인덱스는 선택도가 높은 조건과 조인 키에 걸고, 복합 인덱스는 등가 조건→범위 조건 순으로 leftmost-prefix를 지키게 설계한다. 필요한 컬럼만 인덱스에 포함하는 커버링 인덱스로 테이블 룩업을 없애되, 성과 테이블처럼 write가 많은 곳은 인덱스 과다가 쓰기 비용을 키우므로 절제한다. 성과 데이터는 시계열이므로 날짜 기준 RANGE 파티셔닝을 검토한다 — 파티션 프루닝으로 조회 범위를 줄이고, 보관기간이 지난 데이터는 DROP PARTITION으로 싸게 지우며, 파티션별 인덱스가 작아 메모리에 올라간다. 파티셔닝·분리 시점의 신호는 단일 인덱스가 메모리에 안 들어가거나, VACUUM/재구축 등 유지보수가 너무 느려지거나, 대량 삭제가 필요할 때다. 조회는 최근 핫 데이터와 집계·아카이브 콜드 데이터를 분리하고, 리포팅용 롤업(사전 집계) 테이블을 별도로 둔다.

Q2. MySQL과 NoSQL을 함께 쓰는 시스템에서, 어떤 데이터를 MySQL에 두고 어떤 데이터를 NoSQL에 둘지 판단 기준을 설명해 보세요. 넵튠의 광고/수익화 백엔드에 맞춰 예를 들어주세요.

A. 기준은 접근 패턴·정합성 요구·쓰기 볼륨·스키마 유연성이다(Kleppmann이 정리한 데이터 모델 선택 관점). 거래적 정합성과 감사가 필요한 데이터는 MySQL에 둔다 — 광고주 계정, 캠페인·예산 설정, 과금·정산 원장은 ACID·제약·조인이 필요하고, 돈은 절대 eventual consistency로 유실되면 안 되기 때문이다. 반대로 쓰기량이 폭증하고 append 위주이며 키 기반 단순 조회인 데이터는 NoSQL로 뺀다 — 노출·클릭 이벤트 로그, 실시간 카운트, 세션·빈도 캡(Redis), 타깃팅 세그먼트/유저 프로필(키-값)이 그 예다. 실무 원칙은 참조 무결성이 생명인 데이터를 결과적 일관성 저장소에 두지 않는 것이다. 노출 카운트는 NoSQL에서 근사치로 빠르게 집계하되, 과금에 쓰는 확정 수치는 배치로 RDB 원장에 **멱등 적재·정합성 대사(reconciliation)**를 거쳐 확정한다. 즉 속도가 필요한 집계는 NoSQL, 돈으로 귀결되는 진실은 MySQL이 소유한다.

Q3. 레거시 서비스가 Node.js로 되어 있고, 새로운 기능부터 Golang으로 전환해야 합니다. 무중단에 가깝게 전환하려면 어떤 순서로 접근하시겠습니까?

A. 스트랭글러 패턴(Martin Fowler) 으로 점진 전환한다. ① 먼저 API Gateway/리버스 프록시를 앞단에 두어 클라이언트가 라우팅 변화를 모르게 하는 파사드를 만든다. ② 경계가 명확하고 위험·트래픽이 낮은 엔드포인트부터 Go로 새로 구현하고, 게이트웨이에서 그 경로만 Go로 라우팅한다. ③ 데이터는 이중 쓰기로 인한 불일치를 피하기 위해 단일 진실원본 DB를 공유하거나, 스키마가 갈리면 anti-corruption layer와 이벤트로 연동한다. ④ 실제 트래픽을 Go로 섀도잉(dark launch) 해 응답을 서빙하지 않고 Node 결과와 비교, 동등성(parity)을 검증한다. ⑤ 검증되면 카나리로 비율을 점진 증가시키고 에러율·지연·비즈니스 지표에 자동 롤백 게이트를 건다. ⑥ 계약 테스트로 API 하위 호환을 강제하고, Node와 동일한 지표·로그를 심어 관측성 parity를 확보한다. ⑦ 커버리지가 늘면 Node 경로를 순차 폐기한다. 문제가 생기면 게이트웨이 라우팅을 되돌리는 것이 즉각적인 탈출구가 된다.

키워드 3: 운영 안정성, 관측성, 보안 운영 협업 능력

Q1. 배포 후 광고 전환율이 갑자기 떨어졌지만 서버 에러는 보이지 않습니다. 어떤 지표를 먼저 확인하고, 어떤 순서로 원인을 좁혀가겠습니까?

A. 기술 지표와 비즈니스 지표를 분리해서 본다. 에러율·p50/p95/p99 지연·처리량이 정상인데 전환율만 떨어졌다면 크래시가 아니라 로직·데이터 회귀일 가능성이 높다. 순서는 ① 배포 시점과 하락 시점의 시간 상관 확인 — 변경이 하락 구간과 겹치면 유력 원인이다. ② 펀널 분해 — 요청→입찰→낙찰→노출→클릭→전환 중 어느 단계가 꺾였는지 본다. 낙찰이 줄었는지, 전환 트래킹 픽셀이 안 터지는지에 따라 방향이 갈린다. ③ 응답 내용 diff — 응답이 성공(2xx)이지만 잘못된 크리에이티브·빈 타깃팅·통화/입찰가 오류를 담고 있는 조용한 장애인지 확인한다. ④ 특정 캠페인·크리에이티브·지역·디바이스로 세그먼트 좁히기. ⑤ 의존성 응답, 피처 플래그, 설정 변경 점검, 카나리와 기준 버전 비교. 배포와 강하게 상관되면 먼저 롤백하고 원인은 사후 분석한다. 실제 수요 감소인지, 클라이언트 측 전환 트래킹 파이프라인 파손인지도 구분해야 한다.

Q2. CI/CD와 모니터링을 구축할 때, 넵튠 같은 광고 수익화 백엔드에서 반드시 넣어야 하는 안전장치는 무엇이라고 생각하십니까?

A. 핵심은 점진 배포(카나리/블루그린) + 자동 롤백이다. 일부 트래픽만 새 버전에 보내고, 에러율뿐 아니라 p95/p99 지연과 비즈니스 가드레일 지표(분당 매출·낙찰률·필률·eCPM) 임계치를 함께 감시해 넘으면 자동 롤백한다 — 조용한 수익 손실은 기술 알람을 안 건드리므로 비즈니스 지표를 게이트에 넣어야 한다. 배포와 릴리스를 분리하는 피처 플래그와 kill switch, 진행 중인 입찰을 흘려보내는 graceful shutdown과 readiness probe도 필수다. CI 게이트에는 테스트·계약 테스트·스키마 호환성 검사·자동 카나리 분석을 건다. DB 마이그레이션은 expand-contract로 하위 호환·멱등하게. 관측성은 RED/USE 지표·분산 트레이싱·비즈니스 KPI 대시보드와 이상 탐지 알람을 갖추고, 파이프라인에 부하·soak 테스트를 포함한다. 배포마다 버전 태깅으로 원인 상관과 롤백을 빠르게 하고, 과금 정합성을 위한 대사(reconciliation) 잡을 상시 돌린다.

Q3. 사내 보안 운영 협업을 백엔드 개발자가 왜 알아야 하는지, 그리고 광고/솔루션 서비스에서 개발자가 직접 고려해야 할 보안 이슈는 무엇인지 설명해 주세요.

A. 애드테크는 시스템을 통해 돈이 직접 흐르므로 보안 사고가 곧 금전 손실이다 — 계정 탈취로 예산이 소진되고, API 오남용으로 입찰·데이터가 스크래핑/스푸핑되며, 데이터 유출은 규제·배상으로 이어진다. 그래서 보안은 기능 뒤에 붙이는 게 아니라 설계 단계에서 심어야 하고(shift-left), 이는 개발자의 책임이다. 개발자가 직접 오너십을 갖는 이슈: 인증·인가(최소권한, 스코프된 API 키/OAuth, 멀티 광고주 테넌트 격리), 입력 검증(인젝션, 크리에이티브 URL 처리의 SSRF), 레이트리밋과 부정 트래픽(IVT) 탐지(IAB/MRC의 GIVT/SIVT 기준으로 이상 CTR·전환 탐지, 중복 제거), 시크릿 관리(하드코딩 금지, KMS/Vault), PII 보호(전송·저장 암호화, 최소 수집, 접근 감사 — 개인정보보호법/GDPR), 안전한 광고 렌더링(광고 마크업 XSS·멀버타이징 방지). 협업 측면에선 보안팀과 위협 모델링, CI에 SAST/DAST, 사고 대응 런북 공유가 필요하다.


아이알큐더스 — 글로벌 금융 IPO 데이터 파이프라인 및 백엔드 개발자 (경력직)

키워드 1: IPO 공시·IR·청약 데이터의 실시간 수집, 정제, 표준화 품질

Q1. 국내외 IPO 공시나 IR 자료를 수집할 때 HTML 구조 변경이나 PDF 포맷 차이 때문에 수집 실패가 자주 발생합니다. 이런 환경에서 수집 안정성과 데이터 정확도를 동시에 확보하려면 어떤 수집·정제 전략을 세우시겠습니까?

A. 소스를 변경 위험도로 분류한다. 구조가 안정적이고 계약이 명시된 소스(DART OpenAPI, SEC EDGAR의 정형 파일)는 API/XBRL 우선으로 붙고, HTML·PDF 스크래핑은 최후 수단으로 둔다. 변경 가능성이 높은 소스는 관대한(tolerant) 파싱 — 단일 셀렉터가 아니라 여러 후보 규칙(구조·라벨·정규식·좌표 기반)을 우선순위로 탐색하고, 하나가 깨져도 폴백하게 한다. 정확도는 파이프라인이 아니라 검증 게이트로 지킨다: ① 필수 필드·타입·값 범위 스키마 검증, ② 직전 스냅샷 대비 급변·결측 이상치 탐지, ③ 임계 초과분은 자동 반영 대신 운영자 검수 큐로 격리. 완전 자동화가 아니라 자동 수집 + 이상치 탐지 + 사람 검수 조합이 핵심이다. 원본 HTML/PDF는 스냅샷 보존해 규칙 수정 후 재처리 가능하게 하고, 구조 변경은 수집 성공률·파싱 confidence 지표로 조기 경보한다.

Q2. IPO 정보는 거래소 공고, 증권신고서, 증권사 배포 자료, IR 자료에서 표기 방식이 다를 수 있습니다. 이런 이질적인 데이터를 하나의 내부 표준 스키마로 통합할 때 어떤 모델링 원칙을 적용하시겠습니까?

A. 원천 사실과 표준 엔터티를 계층으로 분리한다. 수집한 값은 소스·수집시각·원문 위치(provenance)와 함께 raw 레이어에 무손실 보존하고, 이를 정규화한 결과만 canonical 레이어에 적재한다. 표준 모델은 도메인을 안정적인 엔터티로 분해한다 — 기업(Company), 공모이벤트(IPO Event), 일정(청약·환불·상장일 등 Schedule), 공모조건(밴드·확정가·물량 등 OfferingTerms). 표기 이질성은 정규화 규칙으로 흡수한다: 통화·단위·날짜 포맷 표준화, 기업은 엔터티 해소(entity resolution) 로 종목코드·CIK·사업자번호를 매핑해 동일성 판정. 핵심 원칙은 원천값 보존 + 재현 가능한 변환이다. 표준값은 항상 원천으로 되짚을 수 있어야(lineage) 하고, 소스 간 충돌 시 신뢰도 우선순위(공식 공시 > 배포자료)로 채택하되 폐기하지 않는다. 이렇게 하면 규칙이 바뀌어도 raw에서 재표준화만 하면 되어 사후 정정과 감사에 강하다.

Q3. 실시간 청약 경쟁률 같은 데이터는 사용자에게 매우 민감하게 노출되며, 지연이나 오표시는 서비스 신뢰도에 직접 타격을 줍니다. 데이터가 분 단위로 변하고 일부 소스는 실패하거나 역전송이 발생할 수 있는 상황에서, 정확도와 최신성의 균형을 어떻게 설계하시겠습니까?

A. 데이터를 온도로 분리한다. 빠르게 변하는 청약 경쟁률은 짧은 주기 증분 수집, 기업·일정 같은 정적 데이터는 긴 주기의 검증형 수집으로 다룬다. 모든 관측치에 수집시각과 소스 발생시각·단조 증가 시퀀스를 붙여, 역전송(늦게 온 과거 값)은 소스시각 기준 stale로 판정해 폐기하고 최신 시각만 반영(source-time last-writer-wins)한다. 소스 실패 시엔 화면을 비우지 않고 마지막 확인값 + 기준시각(as-of) 을 함께 노출해 최신성 저하를 사용자에게 정직하게 드러낸다. 정확도와 최신성은 값의 성격으로 구분한다: 경쟁률처럼 계속 바뀌는 값은 잠정치로 최신성을 우선하되 라벨을 명시하고, 확정 공모가·배정 결과처럼 되돌릴 수 없는 값은 여러 소스 교차검증 후 확정으로만 노출한다. 오표시가 신뢰에 직접 타격을 주므로, 이상 급변은 서빙 전에 이상치 탐지로 한 번 걸러 오염 전파를 막는다.

키워드 2: 금융 데이터 백엔드 아키텍처와 멀티소스 연동 설계

Q1. 여러 국가와 여러 데이터 소스를 통합하는 IPO 데이터 백엔드를 설계한다고 할 때, 어떤 컴포넌트로 나누고 어떤 책임을 각각 부여하시겠습니까?

A. 수집·표준화·저장·제공 4계층으로 나눈다. 수집 계층은 소스별 어댑터를 두어 스케줄링·인증·레이트리밋을 캡슐화하고, 핵심은 소스별 실패 격리(bulkhead) — 한 국가·한 소스의 장애가 전체 파이프라인을 멈추지 못하게 한다. 표준화 계층은 형식 변환, 중복 제거, 엔터티 해소, 스키마 검증을 담당해 canonical 모델을 만든다. 저장 계층은 raw(무손실·append-only)와 canonical을 분리 보관하고 시각 기반 버전을 남겨 재처리·감사를 지원한다. 제공 계층은 읽기 최적화 뷰와 목적별 DTO, 캐시를 두어 클라이언트에 응답한다. 계층 간은 큐/이벤트로 비동기 결합해 수집 지연이 제공 지연으로 번지지 않게 하고, 각 단계는 재시도·멱등 소비로 at-least-once를 안전하게 흡수한다. 국가별 차이(공시 구조, 시간대, 통화, 휴장일)는 수집·표준화 계층의 소스별 설정으로 흡수하고, 상위 계층은 표준 모델만 보게 해 확장 시 어댑터 추가만으로 대응한다.

Q2. 외부 API를 연동할 때 상대 시스템의 응답 지연, 레이트 리밋, 스키마 변경이 잦다면 어떻게 운영하시겠습니까?

A. 호출 회복력을 기본 장착한다: 타임아웃, 지수 백오프+지터 재시도(멱등 호출에 한정), 서킷 브레이커, 그리고 stale-while-revalidate 캐시로 상대 장애를 흡수한다. 레이트리밋은 클라이언트 측 토큰버킷으로 선제 제어하고, 429Retry-After를 존중하며, 요청을 큐로 평탄화한다. 우선순위가 낮은 호출(과거분 백필, 보조 지표)은 비동기·배치화해 실시간 경로의 커넥션을 지킨다. 스키마 변경에는 엄격한 파싱 대신 관대한 리더(tolerant reader) — 모르는 필드는 무시, 결측은 기본값으로 처리해 사소한 변경에 파이프라인이 죽지 않게 한다. 대신 필수 필드 검증에 실패하면 격리 큐로 quarantine하고, 응답 스키마 지표를 관측해 계약 변경을 조기 감지·알람한다. 소스별 격리 스레드풀/커넥션풀(bulkhead) 로 한 상대의 지연이 전체 스레드를 삼키지 못하게 한다. 이렇게 회복력·백프레셔·격리를 조합해 불안정한 외부 의존 위에서도 예측 가능한 SLO를 유지한다.

Q3. 아이알큐더스처럼 데이터 상품을 제공하는 조직에서, 수집 처리 로직과 제공 API가 강하게 결합되면 어떤 문제가 생기고 이를 어떻게 풀겠습니까?

A. 강결합의 문제는 변경 속도 불일치다. 크롤러가 소스 구조 변경에 맞춰 자주 바뀌는데, 그 스키마가 제공 API에 직접 노출되면 수집 변경이 API 계약을 깨뜨리고 소비자까지 파급된다. 또한 수집 배포가 API 배포를 강제해 릴리스가 서로 묶이고, 소스별 세부(원시 필드명·포맷)가 외부로 새어 상품 추상화가 무너진다. 해법은 부패방지 계층(anti-corruption layer) 이다: 원천 데이터를 내부 표준 모델로 변환한 뒤, 제공 API는 서비스 레이어에서 목적별 DTO로 다시 조립한다. 이러면 수집 단계의 스키마 변경은 표준화 매핑에서 흡수되어 외부 계약까지 새지 않는다. API는 독립적으로 버전을 관리하고 하위 호환(필드 추가 optional, 삭제 금지)을 지키며, 소비자 주도 계약 테스트(CDC) 를 CI 게이트로 강제해 제공 스키마 변경이 소비자를 깨는지 사전 검출한다. 수집·표준·제공이 각자 속도로 진화하도록 경계를 계약으로 못박는 것이 핵심이다.

키워드 3: 비정형 금융 문서, 어닝콜, 음성 데이터의 구조화와 도메인 이해

Q1. 어닝콜 스크립트나 IR 문서에서 숫자, 가이던스, 리스크 문장을 추출해 서비스에 쓰려면 어떤 처리 단계를 거치시겠습니까?

A. 먼저 문서를 구조 단위로 분해한다 — 제목, 본문, 표, Q&A 섹션을 레이아웃 인식으로 세그먼트화하고, 표는 셀 관계를 보존하는 전용 파서로 처리한다. 숫자 추출은 단순 정규식이 아니라 문맥 인식으로 한다: 값과 함께 단위·통화·기간(FY/Q), 증감 방향, 비교 기준(YoY vs QoQ, 가이던스 vs 실적)을 하나의 구조로 묶어 뽑아야 "매출 12% 증가"의 의미가 깨지지 않는다. 가이던스·리스크 문장은 분류(가이던스/리스크/일반)와 추출을 결합하되, 각 항목에 원문 근거 스팬(문서·위치) 을 붙여 서비스에서 출처를 되짚을 수 있게 한다. 투자 판단에 쓰이는 민감 항목은 confidence 낮은 건을 운영자 검수 큐로 보내 자동 반영을 막는다. 저장은 추출값 + provenance를 함께 남겨 감사·재현이 가능하게 하고, 문서 버전(정정 공시)이 오면 재처리한다. 즉 구조 분해 → 문맥 기반 추출 → 근거 결합 → 검증/검수의 파이프라인이다.

Q2. 컨퍼런스콜 음성 데이터를 STT와 화자 분리로 처리할 때, 실무에서 가장 먼저 마주치는 품질 문제는 무엇이며 어떻게 대응하시겠습니까?

A. 가장 먼저 부딪히는 건 도메인 용어·고유명사·숫자의 오인식이다. 회사명·티커·재무 전문용어·수치가 범용 STT에서 자주 틀리고, 비원어민 발음과 통화 잡음·크로스토크가 이를 악화시킨다. 대응은 세 축이다: ① 도메인 사전으로 사용자 정의 vocabulary/바이어스를 추가해 기업명·용어 인식률을 올리고, ② 숫자·고유명사에는 후처리 교정 규칙(사전 매칭·정규화)으로 STT 오류를 보정하며, ③ 화자 분리는 참석자 명단이 사전에 알려진 어닝콜 특성을 활용해 발표자·애널리스트를 고정(speaker enrollment/roster 매핑) 한다. 발표 구간은 화자가 안정적이라 정확도가 높지만 Q&A 구간의 발언 겹침이 최대 난점이므로, 겹침 구간과 confidence 낮은 구간은 사람 검수로 넘긴다. 숫자·가이던스처럼 오류 비용이 큰 부분은 원음성 타임스탬프를 남겨 되짚기 가능하게 하고, 최종적으로 STT 산출물도 근거(오디오 위치)와 함께 저장해 하류의 추출·요약이 출처를 유지하도록 한다.

Q3. LLM을 이용해 IPO·IR 데이터를 요약하거나 질의응답 형태로 제공할 때, 환각과 출처 왜곡 문제를 어떻게 통제하시겠습니까?

A. LLM을 지식 생성기가 아니라 '근거 기반 재서술 엔진' 으로 쓴다. 구조는 RAG — 검색된 원문 조각만 컨텍스트로 주고, 모델은 그 근거 안에서만 답하도록 제약한다. 답변에는 반드시 출처 문서와 위치(문단·페이지·타임스탬프)를 인용하고, 인용이 실제 원문과 일치하는지 citation 검증을 거친다. 근거가 없으면 지어내지 말고 "확인 불가/근거 없음" 으로 응답하게 가드레일을 건다. 금융 데이터 특성상 숫자는 원문에서 그대로 인용하고 모델이 계산·추정하지 않게 하며, 필요한 산술은 결정론적 코드로 별도 수행한다. 운영에서는 groundedness/faithfulness 평가(답변이 근거에 실제로 지지되는지)를 자동 측정하고, temperature를 낮추며 구조화 출력으로 형식을 강제한다. 투자 판단·가이던스 해석처럼 위험이 큰 답변은 사람 검수를 필수 경로로 두어, 요약 결과도 항상 원 공시로 되짚어 검증할 수 있게 한다. 핵심은 검색된 근거 + 강제 인용 + 무근거 시 거부로 환각과 출처 왜곡을 구조적으로 차단하는 것이다.


CJ대한통운 — 선행개발 SW엔지니어 (경력직)

키워드 1: 디지털물류플랫폼 백엔드 아키텍처 설계 및 확장성

Q1. 물류 이벤트(집하 스캔, 허브 도착, 분류 완료, 배송 출발 등)가 초당 수만 건으로 들어오고, 동시에 고객/기사 앱에서 상태 조회 트래픽도 폭증합니다. Java/Spring 기반으로 '상태 조회 API'와 '이벤트 수집/처리'를 같은 DB(PostgreSQL)에 얹을지, CQRS/이벤트 기반으로 분리할지 설계를 제안해 주세요. 어디까지를 강한 정합성으로 보고 어디부터 최종 일관성으로 둘 건지도 기준을 말해 주세요.

A. 쓰기(이벤트 수집)와 읽기(상태 조회)는 부하 특성과 확장 축이 달라 CQRS로 분리합니다. 쓰기 경로는 스캔 이벤트를 append-only 불변 로그로 적재하고 Kafka로 발행하되, 운송장번호를 파티션 키로 두어 동일 화물의 순서를 보장합니다. 읽기 경로는 그 로그를 소비해 갱신하는 조회 전용 read model(현재 상태·타임라인)로 서빙합니다. 정합성 경계는 도메인 성격으로 가릅니다. 금전·계약이 걸린 정산 확정·배차 확정은 강한 정합성으로 단일 트랜잭션·UNIQUE 제약 안에서 처리하고, 고객·기사에게 보이는 배송 상태 조회는 최종 일관성을 허용합니다(수백 ms 지연 용인). read model이 뒤처져도 로그가 진실의 원천이라 언제든 재구성 가능합니다. 초기에는 같은 PostgreSQL 안에서 쓰기 테이블과 조회용 테이블을 논리 분리만 하다가, 조회 트래픽이 커지면 읽기 복제본·검색엔진으로 물리 분리해 점진적으로 확장합니다.

Q2. 당신이 선행개발자로서 신규 서비스(예: 라스트마일 최적화 기능)를 플랫폼에 붙이려고 합니다. 기존 레거시(배차/정산/배송)와의 결합이 강한 상황에서, '큰 개편 없이도' 점진적으로 아키텍처를 개선하는 6개월 로드맵을 제시해 주세요. 기술 부채를 무엇부터 갚을지 우선순위 기준도 포함하세요.

A. 스트랭글러 무화과(Strangler Fig) 로 레거시를 한 번에 걷어내지 않고 신규 서비스가 점진 대체하게 합니다. 1~4주: 경계 정의 — 이벤트·호출 관계를 매핑해 라스트마일 도메인의 seam을 찾고, 레거시 앞에 파사드/게이트웨이를 두어 트래픽을 가로챌 지점을 확보합니다. 2~3개월: 위험 낮은 분리 — 부작용 없는 조회·읽기 기능부터 신규 서비스로 떼어내고, 레거시 DB 직접 참조는 anti-corruption layer로 감싸 도메인 오염을 막습니다. 4~6개월: 트래픽 경로 개선 — 라스트마일 최적화를 카나리로 일부 물량만 신규 경로로 흘려 지표로 검증하고, 안정되면 비중을 늘립니다. 기술부채 우선순위 기준은 변경 빈도 × 장애 반경 × 결합도 — 자주 바뀌면서 터지면 파급이 큰 강결합 지점(예: 배차↔정산 테이블 공유)을 먼저 이벤트/API 계약으로 끊습니다. 각 단계는 반드시 롤백 가능하게 유지합니다.

Q3. 핵심 알고리즘(최적화/분류) 팀이 '배차 결과를 300ms 내로 응답해야 한다'고 요구합니다. 하지만 알고리즘은 최악 케이스에서 3초가 걸립니다. 백엔드 API 레이어에서 어떤 계약(Contract)과 폴백 전략을 설계하겠습니까? 단순히 타임아웃을 걸겠다는 답은 금지입니다.

A. 계약을 '300ms 내 반드시 반환하는 최소 보장 결과 + 이후 비동기로 도착하는 개선 결과' 로 재정의합니다. 동기 응답은 SLA 준수를 최우선으로, 정밀 최적화 대신 캐시된 이전 배차 결과나 휴리스틱 근사해(최근접 거점, greedy 할당)를 300ms 안에 돌려주고, 응답에 result_type=approx와 개선 결과를 받을 경로(콜백/이벤트/job id)를 명시합니다. 전체 알고리즘은 백그라운드에서 계속 계산해 완료되면 최적해로 교체하고 이벤트로 통지합니다. 방어 장치로 동기 호출엔 300ms 타임아웃 + 서킷브레이커를 두되, 열렸을 때의 행동은 실패가 아니라 근사해 폴백입니다. 벌크헤드로 알고리즘 팀 지연이 상태 조회 스레드를 삼키지 않게 격리합니다. SLA는 문서가 아니라 양팀 합의 계약(응답 스키마 + p99 목표 + 폴백 시멘틱) 으로 못박고, 근사해와 최적해의 괴리율을 지표로 모니터링해 휴리스틱 품질을 지속 개선합니다.

키워드 2: 물류 도메인 이벤트/상태전이 모델링과 데이터 정합성

Q1. 배송상태를 '집하완료→허브도착→분류완료→배송출발→배송완료'로 관리합니다. 그런데 현장에서는 '배송완료' 스캔이 먼저 들어온 뒤 10분 후 '분류완료' 이벤트가 늦게 들어오기도 합니다. 이때 고객에게 노출되는 '현재 상태'와 내부 감사/정산을 위한 '이력'은 어떻게 설계하겠습니까?

A. 이력(StatusHistory, append-only)현재 상태(Current Snapshot) 를 분리합니다. 모든 스캔은 발생시각(event_time)·수신시각(ingest_time)과 함께 이력에 전부 append합니다 — 늦게 온 분류완료도 버리지 않습니다. 반면 고객에게 보이는 현재 상태는 단조(monotonic) 전이 규칙을 따릅니다. 상태에 순서 랭크(집하10·허브도착20·분류완료30·배송출발40·배송완료50)를 부여해, 이미 배송완료(50)에 도달한 뒤 온 분류완료(30)는 과거 이벤트로 간주해 스냅샷을 되돌리지 않습니다(no-op, 이력에만 기록). 즉 현재 상태 = 이력 중 event_time 기준으로 도달한 최고 랭크입니다. 내부 감사·정산은 스냅샷이 아니라 이력을 event_time으로 재정렬해 구간별 소요·SLA 위반을 계산하므로 늦은 이벤트도 정확히 반영됩니다. 이렇게 하면 고객 UX는 상태가 뒤로 튀지 않아 안정적이고, 회계는 손실 없는 원천 로그로 정확성을 확보합니다.

Q2. 스캔 이벤트가 중복으로 들어오거나(네트워크 재전송), 누락되거나(오프라인), 잘못된 운송장에 찍히는 경우가 있습니다. 멱등성 키를 무엇으로 잡고, 누락/오류를 어떻게 복구하며, 그 과정이 정산(비용/패널티)에 미치는 영향까지 설계해 보세요.

A. 멱등성 키를 (운송장번호 + 이벤트타입 + 발생시각 + 거점/단말ID + 시퀀스) 조합으로 잡고, 이력 테이블에 이 키로 UNIQUE 제약 + upsert(ON CONFLICT DO NOTHING) 를 걸어 재전송된 중복 스캔이 삽입되지 않게 합니다. 단말이 단조 증가 시퀀스를 부여하면 중복·역전 판별이 쉽습니다. 누락(오프라인) 은 두 방향으로 복구합니다. 단말이 오프라인 큐에 쌓았다가 재연결 시 재전송(멱등하므로 안전)하고, 상태 랭크 gap 탐지로 '분류완료 없이 배송출발'처럼 건너뛴 전이를 이상으로 표시합니다. 오적재(잘못된 운송장) 는 원본을 삭제하지 않고 정정 이벤트(reversal) 로 무효화한 뒤 올바른 건을 추가합니다(append-only 유지). 정산 영향: 비용·패널티는 확정된 이력에서 파생되므로 스캔이 정정되면 재계산이 아니라 조정 트랜잭션(adjustment) 으로 반영하고, 늦은/누락 이벤트가 패널티 계산을 흔들지 않도록 정산 마감창(cutoff) 을 두어 마감 이후 도착분은 다음 주기의 조정으로 넘깁니다.

Q3. PostgreSQL에서 운송장(Shipment) 테이블과 상태이력(StatusHistory) 테이블을 설계한다고 합시다. 조회는 '운송장 단건의 타임라인', '거점별 지연 TOP-N', '기사별 완료율'이 많고, 적재는 이벤트 스트림입니다. 인덱스/파티셔닝/정규화 수준을 어떻게 잡겠습니까?

A. StatusHistory 는 append 중심이라 정규화를 유지하고, 조회 패턴별 인덱스를 얹습니다. '운송장 단건 타임라인'용 (shipment_id, event_time DESC) 복합 인덱스, '거점별 지연 TOP-N'용 (hub_id, event_time), '기사별 완료율'용 (courier_id, status, event_time)입니다. 최신 상태는 이력을 매번 정렬하지 말고 Shipment 테이블에 현재 상태를 비정규화(캐시) 해 단건 조회를 O(1)로 만듭니다(쓰기 시 스냅샷 갱신). 대용량은 event_time 기준 range 파티셔닝(월/주 단위)으로 최신 파티션만 뜨겁게 유지하고 오래된 파티션은 압축·아카이브·detach합니다. 거점/기사 집계는 실시간 조회 대신 사전 집계(materialized view나 배치 롤업 테이블) 로 분리해 적재 경로와의 경합을 피합니다. 인덱스는 쓰기 비용을 늘리므로 초당 수만 건 적재 경로에는 최소한만 두고, 무거운 분석 인덱스는 읽기 복제본에 두어 OLTP와 OLAP를 분리합니다.

키워드 3: AI 기반 개발 생산성 향상과 안전한 거버넌스

Q1. 'AI 기반 개발 생산성'이 목표일 때, 우리 조직에 도입할 3가지 유스케이스를 고르고 각각의 성공지표(KPI)와 실패했을 때의 가드레일(보안/품질)을 제시해 주세요. 단, '코드 자동생성' 같은 포괄적 표현은 금지입니다.

A. 포괄적 '코드 자동생성' 대신 경계가 뚜렷한 3가지를 고릅니다. ① PR 리뷰 보조 — 아키텍처 규칙 위반, N+1 쿼리 가능성, 멱등성 누락을 사전 점검. KPI: 리뷰 리드타임 20% 단축·결함 유출률 감소. 가드레일: AI 지적은 참고 코멘트일 뿐 사람 승인 필수(자동 머지 금지). ② 테스트/픽스처 생성 — 상태전이·엣지 케이스 테스트 초안 자동화. KPI: 커버리지·경계 케이스 수 증가. 가드레일: 생성 테스트는 실패-통과 검증 후 채택하고 무의미한 assert를 걸러냅니다. ③ 운영 지식 검색(RAG) — 사내 런북·장애 이력 기반 온콜 보조. KPI: MTTR·1차 해결률. 가드레일: 답변에 출처(문서 링크) 강제, 근거 없으면 '모름' 반환, 민감 데이터는 인덱싱 제외. 공통으로 모든 유스케이스는 롤백 가능하게 도입하고, 정량 실패 시(예: 오탐률 급증) 자동 비활성화 스위치를 둡니다.

Q2. AI가 생성한 코드가 보안 취약점을 만들거나 라이선스 이슈를 유발할 수 있습니다. 선행개발자로서 'AI를 쓰되 안전하게 쓰는' 팀 규칙과 파이프라인을 어떻게 설계하겠습니까?

A. 프롬프트·컨텍스트 통제 / 산출물 검증 자동화 / 추적성 3축으로 규칙을 세웁니다. ① 입력 통제 — 고객·운송장 등 민감 데이터는 DLP 필터·마스킹으로 프롬프트 유입을 차단하고, 승인된 사내 모델·게이트웨이만 사용해 외부 SaaS로의 코드·PII 유출을 금지합니다. ② 산출물 검증 — AI가 생성한 코드도 사람 코드와 동일한 CI 게이트를 통과해야 머지됩니다: SAST, 의존성 취약점 스캔(SCA), 시크릿 스캐너, 라이선스 스캐너(GPL 등 카피레프트·비허용 라이선스 검출 시 차단), 테스트·리뷰 필수. ③ 추적성 — 어떤 산출물이 AI 보조로 나왔는지 커밋/PR에 태깅해 사후 감사와 리콜을 가능하게 합니다. 원칙적으로 AI는 제안자, 책임은 사람(작성자·리뷰어) 에게 귀속시키고 무검증 반영은 금지합니다. 취약점·라이선스 사고가 나면 포스트모템으로 프롬프트 가이드와 차단 룰을 갱신하는 피드백 루프를 둡니다.

Q3. 면접에서 30분 내로 '물류 상태조회 API' 미니 설계를 하고, 이어서 간단한 코딩 또는 SQL을 보게 될 수 있습니다. 본인이 그 자리에서 어떤 순서로 문제를 풀고, 어떤 산출물을 남길 건가요? (설계-구현-검증 순서와 체크리스트를 말해보세요.)

A. 정해진 순서로 산출물을 남깁니다. ① 요구사항 확정(약 5분) — SLO(조회 p99·가용성), 정합성 경계(어디까지 강한 정합성/최종 일관성), 트래픽(읽기:쓰기 비율·피크), 실패 모드(중복·늦은 도착·누락) 4가지를 못박습니다. ② API 스펙·데이터 모델(약 10분) — 상태조회 엔드포인트 계약(요청/응답·에러·페이지네이션)과 스냅샷 + 이력 분리 모델, 멱등성 키를 그립니다. ③ 확장·병목 설계(약 8분) — 조회용 복합 인덱스·파티셔닝, 캐시/read model, CQRS 분리 지점을 표시하고 트레이드오프를 말로 설명합니다. ④ 검증(약 5분) — 핵심 조회의 병목 쿼리를 EXPLAIN으로 점검하고, 멱등성·늦은 이벤트 처리를 간단한 SQL(UNIQUE upsert)이나 의사코드로 시연합니다. 남길 산출물은 API 계약서, ERD(스냅샷/이력), 인덱스·파티션 전략 메모, 실패모드-대응 표, 검증 쿼리입니다. 마지막 2분은 가정과 미해결 리스크를 명시해 무엇을 더 확인해야 하는지를 남깁니다.


같다(gatda Corp) — Database/Backend Engineer (경력직)

키워드 1: 환경자원 플랫폼의 도메인 모델링과 데이터 정합성 설계

Q1. 빼기 같은 환경자원 플랫폼에서 배출 신청, 수거 배차, 완료, 정산을 모두 하나의 테이블이나 단순 상태값으로 관리하면 어떤 문제가 생기고, 이를 어떻게 나누어 설계하시겠습니까?

A. 배출·배차·완료·정산은 변경 책임과 변경 주기가 모두 다릅니다. 하나의 넓은 테이블에 단일 status 컬럼으로 몰면 ① 정산 정정이 배출 신청 행까지 잠그는 락 경합, ② 하나의 상태값으로는 "수거 진행 중이면서 정산 대기" 같은 병렬 라이프사이클을 표현 못 함, ③ 스키마가 특정 도메인 변경마다 흔들려 배포 위험이 커집니다. 그래서 request(배출 신청)·fulfillment(수거 배차·완료)·settlement(정산) 를 별도 애그리거트로 분리하고, 각 도메인은 자기 상태 전이표를 가진 상태 머신으로 관리합니다. 신청 ID를 공유 키로 두되 소유권은 각 서비스에 둡니다. 상태 자체는 현재 스냅샷으로 유지하고, "언제 무엇이 왜 바뀌었는가"는 append-only 이벤트 로그에 남겨 감사·재구성이 가능하게 합니다. 정산처럼 금전이 걸린 영역은 별도 원장(ledger) 으로 두어 되돌림도 삭제가 아닌 조정 트랜잭션으로 처리합니다.

Q2. 지자체별로 수거 정책과 정산 규칙이 다르고, 같은 서비스라도 파트너사에 따라 수수료 계산 방식이 달라진다면 데이터 모델을 어떻게 확장 가능하게 설계하시겠습니까?

A. 핵심은 불변의 도메인 데이터와 자주 바뀌는 정책 데이터를 분리하는 것입니다. 배출 신청·수거 이력 같은 사실(fact)은 안정적이지만, 지자체·파트너별 요율은 수시로 바뀌므로 이를 코드에 하드코딩하면 배포 없이는 못 바꾸고 조건 분기가 폭발합니다. 정책을 데이터로 다뤄 rule_set(지자체·파트너·품목 스코프)과 rule_version(유효 기간·수수료 산식·파라미터)으로 모델링하고, 정책 엔진(지자체, 파트너, 적용 시점) 을 받아 해당 버전을 resolve합니다. 새 지자체 온보딩은 새 rule_set 행 추가로 끝나 확장에 스키마 변경이 필요 없습니다. 결정적으로, 각 거래에는 계산에 실제 적용한 rule_version을 고정 저장합니다. 정책이 나중에 바뀌어도 과거 거래는 저장된 버전으로만 재현되어 회계·감사 정합성이 유지되고, 규칙 변경은 신규 거래부터 적용됩니다. 정책 변경은 이력 테이블로 감사 가능하게 남깁니다.

Q3. 230만 가입자와 수백만 건의 데이터가 있는 환경에서 중복 신청, 중복 수거 처리, 이중 정산 같은 문제가 발생하지 않도록 데이터 정합성을 어떻게 보장하시겠습니까? 트랜잭션만으로 부족한 경우를 포함해서 답변해 주세요.

A. 먼저 클라이언트 요청에 idempotency key 를 부여하고, 서버는 (user_id, request_type, idempotency_key)유니크 제약을 걸어 중복 신청을 DB 레벨에서 원천 차단합니다. 재시도가 와도 두 번째는 유니크 위반으로 걸러 기존 결과를 반환하므로 애플리케이션 로직의 실수를 최종 안전망이 덮습니다. 트랜잭션만으로 부족한 경우는 처리가 여러 서비스·비동기 메시지·재시도에 걸쳐 있을 때입니다. 로컬 트랜잭션은 한 DB 안에서만 원자적이므로, 수거 완료→정산 생성처럼 경계를 넘는 흐름은 Outbox 패턴으로 이벤트를 신뢰성 있게 발행하고 소비자를 멱등하게 만듭니다. 이중 정산은 정산 원장에 settlement(request_id) 유니크 제약 + append-only로 막아, at-least-once 재전송이 와도 두 번째 삽입이 실패하게 합니다. 동시 수거 배차는 UPDATE ... WHERE status='REQUESTED' 같은 조건부 원자 갱신(영향 행 0이면 이미 처리됨)으로 경합을 흡수합니다. 즉 멱등 키·유니크 제약·멱등 소비자를 계층으로 쌓습니다.

키워드 2: 대용량 데이터 조회와 PostgreSQL 성능 튜닝, 그리고 데이터 성장 대응

Q1. 빼기 서비스에서 조회가 느린 화면이 있다고 할 때, 먼저 무엇을 보고 어디부터 원인을 분리해서 보시겠습니까?

A. 먼저 API 응답 시간, DB 쿼리 시간, 쿼리 횟수, 캐시 hit ratio 를 함께 보고 병목이 DB인지 애플리케이션인지 가릅니다. APM 분산 트레이스로 시간이 DB 스팬에서 소모되는지, 직렬화·외부 호출·GC에서 소모되는지 구분합니다. 쿼리 횟수가 화면 대비 비정상적으로 많으면 N+1 을 의심하고, 한두 쿼리가 무거우면 그 쿼리를 집는 순서로 좁힙니다. DB 쪽이면 PostgreSQL pg_stat_statements 로 총시간 상위 쿼리를 뽑고, 해당 쿼리에 EXPLAIN (ANALYZE, BUFFERS) 를 걸어 실제 실행 계획을 봅니다 — Seq Scan인지, 예상 행수와 실제 행수 괴리로 통계가 낡았는지, shared read가 많아 buffer가 안 맞는지, Sort/Hash가 work_mem을 넘겨 디스크로 쏟아지는지 확인합니다. 그다음에야 인덱스 추가·쿼리 재작성·캐시를 판단합니다. 즉 추측으로 인덱스를 넣지 않고, 트레이스→pg_stat_statements→EXPLAIN 순으로 데이터에 근거해 원인을 특정한 뒤 처방합니다.

Q2. PostgreSQL에서 인덱스를 많이 만들면 왜 항상 좋은 것이 아니며, 어떤 기준으로 인덱스를 설계하시겠습니까?

A. 인덱스는 조회를 빠르게 하지만 쓰기 비용과 저장 공간을 늘립니다. 모든 INSERT/UPDATE/DELETE가 관련 인덱스를 함께 갱신하므로, 신청·배차처럼 계속 갱신되는 테이블에 인덱스가 많으면 쓰기 지연이 커집니다. 특히 PostgreSQL은 갱신 시 인덱스된 컬럼이 바뀌면 HOT update 최적화가 깨져 모든 인덱스에 새 튜플 엔트리가 생기고, autovacuum 부담과 인덱스 bloat, 플래너의 계획 선택 혼란까지 유발합니다. 그래서 기준은 ① 실제 쿼리·조인·정렬 패턴에서 쓰이는가(안 쓰이면 제거), ② 선택도(카디널리티) 가 높아 이득이 큰가, ③ 복합 인덱스는 WHERE 등호 컬럼을 선두에 두고 범위/정렬 컬럼을 뒤로 배치했는가입니다. 특정 상태만 자주 조회하면 부분 인덱스(WHERE status='REQUESTED')로 크기를 줄이고, 커버링이 필요하면 INCLUDE 로 index-only scan을 노립니다. 후보 인덱스는 pg_stat_user_indexes 로 실사용을 확인하고 미사용 인덱스는 회수합니다.

Q3. 정산 정확성이 매우 중요한 서비스에서, 수백만 건의 기록을 대상으로 배치와 실시간 계산을 섞어야 한다면 어떤 기준으로 아키텍처를 나누고, 데이터 일관성을 어떻게 검증하시겠습니까?

A. 실시간 계산이 필요한 사용자 화면과 회계적 확정값이 필요한 배치를 분리하되, 계산 로직은 한 곳에서 재사용합니다. 사용자 화면의 예상 수수료·미리보기는 낮은 지연이 우선이고 약한 정합성이 허용되므로 실시간(온디맨드) 경로로, 실제 청구·지자체 정산 확정값은 정확성·감사가 우선이므로 배치로 계산해 원장에 확정합니다. 두 경로가 결과가 다르면 신뢰가 깨지므로 산식은 공통 라이브러리/도메인 서비스로 단일화하고, 배치는 그 순간의 입력 스냅샷(적용 rule_version·물량·요율) 을 함께 저장해 결정론적으로 재현 가능하게 합니다. 일관성 검증은 ① 배치 재실행 시 같은 입력→같은 결과인 재현성 검사, ② 원천(수거 완료 건수·중량) 합계와 정산 합계를 맞추는 정산 대사(reconciliation), ③ 음수·중복·미매칭 정산을 잡는 불변식 어서션으로 합니다. 실시간 값과 확정 값의 허용 오차를 모니터링해 산식 드리프트를 조기에 탐지하고, 확정 후 정정은 원본 보존 + 조정 트랜잭션으로만 반영합니다.

키워드 3: Monolith에서 MSA로의 점진적 분리, API 아키텍처, 그리고 운영 안정성

Q1. 모노리스 상태의 서비스를 MSA로 옮길 때, 가장 먼저 어떤 경계를 기준으로 분리하시겠습니까?

A. 도메인(비즈니스 능력) 경계를 기준으로 분리합니다. 사용자 인증, 배출 요청, 수거 운영, 정산 회계, 파트너 관리처럼 변경 이유와 변경 속도가 다른 영역을 DDD의 bounded context로 식별합니다. 기술 계층(컨트롤러/서비스/DAO)으로 자르면 하나의 기능 변경이 여러 서비스에 걸쳐 결합이 오히려 심해지므로 피합니다. 첫 분리 대상은 경계가 명확하고 결합이 낮으며 독립 배포 이득이 큰 것 — 예컨대 인증이나, 규칙 변경이 잦아 격리 가치가 큰 정산 같은 영역을 우선합니다. 절차는 빅뱅 재작성이 아니라 Strangler Fig 패턴으로, 신규·분리 대상 트래픽만 새 서비스로 점진적으로 돌리며 모노리스를 조금씩 걷어냅니다. 이때 데이터 소유권이 핵심입니다 — 각 서비스가 자기 데이터를 소유하고 공유 테이블 직접 접근을 금지하며, 다른 도메인 데이터는 API·이벤트로만 참조합니다. 분리 전 이 경계를 모듈러 모노리스로 먼저 정리해두면 실제 물리 분리 위험이 크게 줄어듭니다.

Q2. 서비스를 나눴는데 한 트랜잭션으로 처리되던 작업이 두 서비스로 분리되면서 정합성이 깨질 수 있습니다. 이 문제를 어떻게 설계적으로 풀겠습니까?

A. 분산 2PC 대신 Saga·이벤트 기반 최종 일관성·보상 트랜잭션을 조합합니다. 예를 들어 수거 완료 이벤트가 발생하면 정산 서비스가 정산을 생성하는 흐름을, 여러 로컬 트랜잭션의 연쇄로 보고 각 단계가 실패하면 보상 트랜잭션(정산 취소·환불 기록)으로 되돌립니다. 안무형(choreography)은 이벤트로 느슨하게 엮여 단순한 흐름에 좋고, 단계가 많고 순서·타임아웃 제어가 중요하면 오케스트레이션으로 흐름을 한 곳에서 관장해 가시성을 높입니다. 정합성의 관건은 이벤트가 유실 없이 딱 맞게 반영되는가인데, 로컬 DB 커밋과 메시지 발행을 원자적으로 묶는 Outbox(Transactional Outbox) 패턴으로 발행을 보장하고, 메시징은 at-least-once이므로 소비자를 멱등하게(처리 이벤트 ID 유니크 기록) 만들어 중복 정산을 막습니다. 사용자에겐 "정산 처리 중" 같은 중간 상태를 노출해 최종 일관성의 시차를 UX로 흡수하고, 미완결 Saga는 모니터링·타임아웃으로 잡아 수동/자동 보상합니다.

Q3. 빼기처럼 외부 이해관계자와 운영 변경이 많은 플랫폼에서, API 버전 관리와 DB 스키마 변경을 어떻게 배포 충돌 없이 운영하시겠습니까?

A. 원칙은 backward compatible한 단계적 배포입니다. API와 DB를 한 번에 뒤엎지 않고 expand-contract(parallel change) 로 나눕니다. DB는 먼저 새 컬럼/테이블을 nullable·기본값으로 추가(expand) 하고, 애플리케이션이 신·구 양쪽을 함께 쓰도록 배포한 뒤, 백필과 검증이 끝나면 옛 컬럼을 나중에 제거(contract) 합니다 — 컬럼 삭제·타입 변경·NOT NULL 즉시 부여 같은 파괴적 변경을 배포와 동시에 하지 않는 것이 핵심입니다. API는 새 필드는 optional로 추가, 기존 필드는 삭제 금지·deprecate만, 의미가 바뀌면 필드를 재활용하지 않고 새 필드를 추가합니다. 파괴적 변경이 불가피하면 /v2 처럼 버전을 병행 운영하며 구버전 소비자에게 마이그레이션 기간을 줍니다. 파서는 tolerant reader로 모르는 필드를 무시합니다. 배포는 소비자 주도 계약 테스트(CDC) 를 CI 게이트로 강제해 파트너·내부 소비자를 깨는지 사전 검출하고, 카나리 + 에러율·p99 기반 자동 롤백으로 무중단을 보장합니다.


원자료: [docs/04-prime-career-job-postings.md]·[docs/05-prime-career-model-answers.md]. 모든 사실은 1차자료로 교차검증(PLAN_INTERVIEW §6-quater). 회사 내부 사실은 지어내지 않았다.

면접 시나리오·트러블슈팅 — 종합·STAR