From dc2806b698277e68a634affc659a822dc429bf56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Thu, 12 Mar 2026 08:24:14 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20[system]=20React=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=95=84=ED=82=A4=ED=85=8D=EC=B2=98=20?= =?UTF-8?q?=ED=98=84=ED=99=A9=20=EB=AC=B8=EC=84=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Atomic Design 적용 현황 분석 (이상 vs 현실) - Import 비율: ui/ 직접 83.7%, 계층 경유 16.2% - 핵심 컴포넌트 사용 빈도 TOP 10 - 테마 시스템 (light/dark/senior) - 신규 화면 개발 가이드 --- INDEX.md | 1 + system/react-component-architecture.md | 481 +++++++++++++++++++++++++ 2 files changed, 482 insertions(+) create mode 100644 system/react-component-architecture.md diff --git a/INDEX.md b/INDEX.md index 90cd6c7..dbe75cf 100644 --- a/INDEX.md +++ b/INDEX.md @@ -74,6 +74,7 @@ docs/ | [overview.md](system/overview.md) | 전체 시스템 아키텍처 | | [api-structure.md](system/api-structure.md) | API 서버 구조 (~1,027 엔드포인트) | | [react-structure.md](system/react-structure.md) | React 프론트엔드 구조 | +| [react-component-architecture.md](system/react-component-architecture.md) | React 컴포넌트 아키텍처 (Atomic Design 적용 현황, UI 스택, 테마) | | [mng-structure.md](system/mng-structure.md) | MNG 관리자 패널 구조 | | [docker-setup.md](system/docker-setup.md) | Docker 환경 + CI/CD | | [database/README.md](system/database/README.md) | DB 스키마 인덱스 | diff --git a/system/react-component-architecture.md b/system/react-component-architecture.md new file mode 100644 index 0000000..8a0da18 --- /dev/null +++ b/system/react-component-architecture.md @@ -0,0 +1,481 @@ +# React 컴포넌트 아키텍처 현황 + +> **작성일**: 2026-03-12 +> **상태**: 현황 분석 완료 +> **관련 문서**: [react-structure.md](react-structure.md) — 전체 프로젝트 구조 + +--- + +## 1. 개요 + +### 1.1 목적 + +SAM React 프로젝트의 컴포넌트 아키텍처 현황을 기록한다. Atomic Design 폴더 구조를 채택했으나 **실제 계층적 의존성은 부분적으로만 작동**하고 있으며, 이 문서는 현실과 이상의 차이를 정리하여 신규 화면 개발 시 올바른 패턴을 참고하도록 한다. + +### 1.2 기술 스택 + +| 계층 | 기술 | 설명 | +|------|------|------| +| UI 프리미티브 | Radix UI (13개 패키지) | 접근성 기반 헤드리스 컴포넌트 | +| 스타일 | shadcn/ui + Tailwind CSS v4 + CVA | 프로젝트 맞춤 커스터마이징 | +| 폼 | React Hook Form + Zod | 타입 안전한 검증 | +| 상태 | Zustand 5 + Immer | 전역 상태 (persist) | +| 아이콘 | Lucide React (550+) | | +| 차트 | Recharts v3 | | +| 에디터 | Tiptap | 리치텍스트 | +| 테마 | CSS 변수 + Zustand | light / dark / senior 3종 | + +--- + +## 2. 폴더 구조 + +``` +src/components/ +├── ui/ ← shadcn/ui 프리미티브 (60개) +│ Button, Input, Select, Dialog, DatePicker, +│ CurrencyInput, PhoneInput, FileDropzone 등 +│ +├── atoms/ ← Atomic Design 원자 (3개) +│ BadgeSm, TabChip, ScrollableButtonGroup +│ +├── molecules/ ← Atomic Design 분자 (13개) +│ FormField, StatusBadge, MobileCard, +│ DateRangeSelector, StandardDialog 등 +│ +├── organisms/ ← Atomic Design 유기체 (14개) +│ PageLayout, PageHeader, DataTable, +│ StatCards, EmptyState, SearchFilter 등 +│ +├── templates/ ← 페이지 템플릿 (5개, 2개 미사용) +│ UniversalListPage, IntegratedDetailTemplate, +│ IntegratedListTemplateV2 +│ +├── layout/ ← 전역 레이아웃 +│ Sidebar, CommandMenuSearch, HeaderFavoritesBar +│ +├── business/ ← 대시보드 +├── accounting/ ← 회계 도메인 +├── hr/ ← 인사 도메인 +├── approval/ ← 전자결재 도메인 +├── items/ ← 품목 도메인 +├── production/ ← 생산 도메인 +├── ... (도메인별 ~600개) +│ +└── common/ ← 공통 (DataTable 독립 구현) +``` + +--- + +## 3. 실제 의존성 분석 + +### 3.1 이상 vs 현실 + +``` +[이상적 Atomic Design] +Page → Templates → Organisms → Molecules → Atoms → ui/ + +[실제 구조] +Page ──→ Templates ──────────────────────→ ui/ (직접) + ├─→ Organisms ──────────────────────→ ui/ (직접) + ├─→ Molecules ──────────────────────→ ui/ (직접) + └─→ ui/ ────────────────────────────→ ui/ (직접) + +계층 간 연결: + atoms → molecules: 2/13만 사용 (17%) + molecules → organisms: 0회 (완전 단절) + organisms → templates: 2회만 (PageLayout, PageHeader) +``` + +### 3.2 Import 비율 (전체 2,186회) + +| 대상 | 횟수 | 비율 | 평가 | +|------|------|------|------| +| **ui/ 직접** | 1,831회 | **83.7%** | 압도적 | +| templates | 192회 | 8.8% | 핵심 2개 템플릿 | +| organisms | 148회 | 6.8% | PageLayout/MobileCard 중심 | +| molecules | 45회 | 2.1% | FormField/StatusBadge 중심 | +| atoms | 15회 | 0.7% | 거의 미사용 | + +### 3.3 계층별 ui/ 의존 + +| 계층 | ui/ import 횟수 | atoms/molecules/organisms import | 비고 | +|------|----------------|--------------------------------|------| +| templates | 25회+ | 4회 | ui/ 직접 의존 | +| organisms | 25회+ | 0회 | molecules 완전 미사용 | +| molecules | 28회+ | 2회 (atoms) | 대부분 ui/ 직접 | +| 도메인 코드 | 1,700회+ | ~300회 | 83% ui/ 직접 | + +--- + +## 4. 핵심 컴포넌트 사용 현황 + +### 4.1 고빈도 컴포넌트 (TOP 10) + +| 순위 | 컴포넌트 | 계층 | 사용 횟수 | 역할 | +|------|---------|------|---------|------| +| 1 | `FormField` | molecules | 216회 | Label + Input 통합 | +| 2 | `UniversalListPage` | templates | 214회 | 목록 페이지 전체 | +| 3 | `IntegratedDetailTemplate` | templates | 182회 | 상세/폼 페이지 전체 | +| 4 | `MobileCard` | molecules+organisms | 172+129회 | 모바일 카드 | +| 5 | `StatusBadge` | molecules | 125회 | 상태 뱃지 | +| 6 | `PageLayout` | organisms | 67회 | 페이지 래퍼 | +| 7 | `BadgeSm` | atoms | 63회 | 소형 뱃지 | +| 8 | `PageHeader` | organisms | 56회 | 페이지 헤더 | +| 9 | `ListMobileCard` | organisms | 50회 | 모바일 목록 카드 | +| 10 | `DateRangeSelector` | molecules | 45회 | 날짜 범위 필터 | + +### 4.2 미사용 컴포넌트 + +| 컴포넌트 | 계층 | 비고 | +|---------|------|------| +| `ListPageTemplate` | templates | 0회 — `UniversalListPage`로 대체됨 | +| `ResponsiveFormTemplate` | templates | 0회 — `IntegratedDetailTemplate`로 대체됨 | + +### 4.3 저사용 컴포넌트 (5회 미만) + +| 컴포넌트 | 계층 | 사용 횟수 | +|---------|------|---------| +| `FormActions` | organisms | 4회 | +| `ScreenVersionHistory` | organisms | 4회 | + +--- + +## 5. 실제 페이지 개발 패턴 + +### 5.1 목록 페이지 (90%+ 사용 패턴) + +```tsx +// 실제 도메인 코드 패턴 +import { UniversalListPage } from '@/components/templates/UniversalListPage'; +import { StatusBadge } from '@/components/molecules'; +import { MobileCard } from '@/components/organisms/MobileCard'; +import { Button } from '@/components/ui/button'; +import { Dialog } from '@/components/ui/dialog'; +``` + +`UniversalListPage`가 내부적으로 테이블, 페이지네이션, 검색 필터, 모바일 대응을 모두 포함한다. + +### 5.2 상세/폼 페이지 (90%+ 사용 패턴) + +```tsx +import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate'; +import { FormField } from '@/components/molecules/FormField'; +import { Input } from '@/components/ui/input'; +import { Select } from '@/components/ui/select'; +``` + +`IntegratedDetailTemplate`이 내부적으로 DetailField, DetailSection, DetailGrid, DetailActions 등 9개 하위 컴포넌트를 자체 포함한다. + +### 5.3 도메인 폴더 내부 구조 + +``` +src/components/accounting/BillManagement/ +├── BillManagementClient.tsx ← 메인 컴포넌트 +├── actions.ts ← Server Action (API 호출) +├── types.ts ← TypeScript 타입 +├── billConfig.ts ← 설정, 필터 옵션 +└── modals/ ← 하위 모달 컴포넌트 +``` + +--- + +## 6. ui/ 컴포넌트 목록 (shadcn/ui 기반, 60개) + +### 6.1 기본 UI + +| 컴포넌트 | 설명 | +|---------|------| +| `button` | 6개 variant (default/destructive/outline/secondary/ghost/link), 4개 size | +| `input` | HTML input 래퍼, aria-invalid 지원 | +| `label` | HTML label 래퍼 | +| `card` | Card/CardHeader/CardTitle/CardContent/CardFooter | +| `badge` | 5개 variant | +| `alert` | Alert/AlertTitle/AlertDescription | +| `skeleton` | 로딩 스켈레톤 | + +### 6.2 폼 입력 + +| 컴포넌트 | 설명 | +|---------|------| +| `checkbox` | Radix UI Checkbox | +| `radio-group` | Radix UI RadioGroup | +| `select` | Radix UI Select (검색 없음) | +| `switch` | Radix UI Switch | +| `slider` | Radix UI Slider | +| `date-picker` | 날짜 선택 | +| `date-range-picker` | 날짜 범위 | +| `date-time-picker` | 날짜+시간 | +| `file-input` | 파일 선택 | +| `file-dropzone` | 드래그앤드롭 파일 | +| `image-upload` | 이미지 업로드 | +| `multi-select-combobox` | 다중 선택 콤보박스 | +| `searchable-select` | 검색 가능 셀렉트 | + +### 6.3 한국형 입력 (자동 포맷팅) + +| 컴포넌트 | 포맷 | 용도 | +|---------|------|------| +| `phone-input` | `010-1234-5678` | 전화번호 | +| `business-number-input` | `123-45-67890` | 사업자등록번호 | +| `personal-number-input` | `123456-7890123` | 주민번호 | +| `account-number-input` | 은행별 포맷 | 계좌번호 | +| `card-number-input` | `1234 5678 9012 3456` | 카드번호 | +| `currency-input` | `1,234,567` | 금액 (천단위) | +| `quantity-input` | 정수 | 수량 | +| `number-input` | 소수점 | 숫자 | + +### 6.4 오버레이/피드백 + +| 컴포넌트 | 설명 | +|---------|------| +| `dialog` | Radix UI Dialog | +| `alert-dialog` | 확인/취소 다이얼로그 | +| `drawer` | Vaul 드로어 | +| `sheet` | 사이드 패널 | +| `popover` | Radix UI Popover | +| `tooltip` | Radix UI Tooltip | +| `dropdown-menu` | Radix UI DropdownMenu | +| `confirm-dialog` | 커스텀 확인 다이얼로그 | +| `command` | cmdk 커맨드 팔레트 | +| `loading-spinner` | 스피너 | +| `progress` | Radix UI Progress | +| `sonner` | 토스트 알림 | + +### 6.5 레이아웃/데이터 + +| 컴포넌트 | 설명 | +|---------|------| +| `table` | HTML table 래퍼 | +| `tabs` | Radix UI Tabs | +| `accordion` | Radix UI Accordion | +| `collapsible` | Radix UI Collapsible | +| `scroll-area` | Radix UI ScrollArea | + +--- + +## 7. 테마 시스템 + +### 7.1 3가지 테마 + +| 테마 | 클래스 | 특징 | +|------|--------|------| +| Light | `:root` (기본) | 밝은 배경, 표준 글자 크기 | +| Dark | `.dark` | 어두운 배경, 밝은 글자 | +| Senior | `.senior` | 큰 글자(18px), 높은 대비, 굵은 폰트 | + +### 7.2 CSS 변수 체계 + +```css +:root { + --primary: #3B82F6; /* 주 색상 */ + --destructive: #EF4444; /* 위험/삭제 */ + --background: #FAFAFA; /* 배경 */ + --card: #FFFFFF; /* 카드 배경 */ + --border: #E2E8F0; /* 테두리 */ + /* 60+ 색상 변수 */ +} + +.dark { --background: #0F172A; --primary: #60A5FA; ... } +.senior { --font-size: 18px; --font-weight-medium: 600; ... } +``` + +### 7.3 Tailwind variant + +```css +@variant dark (&:is(.dark *)); +@variant senior (&:is(.senior *)); +``` + +코드에서 `dark:bg-slate-800 senior:text-lg` 형태로 사용한다. + +### 7.4 상태 관리 + +``` +Zustand (themeStore) → document.documentElement.className 변경 + → localStorage persist (새로고침 유지) +``` + +--- + +## 8. 모바일 반응형 전략 + +### 8.1 자동 전환 + +| 뷰포트 | 표시 방식 | +|--------|----------| +| 768px 이상 (md:) | `DataTable` (테이블) | +| 768px 미만 | `MobileCard` / `ListMobileCard` (카드 목록) | + +### 8.2 핵심 모바일 컴포넌트 + +| 컴포넌트 | 용도 | 사용 횟수 | +|---------|------|---------| +| `MobileCard` (molecules) | 필터/정보 카드 | 172회 | +| `MobileCard` (organisms) | 상세 조회 카드 | 129회 | +| `ListMobileCard` | 목록 카드 | 50회 | +| `MobileFilter` | 모바일 필터 | 14회 | + +### 8.3 iOS Safe Area + +```css +:root { + --safe-area-inset-top: env(safe-area-inset-top, 0px); + --safe-area-inset-bottom: env(safe-area-inset-bottom, 0px); +} +``` + +Capacitor 기반 모바일 앱 대응이 포함되어 있다. + +--- + +## 9. 폼 패턴 + +### 9.1 신규 폼 (권장 패턴) + +```tsx +import { z } from 'zod'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; + +const schema = z.object({ + name: z.string().min(1, '필수'), + amount: z.number().min(0), + status: z.enum(['active', 'inactive']), +}); + +type FormData = z.infer; + +export function MyForm() { + const form = useForm({ + resolver: zodResolver(schema), + defaultValues: { name: '', amount: 0, status: 'active' }, + }); + return
...
; +} +``` + +### 9.2 CVA 기반 variant 시스템 + +```tsx +import { cva } from 'class-variance-authority'; + +const buttonVariants = cva( + 'inline-flex items-center justify-center gap-2 rounded-md', + { + variants: { + variant: { + default: 'bg-primary text-primary-foreground', + destructive: 'bg-destructive text-white', + outline: 'border bg-background', + ghost: 'hover:bg-accent', + }, + size: { + default: 'h-9 px-4 py-2', + sm: 'h-8 px-3', + lg: 'h-10 px-6', + icon: 'size-9', + }, + }, + } +); +``` + +--- + +## 10. 신규 화면 개발 가이드 + +### 10.1 목록 페이지 작성 시 + +``` +1. UniversalListPage 사용 (목록 90%+ 커버) +2. 도메인 폴더에 actions.ts, types.ts, config.ts 분리 +3. StatusBadge로 상태 표시 +4. MobileCard 렌더 함수 정의 (모바일 대응) +5. 추가 UI는 ui/ 에서 직접 import +``` + +### 10.2 상세/폼 페이지 작성 시 + +``` +1. IntegratedDetailTemplate 사용 (상세/폼 90%+ 커버) +2. FormField로 Label + Input 조합 (일관성) +3. Zod 스키마 정의 (검증) +4. 추가 UI는 ui/ 에서 직접 import +``` + +### 10.3 Import 우선순위 + +``` +1순위: templates (UniversalListPage, IntegratedDetailTemplate) +2순위: molecules (FormField, StatusBadge, DateRangeSelector) +3순위: organisms (PageLayout, PageHeader, EmptyState) +4순위: ui/ (Button, Dialog, Input 등 프리미티브) +``` + +> atoms는 현재 3개뿐이므로 필요 시 ui/에서 직접 사용한다. + +### 10.4 하지 말 것 + +``` +❌ ListPageTemplate 사용 (dead code — UniversalListPage 사용) +❌ ResponsiveFormTemplate 사용 (dead code — IntegratedDetailTemplate 사용) +❌ 새 atoms 만들기 (ui/ 컴포넌트로 충분) +❌ 도메인 코드에서 다른 도메인 컴포넌트 import +``` + +--- + +## 11. 현황 평가 요약 + +### 11.1 잘 작동하는 부분 + +| 항목 | 설명 | +|------|------| +| **2개 핵심 템플릿** | `UniversalListPage`(214회) + `IntegratedDetailTemplate`(182회)가 전체 페이지의 90%+ 커버 | +| **shadcn/ui 기반 ui/** | 60개 프리미티브가 일관된 디자인 시스템 제공 | +| **고빈도 molecules** | `FormField`(216회), `StatusBadge`(125회)가 폼/상태 표시 표준화 | +| **모바일 대응** | MobileCard 기반 자동 전환 | +| **테마 시스템** | light/dark/senior 3종 CSS 변수 기반 | +| **한국형 입력** | 전화번호, 사업자번호, 계좌번호 등 자동 포맷팅 | + +### 11.2 개선이 필요한 부분 + +| 항목 | 현상 | 영향 | +|------|------|------| +| **계층 간 의존성 붕괴** | organisms가 molecules를 0회 사용 | Atomic Design 의미 퇴색 | +| **atoms 유명무실** | 3개만 존재, ui/ 60개에 비해 극소 | 계층 존재 이유 불분명 | +| **ui/ 과의존** | 전체 import의 83.7% | 추상화 효과 없음 | +| **Dead code** | ListPageTemplate, ResponsiveFormTemplate 미사용 | 혼란 유발 | +| **index.ts 비일관** | atoms/templates는 index.ts 미사용 | import 패턴 불통일 | + +### 11.3 결론 + +**실제 아키텍처는 "Templates + UI Components" 2계층 구조**이다. + +``` +실제 작동 구조: + +Layer 1 — 페이지 템플릿 (2개가 전체 지배) + ├── UniversalListPage ← 목록 페이지 + └── IntegratedDetailTemplate ← 상세/폼 페이지 + +Layer 2 — UI 프리미티브 (shadcn/ui) + └── ui/ 60개 컴포넌트 ← 모든 곳에서 직접 사용 + +보조 — molecules (FormField, StatusBadge 등 고빈도 유틸) +보조 — organisms (PageLayout, PageHeader, MobileCard) +``` + +Atomic Design 폴더명(atoms/molecules/organisms/templates)은 유지되어 있으나, 실제 개발 시에는 **templates → ui/ 직접 사용** 패턴을 따르는 것이 현실적이다. + +--- + +## 관련 문서 + +- [React 프론트엔드 구조](react-structure.md) — 프로젝트 규모, 도메인, 아키텍처 패턴 +- [API 서버 구조](api-structure.md) — API 서버 구조 +- [MNG 관리자 패널 구조](mng-structure.md) — MNG 구조 + +--- + +**최종 업데이트**: 2026-03-12