[React] Compound Component 패턴

leem·2026년 8월 12일

Compound Component 패턴?

하나의 복합적인 UI를 여러 개의 관련된 컴포넌트로 분리하고,
이들을 하나의 API로 묶어 사용하는 쪽에서 원하는 구조로 조립할 수 있는 컴포넌트 설계 패턴

즉, 관련된 컴포넌트들을 하나의 기능을 구성하는 부품으로 묶었기 때문에 서로 상태를 공유할 수 있다.

개요

일반적으로 컴포넌트 제작 시, 가장 보편적으로 사용하는 패턴은 Control Props 일 것이다.

<input
  value={value}
  onChange={setValue}
/>

이런 식으로 정의된 패턴을 주로 사용했을 것이다.

그러나 이러한 패턴은 로직이 추가될수록,

  • 기존 컴포넌트가 감당해야할 내용이 많아짐
  • 기존 컴포넌트 자체에 수정이 필요함

의 문제를 가지고 있다.

따라서 이 부분을 완화할 수 있는 것이 바로 Compound Component 패턴이다.


children과의 차이?

처음 이 개념을 접했을 때, 막연히 "부모 컴포넌트에 속한 하위(자식) 컴포넌트" 정도로 다가왔다.
children과의 차이점을 집어보면서 그 개념을 정립할 수 있었다.

children이란?

부모 컴포넌트 태그 사이에 들어올 내용을 전달받는 props

즉, 부모 컴포넌트라는 껍데기 안에 단순히 원하는 내용을 넣을 수 있다는 것이다.
다르게 말하자면, 부모 컴포넌트와는 독립적으로 존재하는 컴포넌트라고 볼 수 있다.

Compound Component는?

앞서 언급했듯이,
관련된 컴포넌트들을 하나의 시스템으로 묶는 설계 패턴 이다.
따라서, 하위에 들어갈 자식 컴포넌트 요소들이 독립적인 존재라기 보단,

  • 부모 컴포넌트와 연관되어 있고
  • 상태를 공유할 수 있는 상태

라는 부수적인 효과가 붙는 것이 근본적인 차이이다.

Compound Component 사용해보기

사실 특별한 문법이 있는 것은 아니다.
서로 연관이 있는 일반 컴포넌트를 여러 개 만든 다음,
부모 컴포넌트와 관련된 하위 컴포넌트라는 관계를 API로 표현하면 된다.

1. 컴포넌트 만들기

먼저 각각의 컴포넌트를 만든다.

import type { ReactNode } from 'react';

type Props = {
  children: ReactNode;
};

function CardRoot({ children }: Props) {
  return (
    <div className="card">
      {children}
    </div>
  );
}

function CardTitle({ children }: Props) {
  return (
    <h2 className="card-title">
      {children}
    </h2>
  );
}

function CardDescription({ children }: Props) {
  return (
    <p className="card-description">
      {children}
    </p>
  );
}

2. API로 연결하기

1) 부모 컴포넌트 객체화

가장 단순한 방법은 자식 컴포넌트를 부모 객체의 프로퍼티처럼 묶는 것이다.

const Card = {
  Root: CardRoot,
  Title: CardTitle,
  Description: CardDescription,
};

그러면 다음과 같은 구조가 된다.
Card
├── Root → CardRoot
├── Title → CardTitle
└── Description → CardDescription

예를 들면,

<Card.Title />

은 Card.Title이 가리키는 CardTitle 컴포넌트를 렌더링한다.

즉, 개념적으로

<Card.Title />

<CardTitle />

과 같은 컴포넌트를 렌더링한다.

문제점

이때 Card 자체는 객체이기 때문에

<Card />

처럼 부모 컴포넌트 자체를 JSX로 사용할 수 없고,

<Card.Root>
  <Card.Title />
  <Card.Description />
</Card.Root>

와 같은 방식으로만 사용 가능하다.

2) 정적 프로퍼티

그래서 나온 방식이 바로 정적 프로퍼티 방식이다.
부모 컴포넌트에 자식 컴포넌트를 정적 프로퍼티로 붙이는 방식이다.

1] 직접 붙이는 방식

const Card = CardRoot;

Card.Title = CardTitle;
Card.Description = CardDescription;

2] Object.assign 방식

const Card = Object.assign(CardRoot, {
  Title: CardTitle,
  Description: CardDescription,
});

=> 여러 개를 한 번에 붙일 수 있는 방식이다.

위 방식대로 작성하면,
부모 컴포넌트는 부모 컴포넌트 대로 단독 사용 가능하고,
자식 컴포넌트는 부모 컴포넌트와 공유되는 Compound Component방식으로 사용할 수 있게 된다.

profile
한 줄의 코드로 세상을 변화 시키고 싶은 개발자 이서림 입니다.

0개의 댓글