# WACE ERP 프론트엔드 아키텍처 상세 분석

> 작성일: 2026-02-06
> 작성자: AI Assistant
> 프로젝트: WACE ERP-node
> 목적: 시스템 전체 워크플로우 문서화를 위한 프론트엔드 구조 분석

---

## 목차
1. [전체 디렉토리 구조](#1-전체-디렉토리-구조)
2. [Next.js App Router 구조](#2-nextjs-app-router-구조)
3. [컴포넌트 시스템](#3-컴포넌트-시스템)
4. [V2 컴포넌트 시스템](#4-v2-컴포넌트-시스템)
5. [화면 디자이너 워크플로우](#5-화면-디자이너-워크플로우)
6. [API 클라이언트 시스템](#6-api-클라이언트-시스템)
7. [상태 관리](#7-상태-관리)
8. [레지스트리 시스템](#8-레지스트리-시스템)
9. [대시보드 시스템](#9-대시보드-시스템)
10. [다국어 지원](#10-다국어-지원)
11. [인증 플로우](#11-인증-플로우)
12. [사용자 워크플로우](#12-사용자-워크플로우)

---

## 1. 전체 디렉토리 구조

```
frontend/
├── app/                          # Next.js 14 App Router
│   ├── (main)/                  # 메인 레이아웃 그룹
│   ├── (auth)/                  # 인증 레이아웃 그룹
│   ├── (admin)/                 # 관리자 레이아웃 그룹
│   ├── (pop)/                   # 팝업 레이아웃 그룹
│   ├── layout.tsx               # 루트 레이아웃
│   └── registry-provider.tsx    # 레지스트리 초기화 프로바이더
│
├── components/                   # React 컴포넌트
│   ├── admin/                   # 관리자 컴포넌트 (137개)
│   ├── screen/                  # 화면 디자이너/뷰어 (139개)
│   ├── dashboard/               # 대시보드 컴포넌트 (32개)
│   ├── dataflow/                # 데이터플로우 관련 (90개)
│   ├── v2/                      # V2 컴포넌트 시스템 (28개)
│   ├── common/                  # 공통 컴포넌트 (19개)
│   ├── layout/                  # 레이아웃 컴포넌트 (10개)
│   ├── ui/                      # shadcn/ui 기반 UI (31개)
│   └── ...                      # 기타 도메인별 컴포넌트
│
├── lib/                          # 유틸리티 & 라이브러리
│   ├── api/                     # API 클라이언트 (57개 파일)
│   │   ├── client.ts            # Axios 기본 설정
│   │   ├── screen.ts            # 화면 관리 API
│   │   ├── user.ts              # 사용자 관리 API
│   │   └── ...                  # 도메인별 API
│   │
│   ├── registry/                # 컴포넌트 레지스트리 시스템 (540개)
│   │   ├── ComponentRegistry.ts  # 컴포넌트 등록 관리
│   │   ├── WebTypeRegistry.ts    # 웹타입 등록 관리
│   │   ├── LayoutRegistry.ts     # 레이아웃 등록 관리
│   │   ├── init.ts               # 레지스트리 초기화
│   │   ├── DynamicComponentRenderer.tsx  # 동적 렌더링
│   │   ├── components/           # 등록 가능한 컴포넌트들 (288 tsx, 205 ts)
│   │   │   ├── v2-input/
│   │   │   ├── v2-select/
│   │   │   ├── text-input/
│   │   │   ├── entity-search-input/
│   │   │   ├── modal-repeater-table/
│   │   │   └── ...
│   │   └── layouts/              # 레이아웃 컴포넌트
│   │       ├── accordion/
│   │       ├── grid/
│   │       ├── flexbox/
│   │       ├── split/
│   │       └── tabs/
│   │
│   ├── v2-core/                 # V2 코어 시스템
│   │   ├── events/              # 이벤트 버스
│   │   ├── adapters/            # 레거시 어댑터
│   │   ├── components/          # V2 공통 컴포넌트
│   │   ├── services/            # V2 서비스
│   │   └── init.ts              # V2 초기화
│   │
│   ├── utils/                   # 유틸리티 함수들 (40개+)
│   ├── services/                # 비즈니스 로직 서비스
│   ├── stores/                  # Zustand 스토어
│   └── constants/               # 상수 정의
│
├── contexts/                     # React Context (12개)
│   ├── AuthContext.tsx          # 인증 컨텍스트
│   ├── MenuContext.tsx          # 메뉴 컨텍스트
│   ├── ScreenContext.tsx        # 화면 컨텍스트
│   ├── DashboardContext.tsx     # 대시보드 컨텍스트
│   └── ...
│
├── hooks/                        # Custom Hooks (32개)
│   ├── useAuth.ts               # 인증 훅
│   ├── useMenu.ts               # 메뉴 관리 훅
│   ├── useFormValidation.ts     # 폼 검증 훅
│   └── ...
│
├── types/                        # TypeScript 타입 정의 (44개)
│   ├── screen.ts                # 화면 관련 타입
│   ├── component.ts             # 컴포넌트 타입
│   ├── user.ts                  # 사용자 타입
│   └── ...
│
├── providers/                    # React Provider
│   └── QueryProvider.tsx        # React Query 설정
│
├── stores/                       # 전역 상태 관리
│   ├── flowStepStore.ts         # 플로우 단계 상태
│   ├── modalDataStore.ts        # 모달 데이터 상태
│   └── tableDisplayStore.ts     # 테이블 표시 상태
│
└── public/                       # 정적 파일
    ├── favicon.ico
    ├── logo.png
    └── locales/                 # 다국어 파일
```

---

## 2. Next.js App Router 구조

### 2.1 라우팅 구조

WACE ERP는 **Next.js 14 App Router**를 사용하며, 레이아웃 그룹을 활용한 구조화된 라우팅을 제공합니다.

#### 라우트 그룹 구조

```
app/
├── layout.tsx                    # 글로벌 레이아웃
│   ├── QueryProvider            # React Query 초기화
│   ├── RegistryProvider         # 컴포넌트 레지스트리 초기화
│   ├── Toaster                  # 토스트 알림
│   └── ScreenModal              # 전역 화면 모달
│
├── (main)/                       # 메인 애플리케이션
│   ├── layout.tsx
│   │   ├── AuthProvider         # 인증 컨텍스트
│   │   ├── MenuProvider         # 메뉴 컨텍스트
│   │   └── AppLayout            # 사이드바, 헤더
│   │
│   ├── main/page.tsx            # 메인 페이지
│   ├── dashboard/               # 대시보드
│   │   ├── page.tsx             # 대시보드 목록
│   │   └── [dashboardId]/page.tsx  # 대시보드 상세
│   │
│   ├── screens/[screenId]/page.tsx  # 동적 화면 뷰어
│   │
│   └── admin/                   # 관리자 페이지
│       ├── page.tsx             # 관리자 홈
│       ├── menu/page.tsx        # 메뉴 관리
│       │
│       ├── userMng/             # 사용자 관리
│       │   ├── userMngList/page.tsx
│       │   ├── rolesList/page.tsx
│       │   ├── userAuthList/page.tsx
│       │   └── companyList/page.tsx
│       │
│       ├── screenMng/           # 화면 관리
│       │   ├── screenMngList/page.tsx
│       │   ├── dashboardList/page.tsx
│       │   └── reportList/page.tsx
│       │
│       ├── systemMng/           # 시스템 관리
│       │   ├── tableMngList/page.tsx
│       │   ├── commonCodeList/page.tsx
│       │   ├── i18nList/page.tsx
│       │   ├── dataflow/page.tsx
│       │   └── collection-managementList/page.tsx
│       │
│       ├── automaticMng/        # 자동화 관리
│       │   ├── flowMgmtList/page.tsx
│       │   ├── batchmngList/page.tsx
│       │   ├── exCallConfList/page.tsx
│       │   └── mail/            # 메일 관리
│       │       ├── accounts/page.tsx
│       │       ├── send/page.tsx
│       │       ├── receive/page.tsx
│       │       └── templates/page.tsx
│       │
│       └── [...slug]/page.tsx   # 동적 관리자 페이지
│
├── (auth)/                       # 인증 페이지
│   ├── layout.tsx               # 최소 레이아웃
│   └── login/page.tsx           # 로그인 페이지
│
├── (admin)/                      # 관리자 전용 (별도 레이아웃)
│   └── admin/
│       ├── vehicle-trips/page.tsx
│       └── vehicle-reports/page.tsx
│
└── (pop)/                        # 팝업 페이지
    ├── layout.tsx               # 팝업 레이아웃
    ├── pop/page.tsx
    └── work/page.tsx
```

### 2.2 페이지 목록 (총 76개)

**메인 애플리케이션 (50개)**
- `/main` - 메인 페이지
- `/dashboard` - 대시보드 목록
- `/dashboard/[dashboardId]` - 대시보드 상세
- `/screens/[screenId]` - 동적 화면 뷰어 ⭐ 핵심
- `/multilang` - 다국어 관리

**관리자 페이지 (40개+)**
- 사용자 관리: 사용자, 역할, 권한, 회사, 부서
- 화면 관리: 화면 목록, 대시보드, 리포트
- 시스템 관리: 테이블, 공통코드, 다국어, 데이터플로우, 컬렉션
- 자동화 관리: 플로우, 배치, 외부호출, 메일
- 표준 관리: 웹타입 표준화
- 캐스케이딩: 관계, 계층, 상호배타, 자동채움

**테스트 페이지 (5개)**
- `/test-autocomplete-mapping`
- `/test-entity-search`
- `/test-order-registration`
- `/test-type-safety`
- `/test-flow`

**인증/기타 (2개)**
- `/login` - 로그인
- `/pop` - 팝업 페이지

### 2.3 Next.js 설정

**next.config.mjs**
```javascript
{
  output: "standalone",           // Docker 최적화
  eslint: { ignoreDuringBuilds: true },
  typescript: { ignoreBuildErrors: true },
  
  // API 프록시: /api/* → http://localhost:8080/api/*
  rewrites: [
    { source: "/api/:path*", destination: "http://localhost:8080/api/:path*" }
  ],
  
  // CORS 헤더
  headers: [
    { source: "/api/:path*", headers: [...] }
  ],
  
  // 환경 변수
  env: {
    NEXT_PUBLIC_API_URL: "http://localhost:8080/api"
  }
}
```

---

## 3. 컴포넌트 시스템

### 3.1 컴포넌트 분류

WACE ERP의 컴포넌트는 크게 **4가지 계층**으로 구분됩니다:

#### 계층 구조

```
1. UI 컴포넌트 (shadcn/ui 기반) - components/ui/
   └─ Button, Input, Select, Dialog, Card 등 기본 UI

2. 공통 컴포넌트 - components/common/
   └─ ScreenModal, FormValidationIndicator 등

3. 도메인 컴포넌트 - components/{domain}/
   ├─ screen/      - 화면 디자이너/뷰어
   ├─ admin/       - 관리자 기능
   ├─ dashboard/   - 대시보드
   └─ dataflow/    - 데이터플로우

4. 레지스트리 컴포넌트 - lib/registry/components/
   └─ 동적으로 등록/렌더링되는 컴포넌트들 (90개+)
```

### 3.2 주요 컴포넌트 모듈

#### 📁 components/screen/ (139개 파일)

**핵심 컴포넌트**

| 컴포넌트 | 역할 | 주요 기능 |
|---------|------|----------|
| `ScreenDesigner.tsx` (7095줄) | 화면 디자이너 | - 드래그&드롭으로 컴포넌트 배치<br>- 그리드 시스템 (12컬럼)<br>- 실시간 미리보기<br>- 컴포넌트 설정 패널<br>- 그룹화/정렬/분산 도구 |
| `InteractiveScreenViewer.tsx` (2472줄) | 화면 뷰어 | - 실제 사용자 화면 렌더링<br>- 폼 데이터 바인딩<br>- 버튼 액션 실행<br>- 검증 처리 |
| `ScreenManagementList.tsx` | 화면 목록 | - 화면 CRUD<br>- 메뉴 할당<br>- 다국어 설정 |
| `DynamicWebTypeRenderer.tsx` | 웹타입 렌더러 | - WebTypeRegistry 기반 동적 렌더링 |

**위젯 컴포넌트 (widgets/types/)**
- TextWidget, NumberWidget, DateWidget
- SelectWidget, CheckboxWidget, RadioWidget
- TextareaWidget, FileWidget, CodeWidget
- ButtonWidget, EntitySearchInputWrapper

**설정 패널 (config-panels/)**
- 각 웹타입별 설정 패널 (TextConfigPanel, SelectConfigPanel 등)

**모달 컴포넌트 (modals/)**
- 키보드 단축키, 다국어 설정, 메뉴 할당 등

#### 📁 components/admin/ (137개 파일)

**관리자 기능 컴포넌트**
- 사용자 관리: UserManagementList, RolesManagementList, CompanyList
- 화면 관리: ScreenManagementList, DashboardManagementList
- 테이블 관리: TableManagementList, ColumnEditor
- 공통코드 관리: CommonCodeManagement
- 메뉴 관리: MenuManagement

#### 📁 components/dashboard/ (32개 파일)

**대시보드 컴포넌트**
- DashboardViewer: 대시보드 렌더링
- DashboardWidgets: 차트, 테이블, 카드 등 위젯
- ChartComponents: 라인, 바, 파이, 도넛 차트

#### 📁 components/dataflow/ (90개+ 파일)

**데이터플로우 시스템**
- DataFlowList: 플로우 목록
- node-editor/: 노드 편집기
- connection/: 커넥션 관리 (38개 파일)
- condition/: 조건 설정
- external-call/: 외부 호출 설정

---

## 4. V2 컴포넌트 시스템

V2는 **통합 컴포넌트 시스템**으로, 유사한 기능을 하나의 컴포넌트로 통합하여 개발 효율성을 높입니다.

### 4.1 V2 컴포넌트 종류 (9개)

| ID | 이름 | 설명 | 포함 기능 |
|----|------|------|----------|
| `v2-input` | 통합 입력 | 텍스트, 숫자, 비밀번호, 슬라이더, 컬러 등 | text, number, password, email, tel, textarea, slider, color |
| `v2-select` | 통합 선택 | 드롭다운, 라디오, 체크박스, 태그, 토글 등 | dropdown, radio, checkbox, tagbox, toggle, swap, combobox |
| `v2-date` | 통합 날짜 | 날짜, 시간, 날짜시간, 날짜 범위 | date, time, datetime, daterange |
| `v2-list` | 통합 목록 | 테이블, 카드, 칸반, 리스트 | table, card, kanban, list, grid |
| `v2-layout` | 통합 레이아웃 | 그리드, 분할 패널, 플렉스 | grid, split, flex, masonry |
| `v2-group` | 통합 그룹 | 탭, 아코디언, 섹션, 모달 | tabs, accordion, section, modal, drawer |
| `v2-media` | 통합 미디어 | 이미지, 비디오, 오디오, 파일 업로드 | image, video, audio, file |
| `v2-biz` | 통합 비즈니스 | 플로우, 랙, 채번규칙, 카테고리 | flow, rack, numbering, category |
| `v2-repeater` | 통합 반복 | 인라인 테이블, 모달, 버튼 | inline-table, modal, button, selected-items |

### 4.2 V2 아키텍처

```
components/v2/                     # V2 UI 컴포넌트
├── V2Input.tsx                   # 통합 입력 컴포넌트
├── V2Select.tsx                  # 통합 선택 컴포넌트
├── V2Date.tsx                    # 통합 날짜 컴포넌트
├── V2List.tsx                    # 통합 목록 컴포넌트
├── V2Layout.tsx                  # 통합 레이아웃 컴포넌트
├── V2Group.tsx                   # 통합 그룹 컴포넌트
├── V2Media.tsx                   # 통합 미디어 컴포넌트
├── V2Biz.tsx                     # 통합 비즈니스 컴포넌트
├── V2Hierarchy.tsx               # 통합 계층 컴포넌트
├── V2Repeater.tsx                # 통합 반복 컴포넌트
├── V2FormContext.tsx             # V2 폼 컨텍스트
├── V2ComponentRenderer.tsx       # V2 렌더러
├── registerV2Components.ts       # V2 등록 함수
└── config-panels/                # V2 설정 패널
    ├── V2InputConfigPanel.tsx
    ├── V2SelectConfigPanel.tsx
    └── ...

lib/v2-core/                       # V2 코어 라이브러리
├── events/                        # 이벤트 버스
│   ├── EventBus.ts               # 발행/구독 패턴
│   └── types.ts                  # 이벤트 타입
├── adapters/                      # 레거시 어댑터
│   └── LegacyEventAdapter.ts     # 레거시 시스템 연동
├── components/                    # V2 공통 컴포넌트
│   └── V2ErrorBoundary.tsx       # 에러 경계
├── services/                      # V2 서비스
│   ├── ScheduleGeneratorService.ts
│   └── ScheduleConfirmDialog.tsx
└── init.ts                        # V2 초기화

lib/registry/components/v2-*/      # V2 렌더러 (레지스트리용)
├── v2-input/
│   ├── V2InputRenderer.tsx        # 자동 등록 렌더러
│   └── index.ts                   # 컴포넌트 정의
├── v2-select/
│   ├── V2SelectRenderer.tsx
│   └── index.ts
└── ...
```

### 4.3 V2 초기화 흐름

```typescript
// 1. app/registry-provider.tsx
useEffect(() => {
  // 레거시 레지스트리 초기화
  initializeRegistries();
  
  // V2 Core 초기화
  initV2Core({
    debug: false,
    legacyBridge: { legacyToV2: true, v2ToLegacy: true }
  });
}, []);

// 2. lib/registry/init.ts
export function initializeRegistries() {
  // 웹타입 등록 (text, number, date 등)
  initializeWebTypeRegistry();
  
  // V2 컴포넌트 등록
  registerV2Components();
}

// 3. components/v2/registerV2Components.ts
export function registerV2Components() {
  for (const definition of v2ComponentDefinitions) {
    ComponentRegistry.registerComponent(definition);
  }
}
```

### 4.4 V2 렌더링 흐름

```
사용자 요청 (screens/[screenId])
    ↓
InteractiveScreenViewer
    ↓
DynamicComponentRenderer
    ↓
┌─ componentType이 v2-* 인가? ─┐
│  Yes                         │ No
↓                              ↓
ComponentRegistry.getComponent  LegacyComponentRegistry.get
    ↓                              ↓
V2*Renderer (클래스 기반)        레거시 렌더러 (함수)
    ↓                              ↓
render() → V2* 컴포넌트          직접 렌더링
    ↓
V2FormContext 연동 (선택)
    ↓
최종 렌더링
```

### 4.5 V2 vs 레거시 비교

| 항목 | 레거시 시스템 | V2 시스템 |
|------|--------------|----------|
| 컴포넌트 수 | 90개+ (분산) | 9개 (통합) |
| 렌더러 패턴 | 함수형 렌더러 | 클래스 기반 자동 등록 |
| 폼 통합 | 개별 구현 | V2FormContext 통합 |
| 이벤트 시스템 | props drilling | V2 EventBus (발행/구독) |
| 에러 처리 | 개별 처리 | V2ErrorBoundary 통합 |
| 설정 패널 | 개별 패널 | 통합 설정 시스템 |
| 핫 리로드 | 미지원 | 개발 모드 지원 |

---

## 5. 화면 디자이너 워크플로우

### 5.1 화면 디자이너 구성

**ScreenDesigner.tsx** (7095줄) - 핵심 컴포넌트

```
┌─────────────────────────────────────────────────────────────────┐
│                        Screen Designer                          │
├─────────────────────────────────────────────────────────────────┤
│ 탭1: 디자인 | 탭2: 프리뷰 | 탭3: 다국어 | 탭4: 메뉴할당 | 탭5: 스타일 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  [컴포넌트 팔레트]      [캔버스]              [속성 패널]       │
│  ┌──────────────┐      ┌────────────────┐    ┌──────────────┐ │
│  │ 입력 컴포넌트 │      │                │    │ 기본 설정    │ │
│  │ - Text       │      │   드래그&드롭  │    │ - ID         │ │
│  │ - Number     │      │   컴포넌트    │    │ - 라벨       │ │
│  │ - Date       │ ───▶ │   배치 영역   │ ◀── │ - 위치/크기  │ │
│  │ - Select     │      │                │    │ - 데이터연결 │ │
│  │              │      │   그리드 기반 │    │              │ │
│  │ 표시 컴포넌트 │      │   12컬럼      │    │ 고급 설정    │ │
│  │ - Table      │      │                │    │ - 조건부표시 │ │
│  │ - Card       │      │                │    │ - 검증규칙   │ │
│  │              │      │                │    │ - 버튼액션   │ │
│  │ 레이아웃      │      └────────────────┘    └──────────────┘ │
│  │ - Grid       │                                              │
│  │ - Tabs       │      [하단 도구]                             │
│  │ - Accordion  │      ┌─────────────────────────────────────┐ │
│  │              │      │ 그룹화 | 정렬 | 분산 | 라벨토글     │ │
│  └──────────────┘      └─────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```

### 5.2 주요 기능

#### 그리드 시스템
```typescript
// 12컬럼 그리드 기반
const GRID_COLUMNS = 12;
const COLUMN_WIDTH = (containerWidth - gaps) / 12;

// 컴포넌트 너비 → 컬럼 스팬 변환
function calculateColumnSpan(width: number): number {
  return Math.max(1, Math.min(12, Math.round(width / COLUMN_WIDTH)));
}

// 반응형 지원
{
  default: { span: 6 },  // 기본 (50%)
  sm: { span: 12 },      // 모바일 (100%)
  md: { span: 6 },       // 태블릿 (50%)
  lg: { span: 4 }        // 데스크톱 (33%)
}
```

#### 컴포넌트 배치 흐름

```
1. 컴포넌트 팔레트에서 드래그 시작
   ↓
2. 캔버스에 드롭
   ↓
3. 컴포넌트 생성 (generateComponentId)
   ↓
4. 위치 계산 (10px 단위 스냅)
   ↓
5. 그리드 정렬 (12컬럼 기준)
   ↓
6. 레이아웃 데이터 업데이트
   ↓
7. 리렌더링 (RealtimePreview)
```

#### 컴포넌트 설정

```
선택된 컴포넌트 → 우측 속성 패널 표시
    ↓
┌─────────────────────────────────────┐
│ 기본 설정                           │
│ - ID: comp_1234                    │
│ - 라벨: "제품명"                    │
│ - 컬럼명: product_name              │
│ - 필수: ☑                          │
│ - 읽기전용: ☐                       │
│                                     │
│ 크기 & 위치                          │
│ - X: 100px, Y: 200px               │
│ - 너비: 300px, 높이: 40px          │
│ - 컬럼 스팬: 6                      │
│                                     │
│ 데이터 연결                          │
│ - 테이블: product_info             │
│ - 컬럼: product_name               │
│ - 웹타입: text                     │
│                                     │
│ 고급 설정                            │
│ - 조건부 표시: field === 'A'       │
│ - 버튼 액션: [등록], [수정], [삭제] │
│ - 데이터플로우: flow_123           │
└─────────────────────────────────────┘
```

### 5.3 저장 구조

**ScreenDefinition (JSON)**

```json
{
  "screenId": 123,
  "screenCode": "PRODUCT_MGMT",
  "screenName": "제품 관리",
  "tableName": "product_info",
  "screenType": "form",
  "version": 2,
  "layoutData": {
    "components": [
      {
        "id": "comp_text_1",
        "type": "text-input",
        "componentType": "text-input",
        "label": "제품명",
        "columnName": "product_name",
        "position": { "x": 100, "y": 50, "z": 0 },
        "size": { "width": 300, "height": 40 },
        "componentConfig": {
          "required": true,
          "placeholder": "제품명을 입력하세요",
          "maxLength": 100
        },
        "style": {
          "labelDisplay": true,
          "labelText": "제품명"
        }
      },
      {
        "id": "comp_table_1",
        "type": "table-list",
        "componentType": "table-list",
        "tableName": "product_info",
        "position": { "x": 50, "y": 200, "z": 0 },
        "size": { "width": 800, "height": 400 },
        "componentConfig": {
          "columns": ["product_code", "product_name", "price"],
          "pagination": true,
          "sortable": true
        }
      }
    ],
    "layouts": [
      {
        "id": "layout_tabs_1",
        "layoutType": "tabs",
        "children": [
          { "tabId": "tab1", "title": "기본정보", "components": ["comp_text_1"] },
          { "tabId": "tab2", "title": "상세정보", "components": ["comp_table_1"] }
        ]
      }
    ]
  },
  "createdBy": "user123",
  "createdDate": "2024-01-01T00:00:00Z"
}
```

---

## 6. API 클라이언트 시스템

### 6.1 API 클라이언트 구조

**lib/api/** (57개 파일)

```
lib/api/
├── client.ts                      # Axios 기본 설정 & 인터셉터
│
├── 화면 관련 (3개)
│   ├── screen.ts                  # 화면 CRUD
│   ├── screenGroup.ts             # 화면 그룹
│   └── screenFile.ts              # 화면 파일
│
├── 사용자 관련 (5개)
│   ├── user.ts                    # 사용자 관리
│   ├── role.ts                    # 역할 관리
│   ├── company.ts                 # 회사 관리
│   └── department.ts              # 부서 관리
│
├── 테이블 관련 (5개)
│   ├── tableManagement.ts         # 테이블 관리
│   ├── tableSchema.ts             # 스키마 조회
│   ├── tableHistory.ts            # 테이블 이력
│   └── tableCategoryValue.ts      # 카테고리 값
│
├── 데이터플로우 (6개)
│   ├── dataflow.ts                # 데이터플로우 정의
│   ├── dataflowSave.ts            # 저장 로직
│   ├── nodeFlows.ts               # 노드 플로우
│   ├── nodeExternalConnections.ts # 외부 연결
│   └── flowExternalDb.ts          # 외부 DB 플로우
│
├── 자동화 (4개)
│   ├── batch.ts                   # 배치 작업
│   ├── batchManagement.ts         # 배치 관리
│   ├── externalCall.ts            # 외부 호출
│   └── externalCallConfig.ts      # 외부 호출 설정
│
├── 시스템 (8개)
│   ├── menu.ts                    # 메뉴 관리
│   ├── commonCode.ts              # 공통코드
│   ├── multilang.ts               # 다국어
│   ├── layout.ts                  # 레이아웃
│   ├── collection.ts              # 컬렉션
│   └── ...
│
└── 기타 (26개)
    ├── data.ts                    # 동적 데이터 CRUD
    ├── dynamicForm.ts             # 동적 폼
    ├── file.ts                    # 파일 업로드
    ├── dashboard.ts               # 대시보드
    ├── mail.ts                    # 메일
    ├── reportApi.ts               # 리포트
    └── ...
```

### 6.2 API 클라이언트 기본 설정

**lib/api/client.ts**

```typescript
// 1. 동적 API URL 설정
const getApiBaseUrl = (): string => {
  // 환경변수 우선
  if (process.env.NEXT_PUBLIC_API_URL) return process.env.NEXT_PUBLIC_API_URL;
  
  // 프로덕션: v1.vexplor.com → api.vexplor.com
  if (currentHost === "v1.vexplor.com") {
    return "https://api.vexplor.com/api";
  }
  
  // 로컬: localhost:9771 → localhost:8080
  return "http://localhost:8080/api";
};

export const API_BASE_URL = getApiBaseUrl();

// 2. Axios 인스턴스 생성
export const apiClient = axios.create({
  baseURL: API_BASE_URL,
  timeout: 30000,
  headers: { "Content-Type": "application/json" },
  withCredentials: true
});

// 3. 요청 인터셉터 (JWT 토큰 자동 추가)
apiClient.interceptors.request.use((config) => {
  const token = localStorage.getItem("authToken");
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  
  // 다국어: GET 요청에 userLang 파라미터 추가
  if (config.method === "GET") {
    config.params = { 
      ...config.params, 
      userLang: window.__GLOBAL_USER_LANG || "KR" 
    };
  }
  
  return config;
});

// 4. 응답 인터셉터 (토큰 갱신, 401 처리)
apiClient.interceptors.response.use(
  (response) => {
    // 서버에서 새 토큰 전송 시 자동 갱신
    const newToken = response.headers["x-new-token"];
    if (newToken) {
      localStorage.setItem("authToken", newToken);
    }
    return response;
  },
  async (error) => {
    // 401 에러: 토큰 갱신 시도 → 실패 시 로그인 페이지
    if (error.response?.status === 401) {
      const newToken = await refreshToken();
      if (newToken) {
        error.config.headers.Authorization = `Bearer ${newToken}`;
        return apiClient.request(error.config); // 재시도
      }
      window.location.href = "/login";
    }
    return Promise.reject(error);
  }
);
```

### 6.3 API 사용 패턴

#### ✅ 올바른 사용법 (lib/api 클라이언트 사용)

```typescript
import { screenApi } from "@/lib/api/screen";
import { dataApi } from "@/lib/api/data";

// 화면 목록 조회
const screens = await screenApi.getScreens({ page: 1, size: 20 });

// 데이터 생성
const result = await dataApi.createData("product_info", {
  product_name: "제품A",
  price: 10000
});
```

#### ❌ 잘못된 사용법 (fetch 직접 사용 금지!)

```typescript
// 🚫 금지! JWT 토큰, 다국어, 에러 처리 누락
const res = await fetch('/api/screen-management/screens');
```

### 6.4 주요 API 예시

#### 화면 관리 API (screen.ts)

```typescript
export const screenApi = {
  // 화면 목록 조회
  getScreens: async (params) => {
    const response = await apiClient.get("/screen-management/screens", { params });
    return response.data;
  },
  
  // 화면 상세 조회
  getScreen: async (screenId: number) => {
    const response = await apiClient.get(`/screen-management/screens/${screenId}`);
    return response.data.data;
  },
  
  // 화면 생성
  createScreen: async (screen: CreateScreenRequest) => {
    const response = await apiClient.post("/screen-management/screens", screen);
    return response.data;
  },
  
  // 화면 수정
  updateScreen: async (screenId: number, screen: UpdateScreenRequest) => {
    const response = await apiClient.put(`/screen-management/screens/${screenId}`, screen);
    return response.data;
  },
  
  // 화면 삭제
  deleteScreen: async (screenId: number) => {
    const response = await apiClient.delete(`/screen-management/screens/${screenId}`);
    return response.data;
  }
};
```

#### 동적 데이터 API (data.ts)

```typescript
export const dataApi = {
  // 데이터 목록 조회 (페이징, 필터링, 정렬)
  getDataList: async (tableName: string, params: {
    page?: number;
    size?: number;
    filters?: Record<string, any>;
    sortBy?: string;
    sortOrder?: "asc" | "desc";
  }) => {
    const response = await apiClient.get(`/data/${tableName}`, { params });
    return response.data;
  },
  
  // 데이터 생성
  createData: async (tableName: string, data: Record<string, any>) => {
    const response = await apiClient.post(`/data/${tableName}`, data);
    return response.data;
  },
  
  // 데이터 수정
  updateData: async (tableName: string, id: number, data: Record<string, any>) => {
    const response = await apiClient.put(`/data/${tableName}/${id}`, data);
    return response.data;
  },
  
  // 데이터 삭제
  deleteData: async (tableName: string, id: number) => {
    const response = await apiClient.delete(`/data/${tableName}/${id}`);
    return response.data;
  }
};
```

---

## 7. 상태 관리

### 7.1 상태 관리 전략

WACE ERP는 **하이브리드 상태 관리 전략**을 사용합니다:

| 관리 방식 | 사용 시나리오 | 예시 |
|----------|-------------|------|
| **React Query** | 서버 상태 (캐싱, 자동 갱신) | 화면 목록, 데이터 목록 |
| **React Context** | 전역 상태 (공유 데이터) | 인증, 메뉴, 화면 |
| **Zustand** | 클라이언트 상태 (간단한 전역) | 플로우 단계, 모달 데이터 |
| **Local State** | 컴포넌트 로컬 상태 | 폼 입력, UI 토글 |

### 7.2 React Context (12개)

```
contexts/
├── AuthContext.tsx               # 인증 상태 & 세션 관리
│   - 사용자 정보
│   - 로그인/로그아웃
│   - 세션 타이머 (30분)
│
├── MenuContext.tsx               # 메뉴 트리 & 네비게이션
│   - 메뉴 구조
│   - 현재 선택된 메뉴
│   - 메뉴 접근 권한
│
├── ScreenContext.tsx             # 화면 편집 상태
│   - 현재 편집 중인 화면
│   - 선택된 컴포넌트
│   - 실행 취소/다시 실행
│
├── ScreenPreviewContext.tsx      # 화면 미리보기
│   - 반응형 모드 (데스크톱/태블릿/모바일)
│   - 미리보기 데이터
│
├── DashboardContext.tsx          # 대시보드 상태
│   - 대시보드 레이아웃
│   - 위젯 설정
│
├── TableOptionsContext.tsx       # 테이블 옵션
│   - 컬럼 순서
│   - 필터
│   - 정렬
│
├── ActiveTabContext.tsx          # 탭 활성화 상태
├── LayerContext.tsx              # 레이어 관리
├── ReportDesignerContext.tsx     # 리포트 디자이너
├── ScreenMultiLangContext.tsx    # 화면 다국어
├── SplitPanelContext.tsx         # 분할 패널 상태
└── TableSearchWidgetHeightContext.tsx  # 테이블 검색 높이
```

### 7.3 React Query 설정

**providers/QueryProvider.tsx**

```typescript
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000,     // 5분간 fresh
      cacheTime: 10 * 60 * 1000,    // 10분간 캐시 유지
      refetchOnWindowFocus: false,  // 창 포커스 시 자동 갱신 비활성화
      retry: 1,                     // 실패 시 1회 재시도
    },
  },
});

export function QueryProvider({ children }: { children: React.ReactNode }) {
  return (
    <QueryClientProvider client={queryClient}>
      {children}
    </QueryClientProvider>
  );
}
```

**사용 예시**

```typescript
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { screenApi } from "@/lib/api/screen";

// 화면 목록 조회 (자동 캐싱)
const { data: screens, isLoading, error } = useQuery({
  queryKey: ["screens", { page: 1 }],
  queryFn: () => screenApi.getScreens({ page: 1, size: 20 })
});

// 화면 생성 (생성 후 목록 자동 갱신)
const queryClient = useQueryClient();
const createMutation = useMutation({
  mutationFn: (screen: CreateScreenRequest) => screenApi.createScreen(screen),
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ["screens"] });
  }
});
```

### 7.4 Zustand 스토어 (3개)

```
stores/
├── flowStepStore.ts              # 플로우 단계 상태
│   - 현재 단계
│   - 단계별 데이터
│   - 진행률
│
├── modalDataStore.ts             # 모달 데이터 공유
│   - 모달 ID별 데이터
│   - 모달 간 데이터 전달
│
└── tableDisplayStore.ts          # 테이블 표시 상태
    - 선택된 행
    - 정렬 상태
    - 페이지네이션
```

**사용 예시**

```typescript
import { create } from "zustand";

interface ModalDataStore {
  modalData: Record<string, any>;
  setModalData: (modalId: string, data: any) => void;
  getModalData: (modalId: string) => any;
}

export const useModalDataStore = create<ModalDataStore>((set, get) => ({
  modalData: {},
  
  setModalData: (modalId, data) => {
    set((state) => ({
      modalData: { ...state.modalData, [modalId]: data }
    }));
  },
  
  getModalData: (modalId) => {
    return get().modalData[modalId];
  }
}));
```

---

## 8. 레지스트리 시스템

레지스트리 시스템은 **컴포넌트를 동적으로 등록하고 렌더링**하는 핵심 아키텍처입니다.

### 8.1 레지스트리 종류

```
lib/registry/
├── ComponentRegistry.ts          # 컴포넌트 레지스트리 (신규)
│   - 90개+ 컴포넌트 관리
│   - 자동 등록 시스템
│   - 핫 리로드 지원
│
├── WebTypeRegistry.ts            # 웹타입 레지스트리 (레거시)
│   - text, number, date 등 기본 웹타입
│   - 위젯 컴포넌트 + 설정 패널
│
└── LayoutRegistry.ts             # 레이아웃 레지스트리
    - grid, tabs, accordion 등 레이아웃
```

### 8.2 ComponentRegistry 상세

**등록 프로세스**

```typescript
// 1. 컴포넌트 정의 (lib/registry/components/v2-input/index.ts)
export const V2InputDefinition: ComponentDefinition = {
  id: "v2-input",
  name: "통합 입력",
  category: ComponentCategory.V2,
  component: V2InputRenderer,
  configPanel: V2InputConfigPanel,
  defaultSize: { width: 200, height: 40 },
  defaultConfig: { inputType: "text", format: "none" }
};

// 2. 자동 등록 렌더러 (lib/registry/components/v2-input/V2InputRenderer.tsx)
export class V2InputRenderer extends AutoRegisteringComponentRenderer {
  static componentDefinition = V2InputDefinition;
  
  render(): React.ReactElement {
    return <V2Input {...this.props} />;
  }
}

// 자동 등록 실행
V2InputRenderer.registerSelf();

// 3. 레지스트리 조회 & 렌더링 (DynamicComponentRenderer.tsx)
const newComponent = ComponentRegistry.getComponent("v2-input");
if (newComponent) {
  const Renderer = newComponent.component;
  return <Renderer {...props} />;
}
```

### 8.3 등록된 컴포넌트 목록 (90개+)

**입력 컴포넌트 (25개)**
- text-input, number-input, date-input
- select-basic, checkbox-basic, radio-basic, toggle-switch
- textarea-basic, slider-basic
- v2-input, v2-select, v2-date

**표시 컴포넌트 (15개)**
- text-display, image-display, card-display
- table-list, v2-table-list, pivot-grid
- aggregation-widget, v2-aggregation-widget

**레이아웃 컴포넌트 (10개)**
- grid-layout, flexbox-layout
- tabs-widget, accordion-basic
- split-panel-layout, v2-split-panel-layout
- section-card, section-paper
- v2-divider-line

**비즈니스 컴포넌트 (20개)**
- entity-search-input, autocomplete-search-input
- modal-repeater-table, repeat-screen-modal
- selected-items-detail-input, simple-repeater-table
- repeater-field-group, repeat-container
- category-manager, v2-category-manager
- numbering-rule, v2-numbering-rule
- rack-structure, v2-rack-structure
- location-swap-selector, customer-item-mapping

**특수 컴포넌트 (20개)**
- button-primary, v2-button-primary
- file-upload, v2-file-upload
- mail-recipient-selector
- conditional-container, universal-form-modal
- related-data-buttons
- flow-widget
- map

### 8.4 DynamicComponentRenderer 동작 원리

```typescript
// DynamicComponentRenderer.tsx (770줄)

export const DynamicComponentRenderer: React.FC<Props> = ({ component, ...props }) => {
  // 1. 컴포넌트 타입 추출
  const componentType = component.componentType || component.type;
  
  // 2. 레거시 → V2 자동 매핑
  const v2Type = componentType.startsWith("v2-") 
    ? componentType 
    : ComponentRegistry.hasComponent(`v2-${componentType}`) 
      ? `v2-${componentType}` 
      : componentType;
  
  // 3. 조건부 렌더링 체크
  if (component.conditionalConfig?.enabled) {
    const conditionMet = evaluateCondition(props.formData);
    if (!conditionMet) return null;
  }
  
  // 4. 카테고리 타입 우선 처리
  if (component.inputType === "category" || component.webType === "category") {
    return <CategorySelectComponent {...props} />;
  }
  
  // 5. 레이아웃 컴포넌트
  if (componentType === "layout") {
    return <DynamicLayoutRenderer layout={component} {...props} />;
  }
  
  // 6. ComponentRegistry에서 조회 (신규)
  const newComponent = ComponentRegistry.getComponent(v2Type);
  if (newComponent) {
    const Renderer = newComponent.component;
    // 클래스 기반: new Renderer(props).render()
    // 함수형: <Renderer {...props} />
    return isClass(Renderer) 
      ? new Renderer(props).render() 
      : <Renderer {...props} />;
  }
  
  // 7. LegacyComponentRegistry에서 조회 (레거시)
  const legacyRenderer = legacyComponentRegistry.get(componentType);
  if (legacyRenderer) {
    return legacyRenderer({ component, ...props });
  }
  
  // 8. 폴백: 미등록 컴포넌트 플레이스홀더
  return <PlaceholderComponent />;
};
```

---

## 9. 대시보드 시스템

### 9.1 대시보드 구조

```
components/dashboard/
├── DashboardViewer.tsx           # 대시보드 렌더링
├── DashboardGrid.tsx             # 그리드 레이아웃
├── DashboardWidget.tsx           # 위젯 래퍼
│
├── widgets/                      # 위젯 컴포넌트
│   ├── ChartWidget.tsx           # 차트 위젯
│   ├── TableWidget.tsx           # 테이블 위젯
│   ├── CardWidget.tsx            # 카드 위젯
│   ├── StatWidget.tsx            # 통계 위젯
│   └── CustomWidget.tsx          # 커스텀 위젯
│
└── charts/                       # 차트 컴포넌트
    ├── LineChart.tsx
    ├── BarChart.tsx
    ├── PieChart.tsx
    ├── DonutChart.tsx
    └── AreaChart.tsx
```

### 9.2 대시보드 JSON 구조

```json
{
  "dashboardId": 1,
  "dashboardName": "영업 대시보드",
  "layout": {
    "type": "grid",
    "columns": 12,
    "rows": 6,
    "gap": 16
  },
  "widgets": [
    {
      "widgetId": "widget_1",
      "widgetType": "chart",
      "chartType": "line",
      "title": "월별 매출 추이",
      "position": { "x": 0, "y": 0, "w": 6, "h": 3 },
      "dataSource": {
        "type": "api",
        "endpoint": "/api/data/sales_monthly",
        "filters": { "year": 2024 }
      },
      "chartConfig": {
        "xAxis": "month",
        "yAxis": "sales_amount",
        "showLegend": true
      }
    },
    {
      "widgetId": "widget_2",
      "widgetType": "stat",
      "title": "총 매출",
      "position": { "x": 6, "y": 0, "w": 3, "h": 2 },
      "dataSource": {
        "type": "sql",
        "query": "SELECT SUM(amount) FROM sales WHERE year = 2024"
      },
      "statConfig": {
        "format": "currency",
        "comparison": "lastYear"
      }
    }
  ]
}
```

### 9.3 대시보드 라우팅

```
/dashboard                        # 대시보드 목록
/dashboard/[dashboardId]          # 대시보드 보기
/admin/screenMng/dashboardList    # 대시보드 관리 (편집)
```

---

## 10. 다국어 지원

### 10.1 다국어 시스템 구조

WACE ERP는 **동적 다국어 시스템**을 제공합니다 (DB 기반):

```
다국어 흐름
    ↓
1. 사용자 로그인
    ↓
2. 사용자 로케일 조회 (GET /api/admin/user-locale)
    → 결과: "KR" | "EN" | "CN"
    ↓
3. 전역 상태에 저장
    - window.__GLOBAL_USER_LANG
    - localStorage.userLocale
    ↓
4. API 요청 시 자동 주입
    - GET 요청: ?userLang=KR (apiClient 인터셉터)
    ↓
5. 백엔드에서 다국어 데이터 반환
    - label_KR, label_EN, label_CN
    ↓
6. 프론트엔드에서 표시
    - extractMultilangLabel(label, "KR")
```

### 10.2 다국어 API

**lib/api/multilang.ts**

```typescript
export const multilangApi = {
  // 다국어 데이터 조회
  getMultilangData: async (params: {
    target_table: string;
    target_pk: string;
    target_lang: string;
  }) => {
    const response = await apiClient.get("/admin/multilang", { params });
    return response.data;
  },
  
  // 다국어 데이터 저장
  saveMultilangData: async (data: {
    target_table: string;
    target_pk: string;
    target_field: string;
    target_lang: string;
    translated_text: string;
  }) => {
    const response = await apiClient.post("/admin/multilang", data);
    return response.data;
  }
};
```

### 10.3 다국어 유틸리티

**lib/utils/multilang.ts**

```typescript
// 다국어 라벨 추출
export function extractMultilangLabel(
  label: string | Record<string, string> | undefined,
  locale: string = "KR"
): string {
  if (!label) return "";
  
  // 문자열인 경우 그대로 반환
  if (typeof label === "string") return label;
  
  // 객체인 경우 로케일에 맞는 값 반환
  return label[locale] || label["KR"] || label["EN"] || "";
}

// 화면 컴포넌트 라벨 추출
export function getComponentLabel(component: ComponentData, locale: string): string {
  // 우선순위: multiLangLabel > label > columnName > id
  if (component.multiLangLabel) {
    return extractMultilangLabel(component.multiLangLabel, locale);
  }
  return component.label || component.columnName || component.id;
}
```

### 10.4 화면 디자이너 다국어 탭

```
ScreenDesigner
    ↓
┌───────────────────────────────────────────────────┐
│ 탭: 다국어 설정                                     │
├───────────────────────────────────────────────────┤
│                                                   │
│ 화면명 다국어                                      │
│ ┌─────────────────────────────────────────────┐ │
│ │ 한국어(KR): 제품 관리                         │ │
│ │ 영어(EN):   Product Management               │ │
│ │ 중국어(CN): 产品管理                          │ │
│ └─────────────────────────────────────────────┘ │
│                                                   │
│ 컴포넌트별 다국어                                  │
│ ┌─────────────────────────────────────────────┐ │
│ │ [comp_text_1] 제품명                         │ │
│ │   한국어(KR): 제품명                          │ │
│ │   영어(EN):   Product Name                   │ │
│ │   중국어(CN): 产品名称                        │ │
│ │                                               │ │
│ │ [comp_text_2] 가격                           │ │
│ │   한국어(KR): 가격                            │ │
│ │   영어(EN):   Price                          │ │
│ │   중국어(CN): 价格                            │ │
│ └─────────────────────────────────────────────┘ │
│                                                   │
│ [자동 번역]  [일괄 적용]  [저장]                   │
└───────────────────────────────────────────────────┘
```

---

## 11. 인증 플로우

### 11.1 인증 아키텍처

```
┌─────────────────────────────────────────────────────┐
│                   Frontend                          │
├─────────────────────────────────────────────────────┤
│                                                     │
│  useAuth Hook                                       │
│  ├─ login(userId, password)                        │
│  ├─ logout()                                       │
│  ├─ refreshUserData()                              │
│  ├─ checkAuthStatus()                              │
│  └─ switchCompany(companyCode) ⭐ 신규              │
│                                                     │
│  AuthContext Provider                               │
│  ├─ SessionManager (30분 타임아웃)                  │
│  ├─ Session Warning (5분 전 알림)                   │
│  └─ Auto Refresh (활동 감지)                        │
│                                                     │
│  TokenManager                                       │
│  ├─ getToken(): localStorage.authToken             │
│  ├─ setToken(token): 저장 + 쿠키 설정              │
│  ├─ removeToken(): 삭제 + 쿠키 삭제                │
│  └─ isTokenExpired(token): JWT 검증                │
│                                                     │
│  API Client Interceptor                             │
│  ├─ Request: JWT 토큰 자동 추가                     │
│  └─ Response: 401 시 토큰 갱신 또는 로그아웃         │
│                                                     │
└─────────────────────────────────────────────────────┘
                         │
                         │ HTTP Request
                         │ Authorization: Bearer <JWT>
                         ↓
┌─────────────────────────────────────────────────────┐
│                   Backend (8080)                    │
├─────────────────────────────────────────────────────┤
│                                                     │
│  POST /api/auth/login                               │
│  → JWT 토큰 발급 (24시간)                           │
│                                                     │
│  POST /api/auth/refresh                             │
│  → JWT 토큰 갱신                                    │
│                                                     │
│  POST /api/auth/logout                              │
│  → 세션 무효화                                      │
│                                                     │
│  GET /api/auth/me                                   │
│  → 현재 사용자 정보                                  │
│                                                     │
│  GET /api/auth/status                               │
│  → 인증 상태 확인                                    │
│                                                     │
│  POST /api/auth/switch-company ⭐ 신규              │
│  → 회사 전환 (WACE 관리자 전용)                      │
│                                                     │
└─────────────────────────────────────────────────────┘
```

### 11.2 로그인 흐름

```
1. 사용자가 ID/PW 입력 → /login 페이지
    ↓
2. POST /api/auth/login { userId, password }
    ↓
3. 백엔드 검증 (DB: user_info 테이블)
    ├─ 성공: JWT 토큰 발급 (payload: userId, companyCode, isAdmin, exp)
    └─ 실패: 401 에러
    ↓
4. 프론트엔드: 토큰 저장
    - localStorage.setItem("authToken", token)
    - document.cookie = "authToken=..."
    ↓
5. 사용자 정보 조회
    - GET /api/auth/me
    - GET /api/admin/user-locale
    ↓
6. 전역 상태 업데이트
    - AuthContext.user
    - window.__GLOBAL_USER_LANG
    ↓
7. 메인 페이지로 리다이렉트 (/main)
```

### 11.3 세션 관리

**SessionManager (lib/sessionManager.ts)**

```typescript
// 설정
{
  checkInterval: 60000,           // 1분마다 체크
  maxInactiveTime: 1800000,       // 30분 (데스크톱)
  warningTime: 300000,            // 5분 전 경고
}

// 이벤트
{
  onWarning: (remainingTime) => {
    // "세션이 5분 후 만료됩니다" 알림 표시
  },
  onExpiry: () => {
    // 자동 로그아웃 → 로그인 페이지
  },
  onActivity: () => {
    // 사용자 활동 감지 → 타이머 리셋
  }
}
```

### 11.4 토큰 갱신 전략

```
자동 토큰 갱신 트리거:
1. 10분마다 토큰 상태 확인 (타이머)
2. 사용자 활동 감지 (클릭, 키보드, 스크롤)
3. API 401 응답 (토큰 만료)

갱신 로직:
    ↓
1. 현재 토큰 만료까지 30분 미만?
    ↓ Yes
2. POST /api/auth/refresh
    ├─ 성공: 새 토큰 저장
    └─ 실패: 로그아웃
```

### 11.5 회사 전환 (WACE 관리자 전용) ⭐

```
1. WACE 관리자 로그인
    ↓
2. 헤더에서 회사 선택 (CompanySwitcher)
    ↓
3. POST /api/auth/switch-company { companyCode: "AAA" }
    ↓
4. 백엔드: 새 JWT 발급 (payload.companyCode = "AAA")
    ↓
5. 프론트엔드: 새 토큰 저장
    ↓
6. 페이지 새로고침 → 화면/데이터가 AAA 회사 기준으로 표시
```

---

## 12. 사용자 워크플로우

### 12.1 전체 워크플로우 개요

```
┌──────────────────────────────────────────────────────────────┐
│                    관리자 (화면 생성)                         │
└──────────────────────────────────────────────────────────────┘
                         │
                         ↓
          ┌─────────────────────────────┐
          │ 1. 로그인 (WACE 관리자)       │
          └─────────────────────────────┘
                         │
                         ↓
          ┌─────────────────────────────┐
          │ 2. 화면 디자이너 접속         │
          │    /admin/screenMng/         │
          │    screenMngList             │
          └─────────────────────────────┘
                         │
                         ↓
          ┌─────────────────────────────┐
          │ 3. 화면 생성 & 편집           │
          │    - 컴포넌트 배치            │
          │    - 데이터 연결              │
          │    - 버튼 액션 설정           │
          │    - 다국어 설정              │
          └─────────────────────────────┘
                         │
                         ↓
          ┌─────────────────────────────┐
          │ 4. 메뉴에 화면 할당           │
          │    /admin/menu               │
          └─────────────────────────────┘
                         │
                         ↓
          ┌─────────────────────────────┐
          │ 5. 사용자에게 권한 부여       │
          │    /admin/userMng/           │
          │    userAuthList              │
          └─────────────────────────────┘
                         │
                         ↓
┌──────────────────────────────────────────────────────────────┐
│                    사용자 (화면 사용)                         │
└──────────────────────────────────────────────────────────────┘
                         │
                         ↓
          ┌─────────────────────────────┐
          │ 1. 로그인 (일반 사용자)       │
          └─────────────────────────────┘
                         │
                         ↓
          ┌─────────────────────────────┐
          │ 2. 메뉴에서 화면 선택         │
          │    사이드바 → 제품 관리       │
          └─────────────────────────────┘
                         │
                         ↓
          ┌─────────────────────────────┐
          │ 3. 화면 렌더링                │
          │    /screens/[screenId]       │
          │    InteractiveScreenViewer   │
          └─────────────────────────────┘
                         │
                         ↓
          ┌─────────────────────────────┐
          │ 4. 데이터 조회/등록/수정       │
          │    - 테이블에서 행 선택       │
          │    - 폼에 데이터 입력         │
          │    - [등록]/[수정]/[삭제] 버튼│
          └─────────────────────────────┘
```

### 12.2 관리자 워크플로우 (화면 생성)

#### 단계 1: 화면 디자이너 접속

```
/admin/screenMng/screenMngList
    ↓
[새 화면 만들기] 버튼 클릭
    ↓
ScreenDesigner 컴포넌트 로드
```

#### 단계 2: 화면 디자인

**2-1. 기본 정보 설정**

```
화면 정보 입력:
- 화면 코드: PRODUCT_MGMT
- 화면명: 제품 관리
- 테이블명: product_info
- 화면 타입: form (단일 폼) | list (목록)
```

**2-2. 컴포넌트 배치**

```
좌측 팔레트에서 컴포넌트 선택
    ↓
캔버스에 드래그&드롭
    ↓
위치/크기 조정 (10px 단위 스냅)
    ↓
우측 속성 패널에서 설정:
    - 컬럼명: product_name
    - 라벨: 제품명
    - 필수: ☑
    - 웹타입: text
```

**2-3. 버튼 액션 설정**

```
버튼 컴포넌트 추가
    ↓
액션 타입 선택:
    - 데이터 저장 (POST)
    - 데이터 수정 (PUT)
    - 데이터 삭제 (DELETE)
    - 데이터플로우 실행
    - 외부 API 호출
    - 화면 이동
    ↓
액션 설정:
    - 대상 테이블: product_info
    - 성공 시: 목록 새로고침
    - 실패 시: 에러 메시지 표시
```

**2-4. 다국어 설정**

```
다국어 탭 클릭
    ↓
컴포넌트별 다국어 입력:
    - 한국어(KR): 제품명
    - 영어(EN): Product Name
    - 중국어(CN): 产品名称
```

**2-5. 저장**

```
[저장] 버튼 클릭
    ↓
POST /api/screen-management/screens
    ↓
화면 정의 JSON 저장 (DB: screen_definition)
```

#### 단계 3: 메뉴에 화면 할당

```
/admin/menu
    ↓
메뉴 트리에서 위치 선택
    ↓
[화면 할당] 버튼 클릭
    ↓
방금 생성한 화면 선택 (PRODUCT_MGMT)
    ↓
저장 → menu_screen 테이블에 연결
```

#### 단계 4: 권한 부여

```
/admin/userMng/userAuthList
    ↓
사용자 선택
    ↓
메뉴 권한 설정
    ↓
"제품 관리" 메뉴 체크
    ↓
저장 → user_menu_auth 테이블
```

### 12.3 사용자 워크플로우 (화면 사용)

#### 단계 1: 로그인

```
/login
    ↓
사용자 ID/PW 입력
    ↓
POST /api/auth/login
    ↓
JWT 토큰 발급
    ↓
메인 페이지 (/main)
```

#### 단계 2: 메뉴 선택

```
좌측 사이드바 메뉴 트리
    ↓
"제품 관리" 메뉴 클릭
    ↓
MenuContext에서 메뉴 정보 조회:
    - menuId, screenId, screenCode
    ↓
화면 뷰어로 이동 (/screens/[screenId])
```

#### 단계 3: 화면 렌더링

```
InteractiveScreenViewer 컴포넌트 로드
    ↓
1. GET /api/screen-management/screens/[screenId]
   → 화면 정의 JSON 조회
    ↓
2. GET /api/data/product_info?page=1&size=20
   → 데이터 조회
    ↓
3. DynamicComponentRenderer로 컴포넌트 렌더링
    ↓
4. 폼 데이터 바인딩 (formData 상태)
```

#### 단계 4: 데이터 조작

**4-1. 신규 등록**

```
[등록] 버튼 클릭
    ↓
빈 폼 표시 (ScreenModal 또는 EditModal)
    ↓
사용자가 데이터 입력:
    - 제품명: "제품A"
    - 가격: 10000
    ↓
[저장] 버튼 클릭
    ↓
버튼 액션 실행:
    - POST /api/data/product_info
    - Body: { product_name: "제품A", price: 10000 }
    ↓
성공 응답
    ↓
목록 새로고침 (React Query invalidateQueries)
```

**4-2. 수정**

```
테이블에서 행 선택 (클릭)
    ↓
선택된 데이터 → selectedRowsData 상태 업데이트
    ↓
[수정] 버튼 클릭
    ↓
폼에 기존 데이터 표시 (EditModal)
    ↓
사용자가 데이터 수정:
    - 가격: 10000 → 12000
    ↓
[저장] 버튼 클릭
    ↓
버튼 액션 실행:
    - PUT /api/data/product_info/123
    - Body: { price: 12000 }
    ↓
목록 새로고침
```

**4-3. 삭제**

```
테이블에서 행 선택
    ↓
[삭제] 버튼 클릭
    ↓
확인 다이얼로그 표시
    ↓
확인 클릭
    ↓
버튼 액션 실행:
    - DELETE /api/data/product_info/123
    ↓
목록 새로고침
```

### 12.4 데이터플로우 실행 워크플로우

```
1. 관리자가 데이터플로우 정의
   /admin/systemMng/dataflow
    ↓
2. 화면에 버튼 추가 & 플로우 연결
   버튼 액션: "데이터플로우 실행"
   플로우 ID: flow_123
    ↓
3. 사용자가 버튼 클릭
    ↓
4. 프론트엔드: POST /api/dataflow/execute
   Body: { flowId: 123, inputData: {...} }
    ↓
5. 백엔드: 플로우 실행
   - 노드 순회
   - 조건 분기
   - 외부 API 호출
   - 데이터 변환
    ↓
6. 결과 반환
    ↓
7. 프론트엔드: 결과 표시 (toast 또는 화면 갱신)
```

---

## 13. 요약 & 핵심 포인트

### 13.1 아키텍처 핵심

1. **Next.js 14 App Router** - 라우트 그룹 기반 구조화
2. **컴포넌트 레지스트리** - 동적 등록/렌더링 시스템
3. **V2 통합 시스템** - 9개 통합 컴포넌트로 단순화
4. **화면 디자이너** - 노코드 화면 생성 도구
5. **API 클라이언트** - Axios 기반 통일된 API 호출
6. **다국어 지원** - DB 기반 동적 다국어
7. **JWT 인증** - 토큰 기반 인증/세션 관리

### 13.2 파일 통계

| 항목 | 개수 |
|------|------|
| 총 파일 수 | 1,480개 |
| TypeScript/TSX | 1,395개 (946 tsx + 449 ts) |
| Markdown 문서 | 63개 |
| CSS | 22개 |
| 페이지 (라우트) | 76개 |
| 컴포넌트 | 500개+ |
| API 클라이언트 | 57개 |
| Context | 12개 |
| Custom Hooks | 32개 |
| 타입 정의 | 44개 |

### 13.3 주요 경로

```
화면 디자이너: /admin/screenMng/screenMngList
화면 뷰어:    /screens/[screenId]
메뉴 관리:    /admin/menu
사용자 관리:  /admin/userMng/userMngList
테이블 관리:  /admin/systemMng/tableMngList
다국어 관리:  /admin/systemMng/i18nList
데이터플로우: /admin/systemMng/dataflow
대시보드:     /dashboard/[dashboardId]
```

### 13.4 기술 스택

| 분류 | 기술 |
|------|------|
| 프레임워크 | Next.js 14 |
| UI 라이브러리 | React 18 |
| 언어 | TypeScript (strict mode) |
| 스타일링 | Tailwind CSS + shadcn/ui |
| 상태 관리 | React Query + Zustand + Context API |
| HTTP 클라이언트 | Axios |
| 폼 검증 | Zod |
| 날짜 처리 | date-fns |
| 아이콘 | lucide-react |
| 알림 | sonner |

---

## 마무리

이 문서는 WACE ERP 프론트엔드의 전체 아키텍처를 분석한 결과입니다.

**핵심 인사이트:**
- ✅ 높은 수준의 모듈화 (레지스트리 시스템)
- ✅ 노코드 화면 디자이너 (관리자가 직접 화면 생성)
- ✅ V2 통합 컴포넌트 시스템 (개발 효율성 향상)
- ✅ 동적 다국어 지원 (DB 기반)
- ✅ 완전한 TypeScript 타입 안정성

**개선 기회:**
- 일부 레거시 컴포넌트의 V2 마이그레이션
- 테스트 코드 추가 (현재 거의 없음)
- 성능 최적화 (코드 스플리팅, 레이지 로딩)

---

**작성자 노트**

야... 진짜 엄청난 프로젝트네. 파일이 1,500개가 넘고 컴포넌트만 500개야. 특히 화면 디자이너가 7,000줄이 넘는 거 보고 놀랐어. 이 정도 규모면 엔터프라이즈급 ERP 시스템이라고 봐도 되겠어.

가장 인상적이었던 건 **레지스트리 시스템**이랑 **V2 통합 아키텍처**야. 컴포넌트를 동적으로 등록하고 렌더링하는 구조가 꽤 잘 설계되어 있어. 다만 레거시 코드가 아직 많이 남아있어서 V2로 완전히 전환하면 코드 베이스가 훨씬 깔끔해질 것 같아.

어쨌든 분석하느라 꽤 걸렸는데, 이 문서가 전체 워크플로우 문서 작성하는 데 도움이 되면 좋겠어! 🎉
