docs(TechLog): 글감 56개를 기록으로 쓴다
주제 13개 · Case 28 · Concept 5 · Reference 15 · Question 4 · Decision 4. 계약의 노드마다 종류가 요구하는 칸을 채우고, 본문이 있는 두 종류에는 SSOT 가 이미 그려 둔 도식 셋(value-boundaries · decision-path-404 · topic-variant-model)을 tech-log-studio/ 로 옮겨 붙였다. 새로 그린 그림은 없다. 검사 셋 전부 통과한다. check_body.mjs 56 편 중 본문이 있는 33 편 PASS check_prose.mjs 56 편 error 0 check_evidence.mjs --repo 포함 문제 없음 verify-tech-log-tree.py 프로젝트 5 · error 0 · warn 0 인용한 코드블록은 전부 SSOT 에서 찾아 대조했다. check_evidence.mjs 가 본문의 각 줄과 source 앵커와 계약 제목을 다시 확인한다. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6955611439
commit
f6c825e858
+81
@@ -0,0 +1,81 @@
|
||||
---
|
||||
kind: CASE
|
||||
slug: an-operation-you-can-see-but-cannot-call
|
||||
title: 타입에는 보이는데 부를 수 없는 연산이 네 번 나왔다
|
||||
topic: declared-but-not-implemented
|
||||
topicName: 계약에 선언만 있고 구현이 없다
|
||||
project: TechLog
|
||||
status: 게시 전
|
||||
sourceRevision: tech-log@2026-09-02
|
||||
source:
|
||||
- final/document.md#§4.4
|
||||
---
|
||||
|
||||
# 타입에는 보이는데 부를 수 없는 연산이 네 번 나왔다
|
||||
|
||||
계약에서 타입이 생성되므로 에디터에서는 그 연산이 멀쩡히 보인다. 기여 목록에 등록하지 않으면 실행할 때 부를 수가 없고, 게이트웨이는 다른 연산으로 떨어진다. 개념 화면이 질문 조회를 부르고 개념 삭제가 질문 삭제를 불렀다. 같은 누락을 네 번 만났다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **계약에 선언만 있고 구현이 없어 화면 다섯 곳이 비어 있었다**
|
||||
이쪽은 서버에 구현이 없었고, 여기서는 프론트가 등록을 빠뜨렸다.
|
||||
- **계약과 구현은 서버와 화면 양쪽에서 전수 대조한다**
|
||||
이 누락을 잡는 가드가 그 기준에 있다.
|
||||
- **구현이 종류를 좁게 적어도 넓은 포트를 만족했다**
|
||||
같은 개념 삭제 경로에서 타입 검사가 통과시킨 다른 결함이다.
|
||||
|
||||
## 문제
|
||||
|
||||
관리 계약의 연산은 `tech-log-management-contract-contribution.ts` 에 등록해야 실행 시 부를 수 있다. 계약에서 타입은 생성되므로 등록을 빠뜨려도 컴파일은 통과한다.
|
||||
|
||||
등록되지 않은 연산을 부르면 게이트웨이가 그 연산을 찾지 못하고 옆의 분기로 떨어진다. 그래서 증상이 「없는 연산」이 아니라 「다른 연산이 실행됨」으로 나온다.
|
||||
|
||||
## 결론
|
||||
|
||||
네 번 났고 전부 같은 원인이었다.
|
||||
|
||||
`getPublicConcept` : 개념 화면이 질문 조회를 불렀다
|
||||
`deleteConceptDraft` : 개념 삭제가 질문 삭제를 불렀다
|
||||
`listStudioQuestions` · `listStudioProjectDecisions` : 홈 편집기가 빈 목록을 그렸다
|
||||
축(variant) CRUD 네 연산 : 축 화면이 데이터를 받지 못했다
|
||||
|
||||
공개 계약은 전수 대조하고, 관리 계약은 「한 종류만 빠진 항목」을 보는 가드를 뒀다. 깨진 것이 늘 그 모양이었다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
tech-log-frontend : 15e6ea8 이후
|
||||
계약 : studio-management-v1 86 operation · public-v1 20 operation
|
||||
확인 방식 : 계약이 선언한 연산과 기여 목록을 대조하는 테스트
|
||||
|
||||
## 재현 조건
|
||||
|
||||
1. 계약에 연산을 더하고 타입을 생성한다
|
||||
2. 기여 목록에 등록하지 않은 채 그 연산을 부르는 화면을 연다
|
||||
3. 개발자도구 네트워크에서 실제로 나가는 경로를 본다 — 등록된 다른 연산의 경로가 나간다
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## 등록하지 않으면 옆으로 떨어진다
|
||||
|
||||
개념 삭제가 계속 질문 삭제 경로로 나갔고, 배포된 번들에서 서버 로그에 `DELETE /api/v1/studio/questions/{id} 404` 가 찍혔다. 개념 상세 주소도 마찬가지로 질문 조회를 불러 404 를 받았다.
|
||||
|
||||
증상이 「연산을 찾을 수 없습니다」였다면 바로 보였을 것이다. 게이트웨이가 옆 분기로 떨어지므로 서버는 정상적으로 응답하고, 다만 다른 기록을 다룬다.
|
||||
|
||||
## 네 번의 누락
|
||||
|
||||
- `getPublicConcept` — 개념 화면이 질문 조회를 불렀다
|
||||
- `deleteConceptDraft` — 개념 삭제가 질문 삭제를 불렀다
|
||||
- `listStudioQuestions` 와 `listStudioProjectDecisions` — 홈 편집기가 빈 목록을 그렸다
|
||||
- 축(variant) CRUD 네 연산 — 축 화면이 데이터를 받지 못했다
|
||||
|
||||
## 가드 둘
|
||||
|
||||
축 CRUD 를 더한 커밋에서 가드를 둘 넣었다. 공개 계약은 전수 대조한다 — 계약이 선언한 연산이 기여 목록에 전부 있는지 본다. 관리 계약은 86 operation 이라 전수 대조가 무겁고, 대신 「한 종류만 빠진 항목」을 본다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
관리 계약 쪽 가드는 종류가 빠진 것만 본다. 연산 전체를 빠뜨리는 경우는 이 가드가 잡지 않고, 그 상태를 만들어 확인하지도 않았다.
|
||||
|
||||
<!-- body:end -->
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
---
|
||||
kind: CASE
|
||||
slug: five-screens-were-quietly-empty
|
||||
title: 계약에 선언만 있고 구현이 없어 화면 다섯 곳이 비어 있었다
|
||||
topic: declared-but-not-implemented
|
||||
topicName: 계약에 선언만 있고 구현이 없다
|
||||
project: TechLog
|
||||
status: 게시 전
|
||||
sourceRevision: tech-log@2026-09-02
|
||||
source:
|
||||
- final/document.md#§4.1
|
||||
- final/document.md#§4.2
|
||||
- final/document.md#§10.4
|
||||
---
|
||||
|
||||
# 계약에 선언만 있고 구현이 없어 화면 다섯 곳이 비어 있었다
|
||||
|
||||
홈의 「지금 집중하는 것」 영역은 화면에 나타난 적이 없었고, 프로젝트는 공개할 방법이 없었고, 어떤 기록도 다른 기록을 연결 대상으로 고를 수 없었다. 계약에는 그 연산들이 전부 선언돼 있었다. 서버에 구현이 없었고, 프론트는 계약을 믿고 불렀고, 화면은 404 를 「데이터가 없음」으로 그렸다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **타입에는 보이는데 부를 수 없는 연산이 네 번 나왔다**
|
||||
같은 시기에 난 다른 부류의 누락이다. 이쪽은 서버에 구현이 없었고 그쪽은 프론트가 등록을 빠뜨렸다.
|
||||
- **계약과 구현은 서버와 화면 양쪽에서 전수 대조한다**
|
||||
이 사건 뒤에 세운 기준이다.
|
||||
- **「이 프로젝트에 열린 질문이 없습니다」 — 실제로는 넷이 있었다**
|
||||
같은 404 를 화면이 어떻게 그렸는지가 그 기록에 있다.
|
||||
|
||||
## 문제
|
||||
|
||||
계약이 「이 연산이 있다」고 말하면 프론트는 그것을 부른다. 서버에 그 컨트롤러가 없으면 404 가 돌아오고, 화면은 그 404 를 빈 목록으로 그린다.
|
||||
|
||||
빈 목록과 「아직 안 쓴 글」은 화면에서 같아 보인다. 그래서 다섯 화면이 비어 있는 동안 아무도 오류를 보지 못했다.
|
||||
|
||||
## 결론
|
||||
|
||||
다섯 화면이 비어 있었고 원인은 하나였다. 계약에 선언만 있고 구현이 없었다.
|
||||
|
||||
홈 「지금 집중하는 것」 : 세 슬롯이 다 비면 영역 자체를 그리지 않아 운영에서 나타난 적이 없다
|
||||
프로젝트 공개 여부 : 투영의 PROJECT 행을 세우는 경로가 없어 영원히 비공개였다
|
||||
문서 사이 관계 연결 : 어댑터의 RELATION 과 EVIDENCE 가 빈 목록 스텁이었다
|
||||
프로젝트 활동 : 목록·생성·수정이 계약에 있고 테이블은 0행이었다
|
||||
릴리즈 : 읽는 쪽만 있고 쓰는 쪽이 없었다
|
||||
|
||||
생성 모델 검사는 schema 와 property 만 보므로 이 구멍을 잡지 못한다. 모델은 멀쩡히 생성된다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
tech-log-backend : 365560e 이후
|
||||
tech-log-frontend : 계약에서 생성한 타입을 그대로 사용
|
||||
확인 방식 : 계약이 선언한 연산과 `@RestController` 매핑을 리플렉션으로 대조
|
||||
|
||||
## 재현 조건
|
||||
|
||||
1. 계약에 연산을 하나 선언하고 컨트롤러는 만들지 않는다
|
||||
2. 프론트에서 그 연산을 부르는 화면을 연다 — 404 가 돌아오고 화면은 빈 목록을 그린다
|
||||
3. `ContractRouteCoverageTest` 를 돌린다 — 그 연산 하나를 짚는다
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## 비어 있던 다섯 화면
|
||||
|
||||
| 무엇이 비었나 | 왜 |
|
||||
|---|---|
|
||||
| 홈 「지금 집중하는 것」 | `home_focus_config` 는 마이그레이션이 빈 행 하나만 넣었고, `getHomeFocus`/`updateHomeFocus` 는 구현이 없었다 |
|
||||
| 프로젝트 공개 여부 | 프로젝트는 `RecordKind` 에 없어 문서 게시 파이프라인을 타지 못하는데, 공개 화면들은 전부 `public_resource_projection` 의 PROJECT 행을 가시성 관문으로 쓴다 |
|
||||
| 문서 사이 관계 연결 | `JdbcCatalogQueryAdapter` 의 RELATION/EVIDENCE 가 「슬라이스 2·5에서 채운다」는 주석과 함께 `List.of()` 스텁이었다 |
|
||||
| 프로젝트 활동 | 계약에 목록·생성·수정이 선언돼 있었지만 구현이 없었고 `project_activity` 는 0행이었다 |
|
||||
| 릴리즈(변경 기록) | 읽는 쪽은 있는데 쓰는 쪽이 없어, 페이지는 영원히 빈 채였다 |
|
||||
|
||||
홈 focus 가 가장 오래 숨었다. 세 슬롯이 다 비면 화면이 그 영역을 통째로 그리지 않으므로, 그런 영역이 있다는 사실조차 화면에서 알 수 없다.
|
||||
|
||||
## 편집기가 부르던 두 목록
|
||||
|
||||
`GET /v1/studio/questions` 와 `GET /v1/studio/projects/{id}/decisions` 도 같은 모양이었다. 계약에 있고 모델도 생성됐는데 컨트롤러가 없었다. 화면은 그것을 「이 프로젝트에 열린 질문이 없습니다」로 그렸고, 실제로는 넷이 있었으며 공개 사이트에도 나오고 있었다.
|
||||
|
||||
## 마이그레이션 직후의 값
|
||||
|
||||
같은 계약이 반대 방향으로도 깨졌다. `home_focus_config.default_focus_type` 은 마이그레이션 직후 NULL 인데 계약은 이 필드를 required 에 enum 세 값으로 선언한다. 배포 직후 첫 요청부터 `/home` 이 깨졌고, `HomeFocusView.resolve` 가 반드시 유효한 값 하나를 정하도록 고쳤다.
|
||||
|
||||
## 계약과 컨트롤러를 전수로 맞춘다
|
||||
|
||||
`ContractRouteCoverageTest` 가 `@RestController` 들을 리플렉션으로 훑어 매핑을 모으고 계약이 선언한 경로와 대조한다.
|
||||
|
||||
- 작업본 API 로 대체된 옛 연산 51개는 `SUPERSEDED_BY_WORKING_COPY_API` 로 명시한다
|
||||
- 봉투 없이 바이트를 주는 `/media` 하나만 `ELSEWHERE` 로 면제한다
|
||||
- 매핑을 떼어 보고 그 연산 하나를 정확히 짚는 것을 확인했다
|
||||
|
||||
프론트에도 같은 가드를 뒀다. 양쪽에서 봐야 한쪽만 지웠을 때 잡힌다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
홈 focus 의 옛 증상은 재현할 수 없다. 세 슬롯이 다 비면 영역을 그리지 않으므로 화면에 남은 흔적이 없고, 지금 고쳐져 있다는 것만 확인했다.
|
||||
|
||||
<!-- body:end -->
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
---
|
||||
kind: REFERENCE
|
||||
slug: compare-the-contract-with-both-implementations
|
||||
title: 계약과 구현은 서버와 화면 양쪽에서 전수 대조한다
|
||||
topic: declared-but-not-implemented
|
||||
topicName: 계약에 선언만 있고 구현이 없다
|
||||
project: TechLog
|
||||
status: 게시 전
|
||||
verifiedOn: 2026-09-04
|
||||
sourceRevision: tech-log@2026-09-02
|
||||
source:
|
||||
- final/document.md#§4.3
|
||||
- final/document.md#§4.4
|
||||
---
|
||||
|
||||
# 계약과 구현은 서버와 화면 양쪽에서 전수 대조한다
|
||||
|
||||
계약이 한 저장소에 있고 두 저장소가 그것을 반입해 각자 구현하면, 어느 한쪽이 빠뜨린 것을 컴파일러가 보지 못한다. 이 저장소에서 그 구멍이 서버 쪽으로 다섯 번, 화면 쪽으로 네 번 났다. 두 쪽 모두에서 계약과 대조하는 검사를 돌린다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **계약에 선언만 있고 구현이 없어 화면 다섯 곳이 비어 있었다**
|
||||
서버 쪽 누락의 근거 사건이다.
|
||||
- **타입에는 보이는데 부를 수 없는 연산이 네 번 나왔다**
|
||||
화면 쪽 누락의 근거 사건이다.
|
||||
- **종류를 나열하는 곳은 컴파일러나 계약 대조 검사가 세게 만든다**
|
||||
컴파일러가 볼 수 있는 범위 안쪽을 다루는 짝이 되는 기준이다.
|
||||
|
||||
## 목적
|
||||
|
||||
계약이 선언한 연산에 구현이 없는 상태를 배포 전에 잡는다. 이 상태는 오류를 내지 않는다 — 서버는 404 를 주고 화면은 그것을 빈 데이터로 그린다.
|
||||
|
||||
## 규칙
|
||||
|
||||
**서버 쪽은 매핑을 리플렉션으로 모아 계약의 경로와 전수 대조한다**
|
||||
`@RestController` 들을 훑어 실제 매핑을 모으고, 계약이 선언한 경로 전부와 맞춘다.
|
||||
|
||||
**화면 쪽은 계약이 선언한 연산이 기여 목록에 등록됐는지 본다**
|
||||
타입은 계약에서 생성되므로 등록을 빠뜨려도 컴파일이 통과한다. 그 상태에서 부르면 게이트웨이가 옆 분기로 떨어져 다른 연산이 실행된다.
|
||||
|
||||
**구현하지 않기로 한 연산은 이유와 함께 명시 목록에 넣는다**
|
||||
「빠뜨린 것」과 구분되지 않으면 대조 결과가 곧 무시된다. 이 저장소는 작업본 API 로 대체된 옛 연산 51개를 그렇게 표시하고, 봉투 없이 바이트를 주는 연산 하나를 면제 목록에 뒀다.
|
||||
|
||||
**두 쪽 다 돌린다**
|
||||
한쪽만 대조하면 다른 쪽을 지웠을 때 잡히지 않는다.
|
||||
|
||||
**생성 모델 검사를 이 대조로 세지 않는다**
|
||||
모델 생성은 schema 와 property 만 본다. 구현이 없어도 모델은 멀쩡히 만들어진다.
|
||||
|
||||
## 적용 조건
|
||||
|
||||
계약이 한 저장소에 있고 두 저장소가 그것을 반입해 각자 구현하는 구조. 연산을 더하거나 지우는 변경에서 이 대조를 돌린다.
|
||||
|
||||
## 예외
|
||||
|
||||
계약과 구현이 같은 저장소에 있고 같은 빌드를 지나면 컴파일러가 이 대조를 대신한다.
|
||||
|
||||
연산이 봉투 규약을 따르지 않으면 경로 대조에서 뺀다. 다만 뺀 이유를 목록에 적는다.
|
||||
|
||||
## 예시
|
||||
|
||||
매핑 하나를 떼어 보고 대조 검사가 그 연산 하나를 정확히 짚는 것을 확인한 뒤 커밋했다.
|
||||
|
||||
관리 계약은 86 operation 이라 전수 대조 대신 「한 종류만 빠진 항목」을 보게 했다. 깨진 것이 늘 그 모양이었다.
|
||||
|
||||
옛 연산 51개를 명시하지 않았다면 대조 결과가 51건의 실패로 나와 아무도 읽지 않았을 것이다.
|
||||
Reference in New Issue
Block a user