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

배경

운영 중인 Node.js API 서버에 TypeScript를 도입하기로 했다. 전체 코드를 한 번에 마이그레이션할 수 없어서 점진적 도입 방식을 선택했다.

초기 설정

먼저 필요한 패키지를 설치했다.

npm install --save-dev typescript @types/node @types/express

tsconfig.json은 기존 JS와 공존할 수 있도록 설정했다.

{
  "compilerOptions": {
    "target": "es2017",
    "module": "commonjs",
    "allowJs": true,
    "checkJs": false,
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": false,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

핵심은 allowJs: truestrict: false다. 기존 JS 파일을 그대로 두고 새 파일만 TS로 작성할 수 있다.

적용 전략

  1. 새로 작성하는 파일은 .ts로 작성
  2. 유틸리티 함수부터 타입 적용
  3. 버그 수정 시 해당 파일을 TS로 전환
// utils/validator.ts
export interface ValidationResult {
  isValid: boolean;
  errors: string[];
}

export function validateEmail(email: string): ValidationResult {
  const errors: string[] = [];
  
  if (!email.includes('@')) {
    errors.push('Invalid email format');
  }
  
  return {
    isValid: errors.length === 0,
    errors
  };
}

마주친 문제들

기존 JS 모듈을 TS에서 import할 때 타입 추론이 안 되는 경우가 많았다. 당장은 any로 우회하고 나중에 정리하기로 했다.

const legacyModule = require('./legacy') as any;

Express 미들웨어 타입 정의도 처음엔 헷갈렸는데, @types/express가 제공하는 타입을 활용하니 편했다.

import { Request, Response, NextFunction } from 'express';

export function authMiddleware(req: Request, res: Response, next: NextFunction) {
  // ...
}

결과

2주간 진행하면서 전체 코드의 약 30%를 TS로 전환했다. IDE의 자동완성과 타입 체크 덕분에 단순 실수가 줄었다. strict 모드는 팀원들이 적응한 후 단계적으로 활성화할 예정이다.

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