docs(TechLog): 주제 4·5 를 스킬대로 다시 쓴다
what-the-compiler-lets-through 중앙값 2,096 → 2,590 자 seams-no-test-crosses → 3,091 자 bivariance 가 왜 느슨한 판정을 받는지, never 캐스트가 왜 아무것도 요구하지 않는지처럼 「이름을 댔으면 왜 있는지도 댄다」를 채웠다. 네 가지가 각각 무엇을 통과시키고 어디서 드러났는지를 표로 갈랐다. 검사 넷 중 무엇이 SQL 을 실제로 돌리는지도 표로 세웠다. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a8ce0dda07
commit
53537e37e8
+11
-5
@@ -68,24 +68,30 @@ deleteDocument(kind: "CASE" | "REFERENCE" | "QUESTION" | "CONCEPT", id: string):
|
||||
deleteDocument(kind: "CASE" | "REFERENCE" | "QUESTION", id: string) { … }
|
||||
```
|
||||
|
||||
메서드 매개변수가 bivariant 이므로 이 둘은 호환된다고 판정된다. 구현 안의 삼항 사슬은 CONCEPT 을 마지막 `else` 로 떨어뜨린다.
|
||||
메서드 문법으로 쓴 매개변수를 TypeScript 가 bivariant 로 다루므로 이 둘은 호환된다고 판정된다. 매개변수를 엄격하게 따지면 콜백을 넘기는 흔한 패턴이 거절되기 때문에 언어가 이 판정을 느슨하게 둔다.
|
||||
|
||||
구현 안의 삼항 사슬은 CONCEPT 을 마지막 `else` 로 떨어뜨린다. 실행하면 개념 삭제가 질문 삭제 경로로 나간다.
|
||||
|
||||
## 왜 반영된 줄 알았나
|
||||
|
||||
앞선 커밋에서 게이트웨이를 고쳤다고 판단한 근거가 타입 검사 통과였다. 좁게 적힌 구현을 그 커밋이 건드리지 않았고, 검사는 그것을 묻지 않았다.
|
||||
|
||||
배포된 번들에서 서버 로그를 보고서야 알았다. 삭제 요청이 질문 경로로 나가고 있었다.
|
||||
배포된 번들에서 서버 로그를 보고서야 알았다. 삭제 요청이 `DELETE /api/v1/studio/questions/{id}` 로 나가고 404 를 받고 있었다.
|
||||
|
||||
## 포트와 어댑터가 필터 타입을 따로 들고 있었다
|
||||
증상이 오류였다는 것이 그나마 다행이었다. 개념 조회 쪽은 같은 원인으로 질문 조회를 불렀고 그쪽도 404 였는데, 만약 그 종류에 실제 기록이 있었다면 다른 기록을 지웠을 것이다.
|
||||
|
||||
포트에 축 필터를 더해도 어댑터의 타입은 그대로였고, `satisfies` 도 같은 이유로 통과했다.
|
||||
## 같은 병이 필터에서도 났다
|
||||
|
||||
포트와 정적 어댑터가 필터 타입을 따로 들고 있었다. 포트에 축 필터를 더해도 어댑터의 타입은 그대로였고, `satisfies` 도 같은 이유로 통과했다 — 어댑터가 자기 타입을 만족하는지만 물었기 때문이다.
|
||||
|
||||
타입을 하나로 합쳐서 고쳤다. 포트가 아는 필터와 어댑터가 아는 필터가 같은 타입이면 한쪽만 늘어날 수 없다.
|
||||
|
||||
## 확인한 것과 확인하지 못한 것
|
||||
|
||||
CONCEPT 을 `deleteQuestion` 으로 되돌려 가드가 깨지는 것을 확인했다.
|
||||
CONCEPT 을 질문 삭제로 되돌려 가드가 깨지는 것을 확인한 뒤 커밋했다.
|
||||
|
||||
배포된 번들에 옛 삼항이 남아 있던 것은 서버 로그의 404 로 확인했다. 그 로그 원문은 이 저장소에 없다.
|
||||
|
||||
같은 모양으로 포트와 구현이 어긋난 곳을 전수로 세지 않았다. 고친 것은 삭제 경로와 필터 타입 둘이다.
|
||||
|
||||
<!-- body:end -->
|
||||
|
||||
+18
-5
@@ -62,20 +62,33 @@ tsconfig : 루트가 project references 만 나열
|
||||
|
||||
## 검사할 파일이 없는 tsconfig
|
||||
|
||||
루트 tsconfig 는 이 저장소에서 컴파일 대상을 갖지 않는다. `"files": []` 에 project references 만 나열한다. 각 프로젝트는 자기 tsconfig 를 갖고 있고, 루트는 그들을 가리키기만 한다.
|
||||
이 저장소의 루트 tsconfig 는 컴파일 대상을 갖지 않는다. `"files": []` 에 project references 만 나열하고, 각 프로젝트는 자기 tsconfig 를 갖는다.
|
||||
|
||||
`npx tsc --noEmit` 은 references 를 따라가지 않는다. 루트가 지목한 파일 집합만 보고, 그 집합이 비어 있으므로 아무것도 검사하지 않은 채 0 으로 끝난다.
|
||||
`npx tsc --noEmit` 은 references 를 따라가지 않는다. 루트가 지목한 파일 집합만 보고, 그 집합이 비어 있으므로 아무것도 검사하지 않은 채 0 으로 끝난다. references 를 따라가려면 그 모드로 부르는 별도 명령이 필요하다.
|
||||
|
||||
## 통과가 무엇을 뜻했나
|
||||
|
||||
이 명령은 루트 tsconfig 가 유효한지까지만 확인하고, 코드가 컴파일되는지는 묻지 않는다.
|
||||
이 명령이 성공했을 때 확인된 것은 「루트 tsconfig 가 유효하다」까지다. 코드가 컴파일되는지는 묻지 않았다.
|
||||
|
||||
운영에서 릴리즈 목록 화면이 비었고 콘솔에 `ReferenceError` 가 났다. 링크 컴포넌트 import 가 빠졌고, 이동 함수는 정의된 적이 없었다. 둘 다 컴파일이 잡는 종류의 오류인데 컴파일이 돌지 않았다.
|
||||
|
||||
## 올바른 명령으로 돌렸을 때
|
||||
|
||||
`npm run check:types` 는 여섯 프로젝트를 각각 돌린다. 그 명령으로 돌리자 릴리즈 목록의 두 오류에 더해 네 가지가 더 나왔다 — export 되지 않은 타입, `unknown` 인 라우트 파라미터, 파라미터를 받도록 등록되지 않은 메시지 키, 목록 항목에 없는 요약.
|
||||
여섯 프로젝트를 각각 돌리는 명령으로 바꾸자 릴리즈 목록의 두 오류에 더해 네 가지가 함께 나왔다.
|
||||
|
||||
| 무엇이 나왔나 | 어떤 종류인가 |
|
||||
|---|---|
|
||||
| `CatalogEntry` 가 export 되지 않음 | 다른 모듈에서 그 타입을 못 쓴다 |
|
||||
| 라우트 파라미터가 `unknown` | 쓰는 쪽에서 캐스트해야 한다 |
|
||||
| 메시지 키가 파라미터를 받도록 등록되지 않음 | 그 문구에 값이 안 들어간다 |
|
||||
| `ReleaseIndexItem` 에 `summary` 가 없음 | 목록이 요약을 그릴 수 없다 |
|
||||
|
||||
넷 다 그 명령이 안 도는 동안 쌓인 것이다. 검사가 통과하고 있었으므로 언제 들어왔는지는 알 수 없다.
|
||||
|
||||
## 남은 것
|
||||
|
||||
루트 tsconfig 는 그대로 뒀다. 누군가 다시 `npx tsc --noEmit` 을 쓰는 것을 막는 검사는 없고, 배포 전에 돌릴 다섯 명령의 목록에 적어 뒀다. 이 건은 메모리에도 남겨 뒀다 — `tech-log-frontend-typecheck-command.md`.
|
||||
루트 tsconfig 는 그대로 뒀다. project references 구성 자체는 프로젝트를 나눠 빌드하기 위한 것이고 그 목적에는 맞는다.
|
||||
|
||||
누군가 다시 그 짧은 명령을 쓰는 것을 막는 검사는 없다. 배포 전에 돌릴 다섯 명령의 목록에 적어 두고 메모리에 남긴 것이 지금의 대책이다.
|
||||
|
||||
<!-- body:end -->
|
||||
|
||||
Reference in New Issue
Block a user