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:
DongHyeonka
2026-09-07 18:54:45 +09:00
co-authored by Claude Opus 5
parent 6feee5ba57
commit a8ce0dda07
15 changed files with 484 additions and 132 deletions
@@ -61,24 +61,38 @@ tech-log-frontend : 31afb4d 이후
## 상세가 없으면 목록이 전부 실어야 한다
결정은 자기 화면을 갖지 않는다. 공개 라우트는 `/projects/{slug}/decisions` 하나이고, 개별 결정은 그 목록 위의 앵커로 간다.
결정은 자기 화면을 갖지 않는다. 공개 라우트는 프로젝트의 결정 목록 하나이고, 개별 결정은 그 목록 위의 앵커로 간다.
상세를 부를 곳이 없으므로 화면이 그리는 칸이 전부 목록 항목에 있어야 한다.
앞선 종류들과 여기서 갈린다. Case 와 Reference 와 Question 은 상세 화면이 있어서 목록 항목이 가볍고 상세를 부르면 나머지가 온다. 결정에는 상세를 부를 곳이 없으므로 화면이 그리는 칸이 전부 목록 항목에 있어야 한다.
## 네 증상이 한 원인이었다
`ProjectDecisionItem``title` 이 없어서 프론트가 `statement` 를 제목 칸에 썼다. 결정문은 한 문장이 아니라 문단일 수 있으므로 제목 칸에 전문이 들어갔다.
목록 항목에 `title`·`summary`·`consequences`·`evidence` 가 빠져 있었고, 화면 쪽이 없는 칸을 각자 메우고 있었다.
`summary` 가 없어서 요약 줄이 비었다. `consequences``evidence` 가 없어서 프론트가 그 둘을 빈 배열로 고정해 뒀다.
| 화면에서 무엇이 보였나 | 목록 항목에 무엇이 없었나 | 프론트가 무엇으로 메웠나 |
|---|---|---|
| 제목 자리에 결정문 전문 | `title` | `statement` 를 제목 자리에 씀 |
| 요약 줄이 비어 있음 | `summary` | 아무것도 그리지 않음 |
| 영향과 근거가 늘 비어 있음 | `consequences` · `evidence` | 빈 배열로 고정 |
줄바꿈은 다른 이유였다. 마크다운이 아닌 칸의 줄바꿈을 화면이 접고 있었다.
결정문은 한 문장이 아니라 문단일 수 있다. 제목 자리에 전문이 들어가면 목록의 한 줄이 문단 높이로 늘어난다.
## DB 에는 값이 다 있었다
줄바꿈은 원인이 달랐다. 마크다운이 아닌 칸의 줄바꿈을 화면이 접고 있었고, 그것은 목록 항목의 문제가 아니라 렌더링의 문제였다.
DB 를 조회하면 작성자가 쓴 제목과 여러 줄 요약과 영향 4건이 있었다. 어느 것도 화면까지 오지 못했다.
## 저장된 값은 그대로 있었다
데이터베이스를 조회하면 작성자가 쓴 제목과 여러 줄 요약과 영향 4건이 있었다. 어느 것도 화면까지 오지 못했다.
Studio 편집기에서는 그 값들이 다 보인다. 두 화면이 같은 데이터를 보는데 한쪽만 비었으므로, 다른 것은 그 사이에 놓인 계약이다.
## 화면 쪽에서 역으로 확인한다
이 부류는 응답에서 출발하면 보이지 않는다. 응답에 없는 칸을 찾는 일이기 때문이다.
화면이 그리는 칸을 먼저 적고 그 칸이 응답에 있는지 하나씩 맞춰야 한다. 상세 endpoint 가 없는 종류에서는 그 목록이 곧 문서 전체가 된다.
## 확인하지 못한 것
저장된 값은 조회로 확인했다. 그 시점의 화면 캡처는 남기지 않았다.
저장된 값이 그대로였다는 것은 조회로 확인했다. 그 시점의 화면 캡처는 남기지 않았다.
<!-- body:end -->
@@ -69,21 +69,45 @@ tech-log-backend : a5f93b9 이후
const summary = body.purposeSummary as string; // 계약에 그런 칸이 없다
```
`as string` 이 붙어 있으므로 컴파일러는 그 이름이 응답 타입에 있는지 묻지 않는다. 실행하면 `undefined` 가 나오고 화면은 빈 문자열을 그린다.
`as` 는 「이 값을 이 타입으로 다루겠다」는 선언이므로, 컴파일러는 그 이름이 응답 타입에 있는지 묻지 않는다. 실행하면 `undefined` 가 나오고 화면은 빈 문자열을 그린다.
계약이 주는 이름은 `scopeSummary`, `appliesTo`, `excludedScope` 다.
| 게이트웨이가 읽던 이름 | 계약이 주는 이름 |
|---|---|
| `purposeSummary` | `scopeSummary` |
| `applyWhenMarkdown` | `appliesTo` |
| `exceptionsMarkdown` | `excludedScope` |
| `examplesMarkdown` | (해당 칸 없음) |
## 본문이 다른 테이블에 있었다
번째 원인은 저장 구조였다. Reference 의 본문은 문서 본문 칸이 아니라 `reference_detail` 의 규칙과 예시에 들어 있다. Studio 편집기가 규칙을 제목과 본문으로 나눠 받고 마크다운 본문을 비워 두기 때문이다.
번째 원인을 고쳐도 본문은 여전히 비어 있다. 두 번째 원인이 저장 구조에 있었다.
공개 조회는 문서 본문만 읽고 `content: ""` 를 내보냈다. 첫 번째 원인을 고쳐도 본문은 여전히 비어 있었다.
Reference 의 본문은 문서 본문 칸이 아니라 규칙과 예시를 담는 별도 테이블에 들어 있다. Studio 편집기가 규칙을 제목과 본문으로 나눠 받고 마크다운 본문을 비워 두기 때문이다. 공개 조회는 문서 본문만 읽고 빈 문자열을 내보냈다.
한 종류의 저장 구조가 다른 종류와 다르면 조회 쪽이 그것을 알아야 한다. 여기서는 몰랐다.
## 같은 신호가 여덟 번 더 있었다
계약에 칸이 없어 값이 화면에 오지 못한 것이 이 건 말고도 여덟 번 있었다.
| 무엇이 비었나 | 원인 |
|---|---|
| 문서 요약(제목 아래 한 줄) | 공개 응답에 `summary` 자리가 없어 유형별 요약을 대신 씀 |
| 프로젝트 「주요 주제」 | `project_topic` 테이블도 조인도 가능했는데 응답에 실을 칸이 없었다 |
| 프로젝트 기록 목록의 요약·주제·게시일 | `RelatedEntry` 를 그대로 실어 칸이 없었다 |
| 질문 목록의 주제 | 지식 목록은 처음부터 `primaryTopic` 을 실었는데 질문 목록만 빠짐 |
| 프로젝트·주제의 논지 | 담을 칸이 없어 `purpose` 를 대신 보여 줌 |
| 주제 목록의 논지·축 | 이름과 개수만 실어, 독자가 들어갈지 말지 정할 근거가 없었다 |
| 프로젝트 목록 행의 slug | 다른 목록이 프로젝트를 가리킬 때 쓰는 것은 id 가 아니라 slug 인데 행이 싣지 않았다 |
| 결정 목록 항목의 slug | 공개 주소가 앵커인데 항목에 slug 가 없어 화면이 앵커를 달 수 없었다 |
문서 요약은 증상이 조금 다르다. 빈 칸에 유형별 요약을 대신 넣었더니 머리말이 바로 아래와 같은 글을 두 번 말했다 — 비어 보이는 대신 겹쳐 보였다.
## 값이 아니라 이름을 지킨다
고친 뒤에 둔 테스트는 값을 비교하지 않는다. 게이트웨이가 읽는 이름이 계약의 타입에 있는지를 `satisfies` 로 묻는다. 계약에서 그 칸이 사라지면 컴파일이 먼저 멈춘다.
값을 비교하는 테스트로는 이 결함이 잡히지 않았을 것이다. 픽스처를 게이트웨이가 읽는 이름으로 만들면 값이 그대로 나오기 때문이다.
값을 비교하는 테스트로는 이 결함이 잡히지 않다. 픽스처를 게이트웨이가 읽는 이름으로 만들면 값이 그대로 나오기 때문이다 — 테스트 작성자와 게이트웨이 작성자가 이름에 대해 합의한 것을 확인할 뿐이다.
## 확인하지 못한 것
@@ -63,18 +63,22 @@ tech-log-backend : 92679f5 이후
## 세 번 버려졌다
관계 목록의 라벨을 고치고 화면을 봤을 때 요약은 여전히 비어 있었다. 값이 지나는 경계를 하나씩 따라가니 세 곳에서 버려지고 있었다.
```text
계약(요약 있음)
└─ flattenRelations 가 담지 않음 ← 1차로 고침
└─ 렌더 모델로 바꿀 때 버림 ← 담을 자체가 없었다
└─ 렌더 모델로 바꿀 때 버림 ← 담을 자리 자체가 없었다
└─ 화면 목록으로 넘길 때 또 버림
```
첫 번째를 고치고 화면을 봤을 때도 요약은 비어 있었다. 두 번째를 고치려고 보니 렌더 모델 계약에 담을 칸이 없었고, `additionalProperties: false` 라 계약을 고치지 않고는 실을 수 없었다.
첫 번째는 코드가 그 칸을 안 읽은 것이라 한 줄로 고쳤다. 두 번째에서 막혔다 — 렌더 모델 계약에 담을 칸이 없었고, 그 스키마가 `additionalProperties: false` 라 계약을 고치지 않고는 실을 수 없었다.
## 계약에 칸을 더할 때 required 를 따로 판단한다
`ResolvedRelation` `summary` 를 더하면서 required 에는 넣지 않았다. 이미 나가 있는 응답에는 그 칸이 없어서, required 로 올리면 배포 순서에 따라 검증이 깨진다.
렌더 모델`summary` 를 더했다. required 에는 넣지 않았다.
이미 나가 있는 응답에는 그 칸이 없다. required 로 올리면 계약을 반입한 쪽이 배포되기 전까지 그 응답이 검증에 걸린다. 배포 순서에 따라 깨지는 것과 값이 안 오는 것 중에서 뒤쪽을 골랐다.
## 한 칸에 셋이 뭉쳐 있었다
@@ -82,11 +86,19 @@ tech-log-backend : 92679f5 이후
| 무엇 | 뜻 | 경로별로 어떻게 나왔나 |
|---|---|---|
| 대상의 종류 | 「근거」「관련 기준」 같은 분류 | 렌더 모델 경로: 작성자의 문장이 이 에 눌려 나옴 |
| 대상의 종류 | 「근거」「관련 기준」 같은 분류 | 렌더 모델 경로: 작성자의 문장이 이 자리에 눌려 나옴 |
| 작성자가 쓴 이유 | 「다음에 무엇을 읽을지」의 답 | 공개 조회 경로: **아예 버려짐** |
| 대상의 요약 | 대상이 무엇인지 | — |
셋을 `label` · `note` · `summary` 로 갈랐다. 설명 칸에는 작성자가 쓴 문장이 있으면 그것을, 없으면 대상의 요약을 보인다. 요약은 대상을 설명하고 문장은 왜 지금 이것을 읽어야 하는지를 설명한다.
한 칸에 셋이 들어가니 경로마다 다른 것이 나왔다. 렌더 모델을 지나는 화면에서는 작성자가 쓴 문장이 분류 자리에 눌려 나왔고, 공개 조회를 지나는 화면에서는 그 문장이 아예 사라졌다.
셋을 `label`·`note`·`summary` 로 갈랐다. 설명 자리에는 문장이 있으면 문장을, 없으면 요약을 보인다. 요약은 대상이 무엇인지 말하고 문장은 왜 지금 그것을 읽어야 하는지 말하므로, 문장이 없을 때 요약으로 대신하는 것은 되지만 반대는 되지 않는다.
## 한 경계를 고치고 판단하면 안 된다
이 건에서 「고쳤다」고 두 번 판단했고 두 번 다 틀렸다. 값이 지나는 경계가 열한 개인데 그중 하나만 보고 판단했기 때문이다.
확인은 배포본에서, 그 값이 실제로 그려지는 곳에서 한다. 타입 검사와 단위 테스트와 「코드를 읽어 보니 맞다」는 전부 중간 지점이다.
## 확인하지 못한 것
@@ -35,6 +35,8 @@ source:
## 열한 번 모양이 바뀐다
공개 화면의 한 줄은 게시 시점에 굳어진 투영 테이블에서 출발해 열한 번 모양을 바꾼 뒤에 그려진다.
```text
PostgreSQL 테이블
└─ public_resource_projection (게시 시점에 굳어진 투영)
@@ -52,30 +54,37 @@ PostgreSQL 테이블
:::evidence key="value-boundaries" alt="저장·백엔드 조립·HTTP envelope·프론트엔드 조립·화면 다섯 묶음을 세 저장소 구역으로 나눠 이은 흐름도" caption=" " zoom="true"
:::
저장 쪽에 둘, 백엔드 조립에 넷, 전선에 하나, 프론트엔드 조립에 셋, 화면에 하나다.
저장 쪽에 둘, 백엔드 조립에 넷, 전선에 하나, 프론트엔드 조립에 셋, 화면에 하나다. 저장소 경계로 보면 백엔드가 여섯, 전선이 하나, 프론트엔드가 넷이다.
이 기간에 적은 결함의 절반 이상이 「이 중 한 경계가 값을 버렸다」는 같은 모양이었다.
## 경계마다 무엇이 값을 지키는가
## 어디서 검사가 끊기는가
같은 경계라도 무엇이 확인되는지가 다르다.
경계마다 무엇이 값을 지키는지가 다르다.
| 어디 | 무엇이 확인되나 | 무엇이 확인되지 않나 |
|---|---|---|
| 어댑터 SQL | 실행할 때 컬럼이 있는지 | 컴파일 시점에는 컬럼 이름을 아무도 안 본다 |
| 생성된 DTO | 계약의 스키마 모양 | 그 칸에 값이 담겼는지 |
| 게이트웨이 매퍼 | 계약이 준 타입의 이름 | `as` 단언을 쓰면 그 확인이 사라진다 |
| 포트와 화면 | 두 타입이 맞는지 | 포트와 어댑터가 타입을 따로 들면 한쪽만 늘어난다 |
JDBC 어댑터 SQL 은 컬럼 이름을 문자열로 적는다. 컬럼이 없거나 이름이 다르면 실행할 때 알게 된다.
어댑터 SQL 은 컬럼 이름을 문자열로 적는다. 이름이 틀리면 실행할 때 알게 되고, 그 SQL 을 실제로 돌리는 검사가 없으면 배포 뒤에 알게 된다.
계약이 만든 DTO 와 생성된 타입 사이는 생성기가 지킨다. 다만 생성기가 보는 것은 스키마의 모양이고, 그 칸에 값이 담기는지는 보지 않는다.
게이트웨이의 매퍼는 계약의 타입을 읽는다. `as` 단언을 쓰면 그 확인이 사라진다.
포트 타입과 화면 컴포넌트 사이는 TypeScript 가 지킨다. 포트와 어댑터가 타입을 따로 들고 있으면 그 확인도 사라진다.
## 값을 버려도 오류가 나지 않는다
이 경계들은 값을 담지 않아도 그렇다고 말하지 않 다음으로 넘긴다.
이 경계들은 값을 담지 않다고 말하지 않는다. 담지 않은 채 다음으로 넘긴다.
`undefined` 는 화면에서 빈 문자열이 된다. 빈 배열은 「항목이 없습니다」가 된다. 그래서 화면만 보면 값이 없는 것과 값을 잃은 것이 같아 보다.
`undefined` 는 화면에서 빈 문자열이 되고 빈 배열은 「항목이 없습니다」가 된다. 그래서 화면만 보면 값이 없는 것과 값을 잃은 것이 같아 보이고, 작성 도구에서는 작성자가 그것을 자기가 아직 쓰지 않은 것으로 읽는다.
## 두 화면이 같은 데이터를 볼 때
## 계약을 지나는 길이 둘이다
Studio 와 공개 화면은 같은 DB 를 보지만 계약이 다르다. Studio 는 작성 계약을, 공개 화면은 조회 계약을 지난다. 한쪽에만 값이 보이면 그 사이의 계약에 칸이 없다.
Studio 편집기와 공개 화면은 같은 데이터베이스를 보지만 다른 계약을 지난다. 작성 쪽은 작성 계약을, 조회 쪽은 조회 계약을 지난다.
두 화면이 같은 값을 두고 다르게 보이면 그 사이에 있는 것은 저장소가 아니라 계약이다. 이 저장소에서 그 신호가 여덟 번 같은 원인을 가리켰다.
## 이 문서의 결함 절반이 여기서 났다
이 기간에 적은 결함의 절반 이상이 「이 중 한 경계가 값을 버렸다」는 같은 모양이었다. 한 경계를 고치고 확인하면 다음 경계가 같은 값을 다시 버리는 일도 났다.
<!-- body:end -->
@@ -28,31 +28,40 @@ Studio 편집기에서는 값이 다 보이는데 공개 화면만 비어 있으
## 목적
DB 에 값이 있는데 화면이 비어 있을 때, 어디를 먼저 볼지 정한다. 이 부류는 오류를 내지 않으므로 로그에서 출발하면 아무것도 나오지 않는다.
데이터베이스에 값이 있는데 화면이 비어 있을 때 어디를 먼저 볼지 정한다.
이 부류는 오류를 내지 않으므로 로그에서 출발하면 아무것도 나오지 않는다. `undefined` 는 빈 문자열로 그려지고 빈 배열은 「항목이 없습니다」로 그려진다.
## 규칙
**Studio 에서는 보이고 공개 쪽만 비면 그 사이의 계약을 먼저 본다**
두 화면이 같은 DB 를 보는데 한쪽만 비면, 다른 것은 그 사이에 놓인 계약이다.
두 화면이 같은 데이터베이스를 보는데 한쪽만 비면, 다른 것은 그 사이에 놓인 계약이다. 작성 쪽은 작성 계약을 지나고 조회 쪽은 조회 계약을 지난다. 이 저장소에서 같은 신호가 여덟 번 같은 원인을 가리켰다.
**화면이 그리는 칸을 먼저 적고 응답에 있는지 하나씩 맞춘다**
응답에서 출발하면 없는 칸은 보이지 않는다. 상세 endpoint 가 없는 종류에서 특히 그렇다 — 목록 항목이 문서 전체를 실어야 다.
응답에서 출발하면 없는 칸은 보이지 않는다. 상세 endpoint 가 없는 종류에서 특히 그렇다 — 부를 상세가 없으므로 목록 항목이 문서 전체를 실어야 하고, 그 목록에 없는 칸은 화면이 각자 메운다.
**한 종류의 저장 구조가 다른 종류와 다르면 조회 쪽이 그것을 알아야 한다**
Reference 의 본문은 문서 본문 칸이 아니라 규칙과 예시를 담는 별도 테이블에 있다. 이름이 맞아도 읽는 곳이 틀리면 빈 문자열이 나온다.
**칸을 더할 때 required 로 올릴지는 따로 판단한다**
이미 나가 있는 응답에는 그 칸이 없다. required 로 올리면 배포 순서에 따라 검증이 깨진다.
이미 나가 있는 응답에는 그 칸이 없다. required 로 올리면 계약을 반입한 쪽이 배포되기 전까지 그 응답이 검증에 걸린다. 배포 순서에 따라 깨지는 것과 값이 안 오는 것 중에서 고른다.
**값이 아니라 이름을 지키는 검사를 둔다**
게이트웨이가 읽는 이름이 계약의 타입에 있는지를 묻는다. 값을 비교하는 검사는 픽스처를 게이트웨이가 읽는 이름으로 만들면 그대로 통과한다.
게이트웨이가 읽는 이름이 계약의 타입에 있는지를 묻는다. 값을 비교하는 검사는 픽스처를 게이트웨이가 읽는 이름으로 만들면 그대로 통과한다 — 테스트 작성자와 게이트웨이 작성자가 이름에 대해 합의한 것을 확인할 뿐이다.
## 적용 조건
같은 데이터를 두 표면이 각자의 계약으로 읽고, 한쪽만 비어 보이는 화면. 작성 계약과 조회 계약이 나뉜 구조에서 걸린다.
계약에 칸을 더하거나 화면에 칸을 더하는 변경에서도 건다. 화면이 먼저 늘면 그 칸이 응답에 있는지 확인할 곳이 없다.
## 예외
두 표면이 같은 계약을 쓰면 이 신호는 성립하지 않는다. 그때는 매퍼나 질의를 먼저 본다.
저장 구조가 종류마다 다르면 계약이 아니라 조회가 원인일 수 있다. Reference 의 본문이 문서 본문 칸이 아니라 별도 테이블에 있던 것이 그 예다.
저장 구조가 종류마다 다르면 계약이 아니라 조회가 원인일 수 있다. 이름이 계약과 맞는데도 값이 비면 그쪽을 본다.
값이 있는데 겹쳐 보이는 경우는 이 신호가 아니다. 빈 칸을 다른 값으로 메우면 같은 글이 두 번 나온다.
## 예시
@@ -63,3 +72,5 @@ DB 에 값이 있는데 화면이 비어 있을 때, 어디를 먼저 볼지 정
질문 목록만 주제가 빠져 있어서 질문 줄의 맥락이 「· 프로젝트」로 시작했다. 지식 목록은 처음부터 그 칸을 싣고 있었다.
프로젝트 목록 행에 slug 가 없었다. 다른 목록이 프로젝트를 가리킬 때 쓰는 것은 id 가 아니라 slug 다.
문서 요약 자리에 유형별 요약을 대신 넣었더니 머리말이 바로 아래와 같은 글을 두 번 말했다.
@@ -29,31 +29,38 @@ source:
## 목적
「고쳤다」는 판단이 틀리는 것을 막는다. 값이 열한 경계를 지나는 구조에서 한 경계만 보고 판단하면, 다음 경계가 같은 값을 다시 버려도 알 수 없다.
「고쳤다」는 판단이 틀리는 것을 막는다.
값이 열한 경계를 지나는 구조에서 한 경계만 보고 판단하면, 다음 경계가 같은 값을 다시 버려도 알 수 없다. 이 저장소에서 그 판단이 세 번 틀렸다.
## 규칙
**고친 값이 실제로 그려지는 곳까지 가서 본다**
배포본에서 그 화면을 열거나, 실제 요청을 보내 응답을 읽는다.
배포본에서 그 화면을 열거나 실제 요청을 보내 응답을 읽는다. 관계 요약은 세 경계에서 연달아 버려졌고, 매번 화면을 보고 나서야 다음 경계가 버리는 것을 알았다.
**타입 검사 통과를 반영의 증거로 쓰지 않는다**
메서드 매개변수의 bivariance, `as` 단언, 검사 대상이 없는 tsconfig 가 각각 통과시킨 사례가 있다.
메서드 매개변수의 bivariance, `as` 단언, 검사 대상이 없는 tsconfig 가 각각 통과시킨 사례가 있다. 통과는 「코드가 맞다」가 아니라 「검사가 그 질문을 하지 않았다」를 뜻할 수 있다.
**게이트웨이를 실제로 불러 어떤 연산이 나가는지 확인한다**
등록을 빠뜨린 연산은 옆 분기로 떨어지므로 서버는 정상 응답을 준다. 나가는 경로를 봐야 알 수 있다.
**여정이 끝나는 곳을 먼저 정하고 시작한다**
어디까지 가면 확인이 끝나는지 모르면 중간에서 멈다.
어디까지 가면 확인이 끝나는지 모르면 중간에서 멈추게 된다. 공개 화면의 한 줄이면 그 줄이 그려지는 화면이 끝이다.
**검사가 덮는 구간을 적어 둔다**
값이 아니라 이름을 지키는 검사를 두면 여정의 한 구간을 그 검사가 대신한다. 그 구간이 어디까지인지 적어 두지 않으면 다음 사람이 검사를 여정 전체로 읽는다.
## 적용 조건
값이 계약·매퍼·포트를 여러 번 갈아타는 구조에서 「고쳤다」를 판단할 때. 계약을 소유한 저장소가 따로 있고 생성기를 지나 들어오면 특히 걸린다.
앞선 커밋이 같은 값을 고치려다 못 고친 이력이 있으면 반드시 건다. 그 커밋이 무엇을 근거로 고쳤다고 판단했는지가 대개 중간 지점이다.
## 예외
경계가 하나뿐이거나 고친 그 곳이 여정의 끝이면 중간 확인으로 충분하다.
값이 아니라 이름을 지키는 검사를 이미 뒀으면 그 검사가 여정의 한 구간을 대신한다. 다만 검사가 덮는 구간이 어디까지인지 적어 둔다.
배포본을 열 수 없는 변경 — 아직 배포되지 않은 경로 — 은 여정의 끝까지 갈 수 없다. 그때는 어디까지 확인했는지를 적는다.
## 예시
@@ -61,4 +68,6 @@ source:
개념 삭제가 계속 질문 삭제 경로로 나갔다. 타입 검사가 통과해서 반영된 줄 알았고, 배포된 번들의 서버 로그에서 404 를 보고 알았다.
CONCEPT 을 `deleteQuestion`로 되돌려 가드가 깨지는 것을 확인했다.
CONCEPT 을 질문 삭제로 되돌려 가드가 깨지는 것을 확인했다.
한 경계를 고치고 판단해 세 번 틀렸다. 값이 지나는 경계가 열한 개다.