- Vite + React 프로젝트 구조 설정 - 불필요한 PDF 파일 삭제 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
13 KiB
13 KiB
디자인 시스템 실전 예제
예제: 품목관리 페이지 분석
품목관리 페이지(ItemManagement.tsx)를 아토믹 디자인 시스템으로 분해해보겠습니다.
페이지 전체 구조
ItemManagement (Page)
│
├── PageHeader (Organism)
│ ├── 📦 Icon (Atom) - Archive icon
│ ├── "품목관리" (Text)
│ └── "제품 및 자재 품목을 관리합니다" (Text)
│
├── StatCards (Organism)
│ ├── StatCard (Molecule)
│ │ ├── 📊 Icon (Atom) - Box icon
│ │ ├── "총 품목" (Label)
│ │ └── "74" (Value)
│ │
│ ├── StatCard (Molecule)
│ │ ├── 📦 Icon (Atom) - Package icon
│ │ ├── "제품" (Label)
│ │ └── "15" (Value)
│ │
│ ├── StatCard (Molecule)
│ │ ├── 🔧 Icon (Atom) - Wrench icon
│ │ ├── "반제품" (Label)
│ │ └── "28" (Value)
│ │
│ └── StatCard (Molecule)
│ ├── 📋 Icon (Atom) - Archive icon
│ ├── "자재" (Label)
│ └── "31" (Value)
│
├── SearchFilter (Organism)
│ ├── SearchBar (Molecule)
│ │ ├── 🔍 Icon (Atom) - Search icon
│ │ └── Input (Atom) - "품목명, 품목코드 검색..."
│ │
│ ├── Select (Atom) - 품목 유형 필터
│ └── Button (Atom) - "+ 품목 등록"
│
└── DataTable (Organism)
├── Table (Atom)
│ ├── 품목코드 (Column)
│ ├── 품목명 (Column)
│ ├── 품목유형 (Column)
│ │ └── StatusBadge (Molecule)
│ │ └── Badge (Atom)
│ ├── 규격 (Column)
│ ├── 단위 (Column)
│ ├── 단가 (Column)
│ ├── 재고 (Column)
│ └── 관리 (Column)
│ └── TableActions (Molecule)
│ ├── Button (Atom) - 보기
│ ├── Button (Atom) - 수정
│ └── Button (Atom) - 삭제
│
└── Pagination (Component)
코드 예제: 품목관리 페이지
1. 기존 방식 (비추천)
// ❌ 모든 것을 직접 구현
export function ItemManagement() {
return (
<div className="p-6">
{/* 헤더를 직접 구현 */}
<div className="flex items-center gap-3 mb-6">
<div className="bg-blue-100 p-3 rounded-lg">
<Archive className="w-6 h-6 text-blue-600" />
</div>
<div>
<h1 className="text-2xl font-semibold">품목관리</h1>
<p className="text-gray-600">제품 및 자재 품목을 관리합니다</p>
</div>
</div>
{/* 통계 카드를 직접 구현 */}
<div className="grid grid-cols-4 gap-4 mb-6">
<div className="bg-white p-4 rounded-lg border">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-600">총 품목</p>
<p className="text-2xl font-semibold">74</p>
</div>
<div className="bg-blue-100 p-3 rounded-lg">
<Box className="w-5 h-5 text-blue-600" />
</div>
</div>
</div>
{/* 나머지 카드들... */}
</div>
{/* 검색/필터를 직접 구현 */}
<div className="flex gap-4 mb-6">
<div className="flex-1 relative">
<Search className="absolute left-3 top-3 w-4 h-4 text-gray-400" />
<input
type="text"
placeholder="품목명, 품목코드 검색..."
className="w-full pl-10 pr-4 py-2 border rounded-lg"
/>
</div>
{/* 필터들... */}
</div>
{/* 테이블을 직접 구현 */}
<table className="w-full">
{/* 테이블 내용... */}
</table>
</div>
);
}
문제점:
- 코드가 길고 복잡함 (500+ 줄)
- 재사용 불가능
- 다른 페이지와 일관성 없음
- 유지보수 어려움
- 스타일 변경 시 모든 페이지 수정 필요
2. 아토믹 디자인 시스템 방식 (권장)
// ✅ 컴포넌트 재사용
import { PageHeader } from "./organisms/PageHeader";
import { StatCards } from "./organisms/StatCards";
import { SearchFilter } from "./organisms/SearchFilter";
import { DataTable } from "./organisms/DataTable";
import { Archive, Box, Package, Wrench } from "lucide-react";
export function ItemManagement() {
const stats = [
{ label: "총 품목", value: "74", icon: Box, iconColor: "text-blue-600" },
{ label: "제품", value: "15", icon: Package, iconColor: "text-green-600" },
{ label: "반제품", value: "28", icon: Wrench, iconColor: "text-orange-600" },
{ label: "자재", value: "31", icon: Archive, iconColor: "text-purple-600" },
];
const columns = [
{ key: "itemCode", label: "품목코드" },
{ key: "itemName", label: "품목명" },
{ key: "itemType", label: "품목유형", render: (value) => <StatusBadge status={value} /> },
{ key: "spec", label: "규격" },
{ key: "unit", label: "단위" },
{ key: "unitPrice", label: "단가" },
{ key: "stock", label: "재고" },
];
return (
<div className="flex-1 overflow-auto">
<PageHeader
icon={Archive}
title="품목관리"
subtitle="제품 및 자재 품목을 관리합니다"
/>
<div className="p-6">
<StatCards stats={stats} />
<SearchFilter
searchValue={searchTerm}
onSearchChange={setSearchTerm}
searchPlaceholder="품목명, 품목코드 검색..."
filters={[
{ type: "select", label: "품목 유형", value: filter, onChange: setFilter }
]}
actions={[
{ label: "+ 품목 등록", onClick: handleCreate, variant: "default" }
]}
/>
<DataTable
columns={columns}
data={filteredItems}
onView={handleView}
onEdit={handleEdit}
onDelete={handleDelete}
/>
</div>
</div>
);
}
장점:
- 코드가 짧고 명확함 (100줄 이하)
- 재사용 가능
- 다른 페이지와 일관성 유지
- 유지보수 쉬움
- 컴포넌트만 수정하면 모든 페이지에 자동 반영
디자인 토큰 활용 예제
색상 변경 시나리오
현재 Primary 색상을 파란색(#3B82F6)에서 초록색(#10B981)으로 변경한다면:
기존 방식:
// ❌ 모든 파일을 찾아서 수정해야 함
// ItemManagement.tsx
<div className="bg-blue-600">...</div>
// CustomerManagement.tsx
<div className="bg-blue-600">...</div>
// OrderManagement.tsx
<div className="bg-blue-600">...</div>
// ... 50개 이상의 파일 수정 필요
디자인 토큰 방식:
/* ✅ globals.css에서 한 줄만 수정 */
:root {
--primary: #10B981; /* #3B82F6에서 변경 */
}
/* 모든 페이지에 자동 반영됨 */
실전 워크플로우
새 페이지 생성하기
Step 1: 템플릿 선택
// 목록 페이지인 경우
import { ListPageTemplate } from "./templates/ListPageTemplate";
// 폼 페이지인 경우
import { FormPageTemplate } from "./templates/FormPageTemplate";
// 대시보드 페이지인 경우
import { DashboardTemplate } from "./templates/DashboardTemplate";
Step 2: 데이터 준비
export function NewPage() {
// 통계 데이터
const stats = [
{ label: "총 개수", value: "100", icon: Box },
{ label: "진행중", value: "50", icon: Clock },
{ label: "완료", value: "30", icon: CheckCircle },
{ label: "보류", value: "20", icon: XCircle },
];
// 테이블 컬럼
const columns = [
{ key: "id", label: "ID" },
{ key: "name", label: "이름" },
{ key: "status", label: "상태", render: (value) => <StatusBadge status={value} /> },
];
// 데이터
const [data, setData] = useState([]);
const [searchTerm, setSearchTerm] = useState("");
}
Step 3: 템플릿 적용
return (
<ListPageTemplate
// Header
title="새 페이지"
description="페이지 설명"
icon={Database}
// Stats
stats={stats}
// Search & Filter
searchValue={searchTerm}
onSearchChange={setSearchTerm}
// Table
columns={columns}
data={filteredData}
onView={handleView}
onEdit={handleEdit}
onDelete={handleDelete}
/>
);
완성! 3단계로 표준화된 페이지 생성 완료
컴포넌트 커스터마이징
예제: StatusBadge 색상 규칙 추가
현재 StatusBadge는 특정 상태값에 따라 자동으로 색상을 매핑합니다:
// StatusBadge.tsx (현재)
const getStatusColor = (status: string) => {
const statusLower = status.toLowerCase();
if (statusLower.includes('완료') || statusLower.includes('성공')) {
return 'bg-green-100 text-green-800';
}
if (statusLower.includes('진행') || statusLower.includes('처리')) {
return 'bg-blue-100 text-blue-800';
}
if (statusLower.includes('대기') || statusLower.includes('보류')) {
return 'bg-yellow-100 text-yellow-800';
}
if (statusLower.includes('취소') || statusLower.includes('실패')) {
return 'bg-red-100 text-red-800';
}
return 'bg-gray-100 text-gray-800';
};
새 상태 "검토중" 추가:
// StatusBadge.tsx (수정)
const getStatusColor = (status: string) => {
const statusLower = status.toLowerCase();
// ... 기존 코드 ...
if (statusLower.includes('검토')) {
return 'bg-purple-100 text-purple-800';
}
return 'bg-gray-100 text-gray-800';
};
이제 모든 페이지의 "검토중" 상태가 자동으로 보라색으로 표시됩니다!
반응형 디자인 자동 지원
아토믹 디자인 시스템의 모든 컴포넌트는 자동으로 반응형을 지원합니다:
// ✅ 자동으로 모바일/데스크톱 대응
<StatCards stats={stats} />
// 데스크톱: 4열 그리드
// 태블릿: 2열 그리드
// 모바일: 1열 스택
<DataTable columns={columns} data={data} />
// 데스크톱: 전체 테이블
// 모바일: 카드 형식으로 자동 전환
다크모드 자동 지원
모든 컴포넌트는 다크모드를 자동으로 지원합니다:
// ✅ 테마 변경만으로 모든 컴포넌트가 다크모드로 전환
setTheme('dark');
// globals.css의 .dark 클래스에 정의된 색상이 자동 적용
실전 팁
1. 컴포넌트 탐색기 활용
디자인 시스템 페이지에서 컴포넌트를 찾고 코드를 복사하세요:
- 기준정보 관리 → 디자인시스템 접속
- Atoms/Molecules/Organisms/Templates 탭 클릭
- 필요한 컴포넌트 찾기
- 사용법 코드 복사 버튼 클릭
- 코드에 붙여넣기
2. 일관성 체크리스트
새 페이지 생성 시:
- PageHeader 사용
- StatCards로 통계 표시 (4개)
- SearchFilter로 검색/필터 구현
- DataTable로 목록 표시
- StatusBadge로 상태 표시
- TableActions로 액션 버튼 구현
3. 성능 최적화
컴포넌트는 이미 최적화되어 있습니다:
- Memoization 적용
- Lazy loading 지원
- Virtual scrolling (큰 테이블)
- Code splitting
4. 접근성
모든 컴포넌트는 접근성을 고려하여 설계되었습니다:
- Keyboard navigation
- Screen reader 지원
- ARIA labels
- Focus management
마이그레이션 가이드
기존 페이지를 아토믹 디자인으로 전환하기
Before:
export function OldPage() {
return (
<div>
<div className="flex items-center gap-3">
<h1>페이지 제목</h1>
</div>
<div className="grid grid-cols-4 gap-4">
{/* 통계 카드들 */}
</div>
<input type="text" placeholder="검색..." />
<table>
{/* 테이블 */}
</table>
</div>
);
}
After:
import { PageHeader } from "./organisms/PageHeader";
import { StatCards } from "./organisms/StatCards";
import { SearchFilter } from "./organisms/SearchFilter";
import { DataTable } from "./organisms/DataTable";
export function NewPage() {
return (
<div className="flex-1 overflow-auto">
<PageHeader icon={Database} title="페이지 제목" />
<div className="p-6">
<StatCards stats={stats} />
<SearchFilter searchValue={search} onSearchChange={setSearch} />
<DataTable columns={columns} data={data} />
</div>
</div>
);
}
마이그레이션 시간: 약 30분
요약
왜 아토믹 디자인 시스템을 사용하나요?
- 재사용성 - 한 번 만들면 어디서나 사용
- 일관성 - 모든 페이지가 동일한 스타일
- 유지보수 - 컴포넌트 하나만 수정하면 전체 반영
- 생산성 - 새 페이지를 빠르게 생성
- 품질 - 검증된 컴포넌트 사용
시작하는 방법
- 학습: 디자인시스템 페이지에서 컴포넌트 구조 파악
- 실습: 간단한 페이지부터 시작 (ListPageTemplate 사용)
- 확장: 필요한 경우 컴포넌트 커스터마이징
- 공유: 새로운 패턴을 팀과 공유
다음 단계
- 디자인 시스템 페이지 탐색
- 컴포넌트 코드 복사하여 사용
- 필요한 경우 새 Variant 추가
- 팀과 베스트 프랙티스 공유