CRA 없이 프로젝트 구성하기 3 - prop-types -> 타입스크립트 적용하기

Seungrok Yoon (Lethe)·2023년 7월 25일
1
post-thumbnail

점진적으로 prop-types부터 typescript까지 적용해보기

이 글에서는 순수 자바스크립트 기반의 리액트 프로젝트에서 컴포넌트 프롭스에 타입 체킹을 적용하는 방법을 다루고 있습니다.

prop-types를 시범적으로 적용해 본 후, typescript를 점진적으로 적용해볼 예정입니다.

✅ 레포: https://github.com/SeungrokYoon/react-from-scratch
✅ PR: https://github.com/SeungrokYoon/react-from-scratch/pull/5

사건의 발단


프로젝트도 어느 정도 세팅했겠다 본격적인 컴포넌트 개발에 돌입하려는 찰나 엄격한 airbnb룰때문에 수많은 에러가 등장하고 있었습니다.

export default function Button({ children, type = 'button', onClick }) {
  return (
    <button type="button" onClick={onClick}>
      {children}
    </button>
  );
}

pros validataion 이 필요하다고 부르짖는 나의 eslint에게 선물을 주도록 하겠습니다.

바로 prop-types패키지라는 선물을 말이지요!

prop-types 시범 도입


prop-types란?

https://legacy.reactjs.org/docs/typechecking-with-proptypes.html

이제는 레거시 문서가 되어버린 이전 리액트 공식문서에 관련 설명이 있어 설명을 위해 인용하려 합니다. 이 문서에서는 다음과 같이 리액트 컴포넌트 프롭에 대한 타입검증의 필요성을 이야기하고있습니다.

As your app grows, you can catch a lot of bugs with typechecking. For some applications, you can use JavaScript extensions like Flow or TypeScript to typecheck your whole application. But even if you don’t use those, React has some built-in typechecking abilities. To run typechecking on the props for a component, you can assign the special propTypes property:

앱이 커질수록 타입체킹이 어렵기에, FLow나 TypeScript를 사용하지 않는 경우 리액트에서 사용할 수 있는 빌트인 타입체킹 도구라고 쓰여있네요.

그런데 현재는 이 기능이 prop-types라는 별도의 패키지로 분리되어서, 사용하기 위해서는 이 패키지를 사용해야합니다.

이미 타입스크립트를 적용한 프로젝트에는 사실상 의미가 없는데, 제 프로젝트는 아직 타입스크립트를 적용하지 않아 과도기 겸 경험 삼아 설치해보겠습니다.

prop-types 설치

패키지를 설치해줍니다.

yarn add prop-types

이제 아까 문제가 되었던 Button의 props에 타입을 적용해봅시다.

children이랑 className, 이렇게 두 가지 props를 사용해보죠.
각각의 타입은 PropTypes를 패키지에서 import해와서 사용하면됩니다.

children은 리액트 element이고, 반드시 주어져야 하는 것으로 설정했습니다.

className은 string 타입에 옵셔널한 프롭스로 설정해주었습니다.

import PropTypes from 'prop-types';

export default function Button({ children, className }) {
  return (
    <button type="button" className={className}>
      {children}
    </button>
  );
}

Button.defaultProps = {
  className: '',
};

Button.propTypes = {
  children: PropTypes.element.isRequired,
  className: PropTypes.string,
};

그러면 Home.jsx컴포넌트에서 Button을 불러와 한 번 사용해보죠.
제대로 props의 타입을 체크하고 있는지 확인하기 위해서 className에는 number 타입을, children에는 string 타입의 props를 줘봅시다.

//Home.jsx
import Button from '@Component/Button';

export default function Home() {
  return (
    <>
      <div>Home</div>
      <Button className={1}>button</Button>
    </>
  );
}

그랬더니 아래처럼 에러가 나는군요. 아주 좋습니다.

https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/require-default-props.md

prop-types의 장단점

간단하게 사용해본 prop-types는 typescript 적용 없이 간단하게 prop의 타입을 체크할 수 있다는 장점이 있었습니다.

하지만 단점도 명확합니다.

매우 코드량이 많아지고, 타입지정이 귀찮습니다.

복잡한 타입설정을 위해서 더 많은 코드를 작성해야하고, 가독성도 좋지 못한 느낌입니다.

이제 타입스크립트로 비로소 넘어가도 되겠네요.

타입스크립트 도입하기


패키지 설치

타입스크립트를 리액트 프로젝트에 적용시키려면 타입스크리트 뿐 아니라, 각종 타입들이 저장되어 있는 패키지들도 함께 설치를 해야합니다.

creat-react-app 문서(링크)에서는 @types/jest까지 설치해주고 있지만, 제 프로젝트에는 jest를 아직 설치하지 않았기에 빼주었습니다.

그런데 그거 아시나요? 리액트 17버전부터는 리액트 타입들이 이미 메인 리액트 패키지에 포함되어 있답니다? 그 말은 즉슨, React18버전을 사용하고 있다면, @types/react를 설치하지 않아도 된다는 말씀!

yarn add typescript @types/node @types/react-dom --dev

그러니 여기까지만 설치해줍시다.

설정사항 반영하기

설치하고 나면, jsconfig.json을 tsconfig.json으로
변경해주죠.

변경해주면서 webpack.config.js 설정값 중, entry: './src/index.jsx'entry: './src/index.tsx'로 확장자를 변경해줍니다. 또한 resolve extension 배열에 '.ts','tsx'를 추가해주죠.

//webpack.config.js
...
 resolve: {
    extensions: ['.ts', '.tsx'],
    alias: {
      '@Page': path.resolve(__dirname, './src/pages'),
      '@Component': path.resolve(__dirname, './src/components'),
    },
  },

아, 그리고 .eslintrc에서 webpack.config.js를 import/resolver 로 사용하고 있었는데, 이 부분도 변경해줘야 하겠네요.

리액트 컴포넌트파일들은 이제 확장자를 .jsx => .tsx로 변경해줍시다. 아직 컴포넌트 개수가 적으니 금방 바꿀 수 있었어요 ㅎㅎ.

webpack.config.js 에 typescript 규칙 추가

우리는 webpack을 사용해서 조금 더 최적화된 파일 사이즈와 asset관리를 목적으로 하고 있습니다.
웹팩에서 loader라는 것은 파일 전처리기입니다. 우리의 설정파일에는 아직 .ts, .tsx와 같은 타입스크립트 파일의 처리 방법이 없어 번들링 시 문제가 생길 수 있습니다.

그러니 webpack에도 타입스크립트 파일을 잘 이해할 수 있도록 관련 패키지와 설정을 추가해줍시다.

yarn add ts-loader ts-node --dev
//webpack.config.js
{
  test: /\.tsx?$/,
  use: 'ts-loader',
  exclude: /node_modules/,
},

문제상황 : package.json

webpack.config.js를 수정하기 전에
package.json에 "type":"module"을 제거해줍니다.

"type":"module"로 인해 webpack.config.js 를 ESModule처럼 바꿔야 하는데, 조금은 아쉽네요.

"type"에 대한 기본값은 CommonJS이니, 삭제해 줍시다.

@babel/preset-typescript vs ts-loader

profile
안녕하세요 개발자 윤승록입니다. 내 성장을 가시적으로 기록하기 위해 블로그를 운영중입니다.

0개의 댓글