defaultProps & propTypes

React 컴포넌트에 전달되는 속성(props) 타입(Types)을 검사하는 방법

JavaScript 타입 검사

JavaScript는 동적 타입을 사용하는 프로그래밍 언어로 자유도가 높은 점이 장점이기도 하고, 단점이기도 합니다. 단점의 예를 들면 데이터 타입(type)이 잘못 전달될 경우 문제가 발생해도 오류를 알려주지 않습니다.

삼각형 둘레를 계산하는 함수 예시를 살펴봅시다.

// 삼각형 둘레(Triangle circumference) 계산
function calcTriangleCirc(x, y, z) {
  return x + y + z
}

// 전달 인자(type)의 유형이 올바른 경우
calcTriangleCirc(10, 5, 8) // 23

// 전달 인자의 유형이 잘못된 경우, 오류 출력하지 않음
calcTriangleCirc('10', '5', '8') // '1058'

JavaScript가 런타임 중에 오류를 알려주지 않기 때문에, 이러한 문제 해결하려면 함수를 제작할 때 전달 인자의 유효성을 직접 검사해야 합니다. 반복되는 유형 검사는 불필요하므로 타입 검사 유티릴티 함수를 만들어 활용할 수 있습니다.

// 데이터 타입 검사 유틸리티 함수
function validType(dataType, typeString) {
  return Object.prototype.toString.call(dataType)
           .slice(8,-1).toLowerCase() === typeString
}

function calcTriangleCirc(x, y, z) {
  // 데이터 타입 검사
  if ( 
    !validType(x, 'number') || 
    !validType(y, 'number') || 
    !validType(z, 'number') 
  ) {
    throw new Error('전달되는 인자의 유형은 오직 숫자(number)여야 합니다.')
  }
  return x + y + z
}

// 전달 인자의 유형이 잘못된 경우, 오류 출력!
// 'Uncaught Error: 전달되는 인자의 유형은 오직 숫자(number)여야 합니다.'
calcTriangleCirc('10', '5', '8')

React 속성 타입 검사

애플리케이션 규모가 커지면 수 많은 컴포넌트가 프로젝트에 사용되고, 팀원도 많아집니다. 규모가 커짐에 따라 오류(버그)가 발생할 확률도 높아집니다. 이런 문제를 사전에 차단하기 위해서는 컴포넌트에 전달되는 속성(props)이 올바른지 사전에 검사할 수 있어야 합니다. 그렇다면 React 컴포넌트에 전달 된 속성 유형은 어떻게 검사해야 할까요?

React 컴포넌트(함수 또는 클래스)는 propTypes 속성을 통해 컴포넌트에 전달 된 속성을 검사할 수 있는 기능을 제공합니다. 검사 할 항목을 검사 객체 멤버 메서드로 전달하면 속성 객체, 속성 이름, 컴포넌트 이름을 순서대로 전달 받습니다.

이를 활용하여 속성 검사를 수행한 뒤, 기대되는 속성 유형과 일치하지 않을 경우 오류 메시지를 출력하도록 설정해 사용자에게 안내할 수 있습니다. 다음은 속성을 직접 검사하는 방법입니다.

// React 컴포넌트 전달 속성 검사
EmotionCard.propTypes = {
  // 전달 속성 객체, 속성 이름, 컴포넌트 이름
  emotion(props, propName, componentName) {
    // 전달 속성 유형
    const propType = typeof props[propName]
    
    // 전달 속성 검사 (문자 값인지 확인)
    if (propType !== 'string') {
      // 문자 값이 아닌 경우 오류 발생
      return new Error(
        // 오류 메시지 출력
        `${componentName} 컴포넌트에 전달 된 속성 ${propName}의 데이터 유형은 
         String이 요구되나, 실제 전달된 속성 유형은 ${propType}이니 확인 바랍니다.`
      )
    }
  }
}

요구 된 속성 유형과 다른 유형의 속성이 컴포넌트에 전달되면 Console 패널에 아래와 같은 오류 메시지가 출력됩니다.

Warning

