shadcn/ui

예진·2024년 11월 6일

개인 공부

목록 보기
13/14
post-thumbnail

shadcn/ui

: Radix UI와 Tailwind CSS의 최신 기술을 기반으로 한 Component Collection
( Vercel의 shadcn이 만든 UI 도구 )

Components library가 아닌 Collection이라는 차이
→ 원하는 컴포넌트만 골라서 작성 가능

뭐가 좋을까?

  • 쉬운 커스터마이징 → 코드를 직접 프로젝트에 복사하여 사용하므로 수정이 자유로움
  • 번들 크기 증가x → 필요한 컴포넌트만 사용해 불필요한 번들 크기가 증가하지 않음
  • 접근성과 스타일링의 조화 → Radix UI의 접근성과 TailwindCSS의 편리한 스타일링을 동시에 활용 가능

shadcn/ui 작동을 위한 프로젝트 구성요소

  • shadcn/ui config를 저장하는 components.json (Config)
  • 컴포넌트가 기본적으로 사용하는 Tailwind CSS와 관련 라이브러리 및 유틸리티 함수 (Style)
  • 원본 컴포넌트 코드 (Source)
  • init , add 명령어를 위한 script (CLI)

1. components.json

components.json은 init 명령어에서 입력받은 프로젝트 설정(alias, 관련 파일들의 위치, rsc, tsx 사용 여부, 테마 등)을 담고있음

CLI를 사용하려면 필요한 파일

// components.json
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "tailwind.config.ts",
    "css": "src/app/globals.css",
    "baseColor": "zinc",
    "cssVariables": true,
    "prefix": ""
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  }
}

프로젝트 설정에 맞춰 변경해서 사용하면 됨

2. Tailwind CSS

프로젝트에서 관장하는 Tailwind CSS 연관 파일은 총 3개

각 파일 모두 shadcn/ui가 관리하는 템플릿이 존재하며, init 과정에서 자동으로 템플릿이 설치됨

  1. tailwind config file

  2. global css file

  3. cn helper 가 들어있는 utils file

    //utils.ts
    
    import { clsx, type ClassValue } from "clsx"
    import { twMerge } from "tailwind-merge"
    
    export function cn(...inputs: ClassValue[]) {
      return twMerge(clsx(inputs))
    }
  • 조건부로 Tailwind CSS 클래스를 쉽게 추가하기 위해 사용함
  • clsx 는 객체, 배열 지원과 조건부 추가 등 className 사용을 편리하게 해주며 동적으로 className string 을 생성함
  • tailwind-merge 는 Tailwind 유틸리티 클래스의 중복과 충돌 제거 등을 처리함

3. 원본 컴포넌트

원본 컴포넌트는 Typescript와 빌드 결과물 JSON 두 가지 형태로 관리됨

  • Typescript 원본 소스
    원본 컴포넌트들은 Radix UI를 기반으로 RSC, TypeScript, Tailwind CSS, Path Alias가 모두 적용되어 있으며, registry/<style>/ui/<name>.tsx 경로에 저장됨
    ( style - 컴포넌트 스타일, name - 컴포넌트 명 )
    스타일 별로 컴포넌트 소스 코드가 다르기 때문에 동일한 타입의 컴포넌트여도 분리되어 있음
  • Transformed JSON
    component 코드와 registry array는 JSON 파일들로 변환되며, CLI의 파일 복붙과 의존성 설치에 사용되게 됨

4. CLI script

project initialize, component add, component diff 총 세 가지 기능

  • init 과정 → config 세팅과 템플릿, 필수 의존성 설치로 이루어짐
  • add → 필요한 컴포넌트 정보를 가져와서 설치

Next.js & Tailwind CSS 프로젝트에 shadcn/ui 적용해보자

1. initialize

pnpm dlx shadcn@latest init

→ components.json 생성

2. 사용할 component 추가

pnpm dlx shadcn@latest add button

pnpm dlx shadcn@latest add 컴포넌트명 : 명령어 뒤에 원하는 컴포넌트 작성

ex. button component 추가

pnpm dlx shadcn@latest add button 실행하면 아래의 컴포넌트가 설치됨

// src/components/ui/button.tsx

import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/lib/utils"

const buttonVariants = cva(
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
  {
    variants: {
      variant: {
        default:
          "bg-primary text-primary-foreground shadow hover:bg-primary/90",
        destructive:
          "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
        outline:
          "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
        secondary:
          "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "text-primary underline-offset-4 hover:underline",
      },
      size: {
        default: "h-9 px-4 py-2",
        sm: "h-8 rounded-md px-3 text-xs",
        lg: "h-10 rounded-md px-8",
        icon: "h-9 w-9",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, asChild = false, ...props }, ref) => {
    const Comp = asChild ? Slot : "button"
    return (
      <Comp
        className={cn(buttonVariants({ variant, size, className }))}
        ref={ref}
        {...props}
      />
    )
  }
)
Button.displayName = "Button"

export { Button, buttonVariants }
  • Slot@radix-ui/react-slot 에서 제공하는 기능, add 명령어 실행 과정에서 자동으로 설치됨 → 컴포넌트의 자식 요소를 더 유연하게 관리할 수 있게 해줌
  • cvaVariantPropsclass-variance-authority 라이브러리에서 제공함 → CSS 클래스의 변형을 쉽게 관리할 수 있도록 해줌
  • cn 은 클래스 이름을 결합하는데 사용하는 내부 유틸리티 함수 → 해당 파일은 tailwindcss 를 merge할 때 발생할 수 있는 클래스 충돌 문제를 해결해주는 역할을 함
  • buttonVariants 은 버튼의 여러 스타일을 정의함
  • Button components 은 버튼의 기능 수행함 → asChild 라는 프로퍼티가 있어, 이를 true로 설정하면 다른 컴포넌트의 자식 요소로 사용 가능 className, variant, size 와 같은 프로퍼티를 통해 버튼의 스타일 수정 가능
  • 컴포넌트의 렌더링 → Comp 라는 변수에 Slot 이나 button 을 할당하여 이를 통해 실제 화면에 버튼 표시 → cn 함수를 사용하여 buttonVariants 에서 정의한 스타일 적용

3. component 사용하기

import { Button } from "@/components/ui/button"

export default function Home() {
  return (
    <div>
      <Button>Click me</Button>
    </div>
  )
}
profile
😊

0개의 댓글