개탕 IT FACTORY

디자인시스템(1).Polymorphic 컴포넌트 제작 본문

Front-end

디자인시스템(1).Polymorphic 컴포넌트 제작

rendar02 2023. 12. 11. 23:11
반응형

개요

회사내에서

(지금은 전직장)

디자인 시스템 제작을 위해 다양한 자료를 찾아보던 와중에
Polymorphic 컴포넌트에 대해서 알게 되어서 관련 내용을 정리해볼까 한다.

디자인 시스템 제작한지는 상당히 시간이 오래지났지만 (23년 봄부터 시작했으니…)
관련된 참고내용과 내가 실제로 적용했던걸 바탕으로 글을 작성할려구 한다.

이 내용은 추후 디자인 시스템 구축을 위한 발판을 위한 기본글임을 알립니다.

Polymorphic이란?

사전적 의미는 다형성이라고 표시되는데, 프로그래밍 특히 객체지향에서는 오버로딩과 오버라이딩등 다양하게 표현되곤한다.

물론 여기서는 그런뜻이 아닌 다양한 형태를 가질수있는 컴포넌트라고 이해하면 쉬울 것 같다.
다양한 형태는 어떤 형태를 말하는 걸까?

  • 태그에 얽매이지 않는 컴포넌트
  • 다양한 속성을 가질수 있는 컴포넌트
  • 다양한 스타일을 가질수있는 컴포넌트

쉽게 말하면 무엇이든 될수 있는 형태를 의미한다. 매우 추상적인 컴포넌트라고 생각하면 될 것 같다.

보통 UI 프레임워크들은 기본적으로 base기반으로 Polymorphic 컴포넌트로 제작되어있는 것 같다.

Polymorphic 컴포넌트 구현

가장 궁금한 부분이지 않을까 싶다.

나 또한 디자인 시스템을 구현하면서 도대체 모든 공통부분을 어찌 관리할까 싶었는데 그 해답이 바로 Polymorphic 컴포넌트였다.

왜 필요할까?

그냥 컴포넌트로 구현해두 되는데 왜 Polymorphic한 컴포넌트로 제작해야만 할까?

간단한 예시를 한번 들어보자

요구사항으로 간단한 button 컴포넌트를 제작했다고 해보자

// button 컴포넌트 
const Button = ({ text, onClick, style, ...props }: ButtonProps) => {
  return (
    <button onClick={onClick} style={style} {...props}>
      {text}
    </button>
  );
};

export default Button;

그런데 요구사항으로 아래와 같은 부분이 추가되었으면 좋겠다는 기획이 나왔다

  • 버튼컴포넌트에 링크로 연결되는 부분이있었으면좋겠어요

단순한 기능이지만 button컴포넌트는 이미 공통 컴포넌트로 제작되어있어서 기능을 넣기엔 리스크가 너무 크다. 그러면 아래와 같이 제작할것이다

import Button from './Button'

const LinkButton = ({ text, onClick, style, ...props }: ButtonProps) => {
  return (
    <a onClick={onClick} style={style} {...props}>
      {text}
    </a>
  );
};

export default LinkButton;

여기서 좀 의구심이 들지 않나?

바로 똑같은 button이라는 기능에 단순히 기능만 차이있을 뿐인데 새로운 컴포넌트를 제작해버렸다.
그만큼 재사용성을 더 어렵게 만들게 되었고, 만약 버튼 스타일이 변경된다면 LinkButton 또한 변경되어야된다.

바로 여기서 필요한게 Polymorphic 컴포넌트이다

만들어봅시다

이 글에서는 기본적은 프로젝트 설정은 생략하고, 프로젝트 설정이 되어있다는 전제하에 설명하도록하겠다.

그러면 Polymorphic한 컴포넌트를 제작해봅시다 (Typescript기반으로 설명합니다)

아래는 간단히 Polymorphic한 View 컴포넌트를 제작했습니다.

import React from 'react'

interface ViewProps{
  component?: T;
}

export const View = ({
  component,
  ...props
}: ViewProps<T>) => {
  const Element = component || "div";
  return <Element {...props} />;
};

오 매우 잘 작성된 것처럼 보이지만 아래와 같은 오류가 발생한다.

바로 내부 속성값을 제대로 타입을 정해주지 않아서 일어난것인데

