Studio 계약을 tech-log-design-package의 canonical studio-v1.yaml 단일 출처로 정합시키고, Asset capability를 편집 흐름에 연결하는 설계를 확정한다. - 계약 drift는 EXTERNAL_PACKAGE provenance의 digest 고정으로 막는다. - Studio 전송은 플랫폼 계약 런타임을 통과한다. multipart 업로드 1개만 전용 seam으로 분리한다 — 런타임이 JSON 본문만 표현할 수 있기 때문이다. - CSRF는 전송 관심사로 어댑터 내부에 둔다. Studio 인증 UI는 추가하지 않는다. - Public 조회의 HTTP 전환은 별도 사이클로 분리한다. 동기 포트를 async로 바꾸는 작업이 19개 파일 31개 호출 지점과 이식 parity 기준선을 흔든다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
23 KiB
TechLog Backend 정합 설계
상태
- 승인일: 2026-08-17
- 기준선:
tech-log-frontendmain(UI 이식 완료 상태) - 원본 요구:
/home/donghyeon/workspace/tech-log-alignment-design/01-tech-log-frontend-alignment-design.md - Canonical 계약:
/home/donghyeon/workspace/tech-log-design-package/contracts/openapi/studio-v1.yaml- Specification Version
2.0.0 - 계약 digest
sha256:85a65004f29880334b9a0a3b54089450a898f1a815f679b2985b87ed8723df5b - 설계 패키지 revision
0ec5582
- Specification Version
- 결정: 현재 Public/Studio UI 기준선을 고정하고, Studio 계약·전송 경계와 Asset capability를 canonical 계약에 정합시킨다. Public 조회의 HTTP 전환은 이 사이클에서 제외한다.
목적
현재 TechLog 프론트엔드는 Studio를 세션 수명 MockStudioGateway로, Public을 정적 동기 catalog로 구동한다. 이 설계는 다음을 달성한다.
- Studio HTTP 계약을 canonical
studio-v1.yaml단일 출처에서 생성하고, 두 계약이 다시 갈라지지 못하게 빌드로 막는다. StudioGateway의 모든 operation을 canonical 계약 기준 HTTP 어댑터로 구현한다.- Backend에 이미 존재하는 Asset/Image/SVG capability를 프론트 편집 흐름에 연결한다.
- 위 전부를 실행 중인 Backend 없이 완료하고, Backend가 완성되면 런타임 스위치만으로 대조할 수 있게 한다.
현재 Public UI 라우트·화면 구성과 Studio의 작업본 → 편집 → 저장 → 검증 → Public Preview → 게시/재게시 → 게시 기록/Snapshot 흐름은 변경하지 않는다.
Source of Truth 우선순위
| 영역 | Source of Truth |
|---|---|
| Public 화면·라우트·사용 흐름 | 현재 tech-log-frontend |
| Studio 화면 구조·사용 흐름 | 현재 tech-log-frontend |
| Studio HTTP 계약 | studio-v1.yaml (단일 canonical) |
| Asset lifecycle·불변 조건 | studio-v1.yaml |
| 전송·오류·재시도·관측 규약 | 현재 프론트 플랫폼 (src/contracts/external-contract-runtime.ts) |
UI는 Backend 도메인 구조를 그대로 노출하지 않는다. 프론트의 WorkingCopy는 Backend Aggregate가 아니라 편집 계약이다.
확인된 사실과 정정
착수 전 검증에서 원본 요구 문서 및 설계 패키지 README와 실제 코드가 어긋나는 지점을 확인했다. 아래는 구현 기준으로 채택하는 정정이다.
이미 충족된 항목
- 설계 패키지 README는 프론트 격차로 "
RecordKind에PROJECT_DECISION없음"을 든다. 이미 충족돼 있다 —contracts/studio/studio-api.openapi.yaml:236,contracts/studio/generated.ts:200. README가 지칭하는 대상은 구techlog-studio-frontend저장소다. application/ports/studio-gateway.ts의 operation 집합·필터·IdempotentOptions는 원본 요구 §4와 이미 일치한다. 포트 재설계가 아니라 구현체 교체가 필요하다.
실재하는 격차
- 프론트 계약에
X-CSRF-TOKEN선언이 없다 (canonical 6회 참조, 프론트 0회). canonical은 모든 mutating operation에 CSRF를 요구한다. - 프론트 계약의
Idempotency-Key선언이 1회로, canonical(2회, 공용 parameter)과 구조가 다르다. - canonical에는 프론트 계약에 없는 operation이 6개 있다:
getStudioSession,listStudioAssets,uploadStudioAsset,getStudioAsset,updateStudioAsset,deleteStudioAsset. canonical 총 operation은 19개(프론트 현재 13개). - canonical 오류 코드는 23개로, 원본 요구 §10의 21개에
IDEMPOTENCY_KEY_REUSED와WARNING_ACKNOWLEDGEMENT_REQUIRED가 추가된다. 두 코드 모두 필요하다 — 전자는 §10이 요구하는 "version conflict와 idempotency replay 구분"의 실제 코드이고, 후자는 이미 존재하는warning-acknowledgements.tsx가 처리해야 하는 거절 사유다. 23개 전부를 채택한다. - 원본 요구는 세션/CSRF를 다루지 않는다. canonical은 CSRF 토큰을
getStudioSession이 발급한다고 정한다. 아래 §"세션과 CSRF"에서 경계를 정한다.
플랫폼 제약
ContractContributionSource의TEMPLATE_FIXTURE는 타입상fixtureId: "REFERENCE_FEATURE_V1"로 닫혀 있다(external-contract-runtime.ts:186-190). TechLog는EXTERNAL_PACKAGE만 사용할 수 있다.- 플랫폼 계약 런타임은
requestBody: "NONE" | "JSON"만 허용하고, 그 외를 구성 시점에 거절한다(external-contract-runtime.ts:145,:407). 저수준client.ts도 본문을JSON.stringify로 고정한다(client.ts:719).multipart/form-data를 표현할 수 없다. - 기존
browser-transfercapability는 presignedGET/PUT과 resumable part 업로드용이다(PresignedTransferMethod = "GET" | "PUT"). canonical의POST /assetsmultipart MVP 계약에 그대로 맞지 않는다. - 설계 패키지의
MANIFEST.sha256은 stale하다(yaml은 2026-08-17 갱신, manifest는 08-11 기준이며preview-v1.yaml을 아직 나열한다). 계약 고정은 manifest가 아니라 위 §상태에 기록한 실측 digest를 기준으로 한다.
범위
포함
| 범위 | |
|---|---|
| A. 계약 정합 | canonical studio-v1.yaml 도입, 타입 생성 자동화, digest 고정, drift 게이트 |
| B. Studio 전송 | 계약 기여(JSON 18개 전체) + StudioGateway HTTP 어댑터(14개 소비) + 세션/CSRF + 오류 매핑 + 런타임 스위치 |
| C. Asset | StudioAssetGateway 포트, 5개 operation(JSON 4 + multipart 1), Asset Library/Picker/Upload UI, /studio/assets 라우트, evidence directive 연결, alt/decorative 규칙 정정 |
제외 — Public 조회의 HTTP 전환
원본 요구 §11은 adapters/static/public-query.ts를 HTTP로 교체하도록 나열한다. 이 사이클에서 제외하고 별도 사이클로 분리한다.
근거:
PublicContentQueries는 전 메서드가 동기이고, 프레젠테이션 19개 파일 31개 호출 지점이 렌더 중 직접 호출한다. async 전환은 전 Public 화면에 loading/error/empty 상태를 도입하는 작업이다.- 방금 완료한 UI 이식의 시각·접근성 parity 기준선을 광범위하게 흔든다.
- 실행 중인 Public Backend가 없어 지금 전환해도 검증할 대상이 없고 사용자 가치도 없다.
- canonical
public-v1.yaml(1,765줄)은 준비돼 있으므로, Public API가 실제로 서비스될 때 자체 spec/plan 사이클로 수행한다.
이 제외는 범위 축소가 아니라 순서 결정이다. 완료 조건에서 해당 항목을 별도로 명시한다.
선택한 접근
A. 계약 정합 — digest로 고정된 단일 출처
canonical yaml을 저장소에 vendor하고, 타입을 생성하고, 계약 기여의 EXTERNAL_PACKAGE provenance로 canonical revision에 암호학적으로 고정한다.
InstalledContractPackageIdentity(external-contract-runtime.ts:173)는 이미 이 목적에 맞는 필드를 요구한다.
packageId tech-log-studio-contract
version 2.0.0 (canonical info.version, exact SemVer)
digest sha256:85a65004… (canonical studio-v1.yaml의 SHA-256)
runtimeProtocolVersion 1
sourceRevision 0ec5582 (설계 패키지 git revision)
assertPackageIdentity는 shape을 검증하므로 npm 레지스트리 없이 지금 사용할 수 있다. 실제 패키지 배포로 승격할 때 같은 필드를 그대로 채운다.
drift 방지는 저장소 관례(generate:* / check:*)를 따르는 스크립트 한 쌍으로 강제한다.
generate:tech-log-contract canonical yaml → vendor 사본 + generated.ts + digest 기록
check:tech-log-contract 재생성 결과가 커밋 내용과 바이트 동일한지, digest가 계약 기여의
선언과 일치하는지 검증. 불일치 시 실패.
openapi-typescript를 devDependency로 고정한다. 현재 generated.ts는 이 도구로 만들어졌으나 도구도 스크립트도 저장소에 없어 재생성이 불가능하다 — 이것이 두 계약이 갈라진 근본 원인이다.
X-CSRF-TOKEN과 공용 Idempotency-Key parameter는 생성 결과에 자동 반영된다. 프론트 yaml을 손으로 고치지 않는다.
B. Studio 전송 — 플랫폼 계약 런타임 사용
계약 기여는 서비스 패키지당 하나이므로, features/tech-log/contracts/tech-log-studio-contract-contribution.ts 한 파일에 canonical의 JSON operation 18개 전부(19개 중 multipart 업로드 제외)를 선언한다. Asset의 JSON operation 4개도 같은 기여에 속한다 — 같은 studio-v1 패키지이기 때문이다. 이 중 StudioGateway가 14개를, StudioAssetGateway가 4개를 소비한다.
선언 형식은 reference-feature-contract-contribution.ts가 확립한 것을 따른다: operation별 inputValidator/outputValidator/problemValidator, acceptedStatuses, retrySemantics, commandRecovery, commandEffect, projectRequest, 그리고 byte limit·deadline·retry budget·diagnostics 이름.
retrySemantics는 canonical의 안전성 구분을 그대로 반영한다. 조회는 SAFE, mutating operation은 KEYED이며 commandRecovery.mode = "IDEMPOTENCY_REPLAY", retry budget은 0이다 — 발신된 KEYED 명령의 자동 재시도는 금지된다.
StudioGateway 구현체는 contractOperations executor 위에 얹고, 포트 시그니처는 변경하지 않는다. UI는 어댑터가 mock인지 HTTP인지 알지 못한다.
세션과 CSRF
CSRF는 전송 관심사이며 UI 관심사가 아니다. 현재 Studio에는 인증 UI가 없고(이식 시 유보), 이 사이클에서도 추가하지 않는다.
따라서 getStudioSession을 포트로 노출하지 않는다. HTTP 어댑터 내부가 첫 mutating 요청 전에 세션을 조회해 CSRF 토큰을 캐시하고, csrfHeaderName으로 헤더를 붙인다. 401/403은 기존 오류 경로로 흘려보낸다. displayName/roles는 이 사이클에서 소비하지 않는다.
이 결정으로 완료 조건 "현재 Studio 작업 흐름이 변경되지 않는다"가 유지된다.
런타임 스위치
config/runtime/*.json에 스위치를 추가한다.
TECH_LOG_STUDIO_SOURCE: "MOCK" | "HTTP"
local, development → MOCK (기본값)
staging, production → HTTP
createTechLogFeatureInstalledInput이 이 값으로 gateway factory를 고른다. mock은 삭제하지 않고 test fixture 겸 fallback으로 유지한다. Backend 완성 시 local.json 한 줄로 대조를 시작한다.
기본값을 MOCK으로 두는 이유는 현재 앱과 이식 parity 테스트가 그대로 통과해야 하기 때문이다.
C. Asset — 포트 분리와 업로드 전송 경계
StudioAssetGateway를 StudioGateway와 별도 포트로 둔다. 파일 전송과 JSON orchestration의 실패 모델이 다르고, 향후 presigned/resumable 교체가 이 포트 뒤에서 끝나야 한다.
StudioAssetGateway
listAssets(query, options) GET /api/v1/studio/assets
uploadAsset(form, options) POST /api/v1/studio/assets (multipart)
getAsset(assetId, options) GET /api/v1/studio/assets/{assetId}
updateAssetMetadata(assetId, cmd, o) PUT /api/v1/studio/assets/{assetId}
deleteAsset(assetId, options) DELETE /api/v1/studio/assets/{assetId}
업로드 전송
플랫폼 계약 런타임이 multipart를 표현할 수 없으므로, 업로드 한 operation만 전용 전송 seam으로 분리한다.
StudioAssetUploadTransport (좁은 인터페이스: form + 헤더 → 결과)
└ fetch + FormData 구현체
런타임 config의 API_BASE_URL·타임아웃 재사용
CSRF·Idempotency-Key 헤더는 B와 동일 경로로 획득
오류는 B와 동일한 코드 매핑 테이블 사용
나머지 4개 JSON operation은 A/B와 같은 계약 런타임을 통과한다. 즉 계약 런타임을 우회하는 것은 19개 중 1개다.
이 경계를 명시적 seam으로 두는 이유는 §7.4의 교체 가능성 요구를 만족시키기 위해서다. 플랫폼에 MULTIPART 모드가 생기거나 presigned로 옮길 때 구현체 한 파일만 바뀐다. 플랫폼 파일(external-contract-runtime.ts, client.ts)은 template 동기화 대상이므로 이 사이클에서 수정하지 않는다.
결정 사유와 우회 범위는 docs/reviews/adapters/에 기록한다.
UI 접근점
Studio primary navigation을 Asset 중심 CMS로 되돌리지 않는다. 두 접근점을 둔다.
- Editor contextual Asset Picker — 작성 흐름의 기본 진입점. 선택 시 evidence directive를 삽입한다.
/studio/assetsAsset Library — 검색·메타데이터·사용처·정리용 보조 화면. navigation에는 secondary utility link로만 노출한다.
/studio/assets는 tech-log-route-contract.ts에 layoutGroup: "STUDIO"로 추가한다(현재 27개 → 28개). 라우트 registry 거버넌스 기준선을 함께 갱신한다.
Evidence Figure와 Asset 연결
현재 content format directive를 유지한다.
:::evidence key="asset-key" alt="설명" caption="캡션" zoom="true"
변경점:
- 정적
evidenceAssets레지스트리(adapters/static/evidence-assets.ts) 대신 Backend Asset의assetKey를 사용한다.assetKey는 canonical에서 immutable이며 공개 이력 이후 재사용이 금지된 안정 key다. - Asset Picker 선택 시 directive를 자동 삽입한다. 사용자가 raw object-storage URL을 Markdown에 직접 넣지 않게 한다.
- 렌더러는 API가 제공한 Asset descriptor를 resolver로 주입받는다. 렌더러 경계는 변경하지 않는다.
QUARANTINEDAsset은 Public/Preview에 렌더링하지 않는다.- 사용자 업로드 SVG 원문을
innerHTML로 주입하지 않는다. 검증된publicPath를<img src>로 렌더링한다.
정적 evidenceAssets는 기존 하드코딩 Case 화면의 parity 유지를 위해 fixture로 남기고, Asset 기반 경로와 공존시킨다.
alt / decorative 규칙 정정
현재 parser는 빈 alt를 syntax error로 차단한다.
parse-case-content.ts:505
if (!attributes.alt) invalid(node, "evidence alt text is required");
Asset capability와 연결하면 이 위치에서는 판단할 수 없다 — 필요 여부가 Asset의 decorative에 달려 있다. 규칙을 옮긴다.
parser 빈 alt를 문법 오류로 차단하지 않는다 (구조만 검증)
publish 검증 Asset decorative=false + 사용 위치 alt 비어 있음 → ERROR
Asset decorative=true → alt="" 허용
이는 content format의 의미 변경이므로 parser/serializer round-trip 픽스처와 기존 검증 테스트를 함께 갱신한다.
대상 아키텍처
src/features/tech-log/
├── contracts/
│ ├── studio/
│ │ ├── studio-api.openapi.yaml canonical vendor 사본 (생성물, 수동 편집 금지)
│ │ ├── generated.ts 생성물
│ │ ├── contract.ts 타입 alias (Asset 계열 추가)
│ │ └── canonical-source.json digest·revision 기록
│ └── tech-log-studio-contract-contribution.ts JSON operation 18개 + EXTERNAL_PACKAGE provenance
├── application/ports/
│ ├── studio-gateway.ts 변경 없음
│ └── studio-asset-gateway.ts 신규
├── adapters/
│ ├── http/
│ │ ├── http-studio-gateway.ts contractOperations 위 구현
│ │ ├── http-studio-asset-gateway.ts JSON 4 + 업로드 위임
│ │ ├── asset-upload-transport.ts multipart seam
│ │ ├── studio-session-csrf.ts CSRF 토큰 획득·캐시
│ │ └── studio-error-mapping.ts 23 코드 → 플랫폼 FailureKind
│ ├── mock/ 유지 (fixture·fallback)
│ └── static/ 유지 (Public, 이 사이클 범위 외)
└── presentation/studio/
├── pages/assets-page.tsx 신규
└── components/
├── asset-library.tsx 신규
├── asset-picker.tsx 신규
└── asset-upload-dialog.tsx 신규
기존 유지 대상: presentation/public/**, presentation/studio/pages/**, document-*, validation-*, public-preview-screen, publish-screen, publication-*, presentation/shared/public-render/**.
오류·동시성 계약
23개 canonical 코드를 전부 처리한다. 매핑은 studio-error-mapping.ts 한 곳에 둔다.
AUTHENTICATION_REQUIRED STUDIO_ACCESS_DENIED
DOCUMENT_NOT_FOUND VERSION_CONFLICT
REQUEST_VALIDATION_FAILED VALIDATION_FAILED
VALIDATION_STALE PREVIEW_NOT_FOUND
PREVIEW_STALE PREVIEW_EXPIRED
PUBLICATION_NOT_FOUND PUBLICATION_CONFLICT
PUBLICATION_EVENT_NOT_FOUND PUBLICATION_SNAPSHOT_NOT_FOUND
IDEMPOTENCY_KEY_REUSED WARNING_ACKNOWLEDGEMENT_REQUIRED
ASSET_NOT_FOUND ASSET_NOT_READY
ASSET_IN_USE ASSET_QUARANTINED
PAYLOAD_TOO_LARGE UNSUPPORTED_MEDIA_TYPE
STUDIO_UNAVAILABLE
규칙:
- 모든 mutation은
Idempotency-Key를 보낸다. 명령 하나당 새 key를 만들고, 그 명령의 안전한 재시도에만 같은 key를 재사용한다. VERSION_CONFLICT(낙관적 잠금 실패)와IDEMPOTENCY_KEY_REUSED(같은 key·다른 요청)를 혼동하지 않는다. 전자는 사용자 데이터 손실 없는 충돌 화면으로, 후자는 클라이언트 결함으로 다룬다.Idempotency-Replayed응답 헤더를 replay 판별에 사용한다.- workflow 상태(
publicationStatus,hasUnpublishedChanges,nextAction)는 서버가 계산한 값을 그대로 신뢰한다. 프론트에서 재계산하지 않는다. - 업로드 상태는
선택 실패 / 업로드 중 / 전송 실패 / READY / REJECTED / QUARANTINED / 크기 초과 / 미지원 형식을 구분한다. 업로드 전송 성공과 서버 검증 성공을 분리한다. - Asset은
READY일 때만 Preview/Publish에 사용한다.
테스트 전략
TDD로 진행한다. 각 단위는 red → green → 게이트 순서를 지킨다.
계약(A)
check:tech-log-contract가 vendor 사본·generated.ts·digest 불일치를 잡는다 (의도적 변조로 red 확인).- canonical 19개 operationId가 전부 덮이는지 parity 검증: 18개는 계약 기여에,
uploadStudioAsset은 업로드 전송 seam에 존재해야 한다. 어느 쪽에도 없는 operationId가 있으면 실패한다. EXTERNAL_PACKAGEidentity가assertPackageIdentity를 통과하고contractSet에 나타나는지 확인.
전송(B) — MSW로 canonical 응답·오류를 재현
- 불완전 draft 저장 성공,
expectedVersion충돌 →VERSION_CONFLICT. - idempotency replay(
Idempotency-Replayed: true)와IDEMPOTENCY_KEY_REUSED구분. - workflow 전이:
INVALID → FIX_VALIDATION,VALID/WARNINGS → CREATE_PREVIEW,CURRENT → PUBLISH, 게시 후NONE, 편집 후VALIDATE복귀, expired preview 재생성. - Publication: 최초
PUBLISHED, 재게시REPUBLISHED, 취소UNPUBLISHED, 과거 Snapshot 불변성. - 23개 코드 전부의 UI 관측 가능한 처리.
- CSRF 토큰 획득 실패·만료 경로.
- 런타임 스위치:
MOCK/HTTP각각에서 gateway 종류가 선택되는지.
Asset(C)
- PNG/JPEG/WebP/SVG 업로드 성공, 미지원 형식
UNSUPPORTED_MEDIA_TYPE, 크기 초과PAYLOAD_TOO_LARGE. decorative=false+ 빈 alt → publish ERROR /decorative=true+alt=""허용.QUARANTINEDAsset의 Public·Preview 렌더링 차단.- 사용 중 Asset hard delete 차단 →
ASSET_IN_USE,ARCHIVED전환 경로. - Asset Picker가 evidence directive를 정확한 문법으로 삽입.
- parser가 빈 alt를 더 이상 syntax error로 차단하지 않음 + round-trip 픽스처 갱신.
렌더러 불변
동일 픽스처에 대해 Instant Preview, Server Public Preview, Published Public, Publication Snapshot의 semantic output이 동일해야 한다. Asset resolver도 같은 렌더러 경계로 주입한다.
회귀
기존 이식 parity 스위트(시각·접근성·라우트·아키텍처 경계)가 전부 통과해야 한다. 기본 스위치가 MOCK이므로 이 스위트는 영향받지 않아야 한다.
위험과 완화
| 위험 | 완화 |
|---|---|
| 실행 중 Backend 없음 → 실응답 미검증 | canonical 계약 기준 MSW 검증. 스위치로 대조 지점을 남긴다. 완료 조건에 "실서버 대조 미포함"을 명시한다. |
| canonical yaml이 계속 변경 중 (오늘도 수정됨) | digest·revision을 커밋에 고정하고 drift 게이트로 감지. canonical 갱신은 의도적 재생성 커밋으로만 반영. |
| 계약 런타임 우회(업로드 1개)가 거버넌스 위반으로 보일 수 있음 | 좁은 seam으로 격리, 사유·범위를 adapter review 문서에 기록, 나머지 18개는 런타임 통과. |
| content format 의미 변경(alt)이 기존 픽스처를 깨뜨림 | parser 변경과 픽스처·검증 갱신을 한 단위로 묶어 red→green으로 수행. |
| 라우트 1개 추가가 registry 기준선을 깨뜨림 | 거버넌스 기준선 갱신을 같은 단위에 포함. |
openapi-typescript 도입이 생성물 diff를 크게 만듦 |
첫 생성 결과를 별도 커밋으로 분리해 리뷰 가능하게 한다. |
완료 조건
- 현재 Public UI·라우트가 변경되지 않는다.
- 현재 Studio 작업 흐름이 변경되지 않는다.
- Studio 계약이 canonical
studio-v1.yaml에서 생성되고, digest 고정과 drift 게이트가 동작한다. StudioGateway의 모든 operation이 HTTP 어댑터로 구현되고 MSW 계약 테스트로 검증된다.- WorkingCopy 저장이 Public Projection을 변경하지 않는다.
- Validation/Preview/Publish가 version과 dependency revision으로 묶인다.
- Publication Event와 Snapshot을 현재 UI에서 조회할 수 있고 과거 Snapshot이 불변이다.
- Image/SVG를 업로드하고 Case content에 Asset 기반 evidence로 삽입할 수 있다.
- Asset은
READY일 때만 Preview/Publish에 사용된다.QUARANTINED는 렌더링되지 않는다. - 23개 오류 코드와 idempotency/version 충돌 구분이 처리된다.
- 프론트가 Backend 도메인 Aggregate를 복제하지 않는다.
- 런타임 스위치로
MOCK/HTTP를 전환할 수 있고, 기본MOCK에서 기존 parity 스위트가 전부 통과한다.
명시적 비완료 항목:
- 실행 중 Backend와의 실응답 대조. Backend Studio 구현 완료 후 별도로 수행한다.
- Public 조회의 HTTP 전환.
public-v1.yaml기준 별도 spec/plan 사이클로 수행한다.