59 lines
5.1 KiB
Markdown
59 lines
5.1 KiB
Markdown
---
|
|
title: interview-prep / operational-error-envelope-and-observability-foundation
|
|
source_type: interview-prep
|
|
status: raw
|
|
related_branches: [feature-operational-error-observability-foundation]
|
|
related_projects: [ca-skeleton]
|
|
tags: [interview-prep, ca-skeleton, error-handling, observability, api-design, logging, security, mdc, testing]
|
|
created: 2026-06-01
|
|
status_label: collecting
|
|
---
|
|
|
|
# interview-prep: operational-error-envelope-and-observability-foundation
|
|
|
|
> Layer: `raw/interviews/` — 면접 질문 원본 수집·연구 노트. 다듬어진 답변은 `/interviewize` 후 `wiki/interview/` 에 별도 작성.
|
|
|
|
## Parent / 부모
|
|
|
|
- [[raw/branch-notes/feature-operational-error-observability-foundation]] — 운영 실패 분류 enum(10-category) + 응답 envelope `meta`/`error.category` + snake_case MDC + inbound 헤더 sanitization 을 구현·검증한 결정/근거.
|
|
- [[raw/project-notes/ca-skeleton-operational-contract]] — ca-tmpl 의 운영 계약 SSOT(§3 응답 envelope, §6 error category, §8 structured log).
|
|
|
|
## 질문 / Question
|
|
|
|
후보 질문 묶음 (실제 면접에서 받은 것 아님 — 본 작업에서 정직하게 도출):
|
|
|
|
1. 응답 포맷을 RFC 7807 ProblemDetail 대신 자체 envelope 으로 가져갔다고 했는데, 이미 운영 중인 envelope 에 `error.category` 와 `meta` 객체를 *기존 계약을 깨지 않고* 어떻게 추가했나요?
|
|
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 아님(값 보존, 줄 위조만 차단).
|
|
|
|
## 관련
|
|
|
|
- [[raw/branch-notes/feature-operational-error-observability-foundation]]
|
|
- [[raw/errors/webmvctest-nested-springbootconfiguration-context-pollution-2026-06-01]]
|
|
- [[raw/blog-topics/operational-error-envelope-meta-category-migration-2026-06-01]]
|