JavaScript 프로젝트에 TypeScript 점진적으로 도입하기
배경
회사 프로젝트가 JavaScript로만 작성되어 있었는데, 코드베이스가 커지면서 타입 관련 버그가 자주 발생했다. 전체를 한 번에 마이그레이션하기엔 리스크가 커서 점진적 도입 방식을 선택했다.
설정
tsconfig.json에서 핵심은 allowJs와 checkJs 옵션이었다.
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": ["es2015", "dom"],
"allowJs": true,
"checkJs": false,
"jsx": "react",
"outDir": "./dist",
"strict": false,
"esModuleInterop": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
allowJs를 켜면 .js 파일도 컴파일 대상에 포함된다. checkJs는 꺼두고 필요한 파일만 // @ts-check 주석으로 검사했다.
진행 방식
- 유틸 함수부터
.ts로 변환 - API 응답 타입 정의 (
interface활용) - 새로운 컴포넌트는
.tsx로 작성 - 기존
.js파일은 수정할 때만 변환
// types/api.ts
export interface User {
id: number;
name: string;
email: string;
}
// api/user.ts
import { User } from '../types/api';
export async function fetchUser(id: number): Promise<User> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
마주친 문제
라이브러리 타입 정의가 없는 경우가 많았다. @types/ 패키지를 찾거나, 없으면 declare module로 임시 타입을 만들었다.
// types/custom.d.ts
declare module 'old-library' {
export function doSomething(value: string): void;
}
소감
strict 모드는 아직 적용하지 않았지만, 기본적인 타입 체크만으로도 오타나 null 참조 에러를 사전에 잡을 수 있었다. 팀원들 반응도 긍정적이라 계속 확대할 예정이다.