컴포넌트 라이브러리(shadcn/ui, Radix UI) 활용법과 스타일 시스템 구축, 접근성(a11y) 원칙, 그리고 디자인 시스템 없이도 일관된 UI를 만드는 방법을 배웁니다.
🎨 왜 컴포넌트 라이브러리를 쓰는가
버튼, 모달, 드롭다운, 날짜 선택기 등 복잡한 UI 컴포넌트를 처음부터 직접 만들면 많은 시간이 걸립니다. 특히 키보드 탐색, 스크린 리더 지원, 포커스 트랩 같은 접근성 기능까지 직접 구현하는 것은 매우 어렵습니다. 컴포넌트 라이브러리를 활용하면 이 문제를 해결합니다.
📦 shadcn/ui — 현재 가장 인기있는 선택
shadcn/ui는 기존 컴포넌트 라이브러리와 다릅니다. 코드를 당신의 프로젝트에 직접 복사합니다. npm 패키지가 아닙니다. 그래서 완전히 커스터마이징 가능합니다.
# 초기 설정 (Vite 프로젝트)
npx shadcn@latest init
# 필요한 컴포넌트 추가
npx shadcn@latest add button
npx shadcn@latest add dialog
npx shadcn@latest add input
설치하면 src/components/ui/에 컴포넌트 코드가 생깁니다. 직접 수정해서 쓸 수 있습니다.
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog'
function App() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">모달 열기</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>설정</DialogTitle>
</DialogHeader>
<div className="space-y-4 py-4">
<div>
<label className="text-sm font-medium">이름</label>
<Input placeholder="이름을 입력하세요" className="mt-1" />
</div>
<Button className="w-full">저장</Button>
</div>
</DialogContent>
</Dialog>
)
}
♿ 접근성(a11y) 기본 원칙
좋은 UI는 마우스 없이도 키보드만으로 사용할 수 있어야 합니다. shadcn/ui는 Radix UI 위에 구축되어 접근성이 이미 내장되어 있습니다. 직접 컴포넌트를 만들 때 지켜야 할 기본 규칙:
// ❌ 접근성 없는 버튼 — 키보드 사용 불가, 스크린 리더 설명 없음
<div onClick={handleDelete} className="cursor-pointer text-red-500">
삭제
</div>
// ✅ 시맨틱 요소 사용
<button
onClick={handleDelete}
aria-label="게시글 삭제" // 아이콘만 있을 때 레이블 제공
className="text-red-500 hover:text-red-700"
>
<Trash2 className="h-4 w-4" />
</button>
// ✅ 이미지 alt 텍스트
<img src="/avatar.jpg" alt="김민준 프로필 사진" /> // 의미 있는 설명
<img src="/decoration.png" alt="" /> // 장식용은 빈 문자열
| 원칙 | 실천 방법 |
|---|---|
| 시맨틱 HTML | button은 <button>, 링크는 <a>, 입력은 <input> |
| 키보드 접근 | 모든 인터랙션이 Tab, Enter, Space, Esc로 동작해야 함 |
| 포커스 표시 | outline을 지우지 마세요 (접근성 필수) |
| 색 대비 | 텍스트와 배경 대비 4.5:1 이상 (WCAG AA) |
| ARIA 레이블 | 아이콘 버튼에 aria-label 추가 |
🎭 cn() 유틸리티 — Tailwind 클래스 병합
조건부 클래스와 기본 클래스를 깔끔하게 병합합니다:
// lib/utils.ts (shadcn/ui가 자동 생성)
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
// 사용
function Badge({ variant = 'default' }: { variant?: 'default' | 'success' | 'danger' }) {
return (
<span
className={cn(
'inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium',
{
'bg-gray-100 text-gray-700': variant === 'default',
'bg-green-100 text-green-700': variant === 'success',
'bg-red-100 text-red-700': variant === 'danger',
}
)}
>
{/* ... */}
</span>
)
}
✅ 정리
- shadcn/ui: 코드를 프로젝트에 복사해 사용 — 완전 커스터마이징 가능.
- Radix UI 기반으로 키보드, 스크린 리더, 포커스 관리가 내장되어 있습니다.
- 직접 컴포넌트 만들 때: 시맨틱 HTML + aria 속성 + 키보드 동작을 반드시 챙기세요.
cn()으로 조건부 Tailwind 클래스를 깔끔하게 병합합니다.
관련 주제
- shadcn/ui
- Radix UI
- 컴포넌트 라이브러리
- 접근성
- 키보드 탐색
- 스크린 리더
- 커스터마이징
- 개발·프로그래밍
- 개발·프로그래밍 강의
- 리액트 입문 — 컴포넌트로 만드는 웹
- 무료강의
- 무료 온라인 강의
- NUGUNA
- 누구나
댓글
0/1000
불러오는 중...
