JavaScript 프로젝트에 TypeScript 점진적으로 도입하기

배경

3년간 운영 중인 Node.js API 서버에 TypeScript를 도입하기로 했다. 런타임 에러가 잦았고, 팀원이 늘면서 코드베이스 파악이 어려워졌기 때문이다.

적용 전략

전체를 한 번에 마이그레이션하는 대신 점진적 적용 방식을 택했다.

1. tsconfig.json 설정

{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": false,
    "noEmit": true,
    "target": "es2017",
    "module": "commonjs",
    "strict": false
  },
  "include": ["src/**/*"]
}

allowJs를 true로 두어 .js와 .ts 파일이 공존할 수 있게 했다. strict는 나중에 켜기로 했다.

2. 우선순위 정하기

  • 유틸 함수부터 시작 (의존성 적음)
  • 새로 작성하는 코드는 무조건 .ts
  • 버그 수정 시 해당 파일만 마이그레이션

3. 타입 정의 추가

// before (utils/validator.js)
function validateEmail(email) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

// after (utils/validator.ts)
function validateEmail(email: string): boolean {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

간단한 함수부터 타입을 명시했다.

4. any 타입 허용

초반엔 복잡한 객체는 any로 두고 넘어갔다. 완벽주의를 추구하면 진행이 안 된다.

function processData(data: any) {
  // 일단 any로 두고 나중에 interface 정의
}

마주친 문제들

라이브러리 타입 정의 없음
@types/ 패키지가 없는 라이브러리들이 많았다. .d.ts 파일을 직접 작성하거나 declare module로 임시 처리했다.

빌드 프로세스 변경
ts-node로 개발 서버를 띄우고, 배포 시엔 tsc로 컴파일하도록 수정했다. CI/CD 파이프라인도 함께 변경했다.

결과

2개월간 전체 코드의 약 40%를 마이그레이션했다. 타입 체크 덕분에 배포 전 잡아낸 버그가 여럿 있었고, VSCode의 자동완성이 확실히 정확해졌다.

아직 갈 길이 멀지만, 점진적 접근이 정답이었다고 생각한다.