PropTypes는 React의 props
에 대한 런타임 유형 검사 도구입니다.
React 15.5부터 사용할 수 있으며, props-types를 사용하면 props
가 필수인지 선택 사항인지 명시하는 것과 같이 컴포넌트에서 예상되는 props
유형을 정의할 수 있습니다. 컴포넌트에 다른 유형을 전달하거나 필수 props
를 전달하지 않으면 JavaScript 콘솔창에 경고문이 표시됩니다.
props
의 타입을 지정하여 다른 타입의 값이 들어왔을 때 콘솔창에서 경고문을 확인할 수 있습니다.
또한 타입 뒤에 .isRequired
를 붙여주면 필수 props
로 인식하여, 값이 없는 경우 콘솔창에 에러가 발생합니다.
- propTypes는 성능상의 이슈로 개발 모드에서만 유효하고, 실제 프로덕션에서는 아무런 영향을 주지 않습니다.
콘솔창에서 에러 로그를 확인 할 수 있긴하지만, vscode(IDE)에서는 오류나 경고를 표시하지 않습니다. PropsTypes는 프로덕션 응용 프로그램(vscode)에서 경고를 따로 제공하지 않으며 경고는개발 중에만
표시되어집니다.
propsTypes의 타입의 종류는 아래와 같습니다.
import PropTypes from 'prop-types';
MyComponent.propTypes = {
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
// ES6 Symbol
optionalSymbol: PropTypes.symbol,
// 렌더링할 수 있는 모든 것(number, string, JSX code, ...)
optionalNode: PropTypes.node,
// React 엘리먼트
optionalElement: PropTypes.element,
// React 엘리먼트 타입 (ie. MyComponent)
optionalElementType: PropTypes.elementType,
// prop가 클래스의 인스턴스임을 선언할 수 있습니다.
// JavaScript의 instanceof 연산자를 사용합니다.
optionalMessage: PropTypes.instanceOf(Message),
// 열거형(enum)으로 처리하여 prop가 특정 값들로 제한되도록 할 수 있습니다.
optionalEnum: PropTypes.oneOf(['News', 'Photos']),
// 여러 종류중 하나의 종류가 될 수 있는 객체
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),
// 특정 타입의 행렬
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
// 객체의 모든 키 값이 주어진 PropType인 객체
optionalObjectOf: PropTypes.objectOf(PropTypes.number),
// 특정 형태를 갖는 객체
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
}),
// An object with warnings on extra properties
optionalObjectWithStrictShape: PropTypes.exact({
name: PropTypes.string,
quantity: PropTypes.number
}),
// 모든 데이터 타입이 가능
requiredAny: PropTypes.any,
};
ex) 사용 예시
import React from "react";
import PropsTypeTest from "./PropsTypeTest";
function App(){
return (<PropsTypeTest value={100}/>)
}
export default App;
import React from "react";
import PropTypes from "prop-types";
function PropsTypeTest({name,value}){
return (
<div>
<p>{name}</p>
<p>{value}</p>
</div>
);
}
PropsTypeTest.propTypes = {
name: PropTypes.string.isRequired,
value: PropTypes.string,
};
export default PropsTypeTest;
위와 같이 name, value 값을 string형, name은 isRequired
를 붙여 필수 값으로 지정했을 때 name 값을 넘기지 않거나 value 값에 number 타입 값을 전달하면 콘솔창에 에러 로그가 찍히는 것을 확인할 수 있습니다.
defaultProps를 사용하면 부모 컴포넌트에서 자식 컴포넌트로 해당 속성을 지정하지 않았을 경우, 지정할 기본값을 설정할 수 있습니다.
propTypes의 타입 확인은 defaultProps에도 적용되게 하기 위하여 defaultProps가 처리된 뒤에 일어납니다.
ex) 사용 예시
PropsTypeTest.defaultProps = {
name: "myName",
value: "myValue",
};
위와 같이 defaultProps를 사용하여 name, value에 기본값을 설정하면 부모 컴포넌트에서 해당 속성을 주지 않았을 때 기본값으로 설정되는 것을 확인할 수 있습니다.
PropTypes를 보다보니 그냥 TypeScript 쓰는거랑 뭐가 다른거지..?
라는 생각이 들어 찾아봤습니다.
TypeScript에 대한 간략한 설명
- TypeScript란 JavaScript에 타입을 부여한 언어입니다.
- 동적인 JavaScript를 정적 언어로 사용하기 위해
- 정적 언어는 컴파일시 타입이 결정되고, 동적 언어는 런타임에 타입이 결정
- TypeScript는 JavaScript와 다르게 브라우저에서 실행하기 위해 파일을 컴파일하는 과정이 필요합니다.
- TypeScript는 예기치 않은
props
를 전달할 때 vscode(IDE)에 즉각적인 경고를 표시합니다.
- 코드를 작성하는 순간 바로 오류 캐치 가능
두 도구는 모두 props
의 타입 체크에 사용됩니다. 그렇다면 어떤 프로젝트에 어떤 도구를 사용하면 좋을지에 대해 고려해야할 몇 가지 주요 차이점에 대해 정리해보겠습니다.
컴파일타임과 런타임 간단 정리
- 컴파일타임(Compile Time)
개발자에 의해 C, JAVA 등과 같은 소스코드가 작성되며, 컴파일 과정을 통해 컴퓨터가 인식할 수 있는 기계어 코드로 변환되어 실행 가능한 프로그램이 되는 과정입니다.
- 컴파일에러
소스코드가 컴파일 되는 과정 중 발생하는 Syntax Error, 파일 참조 에러 등과 같은 문제들로 인해 컴파일이 방해되어 발생하는 오류를 의미하며, 현재 문제가 되는 소스코드를 IDE에서 알려줍니다.
- 런타임(Run Time)
컴파일 과정을 마친 응용 프로그램이 사용자에 의해서 실행되어 지는 때를 의미합니다.
- 런타임에러
이미 컴파일이 완료되어 프로그램이 실행중임에도 불구하고, 의도치 않은 예외상황으로 인하여 프로그램 실행 중에 발생하는 오류 형태를 의미합니다.
🧐 어떤 경우에 런타임 단계에서 TypeScript에서 체크하지 못하는 타입 불일치가 발생할까?
컴파일 단계를 건너뛰고 런타임에서 처음 등장하는 데이터는 외부 데이터(API 응답 데이터, 로컬 스토리지 등) 뿐입니다.
두 도구 모두 컴포넌트의 props에 대한 정보와 함께 자동 완성 기능을 제공하는 플러그인이 있고, TypeScript의 표시 도구가 PropTypes를 능가한다고 합니다.
PropTypes가 제공할 수 없는 TypeScript의 props 유형을 지정할 수 있는 방법은 여러 가지가 있습니다. 예를 들면 인터페이스끼리 합치거나, 조건부 타입 체킹이 가능합니다.
InferPropTypes @types/prop-types의 유형을 사용하여 PropTypes 정의에서 유형 정의를 만들 수 있습니다.
import React from "react";
import PropTypes, { InferProps } from "prop-types";
const BlogCardPropTypes = {
title: PropTypes.string.isRequired,
createdAt: PropTypes.instanceOf(Date),
authorName: PropTypes.string.isRequired,
};
type BlogCardTypes = InferProps<typeof BlogCardPropTypes>;
const BlogCard = ({ authorName, createdAt, title }: BlogCardTypes) => {
return <span>Blog Card</span>;
};
BlogCard.propTypes = BlogCardPropTypes;
export default BlogCard;
구성 요소에 PropTypes 정의가 있으며 TypeScript 유형을 위반할 경우 오류가 발생합니다. 위처럼 'authorName'은 누락되었다고 표시되지만 BlogCardPropTypes 속성에 isRequired를 추가하지 않았기 때문에 createdAt은 누락되었다고 표시되지 않습니다.
babel-plugin-typescript-to-proptypes를 사용하여 TypeSciprt 유형 정의에서 PropTypes를 생성할 수 있습니다. props
유형을 지정하면 플러그인이 해당 유형 정의를 PropTypes 정의로 변환합니다.
import React from "react";
type Props = {
title: string;
createdAt: Date;
authorName: string;
};
const BlogCard = ({ title, createdAt, authorName }: Props) => {
return <span>Blog card</span>;
};
export default BlogCard;
위 코드는 아래와 같이 작성된 것처럼 작동합니다.
import React from 'react'
import PropTypes from 'prop-types'
const BlogCard = ({ title, createdAt, authorName }) => {
return <span>Blog card</span>;
};
BlogCard.propTypes = {
title: PropTypes.string.isRequired,
createdAt: PropTypes.instanceOf(Date),
authorName: PropTypes.string.isRequired,
}
export default BlogCard
타입스크립트 유틸리티 타입을 통해 런타임 타입 체크를 하는 구현 방법입니다.
Adopting Typescript at Scale 강의를 참고하면 좋습니다.
타입스크립트 런타임 타입 시스템인 io-ts를 기반으로 제작된 라이브러리입니다.
props 인터페이스를 기반으로 런타임 타입 체크 코드를 자동으로 생성해주는 웹팩 로더입니다.
검색해본 결과 범용적으로 TypeScript를 더 많이 사용하고 있는 것 같습니다. 하지만 외부 데이터(API 응답 데이터, 로컬 스토리지 등)를 자주 가져오는 상황이라면 타입 불일치로 인한 런타임 에러의 가능성(런타임 에러가 최악인 이유 보러가기)을 염두해두어여하고 완벽한 휴먼 에러의 사전 방지를 원한다면 PropTypes의 사용이 더 좋을 것 같다는 생각이 듭니다.
각 프로젝트 별로 적합한 도구를 선택하여 사용하거나 양쪽 모두의 장점을 살릴 수 있는 방법을 채택하는 것도 좋을 것 같습니다.
🙇🏻♂️ 참고한 글
Comparing TypeScript and PropTypes in React applications
타입스크립트에서 prop-types를 사용할까?
React Component Props Typing with propTypes and defaultProps in TypeScript