Failed prop type: EmotionCard 컴포넌트에 전달 된 속성 emotion의 데이터 유형은 String이 요구되나, 실제 전달된 속성 유형은 number이니 확인 바랍니다. at EmotionCard (https://3hxii.csb.app/src/components/EmotionCard.js:81:22) at App

라이브 예제

커스텀 전달 속성 검사 모듈

emotion 전달 속성 검사는 String 유형을 검사합니다. 컴포넌트에 전달 받는 속성 중 String 유형은 빈번하므로 재사용 할 수 있도록 함수로 만들면 좋을 것 같습니다. 그리고 다른 유형도 검사하는 함수를 만들어 사용하는 것이 효율적입니다. 이런 경우 검사 함수를 묶어 관리 할 모듈(네임스페이스)을 만들어 관리하면 효과적일 것입니다.

PropType.js
const PropTypes = {
  // String 유형 검사 함수
  string(props, propName, componentName) {
    const propType = typeof props[propName]
    if (propType !== 'string') {
      return new Error(
        `${componentName} 컴포넌트에 전달 된 속성 ${propName}의 데이터 유형은 String이 요구되나, 실제 전달된 속성 유형은 ${propType}이니 확인 바랍니다.`
      )
    }
  },
  // Array 유형 검사 함수
  array(props, propName, componentName) {
    const propValue = props[propName]
    const propType = Object.prototype.toString.call(propValue).slice(8,-1)
    if (!Array.isArray(propValue)) {
      return new Error(
        `${componentName} 컴포넌트에 전달 된 속성 ${propName}의 데이터 유형은 Array가 요구되나, 실제 전달된 속성 유형은 ${propType}이니 확인 바랍니다.`
      )
    }
  },
  // ...
}

export default PropTypes

PropTypes 모듈을 불러와 다음과 같이 검사 할 컴포넌트 속성에 연결하면 간단하게 속성을 검사할 수 있습니다.

import PropTypes from './PropTypes'

EmotionCard.propTypes = {
  emotion: PropTypes.string
}

라이브 예제

PropTypes 모듈

앞서 직접 만든 커스텀 PropTypes를 통해 React 컴포넌트에 전달 받는 속성을 검사할 수도 있습니다. React는 이러한 문제를 해결할 수 있는 방법을 자체적으로 제공하고 있으니 React가 제공하는 PropTypes 모듈을 사용해봅시다.

React는 v15.5부터 React.PropTypes를 분리하였으므로 PropTypes를 사용하려면 설치해야 합니다.

npm i -D prop-types

컴포넌트 props 타입 검사

컴포넌트에 전달되는 속성 검사를 위해 먼저 prop-types 모듈을 불러 옵니다. 그리고 컴포넌트에 propTypes 속성을 추가한 후, 전달 속성 검사를 설정하는 객체를 할당합니다. 아래 예시 코드를 살펴봅니다.

import React from 'react'

// PropTypes 모듈 불러오기
import PropTypes from 'prop-types'


class Worker extends React.Component {
  // Worker 컴포넌트에 전달된 속성 props 유효성 검사 설정
  static propTypes = {
    name: PropTypes.string.isRequired,
    career: PropTypes.number.isRequired,
    isLeave: PropTypes.bool,
  }
  
  render() {
    const { name, career, isLeave } = this.props
    
    return (
      <div className="worker">
        <span classNme="worker-name">{name}</span>
        <span classNme="worker-career">{career}</span>
        <span classNme="worker-isLeave">{!isLeave || '재직'}</span>
        <button type="button">커리어 업</button>
      </div>
    )
  }
}

export default Worker;

컴포넌트 전달 속성을 검사하는 데 사용된 PropTypes 모듈 함수는 다음과 같습니다.

설정

설명

PropTypes.string

String 유형 검사

PropTypes.number

Number 유형 검사

PropTypes.bool

Boolean 유형 검사

.isRequired는 "필수 전달 속성"이 전달되지 않은 경우 오류를 출력하도록 설정합니다.

함수 컴포넌트 또한 검사 방법은 동일합니다만, 함수 객체의 속성으로 propTypes를 설정한다는 점이 static 멤버로 propTypes를 사용한 클래스 컴포넌트와 다소 다릅니다. (🎩 클래스는 함수 방법도 사용할 수 있습니다.)

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


const Worker = ({ name, career, onCareerUp, isLeave }) => (
  <div className="worker">
    <span classNme="worker-name">{name}</span>
    <span classNme="worker-career">{career}</span>
    <span classNme="worker-isLeave">{!isLeave || '재직'}</span>
    <button type="button">커리어 업</button>
  </div>
)

// 전달 속성 유효성 검사
Worker.propTypes = {
  name: PropTypes.string.isRequired,
  career: PropTypes.number.isRequired,
  isLeave: PropTypes.bool,
}

export default Worker;

PropTypes 검사 요약

PropTypes를 통해 검사 가능한 타입은 아래 나열된 목록을 참고합니다. (참고)

타입

검사 방법

비고

모든 타입

PropTypes.any

Number 객체

PropTypes.number

String 객체

PropTypes.string

Boolean 객체

PropTypes.bool

Function 객체

PropTypes.func

Array 객체

PropTypes.array

Object 객체

PropTypes.object

Symbol 객체

PropTypes.symbol

Node 객체

PropTypes.node

컴포넌트가 반환할 수 있는 모든 데이터 유형

React 요소

PropTypes.element

React 요소(Element)

React 컴포넌트

PropTypes.elementType

React 컴포넌트(Component)

여러 타입 중 하나

PropTypes.oneOfType([ TypeA, TypeB ])

특정 클래스의 인스턴스

PropTypes.instanceOf( Class )

전달 속성 값 제한

PropTypes.oneOf([ value1, value2 ])

열거형 (Enumerable)

특정 타입 집합으로 제한

PropTypes.arrayOf( Type )

속성 값을 특정 타입으로 제한

PropTypes.objectOf( valueType )

특정 형태를 갖는 객체 제한

PropTypes.shape({ prop1, prop2 })

인터페이스 (Interface)

PropTypes.objectOf vs PropTypes.shape 비교

PropTypes.objectOf는 속성이 모두 동일한 유형의 객체를 설명 할 때 사용됩니다.

import { objectOf, number } from 'prop-types';

const geoProps = {
  latitude: 37.331706,
  longitude: -122.030783
}

ReactComponent.propTypes = {
  geoProps: objectOf(number)
}

반면 PropTypes.shape는 미리 설정된 객체를 설명 할 때 사용되며 속성 별 타입을 설정할 수 있습니다.

import { shape, string, array } from 'prop-types';

const personProp = {
  name: '야무',
  job: [
    '강사',
    '디자이너',
    '개발자'
  ]
}

ReactComponent.propTypes = {
  personProp: shape({ name: string, job: array })
}

JSON 데이터 속성 검사

arrayOf, shape을 사용해 JSON 데이터 유형의 전달 속성을 검사할 수 있습니다.

import { arrayOf, shape } from 'prop-types'

const styleList = [
  { id: 'style-z01', color: '#3f0a28', fontSize: 22 },
  { id: 'style-z02', color: '#ecde19', fontSize: 17 },
]

ReactComponent.propTypes = {
   arrayWithShape: arrayOf(
     shape({
       id: string.isRequired,
       color: string,
       fontSize: number,
     })
   ),
}

규모가 큰 프로젝트에서의 속성 검사

큰 코드 베이스에서 PropTypes 모듈 사용은 적합하지 않습니다. Flow 또는 TypeScript를 사용할 것을 React 공식 문서는 권장하고 있습니다.

전달 속성 기본 값

컴포넌트에 전달할 속성을 모두 필수로 만들 필요는 없습니다. 사용자에 의해 커스터마이징 될 수 있지만, 그렇지 않을 경우 기본으로 사용(Default Props)되는 값을 설정할 수도 있습니다. JavaScript와 React에서 각각 전달 속성 기본 값을 설정하는 방법을 살펴봅시다.

JavaScript 매개변수 기본 값

JavaScript 프로그래밍(ES 6+)에서 매개변수 기본 값 설정은 다음과 같이 합니다.

function greetingMessage(message='안녕하세요') {
  return `${message} 여러분!`
}

// 기본 값 사용
greetingMessage() // '안녕하세요 여러분!'

// 사용자 정의
greetingMessage('Hello') // 'Hello 여러분!'

React 전달 속성 기본 값

컴포넌트에 전달될 속성의 기본 값을 설정하는 방법은 defaultProps 속성을 설정하는 것입니다.

import React, { Component } from 'react'


const Worker = ({ name, career, onCareerUp, isLeave }) => (
  // ...
)

// Worker 컴포넌트 전달 속성 기본 값 설정
Worker.defaultProps = {
  name: '야무',
  career: 21,
  onCareerUp: () => console.log('커리어 업!!'),
  isLeave: true,
}

export default Worker;

클래스 필드 활용

클래스 컴포넌트는 표준 제안 중인 클래스 필드 문법을 사용해 전달 속성 검사를 하거나, 기본 값을 설정할 수 있습니다.

class Worker extends Component {

  // 전달 속성 검사
  static propTypes = {
    name: PropTypes.string.isRequired,
    career: PropTypes.number.isRequired,
    onCareerUp: PropTypes.func.isRequired,
    isLeave: PropTypes.bool.isRequired,
  }

  // 기본 값  설정
  static defaultProps = {
    name: '야무',
    career: 21,
    onCareerUp: () => console.log('커리어 업!!'),
    isLeave: true,
  }

  // ...
}

Last updated