reopt designreopt design
DocsExploreToolsPricingBuilder
Start
Overview
Start
Next.js 설치
Private install
Core Concepts
아키텍처
Composition Patterns
Accessibility
Keyboard Patterns
Styling
Theme System
Advanced Patterns
Build & Operate
Skills
AI Integration
CLI (opt surface add)
Dependency Graph
Tools
Canvas Catalog
Theme Builder
Form Builder
Templates
Templates
Releases
Release Notes
Oopt-ui
reopt designreopt design

A design system for the AI era

  • Docs
  • Pricing
  • Releases
  • GitHub
  • Terms of Service
  • Privacy Policy

© 2026 reopt-ai. All rights reserved.

Core Concepts
  1. Docs
  2. /
  3. Core Concepts
  4. /
  5. Advanced Patterns

고급 패턴

opt-ui 컴포넌트의 고급 사용법, 커스텀 스타일링, 상태 관리 패턴을 설명합니다.

reopt design · Updated Jun 26, 2026

DataTable 필터링

DataTable과 Input을 조합하여 실시간 필터링을 구현합니다. 컬럼별 커스텀 렌더링과 Badge를 활용한 상태 표시도 함께 적용합니다.

상품 목록

상품명카테고리가격재고상태
프리미엄 무선 키보드전자기기₩129,00045개판매중
에르고노믹 마우스전자기기₩89,000120개판매중
USB-C 허브 7포트액세서리₩59,0000개보관됨
모니터 스탠드가구₩45,00023개판매중
노트북 파우치 15인치액세서리₩35,00089개준비중
블루투스 스피커전자기기₩79,00015개판매중
code
import { DataTable, Input, Badge } from "@reopt-ai/opt-ui";

const [search, setSearch] = useState("");

// 필터링 로직
const filtered = products.filter(
  (p) => p.name.includes(search) || p.category.includes(search)
);

// 커스텀 컬럼 렌더링
const columns = [
  { id: "name", header: "상품명", accessor: "name" },
  {
    id: "status",
    header: "상태",
    accessor: (row) => (
      <Badge variant={statusVariant[row.status]}>
        {statusLabel[row.status]}
      </Badge>
    ),
  },
];

<Input value={search} onChange={(e) => setSearch(e.target.value)} />
<DataTable data={filtered} columns={columns} />

DashboardGrid 반응형

DashboardGrid의 columns prop을 동적으로 변경하여 반응형 레이아웃을 구현합니다. 기간 필터와 함께 데이터 변경도 시뮬레이션합니다.

컬럼:
기간:
↑↓←→ 키보드로 탐색
총 매출₩12.4M↑ +12.5%
주문 수1,234↑ +8.2%
신규 고객89↓ -3.1%
전환율3.2%↑ +0.4%
이탈률42%↑ -2.1%
평균 주문액₩89,000↑ +5.3%
code
import { DashboardGrid, Button } from "@reopt-ai/opt-ui";

const [columns, setColumns] = useState(3);

// 화면 크기에 따른 반응형
useEffect(() => {
  const handleResize = () => {
    if (window.innerWidth < 640) setColumns(1);
    else if (window.innerWidth < 1024) setColumns(2);
    else setColumns(3);
  };
  window.addEventListener("resize", handleResize);
  return () => window.removeEventListener("resize", handleResize);
}, []);

<DashboardGrid stats={stats} columns={columns} />

Toast 알림 시스템

ToastProvider로 앱을 감싸고, useToast hook으로 어디서든 알림을 표시합니다. 변형별 스타일과 지속 시간 커스텀이 가능합니다.

버튼을 클릭하면 화면 우측 상단에 토스트가 표시됩니다.

code
import { ToastProvider, useToast } from "@reopt-ai/opt-ui";

// 1. 앱 최상위에 Provider 배치
function App() {
  return (
    <ToastProvider position="top-right" defaultDuration={5000}>
      <MyApp />
    </ToastProvider>
  );
}

// 2. 컴포넌트에서 useToast 사용
function SaveButton() {
  const { addToast } = useToast();

  const handleSave = async () => {
    try {
      await saveData();
      addToast({
        variant: "success",
        title: "저장 완료",
        message: "변경사항이 저장되었습니다.",
      });
    } catch (error) {
      addToast({
        variant: "error",
        title: "오류",
        message: "저장에 실패했습니다.",
        duration: 8000, // 8초 (기본값 오버라이드)
      });
    }
  };

  return <Button onClick={handleSave}>저장</Button>;
}

