Skip to content

TypeScript Unused Gate

이 문서는 기존 TypeScript unused 부채를 한 번에 오류로 바꾸지 않으면서 신규 부채를 CI에서 차단하는 기준을 고정한다.

명령

bash
npm run check:unused

GitHub Actions의 verify job은 기본 npm run check가 통과한 뒤 이 명령을 별도 단계로 실행한다. 따라서 공용 TypeScript 설정이나 기본 로컬 검사에는 영향을 주지 않으면서 pull request와 main push에서는 항상 강제된다. 로컬에서는 baseline을 줄이거나 게이트 자체를 수정할 때 위 명령을 명시적으로 실행한다.

CI 전용 정책

tsconfig.base.jsontsconfig.json에는 noUnusedLocals 또는 noUnusedParameters를 넣지 않는다. 대신 scripts/check-typescript-unused.mjs가 TypeScript compiler API로 기존 설정을 읽은 뒤 두 옵션을 일시적으로 활성화한 별도 프로그램을 만든다. baseline은 사용 중인 TypeScript의 정확한 버전도 고정하므로 compiler 업데이트로 진단 의미가 달라질 때 반드시 리뷰가 발생한다.

게이트가 소유하는 진단 코드는 다음과 같다.

  • TS6133: 읽히지 않는 local, import, parameter
  • TS6138: 읽히지 않는 선언된 property
  • TS6192: 전부 사용되지 않는 import declaration
  • TS6196: 사용되지 않는 declaration 또는 type
  • TS6198: 전부 사용되지 않는 destructuring declaration
  • TS6199: 전부 사용되지 않는 variable declaration
  • TS6205: 전부 사용되지 않는 type parameter declaration

TypeScript 규칙에 따라 이름이 _로 시작하는 parameter는 noUnusedParameters의 예외다. 공개 callback 또는 외부 계약 때문에 parameter 자리를 유지해야 할 때만 이 형식을 사용한다.

Baseline ratchet

typescript-unused-baseline.json은 기존 진단마다 파일 경로와 다음 AST 구조 fingerprint를 고정한다.

  1. TypeScript 진단 코드
  2. 선언 symbol 또는 진단 span
  3. declaration syntax kind
  4. 가장 가까운 이름 있는 함수·클래스·모듈 컨테이너
  5. import라면 module specifier

줄·열과 공백은 fingerprint에서 제외한다. 오류 문구는 실패 로그에만 표시하고 동일성 판정에는 쓰지 않는다. 같은 구조 fingerprint가 한 파일에 두 번 생기면 모호한 baseline을 만들지 않고 collision으로 실패한다.

  • 새로운 fingerprint가 생기면 CI가 실패한다.
  • 기존 fingerprint가 사라져도 baseline을 같은 PR에서 줄일 때까지 CI가 실패한다.
  • 기존 진단 하나를 없애고 같은 파일에 다른 진단을 추가하는 바꿔치기도 실패한다.
  • baseline은 기존 부채의 상한선이며 새 항목을 추가하는 용도로 사용하지 않는다.

최초 baseline은 type-check되는 33개 파일의 139개 진단이다. 이와 별도로 최초 @ts-nocheck design-intake 파일 23개를 exact inventory로 고정했다. 첫 정리 배치에서 진단이 없던 fixture 9개를 type-check 영역으로 편입했고, 두 번째 배치에서 desktopFreshFixture.tsdesktopSessionFixture.ts를 추가 편입했다. 세 번째 배치에서는 DesktopProfileScreen.tsxDesktopSearchScreen.tsx를 상시 type-check 영역으로 옮겼다. 네 번째 배치에서는 DesktopOnboarding.tsx를 상시 type-check 영역으로 옮겼고, 다섯 번째 배치에서는 DesktopFavGroupsScreen.tsx를 편입했다. 여섯 번째 배치에서는 DesktopHomeScreen.tsx를 상시 type-check 영역으로 옮겼다. 일곱 번째 배치에서는 DesktopLogTableScreen.tsx를 편입했고, 여덟 번째 배치에서는 src/react/ui/shared/fixtures/prFixture.ts를 편입했고, 이후 디자인 소유권 이동을 거쳐 2026-08-19 시연 재료(fixture·preview)의 레포 제거로 해당 파일들은 검사 대상에서 사라졌다 (Claude Design 로컬 전용 — docs/process/design-codex-transfer-contract.md). 아홉 번째 배치에서는 DesktopWidgets.tsx를 편입했고, 열 번째 배치에서는 DesktopVolumeScreen.tsx를 편입했다. 열한 번째 배치에서는 DesktopPrScreen.tsx, 열두 번째 배치에서는 DesktopSessionScreen.tsx를 편입했다. 열세 번째 배치에서는 데스크톱 preview harness를 편입해 현재 inventory는 0개다. 새 suppression이 추가되거나 기존 suppression이 제거되면 baseline을 함께 리뷰하기 전까지 게이트가 실패한다. suppression은 TypeScript parser가 실제로 인식한 directive만 판별하며, 기존 boundary gate도 같은 판별기를 사용해 line/block @ts-ignore@ts-expect-error 우회를 차단한다.

