프로젝트에 TypeScript 도입하기 - 점진적 마이그레이션 전략

배경

회사 프로젝트가 커지면서 타입 관련 버그가 잦아졌다. API 응답 구조 변경, 함수 파라미터 실수 등 런타임 에러가 반복되어 TypeScript 도입을 검토했다.

전체 코드베이스를 한 번에 마이그레이션하기엔 리스크가 컸다. 점진적 적용 방식을 택했다.

설정

// tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "lib": ["es2015", "dom"],
    "allowJs": true,
    "checkJs": false,
    "jsx": "react",
    "outDir": "./dist",
    "strict": false,
    "noImplicitAny": false,
    "esModuleInterop": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

allowJs를 켜고 strict는 끈 상태로 시작했다. 기존 JS 파일과 새로 작성하는 TS 파일이 공존할 수 있다.

마이그레이션 순서

  1. 유틸리티 함수부터: 의존성이 적고 테스트가 있는 파일을 먼저 .ts로 변환
  2. 타입 정의 파일 작성: API 응답, Props 등 자주 쓰는 타입을 types.ts에 정의
  3. 신규 컴포넌트는 TS로: 새로 만드는 파일은 무조건 TypeScript로 작성
  4. 점진적 strict 옵션 활성화: 안정화되면 noImplicitAny 등을 하나씩 켬

실제 적용 예시

// Before (JS)
function fetchUser(userId) {
  return fetch(`/api/users/${userId}`).then(res => res.json());
}

// After (TS)
interface User {
  id: number;
  name: string;
  email: string;
}

function fetchUser(userId: number): Promise<User> {
  return fetch(`/api/users/${userId}`).then(res => res.json());
}

API 함수에 타입을 붙이니 호출하는 쪽에서 자동완성이 동작했다. VSCode의 인텔리센스 지원도 확실히 좋아졌다.

소감

처음엔 러닝커브가 부담스러웠지만, 일주일 정도 적응하니 생산성이 오히려 올랐다. 특히 리팩토링할 때 타입 에러로 사이드 이펙트를 미리 발견할 수 있었다.

당장 전체를 마이그레이션하지 않아도 된다는 점이 가장 큰 장점이었다. 팀원들도 부담 없이 따라올 수 있었다.