reopt designreopt design
DocsExploreToolsPricingBuilder
시작하기
개요
시작하기
Next.js 설치
Private install
핵심 개념
아키텍처
컴포지션 패턴
접근성
키보드 패턴
스타일링
테마 시스템
고급 패턴
구축·운영
Skills
AI 연동
CLI (opt surface add)
의존 그래프
도구
Canvas 카탈로그
Theme Builder
Form Builder
템플릿
템플릿
릴리즈
릴리즈 노트
Oopt-ui
reopt designreopt design

AI 시대를 위한 디자인 시스템

  • 문서
  • 가격
  • 릴리즈 노트
  • GitHub
  • 서비스 약관
  • 개인정보처리방침

© 2026 reopt-ai. All rights reserved.

핵심 개념
  1. 문서
  2. /
  3. 핵심 개념
  4. /
  5. 컴포지션 패턴

컴포지션 패턴

Shell이 Core를 감싸서 기능을 확장하는 방식과 커스텀 Shell을 만드는 가이드입니다.

reopt design · 업데이트 2026년 6월 26일

Shell → Core 조합

Shell은 Core를 import하여 감싸고 기능을 확장합니다. ContentTabs를 예로 살펴봅니다.

tsx
"use client";

import { type TabDef, TabsRoot, TabList, Tab, TabPanel } from "@reopt-ai/opt-ui";

export interface ContentTabsProps {
  tabs: TabDef[];
  defaultTabId?: string;
}

export function ContentTabs({ tabs, defaultTabId }: ContentTabsProps) {
  return (
    <TabsRoot defaultSelectedId={defaultTabId ?? tabs[0]?.id}>
      <TabList aria-label="콘텐츠 탭">
        {tabs.map((tab) => (
          <Tab key={tab.id} id={tab.id}>{tab.label}</Tab>
        ))}
      </TabList>
      {tabs.map((tab) => (
        <TabPanel key={tab.id} tabId={tab.id}>
          {tab.content}
        </TabPanel>
      ))}
    </TabsRoot>
  );
}

패턴 요약: Shell은 (1) 데이터 인터페이스를 정의하고, (2) Core를 import하고, (3) 데이터를 Core에 매핑합니다. Shell 자체에는 스타일 코드가 없습니다.

데이터 주도 렌더링

Shell은 데이터 배열을 props로 받아 내부에서 Core에 매핑합니다. 이 패턴 덕분에 사용처에서는 데이터만 준비하면 됩니다.

tsx
// 사용처 (서버 컴포넌트)
const tabs: TabDef[] = [
  { id: "overview", label: "개요", content: <Overview /> },
  { id: "api", label: "API", content: <ApiDocs /> },
];

// Shell에 데이터 전달 — 렌더링은 Shell이 담당
<ContentTabs tabs={tabs} />

Provider 중첩

FaqAccordion은 CompositeProvider 안에 여러 DisclosureRoot를 중첩합니다. Composite가 키보드 탐색을, 각 DisclosureRoot가 개별 열기/닫기 상태를 관리합니다.

tsx
"use client";

import {
  CompositeZone,
  CompositeRow,
  CompositeItem,
  DisclosureRoot,
  DisclosureTrigger,
  DisclosureContent,
  type FaqItem,
} from "@reopt-ai/opt-ui";

export function FaqAccordion({ items }: { items: FaqItem[] }) {
  return (
    <CompositeProvider focusLoop orientation="vertical">
      <Composite role="region" aria-label="자주 묻는 질문" className="...">
        {items.map((faq) => (
          <DisclosureRoot key={faq.id}>
            <div>
              <CompositeItem render={<DisclosureTrigger />}>
                {faq.question}
              </CompositeItem>
              <DisclosureContent>
                <p>{faq.answer}</p>
              </DisclosureContent>
            </div>
          </DisclosureRoot>
        ))}
      </Composite>
    </CompositeProvider>
  );
}

CompositeItem의 render prop에 DisclosureTrigger를 넘기는 패턴은 render prop을 활용하여 Composite 아이템과 Disclosure 트리거를 하나의 요소로 합성합니다.

커스텀 Shell 만들기

4단계로 새로운 Shell을 만들 수 있습니다.

Step 1. Core 선택

필요한 UI 패턴에 맞는 Core를 선택합니다. 예: 알림 목록이라면 Disclosure(열기/닫기)와 Composite(키보드 탐색)가 필요합니다.

Step 2. 데이터 인터페이스 정의

tsx
// lib/types.ts에 추가
export interface NotificationItem {
  id: string;
  title: string;
  body: string;
  time: string;
}

Step 3. Core 조합

tsx
// shells/notification-list.tsx
"use client";

import {
  CompositeZone,
  CompositeRow,
  CompositeItem,
  DisclosureRoot,
  DisclosureTrigger,
  DisclosureContent,
  type NotificationItem,
} from "@reopt-ai/opt-ui";

export interface NotificationListProps {
  items: NotificationItem[];
}

export function NotificationList({ items }: NotificationListProps) {
  return (
    <CompositeProvider focusLoop orientation="vertical">
      <Composite role="region" aria-label="알림 목록" className="...">
        {items.map((item) => (
          <DisclosureRoot key={item.id}>
            <div>
              <CompositeItem render={<DisclosureTrigger />}>
                <span>{item.title}</span>
                <time>{item.time}</time>
              </CompositeItem>
              <DisclosureContent>
                <p>{item.body}</p>
              </DisclosureContent>
            </div>
          </DisclosureRoot>
        ))}
      </Composite>
    </CompositeProvider>
  );
}

Step 4. export 추가

shells/index.ts에 배럴 export를 추가하면 완료됩니다.

tsx
// shells/index.ts
export { NotificationList } from "./notification-list";
Previous아키텍처opt-ui Core/Shells/Surfaces와 opt-charts 분리, docs/registry 연결 방식을 설명합니다.핵심 개념
아키텍처 페이지로 이동
Next접근성Spatial Navigation, 포커스 관리, WAI-ARIA 역할, 키보드 단축키핵심 개념