TypeScript 프로젝트에 strict 모드 점진적으로 도입하기

문제 상황

회사 프로젝트가 TypeScript 2.x 시절부터 시작되어 strict: false 상태로 운영되고 있었다. 타입 안정성을 높이기 위해 strict 모드를 켜려고 했더니 300개가 넘는 에러가 발생했다.

// tsconfig.json
{
  "compilerOptions": {
    "strict": true  // 이거 하나로 지옥행
  }
}

팀원들과 논의 끝에 일괄 적용은 불가능하다고 판단했다. 신규 개발을 멈출 수 없었고, 에러를 //@ts-ignore로 때우는 건 의미가 없었다.

해결 방법

strict는 사실 여러 옵션의 묶음이다. 하나씩 켜기로 했다.

{
  "compilerOptions": {
    "strict": false,
    "noImplicitAny": true,
    "strictNullChecks": false,
    "strictFunctionTypes": false,
    "strictBindCallApply": false,
    "strictPropertyInitialization": false,
    "noImplicitThis": false,
    "alwaysStrict": true
  }
}

1주차: noImplicitAny

가장 기본적인 옵션부터 시작했다. any 타입이 암묵적으로 추론되는 경우를 잡아낸다.

// Before
function getUser(id) {  // Parameter 'id' implicitly has an 'any' type
  return users.find(u => u.id === id);
}

// After
function getUser(id: string) {
  return users.find(u => u.id === id);
}

약 80개 에러가 발생했고, 2일 정도 걸려서 수정했다. 대부분 함수 파라미터 타입 지정이었다.

3주차: strictNullChecks

이게 제일 고됐다. nullundefined를 명시적으로 처리해야 한다.

// Before
const user = users.find(u => u.id === id);
return user.name;  // Object is possibly 'undefined'

// After
const user = users.find(u => u.id === id);
if (!user) throw new Error('User not found');
return user.name;

약 180개 에러가 나왔다. 1주일 동안 모듈별로 나눠서 수정했다. Optional chaining이 TS 3.7에서 나온다는 소식을 들어서 기다려볼까 했지만, 그냥 진행했다.

결과

5주 정도 걸려서 모든 strict 옵션을 활성화했다. 그 과정에서 실제 버그를 3건 발견했다. undefined 처리가 안 된 부분에서 프로덕션 에러가 간헐적으로 발생하고 있었다.

점진적 도입이 정답이었다. 한 번에 했으면 팀원들의 반발도 있었을 것이고, 에러를 대충 처리했을 가능성이 크다.

TypeScript 프로젝트에 strict 모드 점진적으로 도입하기