Files
sam-design/src/DESIGN_SYSTEM_STANDARDIZATION_GUIDE.md

546 lines
13 KiB
Markdown
Raw Normal View History

# 디자인 시스템 표준화 가이드
## 목적
SAM MES 시스템의 모든 페이지가 일관된 디자인을 유지하도록 표준화합니다.
---
## 1. 필수 컴포넌트 사용
### 1.1 PageLayout
**모든 페이지는 반드시 PageLayout으로 감싸야 합니다.**
```tsx
import { PageLayout } from "./organisms/PageLayout";
export function YourPage() {
return (
<PageLayout maxWidth="full">
{/* 페이지 내용 */}
</PageLayout>
);
}
```
**maxWidth 옵션:**
- `full`: 대시보드, 목록 페이지 (기본값)
- `2xl`: 넓은 폼 페이지
- `xl`: 일반 폼 페이지
- `lg`: 좁은 폼 페이지
### 1.2 PageHeader
**모든 페이지는 반드시 PageHeader를 사용해야 합니다.**
```tsx
import { PageHeader } from "./organisms/PageHeader";
import { Package } from "lucide-react";
import { Button } from "./ui/button";
<PageHeader
title="페이지 제목"
description="페이지에 대한 간단한 설명"
icon={Package}
actions={
<>
<Button variant="outline">취소</Button>
<Button>등록</Button>
</>
}
/>
```
**금지 사항:**
```tsx
// ❌ 직접 헤더 작성 금지
<div className="flex justify-between">
<h1>제목</h1>
<Button>등록</Button>
</div>
// ✅ PageHeader 사용
<PageHeader title="제목" actions={<Button>등록</Button>} />
```
---
## 2. 표준 페이지 구조
### 2.1 기본 목록 페이지 구조
```tsx
import { PageLayout } from "./organisms/PageLayout";
import { PageHeader } from "./organisms/PageHeader";
import { StatCards } from "./organisms/StatCards";
import { SearchFilter } from "./organisms/SearchFilter";
import { DataTable } from "./organisms/DataTable";
export function ListPage() {
return (
<PageLayout>
{/* 1. 헤더 (제목 + 서브제목 + 아이콘 + 액션 버튼) */}
<PageHeader
title="페이지 제목"
description="페이지 설명"
icon={Icon}
actions={<Button>등록</Button>}
/>
{/* 2. 통계 카드 (4개) */}
<StatCards
stats={[
{ title: "통계1", value: "100", icon: Icon, color: "blue" },
{ title: "통계2", value: "200", icon: Icon, color: "green" },
{ title: "통계3", value: "300", icon: Icon, color: "yellow" },
{ title: "통계4", value: "400", icon: Icon, color: "purple" },
]}
/>
{/* 3. 검색/필터 */}
<SearchFilter
searchValue={searchValue}
onSearchChange={setSearchValue}
filters={filterConfig}
/>
{/* 4. 콘텐츠 (테이블) */}
<DataTable
columns={columns}
data={filteredData}
/>
</PageLayout>
);
}
```
### 2.2 대시보드 페이지 구조
```tsx
export function DashboardPage() {
return (
<PageLayout>
<PageHeader
title="대시보드"
description="실시간 현황을 확인하세요"
icon={LayoutDashboard}
/>
<StatCards stats={stats} />
{/* 차트 및 위젯 */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<Widget1 />
<Widget2 />
<Widget3 />
</div>
</PageLayout>
);
}
```
### 2.3 폼 페이지 구조
```tsx
export function FormPage() {
return (
<PageLayout maxWidth="xl">
<PageHeader
title="신규 등록"
description="필수 항목을 입력하세요"
icon={Plus}
actions={
<>
<Button variant="outline" onClick={handleCancel}>
취소
</Button>
<Button onClick={handleSubmit}>
저장
</Button>
</>
}
/>
<FormSection title="기본 정보">
{/* 폼 필드들 */}
</FormSection>
<FormSection title="추가 정보">
{/* 폼 필드들 */}
</FormSection>
</PageLayout>
);
}
```
---
## 3. 스타일 가이드
### 3.1 금지된 패턴
```tsx
// ❌ 직접 패딩/여백 정의 금지
<div className="p-6 space-y-6">
// ❌ 배경 그라데이션 금지 (특별한 경우 제외)
<div className="bg-gradient-to-br from-slate-50 to-blue-50/30">
// ❌ 불필요한 min-h-screen 금지
<div className="min-h-screen">
// ❌ 인라인 헤더 스타일 금지
<h1 className="text-3xl font-bold text-foreground">
// ❌ 커스텀 카드 스타일 금지
<div className="bg-card border border-border/20 rounded-xl p-6">
```
### 3.2 권장 패턴
```tsx
// ✅ PageLayout 사용
<PageLayout>
// ✅ PageHeader 사용
<PageHeader title="제목" />
// ✅ Card 컴포넌트 사용
import { Card, CardHeader, CardTitle, CardContent } from "./ui/card";
<Card>
<CardHeader>
<CardTitle>제목</CardTitle>
</CardHeader>
<CardContent>
{/* 내용 */}
</CardContent>
</Card>
// ✅ 그리드 레이아웃
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
```
---
## 4. 타이포그래피 표준
### 4.1 절대 사용하지 말 것
**globals.css에 타이포그래피가 정의되어 있으므로 Tailwind 클래스를 사용하지 마세요.**
```tsx
// ❌ 절대 금지!
className="text-2xl"
className="text-xl"
className="text-lg"
className="font-bold"
className="font-semibold"
className="leading-tight"
```
### 4.2 올바른 사용법
```tsx
// ✅ HTML 태그 사용 (자동 스타일 적용)
<h1>대제목</h1>
<h2>중제목</h2>
<h3>소제목</h3>
<p>본문</p>
<label>레이블</label>
```
**예외:** 사용자 요청이 있을 때만 타이포그래피 클래스 사용
---
## 5. 색상 시스템
### 5.1 디자인 토큰 사용
```tsx
// ✅ CSS 변수 사용
className="bg-primary text-primary-foreground"
className="bg-secondary text-secondary-foreground"
className="bg-muted text-muted-foreground"
className="bg-destructive text-destructive-foreground"
className="border-border"
className="bg-background text-foreground"
className="bg-card text-card-foreground"
```
### 5.2 아이콘 배경 색상
```tsx
// ✅ 표준 아이콘 배경
<div className="p-2 bg-primary/10 rounded-lg">
<Icon className="w-6 h-6 text-primary" />
</div>
// ✅ 다른 색상 옵션
bg-blue-100 text-blue-600
bg-green-100 text-green-600
bg-yellow-100 text-yellow-600
bg-purple-100 text-purple-600
bg-orange-100 text-orange-600
bg-red-100 text-red-600
```
---
## 6. 반응형 디자인
### 6.1 브레이크포인트
```tsx
// 모바일 우선
className="grid-cols-1" // < 768px
className="md:grid-cols-2" // 768px ~ 1023px (태블릿)
className="lg:grid-cols-4" // 1024px+ (데스크톱)
```
### 6.2 통계 카드 레이아웃
```tsx
// ✅ 표준 반응형 그리드
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<StatCard />
<StatCard />
<StatCard />
<StatCard />
</div>
```
### 6.3 폼 레이아웃
```tsx
// ✅ 반응형 폼
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<FormField />
<FormField />
<FormField />
</div>
```
---
## 7. 컴포넌트 사용 우선순위
### 7.1 Organisms (가장 높은 우선순위)
1. **PageLayout** - 모든 페이지 필수
2. **PageHeader** - 모든 페이지 필수
3. **StatCards** - 통계 카드가 있는 페이지
4. **SearchFilter** - 검색/필터가 있는 페이지
5. **DataTable** - 테이블이 있는 페이지
6. **FormSection** - 폼이 있는 페이지
7. **EmptyState** - 데이터가 없을 때
### 7.2 Molecules
1. **StatCard** - 개별 통계 카드
2. **FormField** - 개별 폼 필드
3. **SearchBar** - 검색 입력
4. **StatusBadge** - 상태 표시
5. **TableActions** - 테이블 액션 버튼
### 7.3 ShadCN UI Components
1. **Card** - 카드 레이아웃
2. **Button** - 버튼
3. **Input** - 입력 필드
4. **Select** - 드롭다운
5. **Dialog** - 다이얼로그
6. **Table** - 테이블 (DataTable 내부에서 사용)
---
## 8. 페이지별 체크리스트
### ✅ 목록 페이지 체크리스트
- [ ] PageLayout으로 감싸져 있는가?
- [ ] PageHeader를 사용하고 있는가?
- [ ] 아이콘이 PageHeader에 전달되었는가?
- [ ] 통계 카드가 4개인가?
- [ ] StatCards 컴포넌트를 사용하는가?
- [ ] SearchFilter 컴포넌트를 사용하는가?
- [ ] DataTable 컴포넌트를 사용하는가?
- [ ] 데이터가 없을 때 EmptyState를 보여주는가?
- [ ] 반응형 레이아웃인가? (grid-cols-1 md:grid-cols-2 lg:grid-cols-4)
### ✅ 대시보드 페이지 체크리스트
- [ ] PageLayout으로 감싸져 있는가?
- [ ] PageHeader를 사용하고 있는가?
- [ ] StatCards를 사용하는가?
- [ ] 위젯이 반응형 그리드로 배치되어 있는가?
- [ ] 타이포그래피 클래스를 사용하지 않았는가?
### ✅ 폼 페이지 체크리스트
- [ ] PageLayout (maxWidth="xl")으로 감싸져 있는가?
- [ ] PageHeader를 사용하고 있는가?
- [ ] 액션 버튼이 PageHeader에 전달되었는가?
- [ ] FormSection으로 섹션을 구분했는가?
- [ ] FormField를 사용하는가?
- [ ] 반응형 폼 레이아웃인가?
---
## 9. 마이그레이션 예시
### Before (❌)
```tsx
export function OldPage() {
return (
<div className="p-6 space-y-6 bg-gradient-to-br from-slate-50 to-blue-50/30 min-h-screen">
<div className="flex justify-between items-center">
<div>
<h1 className="text-3xl font-bold text-foreground">페이지 제목</h1>
<p className="text-sm text-muted-foreground mt-1">설명</p>
</div>
<Button>등록</Button>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<Card>
<CardHeader>
<CardTitle>통계 1</CardTitle>
</CardHeader>
<CardContent>
<p className="text-2xl font-bold">100</p>
</CardContent>
</Card>
{/* ... */}
</div>
<div className="bg-card border rounded-xl p-4">
<Input placeholder="검색..." />
</div>
<Card>
<Table>
{/* ... */}
</Table>
</Card>
</div>
);
}
```
### After (✅)
```tsx
import { PageLayout } from "./organisms/PageLayout";
import { PageHeader } from "./organisms/PageHeader";
import { StatCards } from "./organisms/StatCards";
import { SearchFilter } from "./organisms/SearchFilter";
import { DataTable } from "./organisms/DataTable";
import { Package } from "lucide-react";
export function NewPage() {
return (
<PageLayout>
<PageHeader
title="페이지 제목"
description="설명"
icon={Package}
actions={<Button>등록</Button>}
/>
<StatCards
stats={[
{ title: "통계 1", value: "100", icon: Package, color: "blue" },
{ title: "통계 2", value: "200", icon: Package, color: "green" },
{ title: "통계 3", value: "300", icon: Package, color: "yellow" },
{ title: "통계 4", value: "400", icon: Package, color: "purple" },
]}
/>
<SearchFilter
searchValue={searchValue}
onSearchChange={setSearchValue}
/>
<DataTable
columns={columns}
data={data}
/>
</PageLayout>
);
}
```
---
## 10. 자주 발생하는 실수
### ❌ 실수 1: 직접 헤더 작성
```tsx
<div className="flex justify-between">
<h1>제목</h1>
<Button>등록</Button>
</div>
```
### ✅ 해결
```tsx
<PageHeader
title="제목"
actions={<Button>등록</Button>}
/>
```
---
### ❌ 실수 2: 커스텀 카드 스타일
```tsx
<div className="bg-card border border-border/20 rounded-xl p-6">
<h3 className="text-lg font-semibold">제목</h3>
<p>내용</p>
</div>
```
### ✅ 해결
```tsx
<Card>
<CardHeader>
<CardTitle>제목</CardTitle>
</CardHeader>
<CardContent>
<p>내용</p>
</CardContent>
</Card>
```
---
### ❌ 실수 3: 타이포그래피 클래스 사용
```tsx
<h1 className="text-3xl font-bold">제목</h1>
```
### ✅ 해결
```tsx
<h1>제목</h1> {/* globals.css에 정의된 스타일 자동 적용 */}
```
---
## 11. 개발 워크플로우
### 새 페이지 생성 시
1. ✅ PageLayout으로 시작
2. ✅ PageHeader 추가
3. ✅ 필요한 Organisms 컴포넌트 추가 (StatCards, SearchFilter, DataTable 등)
4. ✅ ShadCN UI 컴포넌트로 나머지 구현
5. ✅ 타이포그래피 클래스 사용하지 않기
6. ✅ 반응형 확인 (모바일, 태블릿, 데스크톱)
7. ✅ 다크모드 확인
### 기존 페이지 수정 시
1. ✅ 현재 PageLayout 사용 여부 확인
2. ✅ PageHeader 사용 여부 확인
3. ✅ 커스텀 스타일 제거
4. ✅ Organisms 컴포넌트로 교체
5. ✅ 타이포그래피 클래스 제거
---
## 12. 참고 자료
- **디자인 토큰**: `/styles/globals.css`
- **공통 컴포넌트**: `/components/organisms/`, `/components/molecules/`
- **ShadCN UI**: `/components/ui/`
- **디자인 시스템 관리**: "기준정보 > 디자인시스템" 메뉴
---
## 요약
### 핵심 원칙 3가지
1. **PageLayout + PageHeader는 필수**
2. **타이포그래피 클래스 사용 금지**
3. **Organisms 컴포넌트 최대한 활용**
이 가이드를 따르면 모든 페이지가 일관된 디자인을 유지할 수 있습니다.