TypeScript 3.9 strictNullChecks와 Optional Chaining 적용 후기
배경
재택근무로 전환되면서 기술 부채 해결에 시간을 할애할 수 있게 되었다. 그동안 미뤄왔던 TypeScript strictNullChecks 옵션을 켜기로 결정했다.
프로젝트는 TS 3.7에서 3.9로 업그레이드한 상태였고, Optional Chaining(?.)과 Nullish Coalescing(??)을 사용할 수 있었다.
문제 상황
strictNullChecks를 켜자마자 300개 이상의 에러가 발생했다. 대부분 다음 패턴이었다.
// 기존 코드
function getUserName(user: User) {
return user.profile.name; // profile이 undefined일 수 있음
}
// API 응답 처리
const data = response.data.result; // data가 null일 수 있음
해결 과정
1. 타입 정의 개선
먼저 실제로 null/undefined가 가능한 필드를 명시했다.
interface User {
id: string;
profile?: {
name: string;
avatar?: string;
};
}
2. Optional Chaining 활용
중첩된 객체 접근에는 Optional Chaining을 적용했다.
function getUserName(user: User) {
return user.profile?.name ?? '익명';
}
const avatarUrl = user.profile?.avatar ?? '/default-avatar.png';
3. Type Guard 함수 작성
반복되는 체크 로직은 Type Guard로 분리했다.
function hasProfile(user: User): user is User & { profile: NonNullable<User['profile']> } {
return user.profile !== undefined;
}
if (hasProfile(user)) {
console.log(user.profile.name); // 안전하게 접근
}
마이그레이션 전략
한 번에 모든 에러를 고치는 건 불가능했다. 다음 순서로 진행했다.
- 유틸 함수와 타입 정의부터 수정
- 도메인별로 하나씩 적용 (auth → user → product)
- 각 PR마다 관련 테스트 케이스 추가
약 2주간 작업했고, 실제로 숨어있던 버그 2건을 발견했다. API 응답이 null인 경우를 제대로 처리하지 않던 코드였다.
결과
strictNullChecks 적용 후 런타임 에러가 눈에 띄게 줄었다. 특히 Cannot read property 'x' of undefined 류의 에러가 거의 사라졌다.
Optional Chaining과 Nullish Coalescing 덕분에 코드도 간결해졌다. 과거에 && 연산자로 체크하던 코드가 훨씬 읽기 좋아졌다.
앞으로 신규 프로젝트는 처음부터 strict 모드로 시작할 예정이다.