React 프로젝트에 TypeScript 점진적으로 도입하기
배경
6개월 전부터 운영 중인 React 프로젝트가 있다. 컴포넌트가 100개를 넘어가면서 props 타입 에러가 런타임에 발견되는 일이 잦아졌다. PropTypes로는 한계가 있다고 판단해 TypeScript 도입을 결정했다.
마이그레이션 전략
한 번에 전체를 .tsx로 변환하는 건 불가능했다. 다음 전략을 택했다.
- tsconfig.json 세팅 (
allowJs: true,checkJs: false) - 신규 컴포넌트는 .tsx로 작성
- 수정이 필요한 컴포넌트만 .tsx로 전환
- 공통 타입부터 정의 (types/index.ts)
초기 설정
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "es2015"],
"jsx": "react",
"module": "esnext",
"moduleResolution": "node",
"allowJs": true,
"strict": false,
"esModuleInterop": true,
"skipLibCheck": true
}
}
strict를 처음부터 켜면 기존 코드에서 에러가 수백 개 발생한다. 일단 false로 두고 새 파일에만 엄격하게 적용했다.
공통 타입 정의
// types/index.ts
export interface User {
id: number;
email: string;
name: string;
role: 'admin' | 'user';
}
export interface ApiResponse<T> {
data: T;
error?: string;
}
가장 자주 쓰이는 User, ApiResponse 같은 타입부터 정의했다. 기존 .js 파일에서도 JSDoc으로 임포트해서 사용할 could 있다.
실제 전환 예시
// UserProfile.tsx
import React from 'react';
import { User } from '../types';
interface Props {
user: User;
onEdit: (id: number) => void;
}
const UserProfile: React.FC<Props> = ({ user, onEdit }) => {
return (
<div>
<h2>{user.name}</h2>
<button onClick={() => onEdit(user.id)}>수정</button>
</div>
);
};
export default UserProfile;
효과
- IDE 자동완성이 정확해졌다
- props 누락/오타가 컴파일 시점에 잡힌다
- 리팩토링 시 변경 영향 범위 파악이 쉽다
3개월 정도 걸려 핵심 컴포넌트 50% 정도를 TS로 전환했다. 급하게 전체를 바꾸려 하지 않은 게 주효했다.