Files
llm-wiki/raw/interviews/operational-error-envelope-and-observability-foundation.md

5.1 KiB

title, source_type, status, related_branches, related_projects, tags, created, status_label
title source_type status related_branches related_projects tags created status_label
interview-prep / operational-error-envelope-and-observability-foundation interview-prep raw
feature-operational-error-observability-foundation
ca-skeleton
interview-prep
ca-skeleton
error-handling
observability
api-design
logging
security
mdc
testing
2026-06-01 collecting

interview-prep: operational-error-envelope-and-observability-foundation

Layer: raw/interviews/ — 면접 질문 원본 수집·연구 노트. 다듬어진 답변은 /interviewizewiki/interview/ 에 별도 작성.

Parent / 부모

질문 / Question

후보 질문 묶음 (실제 면접에서 받은 것 아님 — 본 작업에서 정직하게 도출):

  1. 응답 포맷을 RFC 7807 ProblemDetail 대신 자체 envelope 으로 가져갔다고 했는데, 이미 운영 중인 envelope 에 error.categorymeta 객체를 기존 계약을 깨지 않고 어떻게 추가했나요?
  2. 같은 식별자가 로그에선 request_id(snake), JSON 응답에선 meta.requestId(camel), HTTP 헤더에선 X-Request-Id(kebab) 로 다르게 나오는데, 이게 버그가 아니라 의도된 설계라는 걸 어떻게 보장하나요?
  3. 클라이언트가 보낸 X-Request-Id 헤더를 로그에 남길 때 어떤 보안 문제가 있고 어떻게 막았나요?
  4. retryable 을 category 로 계산하지 않고 per-code 로 둔 이유는?

질문 의도 추론 / Why this question

  • 핵심 평가 대상:
    • 하위호환 확장: 운영 중인 직렬화 계약에 필드를 additive 로 추가하는 감각 (record 컴포넌트 추가 시 모든 호출부/테스트가 깨지는 blast radius 를 어떻게 통제했는가 — 본 작업에선 PortfolioErrorCode/BulkEnvelopeTest 같은 숨은 consumer 까지 빌드로 잡아냄).
    • 표현 계층 분리: 동일 논리 식별자의 case 표현이 계층(MDC/JSON/HTTP/W3C)마다 다른 게 관례임을 알고, 매핑을 SSOT(registry)로 고정해 drift 를 막는 인식.
    • 보안 기본기: CWE-117 log injection / log forging 을 구조화 JSON 로깅 전제 에서 어떻게 다르게 다루는지 (CR/LF·제어문자 strip vs reject vs encode 의 trade-off).
    • 계약 vs 런타임 분리: category 는 식별(문서/메트릭 차원)이고 retryable 은 per-code 런타임 신호라는 책임 분리 인식.
  • 함정 / 흔히 빠지는 답변 패턴:
    • "envelope 만들었다"로 끝내고 왜 ProblemDetail 을 거부했는지(success/error 대칭 + retryable 1급 + 표준 lock-in 회피)와 그 trade-off(표준 호환성 손실)를 말 못함.
    • snake↔camel↔kebab 을 "그냥 컨벤션"이라 하고 단일 case 로 통일하면 왜 안 되는지(HTTP/W3C/JSON 관례 충돌)를 설명 못함.
    • log injection 을 "입력 검증"으로 뭉뚱그리고 구조화 로깅에선 위협이 줄 위조(CR/LF) 라는 점, strip 의 한계(필드 smuggling/길이 폭주는 length cap 으로 별도 처리)를 모름.
    • retryable 을 category default 로 계산한다고 답해 INTERNAL_ERROR(retryable=true) 같은 per-code 예외를 설명 못함.
  • 따라올 만한 후속 질문:
    • 인터페이스에 추상 메서드(category())를 추가했을 때 다운스트림 enum 이 전부 깨지는데, 이걸 컴파일러로 강제하는 게 장점인가 단점인가?
    • meta.traceId 가 tracing 비활성 환경에서도 비면 안 된다고 했는데(D7) 어떻게 보장하나? (generated opaque id fallback)
    • 이 계약 테스트를 app-bootstrap 풀 컨텍스트가 아니라 adapter 모듈 standalone MockMvc 로 옮긴 이유는? (→ raw/errors/webmvctest-nested-springbootconfiguration-context-pollution-2026-06-01)

답변 뼈대 / Answer skeleton (raw)

  • ProblemDetail 거부 = success/error 대칭 + retryable/category 1급화 + 표준 lock-in 회피. trade-off = RFC 표준 호환성 포기(의식적).
  • additive 확장 = error.category(필드 추가), meta(flat traceId→객체) 모두 boundary D5/D6(대칭/ProblemDetail 거부)을 불변으로 두고 위에 얹음. 깨지는 consumer 는 컴파일러가 전부 노출 → 한 패스로 마이그레이션.
  • 식별자 매핑 = registry(mdc-keys.yaml/headers.yaml)에 mdc_key/envelope_meta_field/header name 3열을 1:1 로 등록, 변환 지점은 ResponseMetaFactory.fromMdc() 단일화(snake→camel).
  • log injection = 구조화 JSON 로깅 전제 → CR/LF/제어문자(<0x20) strip + length cap, reject/encode 아님(값 보존, 줄 위조만 차단).

관련