point of view.

함수형 React 컴포넌트 시대, defaultProps는 더 이상 필요하지 않다.

Jaewook Ahn1 March, 2023

함수형 React 컴포넌트 시대, defaultProps는 더 이상 필요하지 않다.

React 컴포넌트를 타입스크립트로 작성할 때 컴포넌트가 Props를 받기 위해서는 Props의 타입을 선언해줘야 한다. Props의 타입을 선언하는 방법에는 interface를 사용하거나 type을 사용하는 두 가지가 있다. (혹은 인라인으로 타입을 명시할 수도 있다.) 물론, 타입스크립트를 사용하지 않는다면 이러한 고민에서 자유로워지긴 한다. (하지만 타입스크립트를 사용하면 수 많은 장점이 있다. 아직 타입스크립트를 경험해 보지 않았다면 하루라도 빨리 사용해보길 바란다.) 이 글에서는 Props의 정의 방식에 대해서는 살짝 덮어두고, 타입스크립트 문법으로 Optional Props를 선언하고 사용하는 방법에 대해 살펴보면서 React의 defaultProps에 대해 이야기 해보려고 한다.

Props 타입 선언

컴포넌트가 모든 Props 데이터를 필수로 받아야 한다면 단순히 필요한 Props들을 선언해주면 되겠지만, Props 데이터 중에 optional하게 필요한 데이터가 있다면 이를 optional하다고 명시해 주면 된다. 아래와 같이 namevalue의 Props를 갖는 컴포넌트가 있다고 가정하여 Props 타입 선언에 대해 더 알아보도록 하자.

interface Props {
  name: string;
  value: number;
}

위 Props 타입에서는 namevalue를 필수로 받아야 한다. 이 컴포넌트에 size라는 number 타입의 값을 optional하게 받을 수 있도록 추가한다면 어떻게 해야 할까? 다음은 size Props를 optional하게 선언할 수 있는 3가지 방법에 대한 코드이다.

// 방법 1
interface Props {
  name: string;
  value: number;
  size?: number;
}

// 방법 2
interface Props {
  name: string;
  value: number;
  size: number | undefined;
}

// 방법 3
interface Props {
  [key: string]: number | undefined;
  name: string;
  value: number;
}

방법 3을 제외하면 모두 똑같은 결과를 보여준다. 방법 3의 경우에는 size 뿐만 아니라 명시하지 않은 다른 어떤 이름의 Props라도 optional하게 number 타입을 가질 수 있어서 일반적인 Props 선언에서 사용하는 것은 개인적으로 추천하지 않는다. (그냥 이렇게도 선언할 수 있다는 것을 보여주기 위해 참고용으로 적었다.)

다시 이야기로 돌아와서, 방법 1에서는 물음표 기호를 이용하여 | undefined를 생략했다. 개인적으로 제일 깔끔한 방법이라고 생각한다. 이렇게 Optional Props를 선언하는 방법에 대해서 간단하게 알아봤다. 그렇다면 선언한 Optional Props에 대해 기본 값을 부여하고 싶으면 어떻게 해야 할까?

기본 값을 가지는 Optional Props

예를 하나 들어보자. 위에 선언한 size라는 Optional Props는 값을 지정해주지 않는다면 undefined 값을 갖겠지만, 해당 컴포넌트를 사용하는 곳에서 size를 생략할 경우, 기본으로 30이라는 값을 갖게 하고 싶다면 어떻게 해야 할까? React에서 제시하는 방법에는 defaultProps를 이용하는 방법이 있다. 아래는 defaultProps로 size의 기본 값을 지정해주는 코드 예시이다.

const ExampleComponent = (props: Props) => { /** 컴포넌트 코드 */ };

ExampleComponent.defaultProps = {
  size: 30,
};

이렇게 하면 JSX 상에서 <ExampleComponent /> 처럼 size 값을 생략하더라도 React에서 자동으로 size에 30이라는 값을 할당해준다. 하지만 타입스크립트에서 무엇인가 매끄럽게 동작하지 않을 것이다. 왜냐하면 React 상에서는 size는 확실히 number 타입을 가지게 되어 undefined 값을 가질 수 없지만, 타입스크립트 추론 시스템에서는 size의 타입을 아직도 number | undefined 타입이라고 추론할 것이기 때문이다.

