TypeScript 프로젝트에 strict 모드 점진적으로 도입하기
배경
작년 하반기부터 진행하던 TypeScript 마이그레이션이 어느 정도 마무리됐다. 기존 JavaScript 코드베이스에 .ts 확장자를 붙이고 기본적인 타입만 추가한 상태였는데, 이제 strict 모드를 켜서 타입 안정성을 높이기로 했다.
문제는 한 번에 strict: true를 켜면 수백 개의 에러가 발생한다는 점이었다. 팀원들과 논의 끝에 strict의 하위 옵션을 하나씩 켜기로 결정했다.
적용 순서
1단계: noImplicitAny
// Before
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
// After
function calculateTotal(items: CartItem[]) {
return items.reduce((sum, item) => sum + item.price, 0);
}
가장 먼저 noImplicitAny를 활성화했다. 파라미터나 변수에 타입이 명시되지 않으면 에러가 발생하도록 했다. 약 120개 정도의 에러가 나왔고, 일주일에 걸쳐 해결했다.
2단계: strictNullChecks
interface User {
name: string;
email?: string;
}
function sendEmail(user: User) {
// Error: Object is possibly 'undefined'
const domain = user.email.split('@')[1];
// Fix
if (user.email) {
const domain = user.email.split('@')[1];
}
}
strictNullChecks는 생각보다 많은 수정이 필요했다. 특히 API 응답 처리 부분에서 optional 체이닝이 없어서 조건문으로 처리해야 했다. (optional chaining은 TS 3.7에서 나올 예정이라고 한다)
3단계: 나머지 옵션들
strictFunctionTypes, strictBindCallApply 등은 상대적으로 에러가 적었다. 대부분 이미 앞선 단계에서 정리가 됐기 때문이다.
결과
약 한 달에 걸쳐 모든 strict 옵션을 활성화했다. 런타임 에러가 눈에 띄게 줄었고, 특히 Cannot read property of undefined 류의 에러가 거의 사라졌다.
팀원들 반응도 긍정적이다. 초반엔 타입 에러 해결이 번거롭다는 의견도 있었지만, IDE의 자동완성과 리팩토링이 훨씬 정확해진 점을 체감하고 있다.