Files
sam-docs/system/react-component-architecture.md
김보곤 dc2806b698 docs: [system] React 컴포넌트 아키텍처 현황 문서 추가
- Atomic Design 적용 현황 분석 (이상 vs 현실)
- Import 비율: ui/ 직접 83.7%, 계층 경유 16.2%
- 핵심 컴포넌트 사용 빈도 TOP 10
- 테마 시스템 (light/dark/senior)
- 신규 화면 개발 가이드
2026-03-12 12:19:28 +09:00

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/ 직접 사용 패턴을 따르는 것이 현실적이다.


관련 문서


최종 업데이트: 2026-03-12