docs(TechLog): 주제 셋을 스킬대로 다시 쓰고 SSOT 를 저장소 실물로 고친다
건너뛴 참조 다섯을 읽고 나서 다시 썼다 — from-ssot-to-records.md 의 「그림과 증거는 배정 대상이다」, code-tables-diagrams.md 의 표·코드 규칙, explaining.md 의 「이름을 댔으면 왜 있는지도 댄다」. **SSOT 를 먼저 고쳤다.** §3.3 의 코드블록이 저장소와 달랐다 — PATH_PREFIX_KINDS 는 Record 표가 아니라 튜플 배열이고, 진짜 경로 표는 EXPLORE_KIND_PATHS 다. 저장소에서 확인해 실물로 바꾸고, javadoc 이 적어 둔 이유를 함께 옮겼다. §16.7 에 BRANCH_FIELDS 와 pathOf 실물을, §4.3 에 ContractRouteCoverageTest 의 javadoc 과 면제 상수 둘을 더했다. 62,643 → 65,737 자. **계약에 ssot-assets·ssot-evidence 를 배정했다.** 그 절차를 건너뛰어서 SSOT 가 이미 가진 그림과 측정이 글감에 배정되지 않은 채였다. TechLog 12 글감, keycloak-session-store 는 그림 21장·증거 19건을 배정하고 붙일 글감이 없는 그림 4장은 이유를 계약에 적었다. 배정하자 검사기가 「배정한 증거를 기록이 쓰지 않는다」 4건을 드러냈다. **주제 셋을 다시 썼다.** hand-listed-kinds 중앙값 1,925 → 3,760 자 declared-but-not-implemented → 2,608 자 values-lost-between-boundaries → 2,602 자 게시된 기록은 keycloak 4,546 · n+1liner 3,190 이다. 표와 코드를 SSOT 에서 옮기고, Reference 에 담을 수 없던 표(§5.5 의 여덟 자리)를 짝이 되는 Case 로 내렸다. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6feee5ba57
commit
a8ce0dda07
+19
-7
@@ -59,20 +59,32 @@ tech-log-frontend : 15e6ea8 이후
|
||||
|
||||
## 등록하지 않으면 옆으로 떨어진다
|
||||
|
||||
개념 삭제가 계속 질문 삭제 경로로 나갔고, 배포된 번들에서 서버 로그에 `DELETE /api/v1/studio/questions/{id} 404` 가 찍혔다. 개념 상세 주소도 마찬가지로 질문 조회를 불러 404 를 받았다.
|
||||
관리 계약의 연산은 기여 목록에 등록해야 실행할 때 부를 수 있다. 계약에서 타입은 생성되므로 등록을 빠뜨려도 에디터에서는 그 연산이 멀쩡히 보이고 컴파일도 통과한다.
|
||||
|
||||
게이트웨이가 옆 분기로 떨어지므로 서버는 정상적으로 응답하고, 다만 다른 기록을 다룬다.
|
||||
증상이 「연산을 찾을 수 없습니다」였다면 바로 보였을 것이다. 게이트웨이는 등록된 것 중에서 고르므로 옆 분기로 떨어지고, 서버는 그 요청에 정상적으로 응답한다. 다만 다른 기록을 다룬다.
|
||||
|
||||
개념 삭제가 계속 질문 삭제 경로로 나갔고, 배포된 번들에서 서버 로그에 `DELETE /api/v1/studio/questions/{id} 404` 가 찍혔다. 개념 상세 주소도 마찬가지로 질문 조회를 불러 404 를 받았다.
|
||||
|
||||
## 네 번의 누락
|
||||
|
||||
- `getPublicConcept` — 개념 화면이 질문 조회를 불렀다
|
||||
- `deleteConceptDraft` — 개념 삭제가 질문 삭제를 불렀다
|
||||
- `listStudioQuestions` 와 `listStudioProjectDecisions` — 홈 편집기가 빈 목록을 그렸다
|
||||
- 축(variant) CRUD 네 연산 — 축 화면이 데이터를 받지 못했다
|
||||
| 연산 | 화면에서 무엇으로 보였나 |
|
||||
|---|---|
|
||||
| `getPublicConcept` | 개념 화면이 질문 조회를 불러 404 |
|
||||
| `deleteConceptDraft` | 개념 삭제가 질문 삭제로 나가 404 |
|
||||
| `listStudioQuestions` · `listStudioProjectDecisions` | 홈 편집기가 빈 목록을 그림 |
|
||||
| 축 CRUD 네 연산 | 축 화면이 데이터를 받지 못함 |
|
||||
|
||||
앞의 둘은 옆 분기로 떨어져 다른 기록을 다뤘고, 뒤의 둘은 아예 값이 오지 않아 빈 화면이 됐다. 등록되지 않은 연산이 무엇으로 보이는지는 게이트웨이가 그 종류를 어떻게 고르느냐에 달려 있다.
|
||||
|
||||
## 기여 목록이 무엇을 들고 있나
|
||||
|
||||
기여 목록은 연산 이름만 나열하는 것이 아니라 그 연산이 쓰는 오류 코드 집합까지 들고 있다. 관리 계약의 오류 코드 상수는 계약의 enum 과 `1:1` 이라고 주석이 못 박아 둔다. 등록이 빠지면 그 연산에 딸린 이 배선이 전부 없는 것이 된다.
|
||||
|
||||
## 가드 둘
|
||||
|
||||
축 CRUD 를 더한 커밋에서 가드를 둘 넣었다. 공개 계약은 전수 대조한다 — 계약이 선언한 연산이 기여 목록에 전부 있는지 본다. 관리 계약은 86 operation 이라 전수 대조가 무겁고, 대신 「한 종류만 빠진 항목」을 본다. 깨진 것이 늘 그 모양이었기 때문이다.
|
||||
축 CRUD 를 더한 커밋에서 가드를 둘 넣었다. 공개 계약은 전수 대조한다 — 계약이 선언한 연산이 기여 목록에 전부 있는지 본다.
|
||||
|
||||
관리 계약은 86 operation 이라 전수 대조가 무겁다. 대신 「한 종류만 빠진 항목」을 본다. 깨진 것이 늘 그 모양이었기 때문이다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
|
||||
+27
-11
@@ -60,6 +60,12 @@ tech-log-frontend : 계약에서 생성한 타입을 그대로 사용
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## 빈 화면과 안 쓴 글이 같아 보인다
|
||||
|
||||
계약이 「이 연산이 있다」고 말하면 프론트는 그것을 부른다. 서버에 그 컨트롤러가 없으면 404 가 돌아오고, 화면은 목록 요청이 실패했을 때와 0건일 때를 같은 그림으로 그린다.
|
||||
|
||||
작성 도구에서 이것이 특히 오래 숨는다. 작성자는 자기가 쓴 것과 화면을 대조하므로, 「없습니다」를 보면 아직 안 썼거나 게시하지 않았다고 읽는다.
|
||||
|
||||
## 비어 있던 다섯 화면
|
||||
|
||||
| 무엇이 비었나 | 왜 |
|
||||
@@ -70,27 +76,37 @@ tech-log-frontend : 계약에서 생성한 타입을 그대로 사용
|
||||
| 프로젝트 활동 | 계약에 목록·생성·수정이 선언돼 있었지만 구현이 없었고 `project_activity` 는 0행이었다 |
|
||||
| 릴리즈(변경 기록) | 읽는 쪽은 있는데 쓰는 쪽이 없어, 페이지는 영원히 빈 채였다 |
|
||||
|
||||
홈 focus 가 가장 오래 숨었다. 세 슬롯이 다 비면 화면이 그 영역을 통째로 그리지 않으므로, 그런 영역이 있다는 사실조차 화면에서 알 수 없다.
|
||||
다섯이 같은 원인인데 숨은 깊이가 달랐다. 프로젝트 공개 여부와 관계 연결은 화면에 자리는 있고 값만 없으므로 「아직 안 채웠다」로 읽힌다. 홈 focus 는 그보다 깊다 — 세 슬롯이 다 비면 화면이 그 영역을 통째로 그리지 않으므로, 그런 영역이 있다는 사실조차 화면에서 알 수 없다. 그래서 운영에서 한 번도 나타난 적이 없다.
|
||||
|
||||
> 화면은 오류를 내지 않고 빈칸을 그렸고, 저는 그것을 "아직 안 쓴 글"로 읽었습니다.
|
||||
## 같은 계약이 반대 방향으로도 깨졌다
|
||||
|
||||
## 편집기가 부르던 두 목록
|
||||
빠진 구현이 화면을 비우는 것과 반대로, 있는 계약이 값을 거절하는 경우도 났다. `home_focus_config.default_focus_type` 은 마이그레이션 직후 NULL 인데 계약은 이 필드를 required 에 enum 세 값으로 선언한다.
|
||||
|
||||
`GET /v1/studio/questions` 와 `GET /v1/studio/projects/{id}/decisions` 도 같은 모양이었다. 계약에 있고 모델도 생성됐는데 컨트롤러가 없었다. 화면은 그것을 「이 프로젝트에 열린 질문이 없습니다」로 그렸고, 실제로는 넷이 있었으며 공개 사이트에도 나오고 있었다.
|
||||
배포 직후 첫 요청부터 `/home` 이 깨졌다. `HomeFocusView.resolve` 가 반드시 유효한 값 하나를 정하도록 고쳤다.
|
||||
|
||||
## 마이그레이션 직후의 값
|
||||
## 생성 모델 검사가 못 잡는 이유
|
||||
|
||||
같은 계약이 반대 방향으로도 깨졌다. `home_focus_config.default_focus_type` 은 마이그레이션 직후 NULL 인데 계약은 이 필드를 required 에 enum 세 값으로 선언한다. 배포 직후 첫 요청부터 `/home` 이 깨졌고, `HomeFocusView.resolve` 가 반드시 유효한 값 하나를 정하도록 고쳤다.
|
||||
계약에서 모델을 생성하는 단계는 스키마와 속성만 본다. 연산에 구현이 없어도 그 연산의 요청·응답 모델은 멀쩡히 만들어지고 컴파일도 통과한다. 그래서 「모델이 생성됐다」는 구현이 있다는 증거가 아니다.
|
||||
|
||||
## 계약과 컨트롤러를 전수로 맞춘다
|
||||
|
||||
`ContractRouteCoverageTest` 가 `@RestController` 들을 리플렉션으로 훑어 매핑을 모으고 계약이 선언한 경로와 대조한다.
|
||||
`ContractRouteCoverageTest` 가 `@RestController` 들을 리플렉션으로 훑어 매핑을 모으고 계약이 선언한 경로와 대조한다. 기대 목록을 손으로 적지 않고 계약에서 읽으므로, 연산을 더하고 컨트롤러를 잊으면 여기서 멈춘다.
|
||||
|
||||
- 작업본 API 로 대체된 옛 연산 51개는 `SUPERSEDED_BY_WORKING_COPY_API` 로 명시한다 — 「구현하지 않기로 한 것」과 「빠뜨린 것」은 다르다
|
||||
- 봉투 없이 바이트를 주는 `/media` 하나만 `ELSEWHERE` 로 면제한다
|
||||
- 매핑을 떼어 보고 그 연산 하나를 정확히 짚는 것을 확인했다
|
||||
면제는 상수 둘로 명시한다.
|
||||
|
||||
프론트에도 같은 가드를 뒀다. 양쪽에서 봐야 한쪽만 지웠을 때 잡힌다.
|
||||
```java
|
||||
private static final Set<String> ELSEWHERE = Set.of("getPublicMedia");
|
||||
private static final Set<String> SUPERSEDED_BY_WORKING_COPY_API =
|
||||
Set.of(
|
||||
"acceptProjectDecision",
|
||||
"addQuestionUpdate",
|
||||
"archiveCase",
|
||||
…);
|
||||
```
|
||||
|
||||
작업본 API 로 대체된 옛 연산 51개가 뒤쪽 목록에 있다. 이것을 적어 두지 않으면 대조 결과가 51건의 실패로 나오고, 그렇게 되면 아무도 결과를 읽지 않는다. 봉투 없이 바이트를 주는 미디어 연산 하나만 앞쪽 목록으로 면제한다.
|
||||
|
||||
매핑을 떼어 보고 그 연산 하나를 정확히 짚는 것을 확인했다. 프론트에도 같은 가드를 뒀다 — 양쪽에서 봐야 한쪽만 지웠을 때 잡힌다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
|
||||
+17
-6
@@ -28,29 +28,36 @@ source:
|
||||
|
||||
## 목적
|
||||
|
||||
계약이 선언한 연산에 구현이 없는 상태를 배포 전에 잡는다. 이 상태는 오류를 내지 않는다 — 서버는 404 를 주고 화면은 그것을 빈 데이터로 그린다.
|
||||
계약이 선언한 연산에 구현이 없는 상태를 배포 전에 잡는다.
|
||||
|
||||
이 상태는 오류를 내지 않는다. 서버는 404 를 주고 화면은 그것을 빈 데이터로 그린다. 계약에서 모델을 생성하는 단계도 스키마와 속성만 보므로 구현이 없어도 모델이 만들어지고 컴파일이 통과한다.
|
||||
|
||||
## 규칙
|
||||
|
||||
**서버 쪽은 매핑을 리플렉션으로 모아 계약의 경로와 전수 대조한다**
|
||||
`@RestController` 들을 훑어 실제 매핑을 모으고, 계약이 선언한 경로 전부와 맞춘다.
|
||||
`@RestController` 들을 훑어 실제 매핑을 모으고 계약이 선언한 경로 전부와 맞춘다. 기대 목록을 손으로 적으면 그 목록이 또 하나의 손 목록이 되므로 계약에서 읽는다.
|
||||
|
||||
**화면 쪽은 계약이 선언한 연산이 기여 목록에 등록됐는지 본다**
|
||||
타입은 계약에서 생성되므로 등록을 빠뜨려도 컴파일이 통과한다. 그 상태에서 부르면 게이트웨이가 옆 분기로 떨어져 다른 연산이 실행된다.
|
||||
타입은 계약에서 생성되므로 등록을 빠뜨려도 에디터에서 그 연산이 보이고 컴파일이 통과한다. 그 상태에서 부르면 게이트웨이가 등록된 것 중에서 고르므로 옆 분기로 떨어지고, 서버는 그 요청에 정상 응답한다.
|
||||
|
||||
**구현하지 않기로 한 연산은 이유와 함께 명시 목록에 넣는다**
|
||||
「빠뜨린 것」과 구분되지 않으면 대조 결과가 곧 무시된다. 이 저장소는 작업본 API 로 대체된 옛 연산 51개를 그렇게 표시하고, 봉투 없이 바이트를 주는 연산 하나를 면제 목록에 뒀다.
|
||||
「빠뜨린 것」과 구분되지 않으면 대조 결과가 곧 무시된다. 이 저장소는 작업본 API 로 대체된 옛 연산 51개를 그 이름의 상수에 담고, 봉투 없이 바이트를 주는 연산 하나를 별도 상수로 면제한다. 면제가 코드에 이름으로 남아 다음 사람이 세어 볼 수 있다.
|
||||
|
||||
**두 쪽 다 돌린다**
|
||||
한쪽만 대조하면 다른 쪽을 지웠을 때 잡히지 않는다.
|
||||
|
||||
**생성 모델 검사를 이 대조로 세지 않는다**
|
||||
모델 생성은 schema 와 property 만 본다. 구현이 없어도 모델은 멀쩡히 만들어진다.
|
||||
모델 생성은 스키마와 속성만 본다. 구현이 없어도 모델은 멀쩡히 만들어진다.
|
||||
|
||||
**전수 대조가 무거우면 깨지는 모양으로 좁힌다**
|
||||
관리 계약은 86 operation 이라 전수 대조가 무겁다. 이 저장소는 대신 「한 종류만 빠진 항목」을 보게 했다 — 깨진 것이 늘 그 모양이었기 때문이다. 좁힌 기준은 무엇을 보지 않는지도 함께 적는다.
|
||||
|
||||
## 적용 조건
|
||||
|
||||
계약이 한 저장소에 있고 두 저장소가 그것을 반입해 각자 구현하는 구조. 연산을 더하거나 지우는 변경에서 이 대조를 돌린다.
|
||||
|
||||
화면이 「데이터가 없습니다」를 그리는데 저장소에는 값이 있을 때 이 대조를 먼저 본다. 구현이 없어서 404 인 것과 정말 0건인 것이 화면에서 같아 보인다.
|
||||
|
||||
## 예외
|
||||
|
||||
계약과 구현이 같은 저장소에 있고 같은 빌드를 지나면 컴파일러가 이 대조를 대신한다.
|
||||
@@ -61,4 +68,8 @@ source:
|
||||
|
||||
매핑 하나를 떼어 보고 대조 검사가 그 연산 하나를 정확히 짚는 것을 확인한 뒤 커밋했다.
|
||||
|
||||
관리 계약은 86 operation 이라 전수 대조 대신 「한 종류만 빠진 항목」을 보게 했다. 깨진 것이 늘 그 모양이었다.
|
||||
두 목록 조회에 컨트롤러가 없어 홈 편집기가 「이 프로젝트에 열린 질문이 없습니다」를 그렸다. 실제로는 넷이 있었고 공개 사이트에도 나오고 있었다.
|
||||
|
||||
옛 연산 51개를 명시하지 않았다면 대조 결과가 51건의 실패로 나와 아무도 읽지 않았을 것이다.
|
||||
|
||||
기여 목록에 등록하지 않은 연산 넷을 만났다. 둘은 옆 분기로 떨어져 다른 기록을 다뤘고 둘은 빈 목록이 됐다.
|
||||
|
||||
Reference in New Issue
Block a user