PropTypes를 TypeScript로 마이그레이션하며

배경

팀 내에서 TypeScript 도입 논의가 있었고, 작은 프로젝트부터 시범 적용하기로 했다. 기존 React 프로젝트는 PropTypes로 타입 체크를 하고 있었는데, 이를 TypeScript로 전환하는 작업을 맡았다.

PropTypes vs TypeScript

PropTypes는 런타임에 타입을 체크한다. 개발 중에는 유용하지만, 프로덕션에서는 오버헤드가 있고 빌드 시점에 에러를 잡지 못한다는 단점이 있었다.

// Before: PropTypes
import PropTypes from 'prop-types';

const UserCard = ({ name, age, email }) => {
  // ...
};

UserCard.propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number,
  email: PropTypes.string.isRequired
};

TypeScript로 전환하면서 컴파일 타임에 타입 에러를 잡을 수 있게 됐다.

// After: TypeScript
interface UserCardProps {
  name: string;
  age?: number;
  email: string;
}

const UserCard: React.FC<UserCardProps> = ({ name, age, email }) => {
  // ...
};

마이그레이션 과정

  1. .js 파일을 .tsx로 변경
  2. PropTypes 정의를 interface로 전환
  3. 컴포넌트에 타입 적용
  4. PropTypes import 제거

처음에는 React.FC를 사용했는데, children이 암묵적으로 포함되는 점이 마음에 들지 않았다. 하지만 일단은 일관성을 위해 전체적으로 적용했다.

예상치 못한 이슈

기존 코드에서 PropTypes를 느슨하게 정의했던 부분들이 문제가 됐다. PropTypes.object로 되어 있던 것들을 구체적인 interface로 정의해야 했고, 이 과정에서 실제 사용되는 구조를 파악하는 시간이 걸렸다.

// 모호했던 부분을 명확하게
interface UserData {
  id: number;
  profile: {
    name: string;
    avatar?: string;
  };
  settings: Record<string, boolean>;
}

결과

  • IDE의 자동완성과 타입 힌트가 개발 속도를 높였다
  • 리팩토링 시 영향받는 부분을 쉽게 파악할 수 있게 됐다
  • 번들 사이즈가 PropTypes만큼 줄어들었다 (프로덕션)

다음 스프린트부터는 새로 작성하는 컴포넌트는 모두 TypeScript로 작성하기로 했다.

PropTypes를 TypeScript로 마이그레이션하며