Project/기타
비슷한 컴포넌트 재사용하기 / 아워홈 사전과제
이의찬
2024. 1. 14. 15:16
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 두 가지 요소를 추가하면 된다.