feat: read the profile's topics from Studio, and add working-copy deletion

Two things an author could not control from Studio.

The profile's "주요 관심 주제" was four strings in the JSX. Creating or removing
a topic in Studio changed nothing, and correcting the list meant a rebuild and
a redeploy. It now renders the published topic list. The old literal opened
with "Backend Architecture", which no record in the catalogue actually carries
— the profile was advertising a topic that did not exist, and nothing could
have caught that while the list lived in the markup.

The working-copy list gained a delete control. It routes by kind because the
contract and the storage both do: Case and Reference share one table split by
type, Question is its own. Decision has no delete — its lifecycle is accept,
reject, supersede, which records what happened rather than erasing it — so the
control does not appear for it.

The list summary carries no version, so deletion reads the working copy first
and uses the version it finds. A stale version from a list left open should
fail as a conflict, not delete whatever is there now.
This commit is contained in:
DongHyeonka
2026-08-21 13:30:37 +09:00
parent ab8c6c14db
commit c5e8735041
15 changed files with 296 additions and 92 deletions
@@ -5,11 +5,12 @@ info:
description: |-
⚠ 봉투 결정(ADR-006) 부분 반영 — 이 파일에는 두 모양이 공존한다.
topics 4개, projects 5개, releases 7개를 studio-v1.yaml과 같은 방식으로
변환했다 (`application/json` + ErrorEnvelope / <Payload>Envelope). 그 16개가
구현된 것이자 소비자가 있는 것이기 때문이다.
topics 4개, projects 5개, releases 7개, 그리고 작업본 삭제 3개
(deleteCaseDraft / deleteReferenceDraft / deleteQuestion)를 studio-v1.yaml과
같은 방식으로 변환했다 (`application/json` + ErrorEnvelope / <Payload>Envelope).
그 19개가 구현된 것이자 소비자가 있는 것이기 때문이다.
나머지 63개는 아직 bare payload + `application/problem+json` + ProblemDetails
나머지 60개는 아직 bare payload + `application/problem+json` + ProblemDetails
다. 구현에 착수할 때 같은 방식으로 따라온다 — 소비자가 없는 operation을 미리
변환해 두면 검증되지 않은 모양이 계약에 고정된다.
@@ -260,45 +261,45 @@ paths:
'400':
description: Bad Request
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
'401':
description: Unauthorized
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
'403':
description: Forbidden
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: Not Found
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
'409':
description: Conflict
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
'422':
description: Unprocessable Content
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: Internal Server Error
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
requestBody:
required: true
content:
@@ -509,45 +510,45 @@ paths:
'400':
description: Bad Request
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
'401':
description: Unauthorized
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
'403':
description: Forbidden
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: Not Found
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
'409':
description: Conflict
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
'422':
description: Unprocessable Content
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: Internal Server Error
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
requestBody:
required: true
content:
@@ -1795,45 +1796,45 @@ paths:
'400':
description: Bad Request
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
'401':
description: Unauthorized
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
'403':
description: Forbidden
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: Not Found
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
'409':
description: Conflict
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
'422':
description: Unprocessable Content
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: Internal Server Error
content:
application/problem+json:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
$ref: '#/components/schemas/ErrorEnvelope'
requestBody:
required: true
content:
@@ -5270,6 +5271,11 @@ components:
- RELEASE_NOT_FOUND
- RELEASE_VERSION_TAKEN
- RELEASE_NOT_PUBLISHABLE
- DOCUMENT_NOT_FOUND
- DOCUMENT_PUBLISHED
- DOCUMENT_IN_USE
- QUESTION_NOT_FOUND
- QUESTION_IN_USE
- INTERNAL_ERROR
description: '`INTERNAL_ERROR` 는 이 기능이 아니라 스켈레톤의 공통 처리기가 내는 코드다. 계약이 그것까지 열거해야 500 응답이 계약을 벗어나지 않는다.'
category: