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); // 안전하게 접근
}

마이그레이션 전략

한 번에 모든 에러를 고치는 건 불가능했다. 다음 순서로 진행했다.

  1. 유틸 함수와 타입 정의부터 수정
  2. 도메인별로 하나씩 적용 (auth → user → product)
  3. 각 PR마다 관련 테스트 케이스 추가

약 2주간 작업했고, 실제로 숨어있던 버그 2건을 발견했다. API 응답이 null인 경우를 제대로 처리하지 않던 코드였다.

결과

strictNullChecks 적용 후 런타임 에러가 눈에 띄게 줄었다. 특히 Cannot read property 'x' of undefined 류의 에러가 거의 사라졌다.

Optional Chaining과 Nullish Coalescing 덕분에 코드도 간결해졌다. 과거에 && 연산자로 체크하던 코드가 훨씬 읽기 좋아졌다.

앞으로 신규 프로젝트는 처음부터 strict 모드로 시작할 예정이다.

TypeScript 3.9 strictNullChecks와 Optional Chaining 적용 후기