[React] TypeScript vs PropTypes

Soozynn·2022년 7월 6일
4

React

목록 보기
2/4

해당 글은 이 블로그를 번역하여 정리해놓은 내용입니다. 참고 바랍니다.


이전 게시물인 callback, Promise, async/await에 대해 정리하다 TypeScript를 왜 사용하는 지에 대해 조금 알게 되었다.

내가 작성하는 코드에서 callback으로 넘겨주는 인자 값들이 어떤 type의 callback 함수인지 예측을 할 수 없어서 이를 제어 해줄 필요가 있었는데 예를 들어 인자로 넘겨받은 함수가 "함수가 아닐 경우"의 예처럼 핸들링을 해주어야할 때가 있었다.
이를 직접 코드로 작성을 해주다보니 자바스크립트는 워낙 유연한 언어이기 때문에 이러한 부분을 세세하게 잡아줄 필요성(?)이 있겠구나 라고 체감을 하였다.
때문에 정적 언어인TypeScript를 쓰는구나! 라는 생각이 들었다.

그러다 문득, 그러면 React에서는 propsPropTypes를 통해 props의 타입을 제어해주는데 왜 현업에서는 TypeScript도 같이 사용을 해주고 있는거지? 라는 의문이 들어 둘의 차이에 대해 조사를 해보게 되었다.

먼저, 이 두가지 방식은 비슷한 목적을 가지고 있지만 "작동방식"에서 차이가 있다고 한다.


React에서 PropTypes란 무엇인가?

PropTypes는 React의 props에 대한 "런타임 유형 검사 도구"이다.
React 15.5부터 PropTypes 유틸리티는 prop-types 패키지를 통해 사용할 수 있는데 PropTypes를 사용하면 props가 필수인지 선택 사항인지 명시하는 것과 같이 컴포넌트에서 예상되는 props 유형을 정의할 수 있는 것이다. 컴포넌트에 다른 유형을 전달하거나 필수 props를 건너뛰면 JavaScript 콘솔에 경고가 표시된다.

import React from 'react';
import PropTypes from 'prop-types'; // ⭐️

export default function ExampleCard({ title }) { 
  return <div>{title}</div>
}

// ⭐️ 아래와 같이 props의 type을 미리 지정해준다.
ExampleCard.propTypes = {
  title: PropTypes.string.isRequired,
}

위처럼 만든 컴포넌트를 다른 모듈에서 가져와 쓸 때 해당 컴포넌트를 사용하기 위해 코드를 작성하고 있다고 가정해보자. 그럼 intelliSense는 입력 시 자동으로 완료된 프로펠러 이름을 표시하고 프로펠러에 대해 예상되는 데이터 유형을 표시해준다.

이 말 뜻이 무엇이냐면 컴포넌트를 사용하기 위해 코드를 작성할 때 자동으로 필요한 props의 name을 표시해주고 데이터 유형 또한 표시해준다는 것이다.

그렇다면, PropTypes를 지정해준 상태에서 지정해준 것과는 다른 데이터 유형을 전달하거나 필요하지 않은 props를 넘기게 되면 어떻게 될까?

👉 확인해보면 vscode(IDE)에서는 오류나 경고를 표시하지 않는다..🤔
코드를 한번 실행(npm start)해서 콘솔을 확인해보자.

아래 사진처럼 콘솔을 열어보면 "Warning: Failed prop type: the prop "어쩌구" is~~"라는 에러를 확인할 수 있다.

해석한 블로그 글을 보면 PropTypes는 프로덕션 응용 프로그램(vscode)에서 경고를 따로 제공하지 않으며 경고는 "개발 중에만" 표시되어진다 고 적혀있다. 응용 프로그램의 프로덕션 버전을 사용하는 동안 예기치 않은 유형이 수신되면 콘솔은 경고를 인쇄하지 않는다.

즉, 개발자 모드에서 직접 실행해보기 전(npm start)까지는 코드를 작성하면서는 해당 오류를 바로 캐치할 수 없다는 것이다.

TypeScript는 또 무엇일까?

React와 함께 TypeScript를 사용하면 컴파일할 때 확인할 내용을 props에 추가할 수 있다.
TypeScript는 브라우저가 이해하는 것이기 때문에 JavaScript로 컴파일된다.

React 응용 프로그램에서 TypeScript를 사용하기 전에 necessary dependencies and configurations을 제공해야 할 수 있다. 예를 들어 Create React App을 사용하는 경우 이 설명서에 따라 기존 응용 프로그램에 TypeScript 지원을 추가할 수 있다.

위의 PropTypes 정의에 대한 TypeScript 해석은 다음과 같다.

import React from 'react'
import PropTypes from 'prop-types'

type Props = {
  message: string;
  type: "error" | "warning" | "success";
  id: string;
}

export default const NotificationCard = ({ message, type, id }: Props) => { 
  return <span>Notification</span>
}

TypeScript를 vscode(IDE) 통해 예기치 않은 props를 전달할 때 vscode(IDE)에 즉각적인 IntelliSense 경고가 표시된다. 잘못된 데이터 유형을 제공하여 테스트해 보면 아래 사진과 같은 경고 메시지가 표시된다.

❗️ 즉, 코드를 작성하는 순간순간 오류를 바로 캐치 할 수 있는 것이다.

올바른 데이터 유형을 제공하면 아래와 같이 메세지가 표시된다.

위에서 볼 수 있듯이 IntelliSense는 오류 해결에 도움이 되는 추가 정보 또한 제공한다. 👍

TypeScript를 사용하면 다음과 같이 props 요구 사항을 위반할 시 npm run build를 실행할 수 없다.


TypeScript와 PropTypes 비교