React에서는 관련된 type을 지원해주었다 바로 React.ComponentPropsWithoutRef<T>

필자도 만들었을시 알게된 props인데 정말 숨겨진게 많았었다.
아래는 관련된 글
https://blog.bitsrc.io/react-with-typescript-cheatsheet-9dd891dc5bfe#d41e

간단하게 설명하자면 HTML 구성요소를 React로 확장할때 쓰는 타입이라고 생각하면된다

다시 Polymorphic 컴포넌트로 돌아와서 설명하자면 아래와 같이 정의해주면 말끔히 사라지는것을 볼수있다.

type ViewProps<T extends React.ElementType> = {
  component?: T;
} & React.ComponentPropsWithoutRef<T>;

export const View = <T extends React.ElementType = "div">({
  component,
  ...props
}: ViewProps<T>) => {
  const Element = component || "div";
  return <Element {...props} />;
};

한번 실행하는것도 볼까?

매우 잘되는 것을 확인할 수 있다.

ref값을 정의해줍시다

react로 컴포넌트를 만들어본사람이라면 알 것이다. ref값의 중요성을
현재 우리가 제작하는 Polymorphic 컴포넌트도 마찬가지로 ref값을 정의해줘야된다.

아래와 같이 단순한 형태로 넘기면 끝난줄 알았지만

import React, { forwardRef } from "react";

type ViewProps<T extends React.ElementType> = {
  component?: T;
} & React.ComponentPropsWithoutRef<T>;

export const View = forwardRef(
  <T extends React.ElementType = "div">(
    { component, ...props }: ViewProps<T>,
    ref: React.ComponentPropsWithRef<T>["ref"]
  ) => {
    const Element: React.ElementType = component || "div";
    return <Element ref={ref} {...props} />;
  }
);

실제론 위와같이 unknown 형태로 들어가고 있었다.

이부분을 해결할려고 하였으나… chatGPT와 구글링을 해서 코드를 넣어봐두 또 다른 오류가 발생하여 포기

다른분의 polymorphic 컴포넌트 코드도 해봤지만 비슷한 오류가 발생해 버전업되면서, 또 다른 타입이 추가된건지 아니면 필자가 잘못한건지 파악할 필요가 있었다.

ref의경우 사실 쓰임새 부분은 크지 않지만(직접 DOM조작이외) 필요한 부분이니, 한번 계속 검색해서 수정해나아갈 예정

결과

결과는 역시 component에 관련된 태그값을 주어 렌더링되는걸 확인하였다.

관련된 코드를 확인하고싶다면 아래 링크를통해서 확인하길 바란다.

GitHub - RendarCP/rendar-design-system at feature/polymorphic-component

마무리

회사에서 구축당시 상당히 고생했던 기억이 있었다.

이유중하나는 바로 디자인시스템 구축을 위해 MUI,Mantine 코드를 리버스 엔지니어링 하면서 뜯어 봤다

MUI의경우 base부터 시작해서 계속 여러갈래로 나눠지다보니 매우 헷갈리는 구조였고, 그나마 Mantine이 제일 단순하고 이해하기 쉬운 구조였다.

제일 참고를 많이했던건 이번글을 쓰면서 많이 참조했던 글의 필자분이 회사내에서 제작한 디자인 시스템인데 매우 도움이 되어서 여기서 감사 말씀을 드리고 싶다.

https://github.com/cobaltinc/co-design

사실 이번글은 계속 만들고 싶었던 개인용 라이브러리 구축을 위한 장엄한 일대기중 첫삽이였다.

아직 갈길이 멀었지만, 첫 시작으로 디자인시스템 구축을 해볼까한다. (

내년안에는 만들겠지…

)

참고자료

https://kciter.so/posts/polymorphic-react-component → 매우 도움되었던 참고자료

https://blog.logrocket.com/build-strongly-typed-polymorphic-components-react-typescript/

https://blog.bitsrc.io/react-with-typescript-cheatsheet-9dd891dc5bfe#d41e

https://www.freecodecamp.org/news/build-strongly-typed-polymorphic-components-with-react-and-typescript/#github-repository

https://github.com/cobaltinc/co-design

https://github.com/kciter/polymorphic-react-component-sample

반응형