2026-07-29 최초 감사에서 23개 파일의 suppression을 메모리상 제거해 조사했을 때, 데스크톱 화면 7개에서 unused 진단 106개가 추가로 드러났다. 이 숫자는 통과 baseline이 아니라 다음 정리 단계의 작업량 참고치다.

부채를 줄이는 방법

  1. 사용 여부와 공개 계약을 확인한 뒤 unused symbol을 제거하거나 실제 사용 경로를 복구한다.
  2. npm run check:unused를 실행해 사라진 fingerprint를 확인한다.
  3. typescript-unused-baseline.json에서 해결된 항목만 제거한다.
  4. npm run check:unused, npm run check, npm run build를 실행한다.

node scripts/check-typescript-unused.mjs --print-baseline은 현재 진단과 suppression inventory를 조사하기 위한 출력 명령이다. 결과로 baseline 전체를 덮어써 신규 부채를 승인하지 않는다.

@ts-nocheck inventory는 0개다. 첫 dead-code 감축 배치에서 자동 JSX runtime 이후 남은 React import와 leaf UI/service의 죽은 선언 23건을 제거해 baseline을 13개 파일 116건으로 줄였다. 두 번째 배치에서는 workout leaf 화면의 렌더 경로가 없는 아이콘·타입 선언 22건을 제거해 12개 파일 94건으로 낮췄다. 세 번째 배치에서는 화면 RPC 전환 뒤 남은 private raw-query helper 6건과 미사용 adapter type import 7건을 제거해 10개 파일 81건으로 낮췄다. 네 번째 배치에서는 read-model 전환 뒤 호출 경로가 끊긴 legacy PR mapper closure 15개를 제거해 9개 파일 74건으로 낮췄다. 다섯 번째 배치에서는 모바일 운동 화면 분리 뒤 남은 아이콘·format helper·진행률 계산 11건을 제거해 8개 파일 63건으로 낮췄다. 여섯 번째 배치에서는 앱 컨테이너에 남은 읽히지 않는 state와 화면 전달 prop 11건을 제거해 7개 파일 52건으로 낮췄다. 일곱 번째 배치에서는 계획 편집기의 폐기된 필터·드로어·세로 종목 드래그 잔재 16건을 제거해 6개 파일 36건으로 낮췄다. 여덟 번째 배치에서는 모바일 Home 개편과 Session drawer 전환 뒤 남은 로컬 렌더 잔재 16건을 제거해 5개 파일 20건으로 낮췄다. 아홉 번째 배치에서는 관리자 종목 화면의 미사용 import와 세션 인라인 편집기 제거 후 남은 선언 5건을 정리해 4개 파일 12건으로 낮췄다. 열 번째 배치에서는 모바일 운동 완료 화면의 onBack을 실제 동작에 연결해 3개 파일 11건으로 낮췄다. 디자인 배송 v83(delivery-20260901, 이슈 #1040)에서는 관리자 종목 상세가 검색 확인 상자·별칭 승격 렌더를 제거하면서 의도적으로 보존한 AdmSearchProbe 함수·onPromoteAlias prop과, Wodup ID 필드 제거 후 시그니처만 남은 exerciseFieldsoptions 파라미터 3건이 늘어 3개 파일 14건이 되었다(부활 요청 시 1줄 복원 계약 — 해소 시 baseline에서 함께 제거). 다음 배치도 수정이 잦고 호출 경계가 단순한 파일부터 진행하며, 진단을 해결한 PR에서는 baseline의 해당 fingerprint를 함께 제거한다.