- Vite + React 프로젝트 구조 설정 - 불필요한 PDF 파일 삭제 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
14 KiB
14 KiB
SAM MES 테이블 디자인 시스템
작성일: 2025년 10월 24일
버전: 1.0.0
📋 목차
개요
SAM MES의 모든 테이블은 통일된 디자인 시스템을 사용합니다. 컬럼과 데이터만 정의하면 자동으로 적절한 형태로 렌더링됩니다.
핵심 원칙
- 일관성: 모든 테이블이 동일한 디자인 패턴
- 타입 안정성: 셀 타입별 자동 렌더링
- 재사용성: 컬럼 정의만으로 테이블 완성
- 반응형: 데스크톱/모바일 자동 대응
테이블 시스템 구조
DataTable (Organism)
├── Column Definition (셀 타입 정의)
├── Cell Renderer (타입별 자동 렌더링)
│ ├── StatusBadge (Molecule)
│ ├── IconWithBadge (Molecule)
│ ├── TableActions (Molecule)
│ └── Badge (Atom)
└── Pagination (Optional)
셀 타입
지원하는 셀 타입
| 타입 | 설명 | 정렬 | 예시 |
|---|---|---|---|
| text | 일반 텍스트 | 좌측 | "홍길동" |
| number | 숫자 (자동 천 단위 구분) | 우측 | 1,234 |
| currency | 통화 (₩ 표시) | 우측 | ₩1,234,567 |
| date | 날짜 | 좌측 | 2025-10-24 |
| datetime | 날짜 + 시간 | 좌측 | 2025-10-24 14:30 |
| status | 상태 배지 | 중앙 | 🟢 활성 |
| badge | 일반 배지 | 중앙 | v1.0 |
| icon | 아이콘 | 중앙 | 📦 |
| iconBadge | 아이콘 + 배지 | 좌측 | 📦 완제품 |
| actions | 액션 버튼들 | 우측 | 👁️ ✏️ 🗑️ |
| custom | 커스텀 렌더링 | 설정 가능 | (사용자 정의) |
컴포넌트 가이드
1. DataTable (Organism)
통일된 테이블 컴포넌트
위치: /components/organisms/DataTable.tsx
Props:
interface DataTableProps<T> {
columns: Column<T>[]; // 컬럼 정의
data: T[]; // 데이터
keyField: keyof T; // 고유 키 필드
onRowClick?: (row: T) => void; // 행 클릭 핸들러
loading?: boolean; // 로딩 상태
emptyMessage?: string; // 빈 메시지
pagination?: { // 페이지네이션
currentPage: number;
totalPages: number;
onPageChange: (page: number) => void;
};
striped?: boolean; // 줄무늬 (기본: false)
hoverable?: boolean; // 호버 효과 (기본: true)
compact?: boolean; // 컴팩트 모드 (기본: false)
}
Column 정의:
interface Column<T> {
key: keyof T | string; // 데이터 키
label: string; // 컬럼 헤더
type?: CellType; // 셀 타입
width?: string; // 너비
align?: "left" | "center" | "right"; // 정렬
// 타입별 설정
statusConfig?: { showDot?: boolean };
badgeConfig?: { variant?: string; className?: string };
iconConfig?: { iconColor?: string; iconBgColor?: string; size?: "sm" | "md" | "lg" };
currencyConfig?: { locale?: string; currency?: string };
// 커스텀
render?: (value: any, row: T) => ReactNode;
format?: (value: any) => string | number;
}
2. StatusBadge (Molecule)
상태 표시 배지
위치: /components/molecules/StatusBadge.tsx
지원 상태:
// 프로세스 상태
"대기" | "진행중" | "완료" | "취소" | "반려"
// 문서 상태
"작성중" | "검토중" | "승인" | "보류"
// 품질 상태
"합격" | "부적합" | "조건부합격" | "검사중"
// 설비 상태
"정상" | "고장" | "점검중"
// 활성화 상태
"활성" | "비활성" | "사용" | "미사용" | "폐기"
// 생산 상태
"생산중" | "배송중" | "생산대기"
// 재고 상태
"입고" | "출고" | "재고부족"
사용법:
<StatusBadge status="활성" />
<StatusBadge status="진행중" showDot />
3. IconWithBadge (Molecule)
아이콘 + 라벨 + 배지 조합
위치: /components/molecules/IconWithBadge.tsx
사용법:
<IconWithBadge
icon={Package}
label="완제품"
iconColor="text-blue-600"
iconBgColor="bg-blue-100"
badge={{ label: "신규", variant: "default" }}
size="md"
/>
4. TableActions (Molecule)
테이블 액션 버튼 그룹
위치: /components/molecules/TableActions.tsx
지원 액션 타입:
view- 상세보기 (👁️)edit- 수정 (✏️)delete- 삭제 (🗑️)copy- 복사 (📋)download- 다운로드 (⬇️)custom- 커스텀
레이아웃:
buttons- 버튼으로 표시dropdown- 드롭다운 메뉴auto- 자동 (3개 이하: 버튼, 4개 이상: 드롭다운)
사용법:
<TableActions
actions={[
{ type: "view", onClick: handleView },
{ type: "edit", onClick: handleEdit },
{ type: "delete", onClick: handleDelete, variant: "destructive" }
]}
layout="auto"
/>
사용 예시
예시 1: 기본 테이블
import { DataTable, Column } from "./organisms/DataTable";
interface Product {
id: string;
code: string;
name: string;
price: number;
status: string;
updatedAt: string;
}
function ProductList() {
const columns: Column<Product>[] = [
{ key: "code", label: "제품코드", type: "text" },
{ key: "name", label: "제품명", type: "text" },
{ key: "price", label: "가격", type: "currency" },
{ key: "status", label: "상태", type: "status" },
{ key: "updatedAt", label: "수정일", type: "date" }
];
return (
<DataTable
columns={columns}
data={products}
keyField="id"
/>
);
}
예시 2: 액션 버튼이 있는 테이블
const columns: Column<Product>[] = [
{ key: "code", label: "제품코드", type: "text" },
{ key: "name", label: "제품명", type: "text" },
{ key: "price", label: "가격", type: "currency" },
{ key: "status", label: "상태", type: "status" },
{
key: "id",
label: "관리",
type: "actions",
render: (_, row) => [
{ type: "view", onClick: () => handleView(row) },
{ type: "edit", onClick: () => handleEdit(row) },
{ type: "delete", onClick: () => handleDelete(row), variant: "destructive" }
]
}
];
예시 3: 아이콘 + 배지
const columns: Column<Item>[] = [
{
key: "type",
label: "품목유형",
type: "iconBadge",
render: (value) => ({
icon: getItemIcon(value),
label: getItemLabel(value),
badge: { label: "신규", variant: "default" }
})
}
];
예시 4: 커스텀 렌더링
const columns: Column<Order>[] = [
{
key: "customer",
label: "고객사",
type: "custom",
render: (value, row) => (
<div>
<p className="font-medium">{value}</p>
<p className="text-xs text-muted-foreground">{row.customerCode}</p>
</div>
)
}
];
예시 5: 페이지네이션
const { currentPage, totalPages, paginatedData, goToPage } = usePagination(data, 10);
<DataTable
columns={columns}
data={paginatedData}
keyField="id"
pagination={{
currentPage,
totalPages,
onPageChange: goToPage
}}
/>
예시 6: 다양한 옵션
<DataTable
columns={columns}
data={data}
keyField="id"
striped // 줄무늬
compact // 컴팩트 모드
hoverable={false} // 호버 효과 비활성화
loading={isLoading} // 로딩 상태
emptyMessage="검색 결과가 없습니다."
onRowClick={handleRowClick} // 행 클릭
/>
Best Practices
1. 올바른 타입 선택
// ✅ 좋은 예
{ key: "price", label: "가격", type: "currency" }
{ key: "quantity", label: "수량", type: "number" }
{ key: "status", label: "상태", type: "status" }
{ key: "createdAt", label: "생성일", type: "date" }
// ❌ 나쁜 예
{ key: "price", label: "가격", type: "text" } // 통화 형식 미적용
{ key: "status", label: "상태", type: "text" } // 배지 미적용
2. 액션 버튼 정리
// ✅ 좋은 예 - 타입 시스템 활용
{
key: "id",
label: "관리",
type: "actions",
render: (_, row) => [
{ type: "view", onClick: () => handleView(row) },
{ type: "edit", onClick: () => handleEdit(row) },
{ type: "delete", onClick: () => handleDelete(row), variant: "destructive" }
]
}
// ❌ 나쁜 예 - 직접 구현
{
key: "id",
label: "관리",
render: (_, row) => (
<div className="flex gap-2">
<Button onClick={() => handleView(row)}>보기</Button>
<Button onClick={() => handleEdit(row)}>수정</Button>
<Button onClick={() => handleDelete(row)}>삭제</Button>
</div>
)
}
3. 상태 통일
// ✅ 좋은 예 - StatusBadge 활용
{ key: "status", label: "상태", type: "status" }
// ❌ 나쁜 예 - 직접 구현
{
key: "status",
render: (value) => (
<Badge className={value === "활성" ? "bg-green-100" : "bg-gray-100"}>
{value}
</Badge>
)
}
4. 정렬 기본값
타입별 자동 정렬을 활용하세요:
- text: 좌측 정렬
- number, currency, actions: 우측 정렬
- status, badge, icon: 중앙 정렬
// ✅ 자동 정렬 활용
{ key: "price", label: "가격", type: "currency" } // 자동 우측 정렬
// ⚠️ 필요시에만 명시
{ key: "price", label: "가격", type: "currency", align: "left" } // 수동 좌측 정렬
5. 반응형 대응
데스크톱과 모바일을 함께 고려하세요:
// 데스크톱: DataTable
<DataTable
columns={columns}
data={data}
keyField="id"
/>
// 모바일: MobileCard
<div className="md:hidden space-y-3">
{data.map((item) => (
<MobileCard
key={item.id}
title={item.name}
subtitle={item.code}
fields={[...]}
/>
))}
</div>
마이그레이션 가이드
기존 테이블 → 새 테이블 시스템
Before:
<Table>
<TableHeader>
<TableRow>
<TableHead>제품코드</TableHead>
<TableHead>제품명</TableHead>
<TableHead>가격</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.map((row) => (
<TableRow key={row.id}>
<TableCell>{row.code}</TableCell>
<TableCell>{row.name}</TableCell>
<TableCell>₩{row.price.toLocaleString()}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
After:
const columns: Column<Product>[] = [
{ key: "code", label: "제품코드", type: "text" },
{ key: "name", label: "제품명", type: "text" },
{ key: "price", label: "가격", type: "currency" }
];
<DataTable columns={columns} data={data} keyField="id" />
효과:
- 코드 라인 수: 20+ 라인 → 5 라인 (75% 감소)
- 일관성: 자동으로 통일된 디자인 적용
- 유지보수: 컬럼 정의만 수정
컴포넌트 계층 구조
Atoms (UI 기본 컴포넌트)
├── Badge
├── Button
└── Table Components
Molecules (작은 조합)
├── StatusBadge ⭐ 상태 배지
├── IconWithBadge ⭐ 아이콘 + 배지
└── TableActions ⭐ 액션 버튼 그룹
Organisms (복잡한 조합)
└── DataTable ⭐ 통합 테이블
Templates (페이지 레이아웃)
└── ListPageTemplate ⭐ 목록 페이지 (DataTable 포함)
적용 대상 페이지
즉시 적용 가능 (40개 이상)
관리 페이지:
- CustomerManagement
- SupplierManagement
- OrderManagement
- PurchaseOrderManagement
- ItemManagement
- EquipmentManagement
- VehicleManagement
- EmployeeManagement
- DepartmentManagement
재고 관리:
- InventoryManagement
- ReceivingManagement
- ShippingManagement
- StockStatus
생산 관리:
- ProductionManagement
- WorkOrderManagement
- ProcessManagement
품질 관리:
- IncomingInspectionManagement
- ProcessInspectionManagement
- FinalInspectionManagement
- NonconformingManagement
회계/영업:
- SalesAccountingManagement
- PurchaseAccountingManagement
- QuoteManagement
- PricingManagement
FAQ
Q1. 커스텀 렌더링과 타입 시스템 중 어떤 것을 사용해야 하나요?
A: 가능하면 타입 시스템을 우선 사용하세요. 커스텀 렌더링은 정말 특수한 경우에만 사용합니다.
// ✅ 타입 시스템 활용 (권장)
{ key: "status", label: "상태", type: "status" }
// ⚠️ 커스텀 렌더링 (특수한 경우만)
{
key: "complex",
render: (value, row) => (
<div>
<p>{value}</p>
<p className="text-xs">{row.detail}</p>
</div>
)
}
Q2. 액션 버튼이 4개 이상일 때 어떻게 표시되나요?
A: 자동으로 드롭다운 메뉴로 변환됩니다.
// 3개 이하: 버튼으로 표시
// 4개 이상: 자동으로 드롭다운 메뉴로 변환
layout="auto" // 기본값
Q3. 테이블 행을 클릭 가능하게 만들려면?
A: onRowClick prop을 사용하세요.
<DataTable
columns={columns}
data={data}
keyField="id"
onRowClick={(row) => navigate(`/detail/${row.id}`)}
/>
Q4. 로딩 상태는 어떻게 표시하나요?
A: loading prop을 사용하세요.
<DataTable
columns={columns}
data={data}
keyField="id"
loading={isLoading}
/>
변경 이력
| 버전 | 날짜 | 변경 내용 |
|---|---|---|
| 1.0.0 | 2025-10-24 | 초기 버전 생성 |
문서 버전: 1.0.0
최종 업데이트: 2025년 10월 24일
작성자: SAM MES 개발팀