타입스크립트에게 타입을 제대로 알려주는 방법

사실 이 내용을 말하려고 앞선 내용들을 빌드업했다. 그래서 기본 값을 가지는 Optional Props가 undefined 타입을 가지지 않도록 하는 방법은 무엇일까? 우선 타입 정의로만 해결하는 방법부터 살펴보자.

interface OptionalWithDefaultProps {
  size: number;
}

interface Props extends OptionalWithDefaultProps {
  name: string;
  value: number;
}

const ExampleComponent = (props: Props) => { /** 컴포넌트 코드 */ };
const defaultProps: OptionalWithDefaultProps = {
  size: 30,
};
ExampleComponent.defaultProps = defaultProps;

위 코드를 보면 interface를 두 개 선언한 것을 알 수 있다. 첫 번째 interface는 기본 값을 가지는 Optional Props에 대한 것이고, 두 번째는 첫 번째로 선언한 interface를 상속하는 ExampleComponent의 Props interface이다. 위 방법의 특징은 Optional Props를 optional하다고 표기하지 않고 처리하는 것이다. React의 defaultProps를 이용하여 Props를 생략하더라도 타입 애러를 피할 수 있다. 다만 이 방법의 단점은, 타입 시스템 상에선 Optional Props를 타입 이름을 보고 개발자가 유추해야 된다는 것이다. 그리고 Props에 대한 타입이 두개라는 것도 단점이라면 단점이다.

그렇다면 다른 방법은 없을까? 다음은 다른 방법으로 처리하는 코드이다.

interface Props {
  name: string;
  value: number;
  size?: number;
}

const ExampleComponent = (props: Props) => {
  const { name, value, size = 30 } = props;
  /** 컴포넌트 코드 */
};

위 방법의 특징은 자바스크립트의 Destructuring 문법을 이용해 기본 값을 지정해 줬다는 것이다. 이렇게 하면 size는 여전히 타입 시스템에서 optional하지만 사용하는 곳에서는 명시적인 타입이 된다. React가 제공하는 defaultProps를 사용하지 않는다는 것도 특징이지만, defaultProps는 대부분의 컴포넌트를 함수형 컴포넌트로 만드는 요즘에는 사용하지 않아도 큰 문제가 되지 않는다. (class로 컴포넌트를 개발하는 경우에는 defaultProps를 이용하는게 좋다.)

결론

Optional Props로 선언했지만 Props가 기본 값을 가져야 하는 경우가 꽤 있다. JSX상에서 Props를 굳이 써 주지 않아도 알아서 기본 값이 들어가게 하고 싶은 경우이다. 이렇게 하면 JSX에 지저분하게 Props가 붙는 것을 방지할 수 있다. 코드가 지저분하면 가독성은 당연히 떨어지고 덩달아 개발자의 기분도 안 좋아진다. *"그래서 너는 어떤 방식을 쓸 건데?"*에 대한 나의 대답은, 나는 마지막에 적은 Destructuring 문법을 이용한 기본 값 지정 방식을 쓸 것 같다. 이 방법은 defaultProps같이 React가 독자적으로 제공하는 방식도 아니고, 자바스크립트 표준 스타일에 가깝기 때문이다. defaultProps를 사용하는 것은 클래스 컴포넌트 형태로 대부분의 컴포넌트를 개발하던 예전에는 의미가 있겠지만 함수형 컴포넌트로 대부분의 컴포넌트를 개발하는 요즘에는 더는 사용할 이유가 없다고 생각한다.

References

  1. TypeScript. "TypeScript: Documentation - Object Types.", TypeScript, https://www.typescriptlang.org/docs/handbook/2/objects.html#optional-properties.
  2. Chris Frewin. "React with TypeScript: Optional Props with Default Values." DEV Community, Nov 11, 2021. https://dev.to/fullstackchris/react-with-typescript-optional-props-with-default-values-33nc.
  3. Wes Bos, "Setting Default Values with JavaScript’s Destructuring.", Wes Bos, Nov 15, 2016. https://wesbos.com/destructuring-default-values.
  4. Chamming2, "defaultProps에 대한 짧은 고찰.", 즐겁게, 코드, Jan 31, 2021. https://merrily-code.tistory.com/25.
reacttypescriptdefault propsfunctional component