자신만의 Shadcn UI 레지스트리 구축 방법

발행: (2026년 1월 31일 오후 07:25 GMT+9)
10 min read
원문: Dev.to

Source: Dev.to

Naud

4주 동안 30개 이상의 랜딩 페이지 블록(히어로, 가격, FAQ, CTA, 고객 후기)을 구축한 뒤, 마침내 완벽한 Shadcn 레지스트리 설정을 찾아냈습니다.

registry.json에 블록(Hero, Pricing, FAQ 등)을 모델링하고 연결하며, 서버 친화적인 미리보기를 렌더링하고, npx shadcn add에 사용할 수 있도록 준비하는 과정을 안내합니다.

1. 소개

Shadcn UI는 공식 레지스트리 컴포넌트와 블록을 제공하지만, 레이아웃과 디자인 시스템에 맞게 직접 레지스트리를 만들 수도 있습니다.

**4주간의 반복 작업 끝에, 히어로, 가격표, FAQ, CTA 등 30개 이상의 블록을 포함하는 랜딩‑페이지‑전용 레지스트리를 구축했습니다. 모든 블록은 registry.json에 정의되고, 라이브 프리뷰와 구문‑하이라이트된 코드를 제공하는 일반적인 /blocks/[name] 페이지에서 렌더링됩니다.

지표: 12개 카테고리에 걸친 30개 이상의 블록, 50 k+ 라인의 Tailwind, 100 % 서버‑컴포넌트 호환.

다룰 내용:

  • 단일 블록(Hero01)이 어떻게 구조화되는지
  • 해당 블록이 registry.json에 어떻게 기술되는지
  • 일반적인 Next.js 페이지 /blocks/[name]가 이름으로 블록을 어떻게 렌더링하는지
  • BlockProvider가 앱의 전역 테마를 건드리지 않고 테마, 화면 크기, 프리뷰 동작을 어떻게 주입하는지
  • 이 설계가 React Server Components와 Shadcn CLI와 어떻게 잘 어울리는지

2. Shadcn “registry block”이란?

높은 수준에서 Shadcn 레지스트리 항목은 하나 이상의 코드 파일을 가리키고 그들의 의존성을 설명하는 JSON 메타데이터입니다.

유형역할예시 값
ComponentLow‑level primitive (button, input)registry:component
BlockPage section (hero, pricing, FAQ)registry:block

In my registry: 30 page sections (hero ×5, pricing ×3, features ×5, CTA ×4, 등) 을 registry:block 으로 모델링했습니다.

Metric: 각 블록은 registryDependencies 를 통해 3–5 Shadcn primitives (button, badge, card) 를 평균적으로 포함합니다.

3. 구체적인 블록: Hero01

Here’s my actual Hero01 – a two‑column hero with badge, title, description, two CTAs, and an image:

interface Hero01Props {
  badge?: string;
  heading?: string;
  description?: string;
  buttons?: {
    primary?: { text: string; url: string; icon?: React.ReactNode };
    secondary?: { text: string; url: string; icon?: React.ReactNode };
  };
  image?: string;
  className?: string;
}

핵심 설계 결정

  • 마케팅 props만 – 저수준 레이아웃 조정 옵션 없음
  • 순수 프레젠테이션 – 훅 0개, 데이터 페칭 0개
  • 서버 컴포넌트 준비use client 지시어 없음
  • Shadcn 구성Button, Badge와 Tailwind 그리드 사용

사용법

Metric: Hero01 = 187 라인, 12 Tailwind 유틸리티 클래스, 2 Shadcn 컴포넌트.

Hero01 preview

4. registry.json에서 블록 설명하기

My hero-01 registry entry:

{
  "name": "hero-01",
  "type": "registry:block",
  "title": "Hero 01",
  "description": "Two-column hero with badge + CTAs",
  "dependencies": ["lucide-react"],
  "registryDependencies": ["button", "badge"],
  "categories": ["hero"],
  "meta": { "image": "/r/previews/hero-01.webp" },
  "files": [
    {
      "path": "src/registry/blocks/hero-01/hero.tsx",
      "target": "components/hero-01.tsx"
    }
  ]
}

핵심 필드

  • registryDependencies → CLI가 buttonbadge를 자동 설치합니다
  • categories/blocks?category=hero 필터링을 지원합니다
  • files.target → 예측 가능한 components/hero-01.tsx 위치

Metric: 30 blocks = 12 categories, 47 total dependencies (npm + registry).

5. BlockProvider: 격리된 프리뷰 매직

Problem: 테마 전환 + 반응형 브레이크포인트를 30개 이상의 블록에서 사이트 테마를 깨뜨리지 않고 미리 보기.

Solution: BlockProvider자체 포함된 프리뷰 유니버스를 생성합니다:

interface BlockContextValue {
  block: SerializableRegistryBlock;
  theme: Theme;
  screenSize: ScreenSize; // 'mobile' | 'tablet' | 'desktop'
  setTheme: (theme: Theme) => void;
  setScreenSize: (size: ScreenSize) => void;
}

