{ "version": "1.1", "id": "decision-path-404", "title": "결정 주소가 게시 시점에 굳어져 방문자가 404 를 만나기까지", "question": "계약은 앵커 주소를 적어 두었는데 방문자는 왜 404 를 만났는가?", "type": "sequence", "direction": "TB", "audience": ["백엔드 개발자", "프론트엔드 개발자"], "summary": "만드는 쪽 두 곳이 계약과 다른 슬래시 주소를 만들었고, 그 주소가 게시 시점에 저장돼 방문자에게 그대로 나갔다.", "alt": "계약, 게시 시점 경로 생성, 저장 테이블, 조회 시점 경로 생성, 방문자, 공개 라우트 여섯 참가자 사이에서 주소가 만들어져 저장되고 방문 시 404 로 끝나는 순서도.", "long_description": "위에서 아래로 여섯 번의 이동이 있다. 계약 ProjectDecisionItem 은 공개 주소가 decisions#{slug} 앵커라고 규정한다. 게시 시점의 PublicPaths.forKind 는 그 대신 decisions/{slug} 를 만들어 public_resource_projection 에 저장한다. 조회 시점의 PublicSql.pathOf 가 저장된 주소를 읽고 방문자에게 링크로 내보낸다. 방문자가 그 주소를 요청하면 공개 라우트에는 projects/{slug}/decisions 하나뿐이라 맞는 라우트가 없고 404 가 돌아온다.", "source_context": { "document": "document.md", "document_sha256": "93b9fec4884efa0e6231de07dc27e2b0ac36c9052d3720e28d102d9747ac4f8f", "anchor": { "kind": "marker", "value": "decision-path-404", "line": 673 } }, "composition": { "profile": "sequence", "diagram_only": true, "reference_ids": ["payment-approval-sequence"], "rationale": "본문은 주소가 계약에서 규정되고, 게시 시점에 만들어져 저장되고, 조회 시점에 읽혀 방문자에게 나가고, 방문했을 때 404 가 되는 순서를 적는다. 「주소가 게시 시점에 굳는다」가 이 결함의 핵심이라 시점의 순서가 그림의 뼈대여야 한다." }, "nodes": [ { "id": "contract", "label": "계약 ProjectDecisionItem", "kind": "participant", "role": "participant", "description": "공개 주소를 앵커로 규정한 OpenAPI 계약.", "evidence": [{ "start_line": 676, "end_line": 678 }], "assumption": false }, { "id": "publish-path", "label": "PublicPaths.forKind", "kind": "participant", "role": "participant", "details": ["게시 시점"], "emphasis": "warning", "description": "게시할 때 공개 주소를 만드는 코드.", "evidence": [{ "start_line": 677, "end_line": 678 }], "assumption": false }, { "id": "projection", "label": "public_resource_projection", "kind": "participant", "role": "participant", "description": "만들어진 주소가 저장되는 투영 테이블.", "evidence": [{ "start_line": 682, "end_line": 683 }], "assumption": false }, { "id": "read-path", "label": "PublicSql.pathOf", "kind": "participant", "role": "participant", "details": ["조회 시점"], "emphasis": "warning", "description": "조회할 때 공개 주소를 만드는 코드.", "evidence": [{ "start_line": 677, "end_line": 678 }], "assumption": false }, { "id": "visitor", "label": "방문자", "kind": "actor", "role": "participant", "description": "「다음에 읽을 것」 링크를 따라간 사람.", "evidence": [{ "start_line": 670, "end_line": 671 }], "assumption": false }, { "id": "public-route", "label": "공개 라우트", "kind": "participant", "role": "participant", "details": ["/projects/{slug}/decisions 하나뿐"], "description": "결정에는 상세 화면이 없어 라우트가 하나뿐이다.", "evidence": [{ "start_line": 675, "end_line": 676 }], "assumption": false } ], "edges": [ { "id": "m1", "from": "contract", "to": "publish-path", "label": "…/decisions#{slug} 로 규정", "kind": "request", "order": 1, "evidence": [{ "start_line": 676, "end_line": 678 }], "assumption": false }, { "id": "m2", "from": "publish-path", "to": "projection", "label": "…/decisions/{slug} 저장", "kind": "request", "order": 2, "emphasis": "warning", "evidence": [{ "start_line": 675, "end_line": 678 }], "assumption": false }, { "id": "m3", "from": "projection", "to": "read-path", "label": "저장된 주소 조회", "kind": "response", "order": 3, "evidence": [{ "start_line": 682, "end_line": 683 }], "assumption": false }, { "id": "m4", "from": "read-path", "to": "visitor", "label": "같은 형태로 링크 전달", "kind": "response", "order": 4, "evidence": [{ "start_line": 677, "end_line": 678 }], "assumption": false }, { "id": "m5", "from": "visitor", "to": "public-route", "label": "…/decisions/{slug} 요청", "kind": "request", "order": 5, "evidence": [{ "start_line": 670, "end_line": 675 }], "assumption": false }, { "id": "m6", "from": "public-route", "to": "visitor", "label": "404", "kind": "response", "order": 6, "emphasis": "warning", "evidence": [{ "start_line": 670, "end_line": 675 }], "assumption": false } ], "legend": [], "metadata": { "rationale": "고친 것 세 갈래(만드는 쪽 수정·V15 마이그레이션·resolvesToPublicRoute)는 같은 절의 문장으로 남긴다. 그림은 결함이 생긴 순서만 담는다.", "profile_deviation": "없음. sequence 는 techviz references 가 고른 후보 안에 있다." } }