fix: 새 목록 항목의 id 가 계약을 건너갈 수 있게 한다
관계를 고르고 저장하면 400 이 돌아왔고, 편집기에는 "saveStudioDocument broke its contract." 한 줄만 남았다. 저장은 통째로 실패했다. 새 관계·규칙·선택지·문장의 id 를 `createLocalId` 로 만들고 있었다. 그 함수는 접두사를 붙여 `relation-<uuid>` 를 돌려준다 — React key 나 Idempotency-Key 로는 맞지만 계약이 그 자리에 요구하는 것은 uuid 다. 서버는 파싱조차 못 하고 InvalidFormatException 으로 거절했다. 목록에 고를 대상이 하나도 없던 동안에는(catalog RELATION/EVIDENCE 가 스텁이었다) 아무도 이 경로를 지나지 않아 드러나지 않았다. 두 결함이 서로를 가리고 있었다. Case 는 목록 항목이 없어 무사했다. Reference 의 규칙, Question 의 선택지, Decision 의 consequences 는 모두 같은 이유로 저장되지 않았을 것이다. `createNewItemId` 로 나눈다. 서버는 자기가 소유하지 않은 id 를 어차피 새로 부여하므로 (`StudioRelationStore.replace`) 이 값은 "이 줄은 새것"이라는 표시일 뿐이다 — 지켜야 할 것은 형식뿐이다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XEHXspz4rv5pB5wiiSsVDu
This commit is contained in:
co-authored by
Claude Opus 5
parent
c87e0a2338
commit
0eb3c86839
@@ -1,5 +1,5 @@
|
||||
import type { components } from "../../../contracts/studio/generated.ts";
|
||||
import { createLocalId } from "../../../domain/studio/local-id.ts";
|
||||
import { createNewItemId } from "../../../domain/studio/local-id.ts";
|
||||
import { FieldNotice, type FieldIssue } from "./field-issues.tsx";
|
||||
import { OrderedTextList } from "./ordered-text-list.tsx";
|
||||
|
||||
@@ -56,7 +56,7 @@ export function QuestionFields({ draft, evidence, issues, onChange }: { draft: Q
|
||||
<label><span>선택지 {index + 1} 설명</span><textarea value={option.description} onChange={(event) => update({ options: orderedOptions(draft.options.map((candidate, candidateIndex) => candidateIndex === index ? { ...candidate, description: event.currentTarget.value } : candidate)) })} /></label>
|
||||
<div className="studio-item-actions"><button type="button" onClick={() => moveOption(index, -1)} disabled={index === 0}>위로</button><button type="button" onClick={() => moveOption(index, 1)} disabled={index === draft.options.length - 1}>아래로</button><button type="button" onClick={() => update({ options: orderedOptions(draft.options.filter((_, candidateIndex) => candidateIndex !== index)) })}>삭제</button></div>
|
||||
</div>)}
|
||||
<button className="studio-add-item" type="button" disabled={draft.options.length >= 50} onClick={() => { if (draft.options.length < 50) update({ options: orderedOptions([...draft.options, { id: createLocalId("option"), title: "", description: "", order: draft.options.length }]) }); }}>선택지 추가</button>
|
||||
<button className="studio-add-item" type="button" disabled={draft.options.length >= 50} onClick={() => { if (draft.options.length < 50) update({ options: orderedOptions([...draft.options, { id: createNewItemId(), title: "", description: "", order: draft.options.length }]) }); }}>선택지 추가</button>
|
||||
</fieldset>
|
||||
<label className="studio-field studio-field--wide"><span>다음 검증</span><textarea value={draft.nextValidation} onChange={(event) => update({ nextValidation: event.currentTarget.value })} /><FieldNotice issues={issues} path="/nextValidation" /></label>
|
||||
{draft.questionStatus === "RESOLVED" && draft.resolution ? <fieldset className="studio-resolution-fields"><legend>해결 내용</legend>
|
||||
|
||||
Reference in New Issue
Block a user