Files
document-haness/docs/TechLog/tech-log-studio/what-the-compiler-lets-through/case/case-a-narrow-implementation-satisfied-a-wide-port.md
T
DongHyeonkaandClaude Opus 5 f6c825e858 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>
2026-09-07 15:29:33 +09:00

4.0 KiB

kind, slug, title, topic, topicName, project, status, lastVerifiedOn, sourceRevision, source
kind slug title topic topicName project status lastVerifiedOn sourceRevision source
CASE a-narrow-implementation-satisfied-a-wide-port 구현이 종류를 좁게 적어도 넓은 포트를 만족했다 what-the-compiler-lets-through 타입 검사가 통과시키는 곳 TechLog 게시 전 2026-09-04 tech-log@2026-09-02
final/document.md#§6.1

구현이 종류를 좁게 적어도 넓은 포트를 만족했다

개념 삭제가 계속 질문 삭제 경로로 나갔다. 앞선 커밋이 게이트웨이를 고치지 못했는데 타입 검사가 통과해서 반영된 줄 알았다. TypeScript 에서 메서드 매개변수는 bivariant 라, 구현이 종류를 좁게 적어도 넓은 포트 시그니처를 만족한 것으로 통과한다.

관계

  • TypeScript 가 검사를 놓아 주는 네 곳 bivariance 가 그중 하나다.
  • 타입에는 보이는데 부를 수 없는 연산이 네 번 나왔다 같은 삭제 경로에서 난 다른 부류의 결함이다.
  • 한 경계를 고쳤으면 값의 여정 끝에서 확인한다 이 사건이 그 규칙의 근거 하나다.

문제

포트는 네 종류를 받는다고 선언돼 있는데 구현은 세 종류만 적혀 있었다. 그 상태로 타입 검사가 통과했다.

CONCEPT 을 넘기면 구현의 삼항 사슬이 마지막 else 로 떨어뜨려 질문 삭제 경로를 부른다. 배포된 번들에서 서버 로그에 DELETE /api/v1/studio/questions/{id} 404 가 계속 찍혔다.

결론

TypeScript 에서 메서드 매개변수는 bivariant 다. 구현이 매개변수를 좁게 적어도 넓은 포트 시그니처를 만족한 것으로 통과한다.

포트 : CASE · REFERENCE · QUESTION · CONCEPT 구현 : CASE · REFERENCE · QUESTION 타입 검사 : 통과 실행 결과 : CONCEPT 이 질문 삭제로 나감

같은 병이 필터 타입에서도 났다. 포트와 정적 어댑터가 타입을 따로 들고 있어, 포트에 필터가 늘어도 어댑터는 모르는 상태가 됐다. satisfies 도 같은 이유로 잡지 못했다. 타입을 하나로 합쳐서 고쳤다.

검증 환경

tech-log-frontend : 6429aee 이후 · 67a5491 에서 필터 타입 통합 TypeScript : 메서드 매개변수 bivariance 확인 방식 : 배포본의 서버 로그에서 실제로 나가는 경로를 확인

재현 조건

  1. 포트에 네 값을 받는 메서드를 선언한다
  2. 구현에서 세 값만 적는다 — 타입 검사가 통과한다
  3. 네 번째 값을 넘겨 실제로 어떤 경로가 나가는지 본다

본문

좁은 구현이 넓은 포트를 만족한다

// 포트 시그니처
deleteDocument(kind: "CASE" | "REFERENCE" | "QUESTION" | "CONCEPT", id: string): Promise<void>;

// 구현이 이렇게 좁게 적혀 있어도 위 시그니처를 "만족"한다
deleteDocument(kind: "CASE" | "REFERENCE" | "QUESTION", id: string) {  }

메서드 매개변수가 bivariant 이므로 이 둘은 호환된다고 판정된다. 구현 안의 삼항 사슬은 CONCEPT 을 마지막 else 로 떨어뜨린다.

왜 반영된 줄 알았나

앞선 커밋에서 게이트웨이를 고쳤다고 판단한 근거가 타입 검사 통과였다. 좁게 적힌 구현을 그 커밋이 건드리지 않았고, 검사는 그것을 묻지 않았다.

배포된 번들에서 서버 로그를 보고서야 알았다. 삭제 요청이 질문 경로로 나가고 있었다.

같은 병이 필터에서도 났다

포트와 정적 어댑터가 필터 타입을 따로 들고 있었다. 포트에 축 필터를 더해도 어댑터의 타입은 그대로였고, satisfies 도 같은 이유로 통과했다.

타입을 하나로 합쳐서 고쳤다. 포트가 아는 필터와 어댑터가 아는 필터가 같은 타입이면 한쪽만 늘어날 수 없다.

확인한 것과 확인하지 못한 것

CONCEPT 을 deleteQuestion 으로 되돌려 가드가 깨지는 것을 확인했다.

배포된 번들에 옛 삼항이 남아 있던 것은 서버 로그의 404 로 확인했다. 그 로그 원문은 이 저장소에 없다.