본문 바로가기

FrontEnd+

[React] 타입 체크을 위한 PropTypes

 

PropTypes는 리액트에서 지원하는 props 타입 체크 방법이다. 리액트 15.5 버전 이전에는 리액트에 포함되어있다가 이후 별도의 패키지로 분리되어 나왔다. PropTypes를 사용해야 하는 이유, 적용 방법에 대해서 알아보자.

 

린터는 왜 PropTypes를 강제할까?

앞서 말한 것과 같이, PropTypes는 JS로 작성한 리액트 코드에서 타입 체크를 도와주는 라이브러리이다. 나는 PropTypes의 존재 자체를 모르다가, 린터가 에러를 띄워주어서 처음 알게되었다. 린터는 왜 설치방법까지 알려주면서 prop-types를 강제하는걸까?

프로젝트 dependencies에 'prop-types' 가 빠져있네요. 'npm i -S prop-types'로 설치하시죠.

 

props를 제대로 전달받는 것이 중요한 이유는 함수에서 argument가 제대로 들어가야 하는 것과 맥락이 같다. 만약 개발자의 실수로 컴포넌트가 꼭 필요한 props를 전달받지 못하거나, 필요한 타입이 아닌 잘못된 타입의 props를 전달받으면 의도치 않은 결과를 야기하게 된다. 이러한 실수로 인해 사용자가 NaN %와 같은 렌더화면을 보게 되거나, 심하면 아예 아무것도 안보이는 깡통 페이지를 보는 상황까지 발생할 수 있다. 

이와 같은 버그를 예방하고, 예상치 못한 에러가 발생하는 것을 막기 위한 방법 중에 하나가 바로 리액트의 PropTypes를 이용하는 것이다. PropTypes는 동적 타이핑을 채택(?)한 JavaScript에서도 데이터의 타입을 검사를 할 수 있다. 즉, PropTypes를 적용하면 더욱 안정적인 코드를 작성할 수 있게 된다. PropTypes로 인도해준 린터에게 고마운 마음을 가지며 PropTypes를 적용해보자. (생각보다 쉽다!)

 

PropTypes 적용 방법

1. prop-types 라이브러리를 설치한다.

// 터미널 명령어
npm install prop-types

2. prop-types에서 import 해온다.

// 컴포넌트 정의한 파일(.js)
import PropTypes from 'prop-types'; // ES6

function MyComponent(props) {
  return (
    <h1>Hello, {props.name}</h1>
  );
}

3. PropTypes를 지정한다.

PropTypes를 지정해두면 리액트는 이를 참고해서 타입 체크를 수행한다. 작성 시 'MyCompoenent.propTypes' 와 같이 p를 소문자로, PropTypes.string과 같은 타입 지정에서는 P를 대문자로 작성해야 함에 유의하자. 다양한 타입을 지정해줄 수 있는데, 이에 대해서는 아래에서 따로 더 살펴보도록 하자.

// 컴포넌트 정의한 파일(.js)
MyComponent.propTypes = {
  name: PropTypes.string.isRequired,
};

4. 필요시 기본값(defaultProps)도 지정한다.

컴포넌트에 기본값을 설정해두면 props의 타입체크 전에 값이 먼저 반영된 후에 타입체크를 진행한다. 따라서 기본값도 props의 타입체크 영향을 받는다.

// 컴포넌트 정의한 파일(.js)
MyComponent.defaultProps = {
  name: 'Haru Kim'
};

 

다양한 PropTypes

1. 원시형 타입 체크

MyComponent.propTypes = {
  myString: PropTypes.string, // 타입이 꼭 string
  myNumber: PropTypes.number, // 타입이 꼭 number
  myBool: PropTypes.bool, // 타입이 꼭 boolean
  mySymbol: PropTypes.symbol, // 타입이 꼭 symbol
};

2. 객체형 타입 체크

MyComponent.propTypes = {
  myFunc: PropTypes.func,
  myArray: PropTypes.array,
  myObject: PropTypes.object,
  mySpecificArray: PropTypes.arrayOf(PropTypes.number), // 요소가 모두 number인 배열
  mySpecificObject: PropTypes.objectOf(PropTypes.number), // 프로퍼티 value가 모두 number인 객체
  
  myInstance: PropTypes.instanceOf(MyClass), // MyClass의 인스턴스
  myElement: PropTypes.element, // 리액트 엘리먼트 <MyComponent />
  myComponent: PropTypes.elementType, // 리액트 컴포넌트 MyComponent
  myNode: PropTypes.node, // 렌더링 될 수 있는 무언가 (number, string, element, array, fragment)
  
  // 객체의 형태를 구체적으로 지정
  myObjectWithShape: PropTypes.shape({
    color: PropTypes.string,
    fontSize: PropTypes.number
  }),
  // 객체의 형태를 구체적으로 지정하되, 프로퍼티 외에 property가 들어오면 경고
  myObjectWithStrictShape: PropTypes.exact({
    name: PropTypes.string,
    quantity: PropTypes.number
  }),
};

PropTypes.element와 PropTypes.elementType의 차이는 여기서 확인할 수 있다.

3. 열거형 타입 체크

MyComponent.propTypes = {
  myOneOfThese: PropTypes.oneOf(['jun', 'poco', 'gongwon']),
  myOneOfTheseTypes: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Coach)
  ]),
};

4. 필수 prop 지정

isRequired를 넣지 않으면 기본적으로 optional이다. 모든 데이터 타입이 가능한 필수 prop은 다음과 같이 지정하면 된다.

MyComponent.propTypes = {
  myAnyButNeccessary: PropTypes.any.isRequired,
};

5. 커스텀

MyComponent.propTypes = {
  myCustom: function(props, propName, componentName) {
    if (!/matchme/.test(props[propName])) {
      return new Error(
        'Invalid prop `' + propName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  },
};

 

참고자료

- prop-types 깃허브
- 리액트 공식문서
- PropTypes in React: A complete guide