왜 작동하나요

  • ✅ 테마 변경이 /blocks/hero-01 내부에 머무릅니다
  • ✅ 화면 크기 토글 = 즉시 적용되는 CSS 뷰포트 클래스
  • 30개 이상의 블록하나의 프리뷰 시스템을 공유합니다
  • ✅ 전역 사이트 테마는 전혀 건드리지 않습니다

Metric: Provider는 5가지 테마 × 3가지 브레이크포인트 = 블록당 15개의 프리뷰 변형을 처리합니다.

6. 서버 컴포넌트 + 레지스트리 = ⚡ 성능

서버‑우선 설계

  • Blocks = 서버 컴포넌트 (use client 없음)
  • Registry JSON = 정적, 빌드 시 읽기 가능
  • Preview shell = 최소한의 클라이언트 경계

결과

✅ Bundle size: Hero01 = 2.1 KB (gzipped)
✅ TTFB: /blocks/hero-01 = 89 ms
✅ Static pages: 30+ generated at build time

프리뷰 셸(BlockProvider 및 해당 컨트롤)은 필요한 경우에만 클라이언트 측에 남습니다.

7. 전체 레지스트리를 위한 하나의 라우트

단일 동적 라우트(pages/blocks/[name].tsx 또는 app/blocks/[name]/page.tsx)를 사용하면, 모든 블록을 필요할 때마다 렌더링할 수 있습니다:

// pages/blocks/[name].tsx
import { getBlockByName } from '@/registry';
import BlockProvider from '@/components/BlockProvider';

export default async function BlockPage({ params }) {
  const block = await getBlockByName(params.name);
  return (
    <BlockProvider block={block}>
      {/* Render the block component dynamically */}
    </BlockProvider>
  );
}
  • 블록당 별도의 페이지가 필요 없습니다.
  • 코드베이스를 DRY하게 유지하고 새 블록을 추가하는 것이 registry.json을 업데이트하는 것만큼 간단합니다.

TL;DR

  • Define each UI section as a registry:block in registry.json. → registry.json에서 각 UI 섹션을 registry:block으로 정의합니다.
  • Implement the block as a server component that only accepts marketing‑level props. → 마케팅 수준의 props만 받는 서버 컴포넌트로 블록을 구현합니다.
  • Render any block through a single dynamic route wrapped in BlockProvider for isolated theme & viewport previews. → 격리된 테마 및 뷰포트 미리보기를 위해 BlockProvider로 감싼 단일 동적 라우트를 통해 모든 블록을 렌더링합니다.
  • Enjoy fast builds, tiny bundles, and a workflow that meshes perfectly with the Shadcn CLI. → 빠른 빌드, 작은 번들, 그리고 Shadcn CLI와 완벽히 맞는 워크플로우를 즐기세요.

즐거운 빌딩 되세요! 🚀

카탈로그: /blocks/[name]

단일 Next.js 페이지가 30개 이상의 모든 블록을 구동합니다

export async function generateStaticParams() {
  return getBlocks().map(block => ({ name: block.name })); // 30+ pages!
}

export default async function BlockPage({ params }) {
  const block = getBlock(params.name);
  const { component, ...serializableBlock } = block; // Server‑safe

  return (
    <BlockProvider block={serializableBlock}>
      {/* Render the block component */}
    </BlockProvider>
  );
}

자동 생성: /blocks/hero-01, /blocks/pricing-01, /blocks/cta-01, …

지표: 22 정적 페이지, 100 % 서버‑렌더링, 전체 SEO 메타데이터.

8️⃣ CLI 통합

npx shadcn add @shadcnship/hero-01

내 레지스트리는 Shadcn 스키마를 정확히 따르기 때문에 CLI 통합이 매끄럽습니다:

# 직접 URL (현재 작동)
npx shadcn add https://mysite.com/r/hero-01.json

# 네임스페이스 방식 (PR 승인 후)
npx shadcn add @shadcnship/hero-01

CLI가 수행하는 작업:

  • hero.tsxcomponents/hero-01.tsx 복사
  • lucide-react 설치
  • button + badge 자동 설치

총 30초

9️⃣ Lessons learned (after 4 weeks + 30 blocks)

✅ Do

  • 마케팅 prop만 (heading, buttons) 사용하기.
  • registryDependencies를 철저히 사용하기.
  • 블록을 서버‑컴포넌트 우선으로 유지하기.
  • 블록을 확장하기 전에 프리뷰 시스템 구축하기.

❌ Don’t

  • 레이아웃 prop(padding, gap)을 노출하지 않기.
  • 프리뷰와 사이트‑테마 상태를 혼합하지 않기.
  • 프리뷰 페이지에 블록 이름을 하드코딩하지 않기.
  • 오버라이드를 위한 className prop을 잊지 않기.

Biggest win: Generic /blocks/[name] + BlockProvider = 블록당 추가 코드가 전혀 없음.

전체 코드 받기

30개 이상의 프로덕션‑레디 블록을 프리뷰 시스템, 테마 전환 및 레지스트리와 함께 제공:

👉 GitHub:

👉 Live demo:

시간을 내 주셔서 감사합니다.

즐기세요! 🚀

Back to Blog

관련 글

더 보기 »