회사에서 프론트엔드 v2 개발을 열심히 진행하던 중, 타입스크립트 컴파일러가 멈춰버리는 현상이 발생하였습니다.
평소와 같았으면 5초면 끝날 컴파일이 몇 분이 지나도 끝나지 않았습니다.
$ ddongs > yarn type
yarn run v1.22.22
$ tsc --noEmit
# ... 멈춰버림 ㅇㅅㅇ..
처음 겪어보는 이슈였고 프로젝트 빌드 전에 타입스크립트 컴파일은 어플리케이션 런타임 에러를 걸러내는데에 있어서 매우 중요한 사항이였기 때문에 필연적으로 잡고가야하는 이슈였습니다.
첫번째로,
마지막 컴파일 시점을 생각해봅니다.
현재 날짜 25/09/06
기준 25/08/14
가 최종 컴파일 및 빌드 시점입니다.
그 이후로 개발된 기능들의 페이지에 종속된 기능들을 위주로 먼저 순환 참조가 될 만한 타입 관련 코드들을 체크해보기 시작했습니다.
그러던 와중,, 아래와 같은 코드를 발견하였습니다.
export interface RevisionListResponseProps extends ResponseModel {
responseObject: RevisionListResponseProps[];
}
????????????????
자기 자신을 이상하게 참조하고 있는 위의 타입 인터페이스 코드를 수정하였습니다.. 회사 가서 옆자리 꿀밤 좀 먹여야겠습니다.
그리고 수정사항은 더 이상 보이지 않아 다시 한 번 컴파일을 해보았습니다.
yarn run v1.22.22
$ tsc --noEmit
분명 위에서 언급 했던 코드 때문일거라고 확신했지만, 코드를 수정한 뒤에도 제 예상과 다르게 1분 이상 컴파일이 지속되었습니다..
이 때 한 가지 생각이 들었습니다.
타입스크립트 컴파일 과정을 로그화 시켜서 볼 순 없을까..?
컴파일 과정 로그 만들기
Typescript 공식문서에서
Reference
--generateTrace
라는 cli 옵션을 찾을 수 있었습니다.
package.json
파일 script 블록에 아래와 같은 script를 추가해주었습니다.
{
"type:trace": "tsc --noEmit --generateTrace ./trace"
}
위 스크립트는, 컴파일 시, 별도의 d.ts
나 js
파일을 배출하지 않고 타입체크만 하며, ./trace
경로에 trace.json
파일을 생성하겠다. 라는 의미입니다.
그리고 스크립트를 실행해주겠습니다. 최대 몇 분까지 진행되는지 보기 위해 프로세스를 kill 하지 않고 진행해보겠습니다.
$ yarn type:trace
./trace
경로에 trace.json
이라는 파일을 생기는 것을 확인할 수 있습니다.
[
{"name":"process_name","args":{"name":"tsc"},"cat":"__metadata","ph":"M","ts":95416.79215431213,"pid":1,"tid":1},
{"name":"thread_name","args":{"name":"Main"},"cat":"__metadata","ph":"M","ts":95416.79215431213,"pid":1,"tid":1},
{"name":"TracingStartedInBrowser","cat":"disabled-by-default-devtools.timeline","ph":"M","ts":95416.79215431213,"pid":1,"tid":1},
{"pid":1,"tid":1,"ph":"B","cat":"program","ts":95960.79206466675,"name":"createProgram","args":{"configFilePath":"/Users/ddongs/Desktop/webconsole_view_v2/tsconfig.json"}},
{"pid":1,"tid":1,"ph":"B","cat":"parse","ts":96923.08306694031,"name":"createSourceFile","args":{"path":"/Users/ddongs/Desktop/webconsole_view_v2/src/App.tsx"}},
//...
]
trace.json
파일의 형식은 위와 같았습니다.
실시간으로 컴파일이 진행되는 과정을 확인하던 중 아래와 같은 형식의 로그가 무한루프로 15분 이상 찍히는 것을 볼 수 있었습니다.
{"pid":1,"tid":1,"ph":"I","cat":"checkTypes","ts":15857198.208,"name":"instantiateType_DepthLimit","s":"g","args":{"typeId":414150,"instantiationDepth":100,"instantiationCount":1239908}},
{"pid":1,"tid":1,"ph":"I","cat":"checkTypes","ts":15857205.75,"name":"instantiateType_DepthLimit","s":"g","args":{"typeId":414150,"instantiationDepth":100,"instantiationCount":1239912}},
{"pid":1,"tid":1,"ph":"I","cat":"checkTypes","ts":15857212.917000001,"name":"instantiateType_DepthLimit","s":"g","args":{"typeId":414150,"instantiationDepth":100,"instantiationCount":1239915}},
{"pid":1,"tid":1,"ph":"I","cat":"checkTypes","ts":15857220.25,"name":"instantiateType_DepthLimit","s":"g","args":{"typeId":414150,"instantiationDepth":100,"instantiationCount":1239919}},
{"pid":1,"tid":1,"ph":"I","cat":"checkTypes","ts":15857227.625,"name":"instantiateType_DepthLimit","s":"g","args":{"typeId":414150,"instantiationDepth":100,"instantiationCount":1239922}}
//..
그리고 이 무한루프의 끝나는 지점을 확인해보니..
{"pid":1,"tid":1,"ph":"X","cat":"check","ts":5278836.75,"name":"checkExpression","dur":954825923.708,"args":{"kind":238,"pos":3074,"end":3257,"path":"/users/ddongs/desktop/webconsole_view_v2/src/pages/management/database/components/form/databaseeditableform.tsx"}}
위의 부분이였습니다. AI의 힘을 빌려 위의 로그는
/users/ddongs/desktop/webconsole_view_v2/src/pages/management/database/components/form/databaseeditableform.tsx
의 경로의 파일의 3074
~ 3257
사이의 문자 인덱스에서 15분 이상 타입 체크 프로세스가 진행되었다. 라는 뜻이라는 것을 알 수 있었습니다.
이로써, 문제가 되는 타입 컴파일 지점을 확인해본 결과,
const triggerFields = [
'jdbcUrlInfo',
'poolInfo.driverClassName',
'poolInfo.jdbcUrl',
'poolInfo.password',
'poolInfo.username',
] satisfies Path<typeof method.formState>;
라는 부분임을 확인할 수 있었습니다.
react-hook-form의 useForm을 별도의 제네릭 타입인자를 받는 다국어 처리용 useTranslationForm 이라는 커스텀 훅으로 사용하고 있었는데, 복잡한 useForm의 formState의 타입을 추론하는데 있어서 어디선가 컴파일이 꼬인 것 같았습니다.
const triggerFields = [
'jdbcUrlInfo',
'poolInfo.driverClassName',
'poolInfo.jdbcUrl',
'poolInfo.password',
'poolInfo.username',
] as const;
특정 필드의 유효성 검사를 위한 배열 상수이기 때문에 일단은 as const
키워드로 타입 단언을 시켜주었습니다.
그 결과 타입스크립트 컴파일이 평소처럼 5초만에 끝나는 것을 확인할 수 있었습니다!!!
🥹 이거 때문에 3일을 삽질한 것 같습니다..
이번 일을 계기로, 다시 한 번 타입스크립트 컴파일 에러가 났을 때, 해결하는데에 있어서 접근 방향을 보다 더 잘 잡을 수 있을 것만 같았습니다.
프로젝트 타입스크립트 컴파일에 고생하는 분들이 조금이나마 이 글을 보고 도움이 되셨으면 좋겠다는 마음으로 포스팅을 마무리하겠습니다.