프로젝트에 TypeScript 3.5 도입하며 겪은 것들
배경
2017년부터 운영 중인 React 프로젝트의 런타임 에러가 잦아졌다. PropTypes로는 한계가 명확했고, 리팩토링 시 사이드 이펙트를 예측하기 어려웠다. TypeScript 도입을 제안했고, 3주간의 검증 기간을 거쳐 승인받았다.
마이그레이션 전략
한 번에 전환하는 것은 리스크가 컸다. .js와 .ts를 공존시키는 방식으로 진행했다.
// tsconfig.json
{
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"strict": false,
"target": "es5",
"module": "esnext",
"jsx": "react"
}
}
초기에는 strict: false로 시작했다. 기존 코드를 건드리지 않으면서 새 코드만 .tsx로 작성하는 방식이었다.
주요 이슈
1. Redux 타입 정의
가장 골치 아팠던 부분이다. connect의 타입 추론이 제대로 되지 않아 any를 남발하게 됐다.
// 초기 시도 - 타입이 제대로 추론되지 않음
const mapStateToProps = (state: any) => ({
user: state.user
});
export default connect(mapStateToProps)(Component);
결국 ReturnType을 활용한 패턴으로 정리했다.
const mapStateToProps = (state: RootState) => ({
user: state.user
});
type StateProps = ReturnType<typeof mapStateToProps>;
const Component: React.FC<StateProps> = ({ user }) => {
// ...
};
2. Third-party 라이브러리 타입
@types/ 패키지가 없는 라이브러리들이 많았다. declare module로 임시 대응했다.
// src/types/custom.d.ts
declare module 'react-some-library' {
export const SomeComponent: any;
}
3. Webpack 설정
ts-loader와 babel-loader를 함께 사용하면서 빌드 시간이 2배 늘었다. transpileOnly 옵션으로 타입 체크를 분리해 해결했다.
결과
3개월간 50% 정도 마이그레이션했다. 런타임 에러는 체감상 30% 줄었고, IDE 자동완성으로 생산성도 올랐다. 다만 학습 곡선과 초기 세팅 비용은 분명 존재했다. 팀원들의 TypeScript 이해도를 높이는 것이 다음 과제다.