Ch.25 컴포넌트 설계 — 재사용 가능한 UI

합성 패턴 — children과 슬롯

children prop으로 래퍼 컴포넌트를 만들 수 있다슬롯 패턴으로 유연한 레이아웃 컴포넌트를 설계할 수 있다Modal, Card, Layout 컴포넌트를 합성 패턴으로 구현할 수 있다

카드 안에 들어갈 내용이 매번 다르다면?

카드 컴포넌트를 만들었는데 어떤 곳에서는 이미지가, 어떤 곳에서는 텍스트만, 또 어떤 곳에서는 버튼이 들어갑니다.

내용물이 달라질 때마다 새 컴포넌트를 만들어야 할까?

합성(Composition) 패턴을 쓰면, 틀은 고정하고 내용만 자유롭게 바꿀 수 있습니다.


article

핵심 내용

children은 컴포넌트 안에 무엇이든 넣을 수 있는 구멍입니다

// 래퍼 컴포넌트 — 테두리만 제공
function Card({ children }: { children: React.ReactNode }) {
  return (
    <div className="rounded-lg border p-4 shadow-sm">
      {children}
    </div>
  );
}

// 사용: 안에 무엇이든 넣을 수 있다
<Card>
  <h2>제목</h2>
  <p>설명 텍스트</p>
</Card>

<Card>
  <img src="/photo.jpg" alt="사진" />
  <button>좋아요</button>
</Card>
// Layout 컴포넌트 — 페이지 공통 구조
function PageLayout({ children }: { children: React.ReactNode }) {
  return (
    <div className="min-h-screen">
      <Header />
      <main className="max-w-4xl mx-auto p-4">
        {children}
      </main>
      <Footer />
    </div>
  );
}

// 모든 페이지에서 재사용
<PageLayout>
  <HomePage />
</PageLayout>

핵심: children 패턴은 "구조는 고정, 내용은 자유"입니다. Card, Layout, Modal 등 틀을 제공하는 컴포넌트에 적합합니다.

children이 구멍 하나라면 슬롯은 구멍이 여러 개입니다

interface DialogProps {
  header: React.ReactNode;   // 슬롯 1: 상단
  children: React.ReactNode; // 슬롯 2: 본문
  footer?: React.ReactNode;  // 슬롯 3: 하단 (선택)
}

function Dialog({ header, children, footer }: DialogProps) {
  return (
    <div className="rounded-xl border shadow-lg">
      <div className="border-b p-4 font-bold">
        {header}
      </div>
      <div className="p-4">
        {children}
      </div>
      {footer && (
        <div className="border-t p-4 flex justify-end gap-2">
          {footer}
        </div>
      )}
    </div>
  );
}
// 사용: 각 슬롯에 원하는 내용 배치
<Dialog
  header={<h2>정말 삭제하시겠습니까?</h2>}
  footer={
    <>
      <button>취소</button>
      <button>삭제</button>
    </>
  }
>
  <p>삭제된 데이터는 복구할 수 없습니다.</p>
</Dialog>

합성 패턴으로 만드는 실전 컴포넌트 3가지

// 1. Modal — 오버레이 + 슬롯
function Modal({ isOpen, onClose, title, children }) {
  if (!isOpen) return null;
  return (
    <div className="fixed inset-0 bg-black/50 flex items-center justify-center">
      <div className="bg-white rounded-xl p-6 max-w-md w-full">
        <div className="flex justify-between mb-4">
          <h2 className="text-lg font-bold">{title}</h2>
          <button onClick={onClose}>✕</button>
        </div>
        {children}
      </div>
    </div>
  );
}
// 2. Card — 이미지 슬롯 + 본문
function Card({ image, children, actions }) {
  return (
    <div className="rounded-xl border overflow-hidden">
      {image && <div className="aspect-video">{image}</div>}
      <div className="p-4">{children}</div>
      {actions && (
        <div className="border-t p-3 flex gap-2">{actions}</div>
      )}
    </div>
  );
}

// 사용
<Card
  image={<img src="/thumb.jpg" alt="" />}
  actions={<button>상세보기</button>}
>
  <h3>프로젝트 제목</h3>
  <p>설명...</p>
</Card>

바이브 코더 팁 AI에게 "children과 슬롯 패턴을 사용하는 Modal 컴포넌트를 만들어줘"라고 하면 유연하고 재사용 가능한 결과를 받을 수 있습니다.

children prop의 역할은?

슬롯 패턴은 header, footer 같은 여러 영역을 Props로 받아 배치하는 방식이다.

다음 중 children 패턴이 가장 적합한 컴포넌트는?

compare_arrows

비교 정리

항목children만슬롯 패턴
구멍 수1개 (children)여러 개 (header, footer 등)
적합한 경우단순 래퍼 (Card, Container)영역이 나뉜 UI (Dialog, Page)
edit_note

정리 노트

합성 패턴(Composition) 핵심 정리

children 패턴

children
컴포넌트 태그 사이의 내용이 자동으로 children prop으로 전달
래퍼 컴포넌트
Layout, Card 등 구조를 감싸고 내부 내용은 자유롭게 변경

슬롯 패턴

named slots
header, footer 등 이름이 있는 Props로 여러 위치에 내용 삽입
활용 사례
Modal의 title/body/footer, Layout의 header/sidebar/content

children은 단일 슬롯, 여러 위치에 내용을 넣으려면 named slot Props를 사용하세요.

퀴즈와 인터랙션으로 더 깊이 학습하세요

play_circle인터랙티브 레슨 시작