이 두 도구는 모두 props의 type-checking에 사용 되어진다. 비록 TypeScript가 최선의 선택지처럼 보일지라도, 하나를 선택하는 것은 필연적으로 절충을 초래할 것이다. 고려해야 할 몇 가지 주요 차이점은 아래와 같다고 한다.

1. 런타임 및 컴파일 시간 유형 확인

PropTypes 애플리케이션이 "브라우저에서 실행되는 동안 런타임 중에 유형 검사를 수행"한다.

그러나 TypeScript TypeScript 코드가 JavaScript로 "컴파일될 때 컴파일 시간 동안 유형 검사를 수행"한다.

이것은 두 유형의 사용 방법에 영향을 미치기 때문에 주목해야한다.
아래 몇 가지 예시를 살펴보자.


API의 데이터:

TypeScript는 API에서 오는 데이터를 유형 검사할 수 없다.. 해당 데이터의 내용은 "런타임에만" 알 수 있기 때문에 이러한 코드의 컴파일은 성공하지 못한다고 한다.
하지만, PropTypes는 예상 유형이 위반되면 경고를 표시한다.

PropTypes를 다음과 유사한(정확하지 않은) 작업을 수행하는 도구로 생각하면 된다고 한다.

if (typeof age !== number) {
  console.warn("Age should have a number data type");
}

age가 하드 코딩되었는지 또는 API에서 가져온 것인지 여부에 관계없이 타입은 여전히 체크된다. 반면 TypeScript는 브라우저로 전송되지 않으므로 TypeChecking을 하드코딩된 데이터로 제한되어진다.

구성 요소 라이브러리 빌드

만약 component library를 생성하는 경우 패키지 관리자에게 프로덕션 코드를 게시할 가능성이 높다. 컴파일 시 TypeScript는 JavaScript로 변환되어진다.

즉, 라이브러리 사용자는 PropTypes의 관련성을 반복하면서 props에 대한 TypeScript 유형 정의에 의존할 수 없다. PropTypes는 일반 JavaScript 코드이므로 라이브러리를 사용할 때 유효성 검사가 가능하다.

2. 구문 및 의미 강조

두 도구 모두 컴포넌트의 props에 대한 정보와 함께 자동 완성 기능을 제공하는 플러그인이 있다. 그러나 TypeScript의 강조 표시 도구는 PropTypes를 능가한다고 한다.
VS Code, WebStorm 및 Atom과 같은 TypeScript IDE 도구에서 다양한 기능을 찾을 수 있는데 TypeScript는 예상되는 props의 데이터 유형이 제공되지 않을 때 구성 요소를 적절하게 강조 표시하고 솔루션에 대한 통찰력을 제공한다고 한다.

3. TypeScript의 고급 기능

PropTypes가 제공할 수 없는 TypeScriptprops 유형을 지정할 수 있는 방법은 여러 가지가 있는데 예를 들어 TypeScript allows you to combine interfaces with types and perform conditional type checking,
속성 A가 true인 경우 속성 C도 제공해야 한다는 조건부 유형 검사를 수행할 수 있습니다.

위 설명에 대한 예시는 아래와 같다.

import React from "react";
import PropTypes from "prop-types";
type Props =
    | {
            type: "error";
            message: "";
      }
    | {
            type: "success";
      };
      
export default const NotificationCard = (props: Props) => {
    return <span>Notification</span>;
};

위의 코드 조각에서 type is error이면 message속성도 필요하지만, success일 경우 다른 속성을 지정할 필요가 없다.


양쪽의 장점을 모두 가지려면?

앞서 언급했듯이 TypeScriptPropTypes 중에서 선택하려면 절충이 필요하다. PropTypes를 사용하여 구문 강조와 같은 TypeScript의 기능을 유지해야할까? 아니면 런타임에 유형 검사를 포기 해야할까? 둘의 장점을 동시에 누릴 수 있는 방법은 없을까? 🤔

두 개중 하나를 선택하기 위해선 애플리케이션이 사용되는 방식에 영향을 받는다고한다.
예를 들어 많은 사람들이 사용할 라이브러리로 애플리케이션을 빌드하는 경우 런타임 유형 검사가 매우 중요한 것처럼.

하지만!! 기능을 희생할 필요 없이 런타임 및 컴파일 시간 유형 검사를 모두 사용할 수 있는 방법이 있다고 한다!!

먼저, 애플리케이션에 대한 유형 정의 및 PropTypes 정의를 작성하도록 선택할 수 있다. 프로그램의 모든 컴포넌트에 대해 두 정의를 모두 작성해야 하는 경우 장기적으로 이는 힘든 작업이 될 것이다. 하나를 작성하고 다른 하나를 자동으로 생성할 수 있는 두 가지 방법은 아래를 통해 확인해보자.

1. InferProps

아래와 같이 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 유형을 위반할 경우 오류가 발생한다. 위처럼 Property 'authorName'이(가) 누락되었다고 표시되지만 BlogCardPropTypes 개체에 해당 속성에 isRequired를 추가하지 않았기 때문에 createdAt가 누락되었다고 표시되지 않는다.

2. babel-plugin-typescript-to-proptypes

아래와 같이 Babel-plugin-type script-to-protype을 사용하여 TypeScript 유형 정의에서 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

결론

PropTypes와 TypeScript를 둘러싼 많은 오해들이 있다고 한다. 어떤 이들은 한 쪽이 중요하다고 제시하지만 이는 보는 사람의 시각에 따라 다를 것이다. 다른 사람이 사용할 도구를 만들지 않는다면 보통 TypeScript에 더 많은 우선순위를 부여한다고 한다.

0개의 댓글