TypeScript 4.6 strictNullChecks와 씨름한 기록
배경
2년 전 시작한 프로젝트의 TypeScript 설정을 강화하기로 했다. strictNullChecks를 켜는 순간 500개가 넘는 에러가 터졌다. 한 번에 해결할 수 없어서 파일별로 점진 적용하는 방식을 선택했다.
자주 마주친 패턴
1. API 응답 타입
// Before
interface User {
id: string;
name: string;
email: string;
}
// After - 실제로는 null이 올 수 있었음
interface User {
id: string;
name: string;
email: string | null;
}
백엔드에서 email이 null로 내려오는 경우가 있었는데, 타입에는 반영되지 않아 런타임 에러가 종종 발생했다.
2. Optional Chaining 남용
// Bad - 무분별한 사용
const userName = user?.profile?.name ?? 'Anonymous';
// Good - null 체크 명시
if (!user || !user.profile) {
return 'Anonymous';
}
const userName = user.profile.name;
?.를 남발하면 실제 버그가 숨겨진다. 어디서 null이 들어올 수 있는지 명확히 하는 게 중요했다.
3. DOM 조작
// Before
const button = document.getElementById('submit');
button.addEventListener('click', handleClick); // 에러
// After
const button = document.getElementById('submit');
if (button) {
button.addEventListener('click', handleClick);
}
getElementById는 HTMLElement | null을 반환한다. 이 부분을 놓쳐서 수정한 곳이 많았다.
점진 적용 전략
//@ts-expect-error로 에러 마킹- 주요 도메인 모델부터 타입 정확하게 수정
- 유틸 함수에 타입 가드 추가
- 매주 20~30개씩 해결
결과
2개월 걸려서 모든 파일에 적용 완료했다. 런타임 에러가 30% 정도 줄었고, 타입 추론이 정확해져서 자동완성도 개선됐다. 초반에 귀찮았지만 충분히 가치 있는 작업이었다.