React 프로젝트에 TypeScript 점진적으로 도입하기
배경
1년 반 동안 운영한 React 프로젝트에 TypeScript를 도입하기로 했다. 컴포넌트가 200개가 넘는 상황에서 한 번에 전환하는 것은 불가능했고, 점진적 마이그레이션 전략이 필요했다.
마이그레이션 전략
1. tsconfig.json 설정
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "es2015"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react"
},
"include": ["src"]
}
핵심은 allowJs: true와 strict: false였다. 기존 JS 파일과 새로운 TS 파일이 공존할 수 있게 했고, 엄격한 타입 체크는 나중으로 미뤘다.
2. 우선순위 설정
하위 컴포넌트부터 상위로 올라가는 Bottom-up 방식을 선택했다. 의존성이 적은 유틸 함수와 공통 컴포넌트를 먼저 전환했다.
// utils/format.ts
export const formatCurrency = (amount: number): string => {
return new Intl.NumberFormat('ko-KR', {
style: 'currency',
currency: 'KRW'
}).format(amount);
};
// components/Button.tsx
interface ButtonProps {
onClick: () => void;
children: React.ReactNode;
variant?: 'primary' | 'secondary';
}
const Button: React.FC<ButtonProps> = ({ onClick, children, variant = 'primary' }) => {
return (
<button className={`btn btn-${variant}`} onClick={onClick}>
{children}
</button>
);
};
3. 타입 정의 파일 활용
서드파티 라이브러리 타입은 @types 패키지로 설치했다. 타입 정의가 없는 라이브러리는 .d.ts 파일을 직접 작성했다.
// types/custom.d.ts
declare module 'legacy-library' {
export function doSomething(param: string): void;
}
실제 효과
한 달간 주요 컴포넌트 50개를 전환했다. Props 타입 오류를 10건 이상 사전에 발견했고, 리팩토링 시 IDE 자동완성으로 생산성이 체감상 20% 정도 올랐다.
교훈
처음부터 strict: true로 시작했다면 팀원들의 거부감이 컸을 것이다. 점진적 전환과 느슨한 설정으로 시작해 서서히 엄격하게 가져가는 전략이 효과적이었다.