본문 바로가기
Project/기타

비슷한 컴포넌트 재사용하기 / 아워홈 사전과제

by 이의찬 2024. 1. 14.

1. Why?

아워홈 사전 과제를 진행하며, 어떻게 하면 컴포넌트의 재사용성을 높일 수 있을까 고민을 많이 했다.

 

다른 프로젝트를 진행하면서도 비슷한 요소의 컴포넌트를 어떻게 재사용/분리여부를 결정할지 고민했었기에, 이 기회에 정리를 해보고자 한다.


2. 도전

아래의 태그 2가지를 랜더링시켜줘야할때, 여러 방법으로 나눠보고 어떤 방법이 적절한 방법인지 생각해보자

오늘의 목표

1. 하위 컴포넌트에서 부담

ListCardTag

type CardTag = 'best' | 'new';

interface ListCardTagProps {
  tag: CardTag;
}

export default function ListCardTag({ tag }: ListCardTagProps) {
  if (tag === 'best') {
    return (
      <span className="w-16 h-8 flex justify-center items-center p2s bg-Red text-white rounded-lg">
        베스트
      </span>
    );
  } else if (tag === 'new') {
    return (
      <span className="w-16 h-8 flex justify-center items-center p2s bg-Orange text-white rounded-lg">
        신상품
      </span>
    );
  }
}

ListCard

import ListCardTag from '../atoms/ListCardTag';

type CardTag = 'best' | 'new';

interface ListCardProps {
  tag: CardTag;
}

export default function ListCard({ tag }: ListCardProps) {
  return (
    <>
      <ListCardTag tag="best" />
    </>
  );
}

명령에 따라서 ListCardTag 컴포넌트가 return을 다르게 출력하는 방식으로 구현했다.

  • 장점 : 상위 컴포넌트인 ListCard 컴포넌트의 관리가 쉬워짐
  • 단점
    • 만약 다른 색상 / 내용의 tag가 필요하다면? 1가지가 늘어날 때마다 return문도 하나씩 늘어날 것이다.
    • 그리고 개인적으로 return문이 여러개인 컴포넌트는 가독성이 떨어진다고 생각한다. 

2. 컴포넌트 분리

ListCardNewTag

export default function ListCardNewTag() {
  return (
    <span className="w-16 h-8 flex justify-center items-center p2s bg-Orange text-white rounded-lg">
      신제품
    </span>
  );
}

ListCardBestTag

export default function ListCardBestTag() {
  return (
    <span className="w-16 h-8 flex justify-center items-center p2s bg-Red text-white rounded-lg">
      베스트
    </span>
  );
}

ListCard

import ListCardBestTag from '../atoms/ListCardBestTag';
import ListCardNewTag from '../atoms/ListCardNewTag';

type CardTag = 'best' | 'new';

interface ListCardProps {
  tag: CardTag;
}

export default function ListCard({ tag }: ListCardProps) {
  return (
    <>
      {tag === 'best' && <ListCardBestTag />}
      {tag === 'new' && <ListCardNewTag />}
    </>
  );
}

 

bestTag와 newTag를 별개의 컴포넌트로 분리하고, 넘겨받은 prop에 따라 적절하게 출력하도록 했다. 

  • 장점 : 상위 컴포넌트에서 쓰일 컴포넌트를 선언하는 방식. 하위 컴포넌트는 그냥 부르면 출력되기만 하면 됨.
  • 단점 : tag당 컴포넌트 하나가 필요하다. 마찬가지로 tag의 숫자가 늘어나면 많은 컴포넌트가 필요해진다.

하위 컴포넌트에서 모든 걸 부담하는 방식과, 아예 컴포넌트를 분리하는 방식으로 구현해보았다. 하지만, 두 방식 모두 재사용성에 하자가 있었다.


3. 결론

위의 방식에서 단점을 보완할 수 있도록 적절하게 컴포넌트를 만들어 보았다.

ListCardTag

import { ReactNode } from 'react';

type CardTagBgColor = 'bg-Red' | 'bg-Orange';

interface ListCardTagProps {
  bgColor: CardTagBgColor;
  children: ReactNode;
}

export default function ListCardTag({ bgColor, children }: ListCardTagProps) {
  return (
    <span
      className={`${bgColor} p2s flex h-6 w-16 items-center justify-center rounded-lg text-white`}
    >
      {children}
    </span>
  );
}

ListCard

import ListCardTag from '../atoms/ListCardTag';

type CardTag = 'best' | 'new';

interface ListCardProps {
  tag: CardTag;
}

export default function ListCard({ tag }: ListCardProps) {
  return (
    <>
      {tag === 'best' && <ListCardTag bgColor="bg-Red">베스트</ListCardTag>}
      {tag === 'new' && <ListCardTag bgColor="bg-Orange">신상품</ListCardTag>
    </>
  );
}

하위 컴포넌트에서 상위 컴포넌트로부터 props를 전달받아 적절히 출력하도록 했다.

  • props를 전달할 때, 유니온 타입을 통해서 bgColor와 CardTag를 각각 두 가지로 고정했다.
    • CardTag는 어떤 tag를 출력하는지 명확히 보여주기 위해서 상위 컴포넌트에 두었고,
    • bgColor은 어느 요소에 사용되는지를 알려주기 위해 하위 컴포넌트에 두었다.
  • 만약 추후에 다른 내용 / 색상의 tag가 늘어난다면 bgColor과 CardTag 두 가지 요소를 추가하면 된다.