- Atomic Design 적용 현황 분석 (이상 vs 현실) - Import 비율: ui/ 직접 83.7%, 계층 경유 16.2% - 핵심 컴포넌트 사용 빈도 TOP 10 - 테마 시스템 (light/dark/senior) - 신규 화면 개발 가이드
16 KiB
16 KiB
React 컴포넌트 아키텍처 현황
작성일: 2026-03-12 상태: 현황 분석 완료 관련 문서: 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%+ 사용 패턴)
// 실제 도메인 코드 패턴
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%+ 사용 패턴)
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 변수 체계
: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
@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
: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 신규 폼 (권장 패턴)
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<typeof schema>;
export function MyForm() {
const form = useForm<FormData>({
resolver: zodResolver(schema),
defaultValues: { name: '', amount: 0, status: 'active' },
});
return <form onSubmit={form.handleSubmit(onSubmit)}>...</form>;
}
9.2 CVA 기반 variant 시스템
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 프론트엔드 구조 — 프로젝트 규모, 도메인, 아키텍처 패턴
- API 서버 구조 — API 서버 구조
- MNG 관리자 패널 구조 — MNG 구조
최종 업데이트: 2026-03-12