Compound Component Pattern

이지·2024년 3월 13일
0
post-thumbnail

Compound Component Pattern?

compound 사전적 의미

  • 복합체
  • 혼합물, 화합물
  • 형) 합성의

리액트 컴포넌트 패턴에서의 Compound Component Pattern은 하나의 복합적인 컴포넌트를 특정 기능을 가지는 서브 컴포넌트들로 분리하고 사용하는 입장에서 원하는 형태로 서브 컴포넌트를 조립하여 컴포넌트를 구성하는 디자인 패턴을 의미합니다.

해당 패턴을 이해하기 쉬운 예제가 바로 select와 option입니다.

출처

// example
<label for="pet-select">Choose a pet:</label>
<select name="pets" id="pet-select">
  <option value="">--Please choose an option--</option>
  <option value="dog">Dog</option>
  <option value="cat">Cat</option>
  <option value="hamster">Hamster</option>
</select>

사용방법

부모 컴포넌트

function CompoundComponentMain() {
  return <div>{children}</div>;
}

const CompoundComponet = Object.assign(CompoundComponentMain, {
  // 서브 컴포넌트 입력
  // 아래는 예시입니다.
  Sub1: SubComponent1,
  Sub2: SubComponent2,
  Sub3: SubComponent3,
  ...
});

export default CompoundComponent;
  • 부모 컴포넌트의 children에는 서브 컴포넌트가 담기게 됩니다.
  • 서브 컴포넌트를 작성하는 것은 객체의 속성에 접근하는 것과 유사. 객체의 key를 사용하여 특정 요소에 접근하듯이, 서브 컴포넌트를 작성할 때에도 객체의 속성을 사용하여 해당 서브 컴포넌트에 접근하게 됩니다.
  • 실제 사용하는 파일에서는 import를 하지 않아도 되는 것이 장점입니다.

활용

function App() {
  return (
    <CompoundComponent>
      <CompoundComponent.Sub1 />
      <CompoundComponent.Sub2 />
      <CompoundComponent.Sub3 />
    </CompoundComponent>
  );
}

실제 적용해보기

부모 컴포넌트는 다음과 같이 작성했습니다.

function SearchResultSectionMain({ children, style }: Props) {
  return <Section className={style}>{children}</Section>;
}

const ResultSection = Object.assign(SearchResultSectionMain, {
  Title: SearchResultTitleOnly,
  TitleWithMove: SearchResultTitleWithMove,
  List: SearchResultList,
  Item: SearchResultItem,
  LinkItem: SearchResultLinkItem,
});

export default ResultSection;

Title과 Item은 이동 여부에 따라 컴포넌트가 2가지로 나뉘기 때문에 서브 컴포넌트가 각각 2개씩 존재합니다.
List에서는 검색 결과가 없는 경우 EmptySearch 컴포넌트를 보여주도록 했습니다.

이전 코드와 비교하기


이전 코드와 비교했을 때

  • 전달받는 데이터가 없는 경우 EmptySearch 컴포넌트를 보여줘야 하는데 이를 List 서브 컴포넌트 안에서 처리하도록 해서 코드 중복이 줄어듦
  • 각 서브 컴포넌트가 하는 역할을 예상하기 쉬워짐

⛏️...ing

기존의 검색 페이지를 Compound Component를 활용해서 리팩토링을 진행하였습니다.

해당 컴포넌트는 데이터를 Map을 사용해서 LinkItem에 각 item 데이터를 전달해야했는데 이 때문에 진행하면서 막히는 부분이 많았습니다.
처음에는 서브 컴포넌트인 List 안에서 데이터를 순회해서 child인 Item에 보내주려고 했는데 받아오는 데이터에 따라 한번 더 속성에 접근해야하는 경우가 생겼고, 이를 해결하기위해 type을 보내주고 type에 따라 처리하는 방식을 고민했지만 코드가 복잡해져 이 부분은 더 진행하지 않았습니다.

결과적으로 위의 이미지와 같이 처리하였습니다.

언제 쓰면 좋을까?

  1. 복잡한 UI 구성 : UI가 복잡하고 다양한 기능을 포함하는 경우, 이를 각각의 작은 조각으로 나누어 구성할 수 있습니다. 각 조각은 하위 컴포넌트로 만들어지고, 이러한 하위 컴포넌트를 조합하여 더 큰 컴파운드 컴포넌트를 생성할 수 있습니다.

  2. 재사용성이 필요한 경우 : 여러 곳에서 동일한 또는 유사한 기능을 사용해야 하는 경우, 이러한 기능을 하위 컴포넌트로 추상화하고 컴파운드 컴포넌트로 조합하여 재사용성을 높일 수 있습니다.

  3. 모듈성이 필요한 경우 : 기능을 독립적인 작은 컴포넌트로 나누어 개발하고 테스트해야 하는 경우, 컴파운드 컴포넌트 패턴을 사용하여 각 컴포넌트를 모듈화할 수 있습니다.

  4. 인터페이스 추상화가 필요한 경우 : 상위 컴포넌트에서 하위 컴포넌트의 구현 세부 사항을 숨기고 인터페이스를 제공해야 하는 경우, 컴파운드 컴포넌트 패턴을 사용하여 구현 세부 사항을 추상화할 수 있습니다.

  5. 유연한 UI 구성이 필요한 경우 : 다양한 UI 요구 사항에 대응하기 위해 조합 가능한 하위 컴포넌트를 사용하여 유연하고 확장 가능한 UI를 구성해야 하는 경우, 컴파운드 컴포넌트 패턴을 사용할 수 있습니다.

원티드 프리온보딩 챌린지에서 SOLID한 컴포넌트를 만들기 위한 방법 중 Compound Component를 소개한 적이 있었는데 직접 써보니까 왜 사용하는지 알 수 있었습니다.


참고
https://fe-developers.kakaoent.com/2022/220731-composition-component/
https://patterns-dev-kr.github.io/design-patterns/compound-pattern/

0개의 댓글

관련 채용 정보