ADR-0053: 고객 컨텍스트 제거 — MTS 위젯 직접 호출, Exposure Log 은퇴¶
- 상태: 승인됨 (2026-08-17 — 구현 착수로 승인 전환, ALPHA-991 PR ②) — "바뀌지 않는 것"의 응답 형상·204 수렴 문면은 대체됨 → ADR-0054 (경로·면책 문구 동반·serving_scope 판정·캐시 방향은 불변, 2026-08-24)
- 날짜: 2026-08-17
- 관련: ALPHA-991 · ADR-0010(하이브리드 온프렘 — 노출 이력 항목 부분 대체) · ADR-0013(Exposure Log — 이 ADR 이 대체) · ADR-0031(Publication 리네이밍 — 호출자 문면 부분 대체) · ADR-0035(위젯 빌드 산출물 — 런타임 경로 문면 부분 대체) · ADR-0051(BYOC 배포 토폴로지) · ALPHA-501(요청 메트릭)
맥락¶
두 가지가 확정됐다(사용자 결정 2026-08-17): 개인화 기능을 두지 않는다, 그리고 고객 단위 노출 이력(민원/감사 재현) 요건을 유지하지 않는다. 둘째는 첫째에서 자동으로 따라오지 않는다 — ADR-0013 의 고객 해시는 개인화가 아니라 감사 재현을 위한 장치였다. 따라서 이 ADR 은 "개인화가 없으니 해시도 불요"라는 추론이 아니라, 감사 요건 자체를 유지하지 않는다는 제품 결정을 입력으로 받는다. 그 위에서 서빙 경로의 두 전제가 무너진다.
첫째, 고객 식별의 존재 이유가 사라진다. X-Customer-Hash(증권사 백엔드가
생성하는 고객 해시, ADR-0013)의 유일한 소비처가 고객 단위 노출 이력이었다.
요건이 사라지면 이 헤더는 필수 검증(400)만 남은 빈 계약이다.
둘째, 증권사 백엔드 경유 호출 전제가 풀린다. 지금 계약 (docs/contracts/publication-api.md)은 "증권사 백엔드가 호출하는 조회 표면"이고, ADR-0035 는 런타임 경로를 "위젯 → 증권사 백엔드/API GW → Publication API"로 문면 확정했다. 경유의 이유는 ① 고객 해시를 생성할 주체가 백엔드뿐이고 ② 원본 고객 ID 비수취 경계를 서버 간 호출로 지키기 위함이었다. ①이 사라지면 ②는 "받을 고객 데이터 자체가 없음"으로 더 강하게 성립하므로, 경유를 강제할 근거가 없다.
배포 형태가 증권사 클라우드 계정(BYOC, ADR-0051)으로 확정되어 있으므로 MTS
위젯(빌드 산출물, ADR-0035)이 publication-api 를 직접 호출해도 트래픽은 증권사
통제 표면 안에서 끝난다 — ADR-0010 의 경계는 벤더/증권사이지 증권사 내부의
호출 토폴로지가 아니다. 데모는 현행 계약대로 중계 형상이다(위젯 →
mock-broker /api/broker/ai-analysis → publication-api, CloudFront 는
mock-broker 를 프록시) — mock-broker 의 고객 해시가 형식 요건만 채우는 껍데기
값이라는 사실이 이 전환의 방증이고, 직접 호출 형상 자체는 이 ADR 구현에서
데모 중계 층을 걷어내며 처음 성립한다.
한편 관측 축은 이미 분리되어 있다 — serving_request_metric(ALPHA-501)은
요청 수·상태·에러 코드를 기록하는 관측 원장이고, 설계 시점부터 "고객 식별자·
문구를 싣지 않는다"를 규약으로 갖는다. 고객 축이 사라진 뒤 서빙 경로에 남는
기록 요구는 이 관측 축이 전부다.
결정¶
- MTS 위젯이 publication-api 를 직접 호출한다. "증권사 백엔드가 호출하는 조회 표면" 전제를 계약에서 걷어낸다. 서빙 응답은 게시분(Published)·비개인화 공통 콘텐츠·읽기 전용이라 단말 직접 노출에 적합하고, 제공 범위 차단은 지금처럼 204 수렴으로 증권사 단말에 제외 사실을 드러내지 않는다.
- 고객 식별을 폐지한다.
X-Customer-Hash헤더·MISSING_CUSTOMER_HASH(SERV4001)·서비스 관통 파라미터를 제거한다. "원본 고객 ID 를 받는 표면을 만들지 않는다"는 원칙은 유지되되, 근거가 "해시로만 받는다"에서 "고객 식별 자체를 받지 않는다"로 강해진다. X-Channel도 함께 폐지한다(SERV4002·SERV4003 포함). 직접 호출에서 채널은 클라이언트 자가 신고 값이라 감사 근거가 못 되고, 위젯 단일 채널이라 구분 정보량도 없다. 채널 구분이 실제로 필요해지는 시점(HTS 등 제2 채널 실물)에 신뢰 가능한 공급 방식과 함께 재도입한다.- Exposure Log 를 은퇴시키고 기록 요구는
serving_request_metric이 흡수한다. ADR-0013 의 "조회=노출 기록, 민원 시점 상태 완전 재현" 요건을 폐기한다 — 고객 단위 재현은 고객 식별 없이는 원리적으로 불가능하고, 남는 관측 요구(어느 경로가 얼마나 호출되고 어떤 상태로 끝났는가)는 요청 메트릭이 이미 전담한다. 메트릭 스키마는 확장하지 않는다 — 노출 문구 스냅샷·티커 축을 옮겨 싣는 것은 폐기한 감사 요건을 관측 원장에 되살리는 일이다.ExposureLogRecorder·ExposureLog·exposure_log테이블을 제거한다 (코드 은퇴 선행 → 수축 마이그레이션의 확장수축 순서, 적용분 수정 금지). - 서빙 표면은 인증 없는 공개 읽기 표면임을 명시적으로 결정한다. 고객 식별을 받지 않으므로 요청 단위 인증으로 보호할 개인 자산이 응답에 없고, 콘텐츠 자체의 공개 등급도 판단한다 — 응답은 전 고객 동일하게 노출될 것을 전제로 게시·검수된 비개인화 투자 참고 정보다(Published 만 서빙·serving_scope 차단은 유지). 남는 위험은 무단 열거·재배포와 트래픽 증폭인데, 이는 콘텐츠의 상업적 이용 권한 문제라 요청 인증이 아니라 증권사-벤더 계약과 노출면 통제의 일로 분류한다 — 의도적 트레이드오프이며, 열거를 막아야 하는 요구가 실물이 되면 단말 무관 토큰(위젯 임베드 키) 층을 그때 결정한다.
진입 형상의 기본형은 위젯 배포 도메인과 동일 오리진의 경로 프록시다 (설명 조회 경로 → publication-api — 데모 CloudFront 프록시와 동형). 이 형상에서 CORS 는 불요하고, rate limit·WAF 등 완화 통제는 그 프록시(증권사 엣지) 소관으로 계약 문서에 책임 경계를 명시한다(벤더 불관여 원칙 — 해시 생성 규칙·salt 가 증권사 영역이었던 것과 같은 결) — 인증 없는 표면이므로 호출 주체 판별·차단은 어느 층도 약속하지 않으며, 이는 위의 열거 위험 수용과 같은 트레이드오프다. 프록시는 쿠키·Authorization 등 인증 헤더를 오리진에 전달하지 않는다(strip) — 위젯 도메인의 MTS 세션 토큰이 publication-api 액세스 로그에 흘러들면 고객 식별 비수취 결정이 뒤로 무너진다. 이 strip 요건은 계약 문서에 명시한다. publication-api 의 CORS 설정(허용 오리진 = 위젯 도메인, 테넌트별 설정값)은 별도 API 호스트를 여는 테넌트에만 성립 요건으로 추가된다 — 어느 형상이든 CORS 는 방어 수단이 아니다. ADR-0051 의 단계 3 internal ALB 와는 충돌하지 않는다 — 공개 진입은 위젯 도메인 프록시 한 곳이고 internal ALB 는 그 뒤 내부 라우팅이다.
단, 요청당 DB 쓰기가 남는 한 남용 통제는 공개 토폴로지의 전제 조건이다
(선택적 위임이 아니다). exposure_log INSERT 는 소멸하지만
serving_request_metric 은 /api/** 전 요청을 — 미매칭 경로·실패 응답까지
— 행으로 적재하고 보존 상한이 없어, 비인증 표면에서는 임의 클라이언트가
무한 쓰기를 유발할 수 있다. 따라서 ① 엣지 rate limit 은 계약 문서에서
"권고"가 아니라 공개 노출의 필수 전제로 명시하고, ② publication-api
자체도 엣지 구성 품질에 생사를 걸지 않도록 메트릭 기록의 자체 상한(미매칭
경로 미기록·보존/집계 정책 중 최소 하나)을 구현 티켓 범위에 넣는다 —
구체 방식은 구현에서 정한다.
이 결정으로 바뀌지 않는 것: 서빙 표면 자체(GET /api/v1/explanations/{etf_ticker} — 경로·응답 형상·204 수렴·면책 문구 동반), 제공 범위(serving_scope) 판정, 게시분 캐시 방향(ALPHA-433 — 직접 호출로 요청량이 단말 수에 비례하게 되어 오히려 전제가 강해진다), 하이브리드 플레인 분리와 데이터 비반출·Pull 단방향(ADR-0010 — 단 같은 ADR 의 "노출 이력을 온프렘으로 이동" 항목은 노출 이력 축 자체가 소멸하므로 이 ADR 이 부분 대체한다), Cloud 의 비개인화 원칙(원래부터 개인 데이터가 없던 플레인이라 무변경).
대안¶
- 경유 전제를 유지하고 헤더만 optional 로 — 호출 주체가 증권사 백엔드로 남는 한 위젯→백엔드→publication-api 의 중계 구현을 증권사에 요구하게 된다. 개인화가 없는데 중계가 하는 일은 헤더 부착뿐이라 납품 협의 표면만 늘린다. 배제.
- exposure_log 를 고객 축만 빼고 유지(문구 스냅샷·티커·시각) — 감사 요건 없이 남는 것은 "요청당 텍스트 스냅샷 적재"라는 쓰기 비용뿐이다. 이 INSERT 는 200 응답이 수행하는 동기 쓰기 2건(exposure_log·serving_request_metric)의 한 축이다 — 요건이 사라진 쓰기를 성능 비용을 내며 유지할 이유가 없다. 노출 문구 재현이 다시 요건이 되면 그때 게시 이력(publication)+정책 이력(policy_version)의 역산이 아니라 스냅샷 재도입을 정면으로 결정한다. 배제.
- 채널(X-Channel)만 존치 — 자가 신고 값의 통계는 엣지(CloudFront·ALB) 로그로도 얻는다. 검증 로직·에러 코드 2종을 유지할 근거가 안 된다. 배제.
- 위젯 전용 BFF(backend-for-frontend) 신설 — 직접 노출이 불안하면 세울 수 있는 층이나, 지금 요구(CORS·읽기 전용 조회)에 대해 모듈 하나가 통째로 과잉이다(온프렘 반입 컴포넌트 최소화, ADR-0051 결정 6과 같은 계열). 배제.
결과¶
- 얻는 것: 증권사 측 납품 요구사항에서 "고객 해시 생성·중계 구현"이 통째로 빠진다 — 위젯 임베드 + 엣지 프록시 구성만 남는다. 서빙 hot path 의 요청당 INSERT 1건(exposure_log)이 사라진다. 사문화된 인접 티켓 2건(ALPHA-778 해시 평문 로그 제거·ALPHA-775 면책 문구 스냅샷)이 함께 종결된다.
- 잃는 것/트레이드오프: 고객 단위 축이 통째로 사라진다 — 요청에 고객
식별자가 없으므로 "그 고객이 접속했는가"조차 어느 로그로도 귀속할 수 없다
(엣지 접속 로그는 IP·경로뿐). 노출 시점의 "요청 ↔ 실제 응답 문구" 결합도
사라진다 — exposure_log 의
summary_snapshot이 그 유일한 기록이었다. 다만 문구 변경 이력 자체는 남는다:publication의 문구는 in-place 갱신이지만 운영자 정정은 append-onlyconsole_action_log에 전후 문구가 기록되므로, 민원 대응은 "게시·정정 이력 + 정책(policy_version) 이력"의 시점 재구성 수준으로 낮아진다(특정 고객에게 무엇이 나갔는가는 재구성 불가). 이 강등은 감사 요건을 유지하지 않는다는 제품 결정의 직접 귀결이며, 요건이 되살아나면 스냅샷 재도입을 정면으로 재결정한다. API 가 단말에 직접 노출되어 엣지 보호가 증권사 구성 품질에 의존한다(계약 문서로 경계만 명시). 채널 축 관측이 사라진다(재도입 조건 명시로 상쇄). - 따라오는 의무: ~~ADR-0013 대체됨 표기, ADR-0010(노출 이력 항목)·ADR-0031
(호출자 문면 + 노출 기록·exposure 컴포넌트 소유 조항)·ADR-0035(런타임 경로
문면 + "계약 영향 없음·새 데이터 표면 없음" 결과 조항)·ADR-0038(ExposureLog
= 유일 가변 JPA 엔티티 문면) 부분 대체 표기, README 색인~~(승인 전환과 함께
이행 — ALPHA-991 PR ②), ~~ADR-0033 부분 대체 표기~~(데모 재배선 ALPHA-992
에서 이행), ~~사문화 티켓 2건(ALPHA-778·775) 종결~~(취소 처리 완료). 문서 정합 — docs/domain/exposure-log.md·
data-residency.md(고객별 노출 이력 서술)·docs/context.md·state-machine.md 등
호출 경로·노출 기록을 문면으로 갖는 문서 전수는 구현 PR 의 docs-sync 게이트가
이 ADR 을 기준으로 쓸어낸다. 계약 문서(publication-api.md·openapi.yaml·
README) 재작성은 구현 PR 과 동행. 구현 순서 — ① 코드 은퇴+호출자 정리
(demo 위젯의 mock-broker 중계 제거는 proxy-site terraform 변경과 한 단위다
— 현행은 전 경로 단일 mock-broker 오리진이므로, 설명 조회 경로만 별도
behavior 로 publication-api 에 분리하고 정적 자산·시세·차트(
/api/broker/ quotes·chart)는 mock-broker 에 남긴다. 오리진을 통째로 바꾸면 데모 화면 전체가 깨진다. publication-api 는 현행 compose 에서 호스트 포트 미공개 (내부망 전용)라 호스트 포트 공개 + CloudFront 전용 SG 제한을 한 단위의 신뢰경계 변경으로 함께 배선해야 한다 — behavior 만 추가하면 오리진 연결이 없어 502 다. 인증 헤더 strip 동반, loadtest·compose 예시 동반) ② 수축 마이그레이션(exposure_log drop)+dbml 재생성.