Combobox 그룹화

ComboboxGroup과 ComboboxGroupLabel로 검색 결과를 카테고리별로 그룹화합니다. 커스텀 아이템 렌더링으로 풍부한 UI를 구현합니다.

code
import {
  ComboboxRoot, ComboboxInput, ComboboxPopover,
  ComboboxItem, ComboboxGroup, ComboboxGroupLabel,
} from "@reopt-ai/opt-ui";

// 부서별 그룹화
const grouped = users.reduce((acc, user) => {
  if (!acc[user.department]) acc[user.department] = [];
  acc[user.department].push(user);
  return acc;
}, {});

<ComboboxRoot setValue={setSearch} resetValueOnHide>
  <ComboboxInput placeholder="팀원 검색..." autoSelect />
  <ComboboxPopover>
    {Object.entries(grouped).map(([dept, users]) => (
      <ComboboxGroup key={dept}>
        <ComboboxGroupLabel>{dept}</ComboboxGroupLabel>
        {users.map((user) => (
          <ComboboxItem key={user.id} value={user.name}>
            <Avatar name={user.name} size="sm" />
            <span>{user.name}</span>
          </ComboboxItem>
        ))}
      </ComboboxGroup>
    ))}
  </ComboboxPopover>
</ComboboxRoot>

커스텀 스타일링

opt-ui 컴포넌트는 className prop으로 Tailwind 클래스를 추가할 수 있습니다. data-* 속성 셀렉터로 상태별 스타일도 적용 가능합니다.

code
// 1. className으로 직접 스타일 추가
<Button className="shadow-lg hover:shadow-xl">
  그림자 버튼
</Button>

// 2. data-* 속성으로 상태 스타일
<ComboboxItem
  className={cn(
    // 기본 스타일
    "px-3 py-2",
    // 활성 상태 (키보드 탐색)
    "data-[active-item]:bg-blue-50 data-[active-item]:text-blue-700",
    // 포커스 visible (Tab 키)
    "data-[focus-visible]:ring-2 data-[focus-visible]:ring-blue-600",
  )}
>

// 3. 조건부 스타일
<Badge
  variant={status === "active" ? "success" : "default"}
  className={status === "error" ? "animate-pulse" : ""}
>
  {statusLabel}
</Badge>

컴포지션 패턴

여러 컴포넌트를 조합하여 복잡한 UI를 구성합니다. Primitives → Blocks → Compositions 계층을 활용합니다.

code
// 예: 검색 + 테이블 + 페이지네이션 조합
function DataExplorer<T>({ data, columns }: Props<T>) {
  const [search, setSearch] = useState("");
  const [page, setPage] = useState(1);

  const filtered = useMemo(
    () => data.filter(/* 검색 로직 */),
    [data, search]
  );

  const paginated = useMemo(
    () => filtered.slice((page - 1) * 10, page * 10),
    [filtered, page]
  );

  return (
    <div className="space-y-4">
      <SearchCombobox
        items={data.map(extractSearchableText)}
        onSelect={setSearch}
      />
      <DataTable data={paginated} columns={columns} />
      <Pagination
        total={filtered.length}
        page={page}
        onChange={setPage}
      />
    </div>
  );
}

키보드 우선 설계

opt-ui의 모든 컴포넌트는 키보드 탐색을 우선 지원합니다. 주요 패턴과 확인 사항입니다.

패턴설명컴포넌트
Roving Tabindex그룹 내 Tab 1회, 방향키로 이동CompositeZone, Tabs, Menu, Toolbar
Focus Trap모달 내부에 포커스 가둠Dialog, CommandPalette
Focus Restore닫힐 때 이전 요소로 복귀Dialog, Menu, Select
Typeahead문자 입력으로 빠른 탐색Select, Combobox, Menu
PreviousTheme SystemCompound Theme 아키텍처, 5개 프리셋 × light/dark 모드, CSS 변수 체계Core Concepts
Go to Theme System
NextSkillsopt-ui 설치, 환경 진단, 컴포넌트 생성, repo 동기화, release 워크플로우를 정리합니다.Build & Operate