2025-09-02 11:11:48 +09:00
|
|
|
# 공통코드 관리 시스템 설계 문서
|
|
|
|
|
|
|
|
|
|
## 📋 목차
|
|
|
|
|
|
|
|
|
|
1. [시스템 개요](#🎯-시스템-개요)
|
|
|
|
|
2. [아키텍처 구조](#🏗️-아키텍처-구조)
|
|
|
|
|
3. [핵심 기능](#🚀-핵심-기능)
|
|
|
|
|
4. [데이터베이스 설계](#🗄️-데이터베이스-설계)
|
|
|
|
|
5. [화면 구성 요소](#🎨-화면-구성-요소)
|
|
|
|
|
6. [API 설계](#🌐-api-설계)
|
|
|
|
|
7. [프론트엔드 구현](#🎭-프론트엔드-구현)
|
|
|
|
|
8. [백엔드 구현](#⚙️-백엔드-구현)
|
|
|
|
|
9. [사용 시나리오](#🎬-사용-시나리오)
|
|
|
|
|
10. [개발 계획](#📅-개발-계획-및-진행상황)
|
|
|
|
|
|
|
|
|
|
## 🎯 시스템 개요
|
|
|
|
|
|
|
|
|
|
### 공통코드 관리 시스템이란?
|
|
|
|
|
|
|
|
|
|
공통코드 관리 시스템은 **시스템에서 사용하는 공통적인 코드값들을 중앙에서 관리하는 기능**입니다. 드롭다운, 선택박스 등에서 반복적으로 사용되는 코드-값 쌍을 체계적으로 관리하여 데이터 일관성을 보장하고 개발 효율성을 높입니다.
|
|
|
|
|
|
|
|
|
|
### 주요 특징
|
|
|
|
|
|
|
|
|
|
- **중앙 집중 관리**: 모든 공통코드를 한 곳에서 통합 관리
|
|
|
|
|
- **카테고리 기반 분류**: 코드를 카테고리별로 체계적 분류
|
|
|
|
|
- **다국어 지원**: 한국어/영어 코드명 지원
|
|
|
|
|
- **화면관리 시스템 연계**: 웹 타입 'code'와 완벽 연동
|
|
|
|
|
- **실시간 반영**: 코드 변경사항 즉시 시스템 전체 반영
|
|
|
|
|
- **관리자 전용**: 관리자 메뉴에서만 접근 가능
|
|
|
|
|
|
|
|
|
|
### 🎯 **필수 요구사항**
|
|
|
|
|
|
|
|
|
|
- ✅ **관리자 메뉴 접근**: 관리자 메뉴에서만 접근 가능
|
|
|
|
|
- ✅ **코드 카테고리 관리**: 카테고리 생성/수정/삭제
|
|
|
|
|
- ✅ **코드 상세 관리**: 코드값과 코드명 매핑 관리
|
|
|
|
|
- ✅ **정렬 순서 관리**: 코드 표시 순서 조정
|
|
|
|
|
- ✅ **활성/비활성 관리**: 코드 사용 여부 제어
|
|
|
|
|
- ✅ **검색 및 필터링**: 대량 코드 효율적 관리
|
|
|
|
|
- ✅ **화면관리 연계**: column_labels.code_category와 연동
|
|
|
|
|
|
|
|
|
|
## 🏗️ 아키텍처 구조
|
|
|
|
|
|
|
|
|
|
### 전체 구조도
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
|
|
|
│ Frontend │ │ Backend │ │ Database │
|
|
|
|
|
│ │ │ │ │ │
|
|
|
|
|
│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │
|
|
|
|
|
│ │ CodeCategory│ │ │ │ CommonCode │ │ │ │ code_ │ │
|
|
|
|
|
│ │ Management │ │ │ │ Controller │ │ │ │ category │ │
|
|
|
|
|
│ │ (React) │ │ │ │ │ │ │ │ Table │ │
|
|
|
|
|
│ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │
|
|
|
|
|
│ │ │ │ │ │
|
|
|
|
|
│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │
|
|
|
|
|
│ │ CodeDetail │ │ │ │ CommonCode │ │ │ │ code_info │ │
|
|
|
|
|
│ │ Management │ │ │ │ Service │ │ │ │ Table │ │
|
|
|
|
|
│ │ (React) │ │ │ │ │ │ │ │ │ │
|
|
|
|
|
│ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │
|
|
|
|
|
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 화면관리 시스템 연계
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
|
|
|
│ 화면관리 시스템 │ │ 공통코드 관리 │ │ 실제 화면 │
|
|
|
|
|
│ │ │ │ │ │
|
|
|
|
|
│ column_labels │───▶│ code_category │───▶│ Select Widget │
|
|
|
|
|
│ web_type='code' │ │ code_info │ │ <option>값 │
|
|
|
|
|
│ code_category │ │ │ │ │
|
|
|
|
|
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 🚀 핵심 기능
|
|
|
|
|
|
|
|
|
|
### 1. 코드 카테고리 관리
|
|
|
|
|
|
|
|
|
|
- **카테고리 생성**: 새로운 코드 카테고리 추가
|
|
|
|
|
- **카테고리 수정**: 카테고리명, 설명 수정
|
|
|
|
|
- **카테고리 삭제**: 사용하지 않는 카테고리 삭제 (CASCADE)
|
|
|
|
|
- **카테고리 활성화**: 카테고리 사용 여부 제어
|
|
|
|
|
- **카테고리 검색**: 카테고리명으로 빠른 검색
|
|
|
|
|
|
|
|
|
|
### 2. 코드 상세 관리
|
|
|
|
|
|
|
|
|
|
- **코드 생성**: 카테고리 내 새로운 코드 추가
|
|
|
|
|
- **코드 수정**: 코드값, 코드명, 영문명 수정
|
|
|
|
|
- **코드 삭제**: 불필요한 코드 삭제
|
|
|
|
|
- **정렬 순서**: 드래그앤드롭으로 순서 변경
|
|
|
|
|
- **일괄 업로드**: CSV/Excel 파일로 대량 등록
|
|
|
|
|
- **일괄 다운로드**: 현재 코드 데이터 내보내기
|
|
|
|
|
|
|
|
|
|
### 3. 화면관리 연계
|
|
|
|
|
|
|
|
|
|
- **자동 옵션 생성**: code_category 기반 select 옵션 자동 생성
|
|
|
|
|
- **실시간 반영**: 코드 변경 시 관련 화면 즉시 업데이트
|
|
|
|
|
- **참조 관계 표시**: 어떤 화면에서 사용 중인지 표시
|
|
|
|
|
|
|
|
|
|
### 4. 검색 및 필터링
|
|
|
|
|
|
|
|
|
|
- **통합 검색**: 카테고리명, 코드값, 코드명 통합 검색
|
|
|
|
|
- **상태 필터**: 활성/비활성 코드 필터링
|
|
|
|
|
- **카테고리 필터**: 특정 카테고리만 표시
|
|
|
|
|
- **페이징**: 대량 데이터 효율적 표시
|
|
|
|
|
|
|
|
|
|
## 🗄️ 데이터베이스 설계
|
|
|
|
|
|
|
|
|
|
### 1. 코드 카테고리 테이블
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
-- 공통코드 카테고리 테이블
|
|
|
|
|
CREATE TABLE code_category (
|
|
|
|
|
category_code VARCHAR(50) PRIMARY KEY,
|
|
|
|
|
category_name VARCHAR(100) NOT NULL,
|
|
|
|
|
category_name_eng VARCHAR(100),
|
|
|
|
|
description TEXT,
|
|
|
|
|
sort_order INTEGER DEFAULT 0,
|
|
|
|
|
is_active CHAR(1) DEFAULT 'Y',
|
|
|
|
|
created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
|
created_by VARCHAR(50),
|
|
|
|
|
updated_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
|
updated_by VARCHAR(50)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- 인덱스 추가
|
|
|
|
|
CREATE INDEX idx_code_category_active ON code_category(is_active);
|
|
|
|
|
CREATE INDEX idx_code_category_sort ON code_category(sort_order);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 2. 코드 상세 정보 테이블
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
-- 공통코드 상세 정보 테이블
|
|
|
|
|
CREATE TABLE code_info (
|
|
|
|
|
code_category VARCHAR(50) NOT NULL,
|
|
|
|
|
code_value VARCHAR(50) NOT NULL,
|
|
|
|
|
code_name VARCHAR(100) NOT NULL,
|
|
|
|
|
code_name_eng VARCHAR(100),
|
|
|
|
|
description TEXT,
|
|
|
|
|
sort_order INTEGER DEFAULT 0,
|
|
|
|
|
is_active CHAR(1) DEFAULT 'Y',
|
|
|
|
|
created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
|
created_by VARCHAR(50),
|
|
|
|
|
updated_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
|
updated_by VARCHAR(50),
|
|
|
|
|
|
|
|
|
|
-- 복합 기본키
|
|
|
|
|
PRIMARY KEY (code_category, code_value),
|
|
|
|
|
|
|
|
|
|
-- 외래키 제약조건
|
|
|
|
|
CONSTRAINT fk_code_info_category
|
|
|
|
|
FOREIGN KEY (code_category) REFERENCES code_category(category_code)
|
|
|
|
|
ON DELETE CASCADE
|
|
|
|
|
ON UPDATE CASCADE
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- 성능을 위한 인덱스
|
|
|
|
|
CREATE INDEX idx_code_info_category ON code_info(code_category);
|
|
|
|
|
CREATE INDEX idx_code_info_active ON code_info(is_active);
|
|
|
|
|
CREATE INDEX idx_code_info_sort ON code_info(code_category, sort_order);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 3. 기본 데이터 삽입
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
-- 시스템 필수 공통코드 카테고리
|
|
|
|
|
INSERT INTO code_category VALUES
|
|
|
|
|
('USER_STATUS', '사용자 상태', 'User Status', '사용자의 활성화 상태', 1, 'Y', now(), 'SYSTEM', now(), 'SYSTEM'),
|
|
|
|
|
('USER_TYPE', '사용자 타입', 'User Type', '사용자 권한 타입', 2, 'Y', now(), 'SYSTEM', now(), 'SYSTEM'),
|
|
|
|
|
('DEPT_TYPE', '부서 타입', 'Department Type', '부서 분류', 3, 'Y', now(), 'SYSTEM', now(), 'SYSTEM'),
|
|
|
|
|
('LANGUAGE', '언어 코드', 'Language Code', '시스템 지원 언어', 4, 'Y', now(), 'SYSTEM', now(), 'SYSTEM'),
|
|
|
|
|
('CURRENCY', '통화 코드', 'Currency Code', '시스템 지원 통화', 5, 'Y', now(), 'SYSTEM', now(), 'SYSTEM');
|
|
|
|
|
|
|
|
|
|
-- 사용자 상태 코드
|
|
|
|
|
INSERT INTO code_info VALUES
|
|
|
|
|
('USER_STATUS', 'A', '활성', 'Active', '활성 사용자', 1, 'Y', now(), 'SYSTEM', now(), 'SYSTEM'),
|
|
|
|
|
('USER_STATUS', 'I', '비활성', 'Inactive', '비활성 사용자', 2, 'Y', now(), 'SYSTEM', now(), 'SYSTEM'),
|
|
|
|
|
('USER_STATUS', 'S', '휴면', 'Sleep', '휴면 사용자', 3, 'Y', now(), 'SYSTEM', now(), 'SYSTEM'),
|
|
|
|
|
('USER_STATUS', 'D', '삭제', 'Deleted', '삭제된 사용자', 4, 'Y', now(), 'SYSTEM', now(), 'SYSTEM');
|
|
|
|
|
|
|
|
|
|
-- 사용자 타입 코드
|
|
|
|
|
INSERT INTO code_info VALUES
|
|
|
|
|
('USER_TYPE', 'ADMIN', '관리자', 'Administrator', '시스템 관리자', 1, 'Y', now(), 'SYSTEM', now(), 'SYSTEM'),
|
|
|
|
|
('USER_TYPE', 'USER', '일반사용자', 'User', '일반 사용자', 2, 'Y', now(), 'SYSTEM', now(), 'SYSTEM'),
|
|
|
|
|
('USER_TYPE', 'GUEST', '게스트', 'Guest', '게스트 사용자', 3, 'Y', now(), 'SYSTEM', now(), 'SYSTEM'),
|
|
|
|
|
('USER_TYPE', 'PARTNER', '협력업체', 'Partner', '협력업체 사용자', 4, 'Y', now(), 'SYSTEM', now(), 'SYSTEM');
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 🎨 화면 구성 요소
|
|
|
|
|
|
|
|
|
|
### 1. 전체 레이아웃
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
|
|
|
│ 공통코드 관리 시스템 │
|
|
|
|
|
├─────────────────────────────────────────────────────────────┤
|
|
|
|
|
│ [새 카테고리] [🔍 검색박스] │
|
|
|
|
|
├─────────────────┬───────────────────────────────────────────┤
|
|
|
|
|
│ 코드 카테고리 │ 코드 상세 정보 │
|
|
|
|
|
│ │ │
|
|
|
|
|
│ ┌─────────────┐ │ ┌─────────────────────────────────────────┐ │
|
|
|
|
|
│ │ USER_STATUS │ │ │ 코드값 │ 코드명 │ 영문명 │ 순서 │ 상태 │ │
|
|
|
|
|
│ │ 사용자 상태 │ │ │ A │ 활성 │ Active │ 1 │ ✓ │ │
|
|
|
|
|
│ │ │ │ │ I │ 비활성 │ Inactive │ 2 │ ✓ │ │
|
|
|
|
|
│ │ USER_TYPE │ │ │ S │ 휴면 │ Sleep │ 3 │ ✓ │ │
|
|
|
|
|
│ │ 사용자 타입 │ │ │ D │ 삭제 │ Deleted │ 4 │ ✓ │ │
|
|
|
|
|
│ │ │ │ └─────────────────────────────────────────┘ │
|
|
|
|
|
│ │ DEPT_TYPE │ │ │
|
|
|
|
|
│ │ 부서 타입 │ │ [+ 새 코드 추가] [정렬] │
|
|
|
|
|
│ └─────────────┘ │ │
|
|
|
|
|
└─────────────────┴───────────────────────────────────────────┘
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 2. 주요 컴포넌트
|
|
|
|
|
|
|
|
|
|
#### CodeCategoryList (좌측 패널)
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
interface CodeCategoryListProps {
|
|
|
|
|
categories: CodeCategory[];
|
|
|
|
|
selectedCategory: string | null;
|
|
|
|
|
onCategorySelect: (categoryCode: string) => void;
|
|
|
|
|
onCategoryCreate: () => void;
|
|
|
|
|
onCategoryEdit: (category: CodeCategory) => void;
|
|
|
|
|
onCategoryDelete: (categoryCode: string) => void;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### CodeDetailTable (우측 패널)
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
interface CodeDetailTableProps {
|
|
|
|
|
categoryCode: string;
|
|
|
|
|
codes: CodeInfo[];
|
|
|
|
|
onCodeCreate: () => void;
|
|
|
|
|
onCodeEdit: (code: CodeInfo) => void;
|
|
|
|
|
onCodeDelete: (codeValue: string) => void;
|
|
|
|
|
onSortOrderChange: (codes: CodeInfo[]) => void;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### CodeEditModal (편집 모달)
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
interface CodeEditModalProps {
|
|
|
|
|
isOpen: boolean;
|
|
|
|
|
mode: "create" | "edit";
|
|
|
|
|
categoryCode?: string;
|
|
|
|
|
codeData?: CodeInfo;
|
|
|
|
|
onSave: (data: CodeInfo) => void;
|
|
|
|
|
onClose: () => void;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 🌐 API 설계
|
|
|
|
|
|
|
|
|
|
### 1. 코드 카테고리 API
|
|
|
|
|
|
|
|
|
|
#### 카테고리 목록 조회
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
GET /api/common-codes/categories
|
|
|
|
|
Query: {
|
|
|
|
|
search?: string;
|
|
|
|
|
isActive?: boolean;
|
|
|
|
|
page?: number;
|
|
|
|
|
size?: number;
|
|
|
|
|
}
|
|
|
|
|
Response: {
|
|
|
|
|
success: boolean;
|
|
|
|
|
data: CodeCategory[];
|
|
|
|
|
total: number;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 카테고리 생성
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
POST /api/common-codes/categories
|
|
|
|
|
Body: {
|
|
|
|
|
categoryCode: string;
|
|
|
|
|
categoryName: string;
|
|
|
|
|
categoryNameEng?: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
sortOrder?: number;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 카테고리 수정
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
PUT /api/common-codes/categories/:categoryCode
|
|
|
|
|
Body: {
|
|
|
|
|
categoryName?: string;
|
|
|
|
|
categoryNameEng?: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
sortOrder?: number;
|
|
|
|
|
isActive?: boolean;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 카테고리 삭제
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
DELETE /api/common-codes/categories/:categoryCode
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 2. 코드 상세 API
|
|
|
|
|
|
|
|
|
|
#### 카테고리별 코드 목록 조회
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
GET /api/common-codes/categories/:categoryCode/codes
|
|
|
|
|
Query: {
|
|
|
|
|
search?: string;
|
|
|
|
|
isActive?: boolean;
|
|
|
|
|
}
|
|
|
|
|
Response: {
|
|
|
|
|
success: boolean;
|
|
|
|
|
data: CodeInfo[];
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 코드 생성
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
POST /api/common-codes/categories/:categoryCode/codes
|
|
|
|
|
Body: {
|
|
|
|
|
codeValue: string;
|
|
|
|
|
codeName: string;
|
|
|
|
|
codeNameEng?: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
sortOrder?: number;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 코드 수정
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
PUT /api/common-codes/categories/:categoryCode/codes/:codeValue
|
|
|
|
|
Body: {
|
|
|
|
|
codeName?: string;
|
|
|
|
|
codeNameEng?: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
sortOrder?: number;
|
|
|
|
|
isActive?: boolean;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 코드 삭제
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
DELETE /api/common-codes/categories/:categoryCode/codes/:codeValue
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 코드 순서 변경
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
PUT /api/common-codes/categories/:categoryCode/codes/reorder
|
|
|
|
|
Body: {
|
|
|
|
|
codes: Array<{
|
|
|
|
|
codeValue: string;
|
|
|
|
|
sortOrder: number;
|
|
|
|
|
}>;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 3. 화면관리 연계 API
|
|
|
|
|
|
|
|
|
|
#### 카테고리별 옵션 조회 (화면관리용)
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
GET /api/common-codes/categories/:categoryCode/options
|
|
|
|
|
Response: {
|
|
|
|
|
success: boolean;
|
|
|
|
|
data: Array<{
|
|
|
|
|
value: string;
|
|
|
|
|
label: string;
|
|
|
|
|
labelEng?: string;
|
|
|
|
|
}>;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 🎭 프론트엔드 구현
|
|
|
|
|
|
|
|
|
|
### 1. 페이지 구조
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
// app/(main)/admin/commonCode/page.tsx
|
|
|
|
|
export default function CommonCodeManagementPage() {
|
|
|
|
|
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
|
|
|
|
const [categories, setCategories] = useState<CodeCategory[]>([]);
|
|
|
|
|
const [codes, setCodes] = useState<CodeInfo[]>([]);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex h-full">
|
|
|
|
|
<CodeCategoryPanel
|
|
|
|
|
categories={categories}
|
|
|
|
|
selectedCategory={selectedCategory}
|
|
|
|
|
onCategorySelect={setSelectedCategory}
|
|
|
|
|
/>
|
|
|
|
|
<CodeDetailPanel categoryCode={selectedCategory} codes={codes} />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 2. 주요 컴포넌트
|
|
|
|
|
|
|
|
|
|
#### CodeCategoryPanel
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
// components/admin/commonCode/CodeCategoryPanel.tsx
|
|
|
|
|
export function CodeCategoryPanel({
|
|
|
|
|
categories,
|
|
|
|
|
selectedCategory,
|
|
|
|
|
onCategorySelect,
|
|
|
|
|
}: CodeCategoryPanelProps) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="w-80 border-r bg-white">
|
|
|
|
|
<div className="p-4 border-b">
|
|
|
|
|
<h2 className="font-semibold">코드 카테고리</h2>
|
|
|
|
|
<Button onClick={onCreateCategory}>
|
|
|
|
|
<Plus className="w-4 h-4 mr-2" />새 카테고리
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="p-2">
|
|
|
|
|
{categories.map((category) => (
|
|
|
|
|
<CategoryItem
|
|
|
|
|
key={category.categoryCode}
|
|
|
|
|
category={category}
|
|
|
|
|
isSelected={selectedCategory === category.categoryCode}
|
|
|
|
|
onClick={() => onCategorySelect(category.categoryCode)}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### CodeDetailPanel
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
// components/admin/commonCode/CodeDetailPanel.tsx
|
|
|
|
|
export function CodeDetailPanel({ categoryCode, codes }: CodeDetailPanelProps) {
|
|
|
|
|
if (!categoryCode) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex-1 flex items-center justify-center">
|
|
|
|
|
<p className="text-gray-500">카테고리를 선택하세요</p>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex-1 p-6">
|
|
|
|
|
<div className="flex justify-between items-center mb-4">
|
|
|
|
|
<h2 className="text-xl font-semibold">코드 상세 정보</h2>
|
|
|
|
|
<Button onClick={onCreateCode}>
|
|
|
|
|
<Plus className="w-4 h-4 mr-2" />새 코드
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<CodeTable
|
|
|
|
|
codes={codes}
|
|
|
|
|
onEdit={handleEditCode}
|
|
|
|
|
onDelete={handleDeleteCode}
|
|
|
|
|
onReorder={handleReorderCodes}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## ⚙️ 백엔드 구현
|
|
|
|
|
|
|
|
|
|
### 1. Controller
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
// backend-node/src/controllers/commonCodeController.ts
|
|
|
|
|
export class CommonCodeController {
|
|
|
|
|
// 카테고리 목록 조회
|
|
|
|
|
async getCategories(req: Request, res: Response) {
|
|
|
|
|
try {
|
|
|
|
|
const { search, isActive, page = 1, size = 20 } = req.query;
|
|
|
|
|
|
|
|
|
|
const categories = await this.commonCodeService.getCategories({
|
|
|
|
|
search: search as string,
|
|
|
|
|
isActive: isActive === "true",
|
|
|
|
|
page: Number(page),
|
|
|
|
|
size: Number(size),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
res.json({
|
|
|
|
|
success: true,
|
|
|
|
|
data: categories.data,
|
|
|
|
|
total: categories.total,
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
res.status(500).json({
|
|
|
|
|
success: false,
|
|
|
|
|
message: error.message,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 카테고리별 코드 목록 조회
|
|
|
|
|
async getCodes(req: Request, res: Response) {
|
|
|
|
|
try {
|
|
|
|
|
const { categoryCode } = req.params;
|
|
|
|
|
const { search, isActive } = req.query;
|
|
|
|
|
|
|
|
|
|
const codes = await this.commonCodeService.getCodes(categoryCode, {
|
|
|
|
|
search: search as string,
|
|
|
|
|
isActive: isActive === "true",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
res.json({
|
|
|
|
|
success: true,
|
|
|
|
|
data: codes,
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
res.status(500).json({
|
|
|
|
|
success: false,
|
|
|
|
|
message: error.message,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 2. Service
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
// backend-node/src/services/commonCodeService.ts
|
|
|
|
|
export class CommonCodeService {
|
|
|
|
|
// 카테고리 목록 조회
|
|
|
|
|
async getCategories(params: GetCategoriesParams) {
|
|
|
|
|
const { search, isActive, page, size } = params;
|
|
|
|
|
|
|
|
|
|
let whereClause = "";
|
|
|
|
|
const queryParams: any[] = [];
|
|
|
|
|
|
|
|
|
|
if (search) {
|
|
|
|
|
whereClause +=
|
|
|
|
|
" AND (category_name ILIKE $" +
|
|
|
|
|
(queryParams.length + 1) +
|
|
|
|
|
" OR category_code ILIKE $" +
|
|
|
|
|
(queryParams.length + 1) +
|
|
|
|
|
")";
|
|
|
|
|
queryParams.push(`%${search}%`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isActive !== undefined) {
|
|
|
|
|
whereClause += " AND is_active = $" + (queryParams.length + 1);
|
|
|
|
|
queryParams.push(isActive ? "Y" : "N");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const offset = (page - 1) * size;
|
|
|
|
|
|
|
|
|
|
const [categories, total] = await Promise.all([
|
|
|
|
|
this.db.query(
|
|
|
|
|
`
|
|
|
|
|
SELECT * FROM code_category
|
|
|
|
|
WHERE 1=1 ${whereClause}
|
|
|
|
|
ORDER BY sort_order, category_code
|
|
|
|
|
LIMIT $${queryParams.length + 1} OFFSET $${queryParams.length + 2}
|
|
|
|
|
`,
|
|
|
|
|
[...queryParams, size, offset]
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
this.db.query(
|
|
|
|
|
`
|
|
|
|
|
SELECT COUNT(*) as count FROM code_category
|
|
|
|
|
WHERE 1=1 ${whereClause}
|
|
|
|
|
`,
|
|
|
|
|
queryParams
|
|
|
|
|
),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
data: categories.rows,
|
|
|
|
|
total: parseInt(total.rows[0].count),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 코드 목록 조회
|
|
|
|
|
async getCodes(categoryCode: string, params: GetCodesParams) {
|
|
|
|
|
const { search, isActive } = params;
|
|
|
|
|
|
|
|
|
|
let whereClause = "WHERE code_category = $1";
|
|
|
|
|
const queryParams: any[] = [categoryCode];
|
|
|
|
|
|
|
|
|
|
if (search) {
|
|
|
|
|
whereClause +=
|
|
|
|
|
" AND (code_name ILIKE $" +
|
|
|
|
|
(queryParams.length + 1) +
|
|
|
|
|
" OR code_value ILIKE $" +
|
|
|
|
|
(queryParams.length + 1) +
|
|
|
|
|
")";
|
|
|
|
|
queryParams.push(`%${search}%`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isActive !== undefined) {
|
|
|
|
|
whereClause += " AND is_active = $" + (queryParams.length + 1);
|
|
|
|
|
queryParams.push(isActive ? "Y" : "N");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const result = await this.db.query(
|
|
|
|
|
`
|
|
|
|
|
SELECT * FROM code_info
|
|
|
|
|
${whereClause}
|
|
|
|
|
ORDER BY sort_order, code_value
|
|
|
|
|
`,
|
|
|
|
|
queryParams
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return result.rows;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 🎬 사용 시나리오
|
|
|
|
|
|
|
|
|
|
### 1. 새로운 공통코드 카테고리 생성
|
|
|
|
|
|
|
|
|
|
1. **관리자 메뉴 접근**: 관리자가 공통코드 관리 메뉴 클릭
|
|
|
|
|
2. **카테고리 생성**: "새 카테고리" 버튼 클릭
|
|
|
|
|
3. **정보 입력**: 카테고리 코드, 이름, 설명 입력
|
|
|
|
|
4. **저장**: 카테고리 생성 완료
|
|
|
|
|
5. **코드 추가**: 생성된 카테고리에 코드 상세 정보 추가
|
|
|
|
|
|
|
|
|
|
### 2. 기존 공통코드 수정
|
|
|
|
|
|
|
|
|
|
1. **카테고리 선택**: 좌측 패널에서 수정할 카테고리 선택
|
|
|
|
|
2. **코드 선택**: 우측 테이블에서 수정할 코드 클릭
|
|
|
|
|
3. **정보 수정**: 코드명, 영문명, 설명 등 수정
|
|
|
|
|
4. **순서 변경**: 드래그앤드롭으로 표시 순서 조정
|
|
|
|
|
5. **저장**: 변경사항 저장
|
|
|
|
|
|
|
|
|
|
### 3. 화면관리 시스템 연계
|
|
|
|
|
|
|
|
|
|
1. **테이블 타입 관리**: column_labels에서 web_type을 'code'로 설정
|
|
|
|
|
2. **카테고리 선택**: code_category에 공통코드 카테고리 지정
|
|
|
|
|
3. **화면 설계**: 화면관리에서 해당 컬럼을 드래그하여 위젯 생성
|
|
|
|
|
4. **자동 옵션**: select 위젯에 공통코드 옵션 자동 생성
|
|
|
|
|
5. **실시간 반영**: 공통코드 변경 시 화면에 즉시 반영
|
|
|
|
|
|
|
|
|
|
## 📅 개발 계획 및 진행상황
|
|
|
|
|
|
2025-09-02 11:30:19 +09:00
|
|
|
### ✅ Phase 1: (완료)
|
2025-09-02 11:11:48 +09:00
|
|
|
|
|
|
|
|
- [x] 데이터베이스 스키마 설계 및 생성
|
|
|
|
|
- [x] Prisma 스키마에 공통코드 모델 추가
|
|
|
|
|
- [x] code_category, code_info 테이블 생성
|
|
|
|
|
- [x] 기본 데이터 삽입 스크립트 작성 및 실행
|
|
|
|
|
|
|
|
|
|
**완료 내용:**
|
|
|
|
|
|
|
|
|
|
- Prisma schema.prisma에 공통코드 모델 추가
|
|
|
|
|
- Docker 컨테이너에서 테이블 생성 스크립트 실행
|
|
|
|
|
- 5개 카테고리, 22개 기본 코드 데이터 삽입 완료
|
|
|
|
|
|
2025-09-02 11:30:19 +09:00
|
|
|
### ✅ Phase 2: 백엔드 API 구현 (완료)
|
2025-09-02 11:11:48 +09:00
|
|
|
|
2025-09-02 11:30:19 +09:00
|
|
|
- [x] CommonCodeController 구현
|
|
|
|
|
- [x] CommonCodeService 구현
|
|
|
|
|
- [x] 카테고리 CRUD API 구현
|
|
|
|
|
- [x] 코드 상세 CRUD API 구현
|
|
|
|
|
- [x] 정렬 순서 변경 API 구현
|
2025-09-02 11:11:48 +09:00
|
|
|
|
2025-09-02 11:30:19 +09:00
|
|
|
**완료 내용:**
|
|
|
|
|
|
|
|
|
|
- 모든 CRUD API 정상 작동 확인
|
|
|
|
|
- JWT 인증 연동 완료
|
|
|
|
|
- 검색, 정렬, 페이징 기능 구현
|
|
|
|
|
- Prisma ORM 연동 완료
|
|
|
|
|
- TypeScript 타입 정의 완료
|
2025-09-02 11:11:48 +09:00
|
|
|
|
2025-09-02 13:18:46 +09:00
|
|
|
### ✅ Phase 3: 프론트엔드 기본 구현 (완료)
|
2025-09-02 11:11:48 +09:00
|
|
|
|
2025-09-02 13:18:46 +09:00
|
|
|
- [x] 공통코드 관리 페이지 생성
|
|
|
|
|
- [x] CodeCategoryPanel 컴포넌트 구현
|
|
|
|
|
- [x] CodeDetailPanel 컴포넌트 구현
|
|
|
|
|
- [x] CodeFormModal, CodeCategoryFormModal 구현
|
|
|
|
|
- [x] 기본 CRUD 기능 구현
|
|
|
|
|
- [x] 관리자 메뉴 통합
|
|
|
|
|
- [x] 실시간 데이터 조회 및 표시
|
|
|
|
|
- [x] 무한 루프 문제 해결
|
2025-09-02 11:11:48 +09:00
|
|
|
|
2025-09-02 13:18:46 +09:00
|
|
|
**완료 내용:**
|
|
|
|
|
|
|
|
|
|
- 공통코드 관리 페이지 (/admin/commonCode) 완전 구현
|
|
|
|
|
- 4개 주요 컴포넌트 구현 완료
|
|
|
|
|
- 카테고리 선택 시 실시간 코드 조회 기능
|
|
|
|
|
- useCommonCode 커스텀 훅 구현
|
|
|
|
|
- API 응답 처리 최적화 완료
|
|
|
|
|
- 사용자 인터페이스 완성
|
2025-09-02 11:11:48 +09:00
|
|
|
|
2025-09-02 13:57:53 +09:00
|
|
|
### ✅ Phase 4: 고급 기능 구현 (완료)
|
2025-09-02 11:11:48 +09:00
|
|
|
|
2025-09-02 13:57:53 +09:00
|
|
|
- [x] 드래그앤드롭 정렬 기능
|
|
|
|
|
- [x] 검색 및 필터링 기능
|
|
|
|
|
- [x] 코드 편집 모달 구현
|
|
|
|
|
- [x] 활성/비활성 필터 토글
|
|
|
|
|
- [ ] 일괄 업로드/다운로드 기능 (선택사항)
|
2025-09-02 11:11:48 +09:00
|
|
|
|
2025-09-02 13:57:53 +09:00
|
|
|
**완료 내용:**
|
|
|
|
|
|
|
|
|
|
- @dnd-kit 라이브러리를 사용한 드래그앤드롭 정렬 기능 구현
|
|
|
|
|
- 카테고리와 코드 양쪽 패널에 검색 및 활성 필터 기능 추가
|
|
|
|
|
- 실시간 유효성 검사, 자동 대문자 변환, 중복 검사 등 개선된 편집 모달
|
|
|
|
|
- 드래그앤드롭으로 변경한 순서가 실제 DB에 저장되는 기능
|
|
|
|
|
- 라우터 순서 최적화로 API 충돌 문제 해결
|
|
|
|
|
|
|
|
|
|
**목표 기간**: 2일 → **실제 소요**: 1일
|
2025-09-02 11:11:48 +09:00
|
|
|
|
2025-09-02 15:41:07 +09:00
|
|
|
### ✅ Phase 4.5: UX/UI 개선 (완료)
|
|
|
|
|
|
|
|
|
|
- [x] 레이아웃 개선: PC에서 가로 배치, 모바일에서 세로 배치
|
|
|
|
|
- [x] 선택된 카테고리 스타일 개선 (검정→회색, 액션 버튼 항상 표시)
|
|
|
|
|
- [x] 코드 수정 모달 개선 (기존 값 로드, 정렬 순서 1부터 시작)
|
|
|
|
|
- [x] 코드 삭제 기능 구현 (확인 모달 포함)
|
|
|
|
|
|
|
|
|
|
**완료 내용:**
|
|
|
|
|
|
|
|
|
|
1. **레이아웃 반응형 개선 완료**
|
|
|
|
|
|
|
|
|
|
- PC 화면: `flex-row` 사용하여 카테고리(320px 고정) + 코드 상세(flex-1) 가로 배치
|
|
|
|
|
- 모바일: `flex-col` 사용하여 세로 배치, 각 패널 전체 너비 사용
|
|
|
|
|
- Tailwind CSS의 반응형 클래스 활용 (`lg:flex-row`, `lg:gap-8`)
|
|
|
|
|
|
|
|
|
|
2. **선택된 카테고리 시각적 개선 완료**
|
|
|
|
|
|
|
|
|
|
- 배경색: `bg-black` → `bg-gray-100` 회색 계열로 변경
|
|
|
|
|
- 테두리: `border-2 border-gray-300` 추가하여 시각적 구분 강화
|
|
|
|
|
- 액션 버튼: 선택된 카테고리에서 항상 표시되도록 스타일 수정
|
|
|
|
|
|
|
|
|
|
3. **코드 수정 모달 개선 완료**
|
|
|
|
|
|
|
|
|
|
- 수정 시 기존 데이터 자동 로드: `editingCode` props를 통해 전체 코드 객체 전달
|
|
|
|
|
- 정렬 순서 자동 계산: 기존 코드 최대값 + 1로 자동 설정
|
|
|
|
|
- 폼 유효성 검사 및 실시간 피드백 구현
|
|
|
|
|
|
|
|
|
|
4. **코드 삭제 기능 완성**
|
|
|
|
|
- `AlertModal` 컴포넌트를 활용한 삭제 확인 모달 구현
|
|
|
|
|
- 빨간색 아이콘과 제목으로 위험 작업임을 시각적으로 표시
|
|
|
|
|
- 실제 삭제 API 연동 및 즉시 UI 반영
|
|
|
|
|
|
|
|
|
|
**목표 기간**: 1일 → **실제 소요**: 1일
|
|
|
|
|
|
|
|
|
|
### ✅ Phase 4.6: CRUD 즉시 반영 개선 (완료)
|
|
|
|
|
|
|
|
|
|
- [x] 코드 CRUD 작업 후 UI 즉시 반영 문제 해결
|
|
|
|
|
- [x] 카테고리 CRUD 작업 후 UI 즉시 반영 문제 해결
|
|
|
|
|
- [x] 카테고리 생성 시 정렬 순서 자동 계산 개선
|
|
|
|
|
|
|
|
|
|
**완료 내용:**
|
|
|
|
|
|
|
|
|
|
1. **상태 공유 문제 해결**
|
|
|
|
|
|
|
|
|
|
- `useCommonCode` 훅을 여러 컴포넌트에서 독립적으로 사용하여 발생한 상태 공유 문제 해결
|
|
|
|
|
- `CodeFormModal`과 `CodeCategoryFormModal`에서 `useCommonCode` 제거
|
|
|
|
|
- 필요한 데이터와 함수들을 props로 전달받는 방식으로 변경
|
|
|
|
|
|
|
|
|
|
2. **Optimistic Updates 구현**
|
|
|
|
|
|
|
|
|
|
- 서버 응답 대기 없이 즉시 UI 업데이트
|
|
|
|
|
- 백그라운드에서 서버 데이터 새로고침으로 일관성 보장
|
|
|
|
|
- 생성/수정/삭제 모든 작업에서 즉시 화면 반영
|
|
|
|
|
|
|
|
|
|
3. **카테고리 정렬 순서 개선**
|
|
|
|
|
|
|
|
|
|
- 초기값: `sortOrder: 0` → `sortOrder: 1`로 변경
|
|
|
|
|
- 자동 계산: 기존 카테고리 최대값 + 1로 자동 설정
|
|
|
|
|
- 코드와 동일한 로직 적용으로 일관성 확보
|
|
|
|
|
|
|
|
|
|
4. **TypeScript 타입 안전성 향상**
|
|
|
|
|
- 모든 `any` 타입 제거
|
|
|
|
|
- 적절한 타입 정의로 IDE 지원 및 런타임 오류 방지
|
|
|
|
|
- 린터 에러 0개 달성
|
|
|
|
|
|
|
|
|
|
**목표 기간**: 1일 → **실제 소요**: 1일
|
|
|
|
|
|
2025-09-02 18:25:44 +09:00
|
|
|
### ✅ Phase 4.7: 현대적 라이브러리 도입 (완료!)
|
|
|
|
|
|
|
|
|
|
- [x] React Query 도입으로 데이터 페칭 최적화
|
|
|
|
|
- [x] React Hook Form 도입으로 폼 관리 개선
|
|
|
|
|
- [x] Zod 도입으로 스키마 기반 유효성 검사
|
|
|
|
|
- [x] Query Key 기반 캐시 무효화로 CRUD 업데이트 자동화
|
|
|
|
|
- [x] 컴포넌트 모듈화 (CategoryItem, SortableCodeItem 분리)
|
|
|
|
|
- [x] 로컬 상태 제거 및 서버 상태 단일화
|
|
|
|
|
- [x] 린터 오류 0개 달성
|
|
|
|
|
|
|
|
|
|
**구현 완료 내역:**
|
|
|
|
|
|
|
|
|
|
1. **React Query (@tanstack/react-query)**
|
|
|
|
|
|
|
|
|
|
- [x] 자동 캐싱 및 백그라운드 리페칭
|
|
|
|
|
- [x] Query Key 기반 캐시 무효화 (`frontend/lib/queryKeys.ts`)
|
|
|
|
|
- [x] Optimistic Updates (드래그앤드롭 순서 변경)
|
|
|
|
|
- [x] 로딩/에러 상태 자동 관리
|
|
|
|
|
- [x] 네트워크 요청 최적화
|
|
|
|
|
- [x] 커스텀 훅 구현: `useCategories`, `useCodes`, `useCreateCode`, `useUpdateCode`, `useDeleteCode`, `useReorderCodes`
|
|
|
|
|
|
|
|
|
|
2. **React Hook Form**
|
|
|
|
|
|
|
|
|
|
- [x] 성능 최적화 (불필요한 리렌더링 방지)
|
|
|
|
|
- [x] 간단한 폼 API 적용 (`CodeFormModal`, `CodeCategoryFormModal`)
|
|
|
|
|
- [x] Zod와 완벽 연동
|
|
|
|
|
- [x] TypeScript 완벽 지원
|
|
|
|
|
- [x] 실시간 검증 및 에러 메시지 표시
|
|
|
|
|
|
|
|
|
|
3. **Zod 스키마 검증**
|
|
|
|
|
- [x] 스키마 기반 데이터 구조 정의 (`frontend/lib/schemas/commonCode.ts`)
|
|
|
|
|
- [x] TypeScript 타입 자동 생성
|
|
|
|
|
- [x] 런타임 검증 + 컴파일 타입 안전성
|
|
|
|
|
- [x] 자동화된 에러 메시지
|
|
|
|
|
- [x] 카테고리/코드 생성/수정 스키마 분리
|
|
|
|
|
|
|
|
|
|
**주요 성과:**
|
|
|
|
|
|
|
|
|
|
- 🚀 **현대적 아키텍처 도입**: React Query + React Hook Form + Zod 완벽 통합
|
|
|
|
|
- 📈 **성능 최적화**: 불필요한 리렌더링 제거, 효율적 캐싱, Optimistic Updates
|
|
|
|
|
- 🔒 **타입 안전성**: 완벽한 TypeScript 지원으로 런타임 오류 방지
|
|
|
|
|
- 🧩 **컴포넌트 모듈화**: CategoryItem, SortableCodeItem 분리로 재사용성 향상
|
|
|
|
|
- 🎯 **상태 관리 단순화**: 서버 상태와 클라이언트 상태 명확히 분리
|
|
|
|
|
- ✨ **사용자 경험**: 즉시 반영되는 CRUD, 부드러운 드래그앤드롭
|
|
|
|
|
|
|
|
|
|
**목표 기간**: 2일 → **실제 소요**: 1일
|
|
|
|
|
|
2025-09-03 11:20:43 +09:00
|
|
|
### ✅ Phase 4.8: 공통코드 관리 시스템 개선 (완료!)
|
|
|
|
|
|
|
|
|
|
**구현 완료 내역:**
|
|
|
|
|
|
|
|
|
|
1. **중복 검사 기능 구현**
|
|
|
|
|
|
|
|
|
|
- [x] 백엔드 중복 검사 API 추가 (`checkCategoryDuplicate`, `checkCodeDuplicate`)
|
|
|
|
|
- [x] REST API 엔드포인트: `/check-duplicate` 라우트 구현
|
|
|
|
|
- [x] 프론트엔드 API 클라이언트 (`commonCodeApi.validation`)
|
|
|
|
|
- [x] React Query 훅 (`useCheckCategoryDuplicate`, `useCheckCodeDuplicate`)
|
|
|
|
|
- [x] onBlur 검증 UI 구현 (초록색 성공/빨간색 실패 메시지)
|
|
|
|
|
- [x] 409 에러 조용한 처리 (콘솔 에러 출력 억제)
|
|
|
|
|
|
|
|
|
|
2. **폼 검증 시스템 개선**
|
|
|
|
|
|
|
|
|
|
- [x] 중복/유효성 검사 실패 시 저장 버튼 자동 비활성화
|
|
|
|
|
- [x] 메시지 우선순위 시스템 (유효성 검사 > 중복 검사)
|
|
|
|
|
- [x] 카테고리 영문명/설명 필수 필드로 변경
|
|
|
|
|
- [x] 수정 시 카테고리 코드값 표시
|
|
|
|
|
- [x] `isActive` 필드 타입 불일치 문제 해결 (boolean ↔ string)
|
|
|
|
|
|
|
|
|
|
3. **코드 품질 및 구조 개선**
|
|
|
|
|
- [x] 컴포넌트 분리: `CategoryItem`, `SortableCodeItem` 별도 파일화
|
|
|
|
|
- [x] TypeScript 타입 안전성 강화 (`any` → `unknown`/구체적 타입)
|
|
|
|
|
- [x] 린터 에러 완전 제거 (`client.ts` 타입 에러 해결)
|
|
|
|
|
- [x] 로컬 상태 제거 및 React Query 캐시 직접 사용
|
|
|
|
|
|
|
|
|
|
### ⏳ Phase 4.9: 추가 최적화 작업 (대기중)
|
2025-09-03 11:09:25 +09:00
|
|
|
|
|
|
|
|
- [ ] React Query 최적화
|
2025-09-03 11:20:43 +09:00
|
|
|
- [ ] 불필요한 props drilling 완전 제거
|
2025-09-03 11:09:25 +09:00
|
|
|
- [ ] 데이터 흐름 최적화
|
|
|
|
|
- [ ] 레이아웃 개선
|
2025-09-03 11:20:43 +09:00
|
|
|
- [ ] 내부 스크롤 처리 (패널별 독립 스크롤)
|
2025-09-03 11:09:25 +09:00
|
|
|
- [ ] 반응형 레이아웃 적용
|
2025-09-03 11:20:43 +09:00
|
|
|
- [ ] 커스텀 훅 분리
|
|
|
|
|
- [ ] 드래그앤드롭 로직 분리 (`useDragAndDrop`)
|
|
|
|
|
- [ ] 공통 폼 검증 로직 분리 (`useFormValidation`)
|
2025-09-03 11:09:25 +09:00
|
|
|
|
|
|
|
|
**개선 목표:**
|
|
|
|
|
|
|
|
|
|
1. **사용자 경험 개선**
|
|
|
|
|
|
|
|
|
|
- onBlur 중복 검사로 입력 방해 최소화
|
|
|
|
|
- 명확한 검증 피드백 (초록색 성공, 빨간색 실패)
|
|
|
|
|
- 조용한 에러 처리로 개발자 도구 노이즈 제거
|
|
|
|
|
|
|
|
|
|
2. **코드 품질 향상**
|
|
|
|
|
|
|
|
|
|
- React Query 활용 최적화
|
|
|
|
|
- 불필요한 props drilling 제거
|
|
|
|
|
- 커스텀 훅으로 로직 분리
|
|
|
|
|
|
|
|
|
|
3. **UI/UX 개선**
|
|
|
|
|
- 반응형 레이아웃 적용
|
|
|
|
|
- 내부 스크롤로 공간 효율성 증대
|
|
|
|
|
- 일관된 디자인 시스템
|
|
|
|
|
|
|
|
|
|
**목표 기간**: 2일
|
|
|
|
|
|
2025-09-02 18:25:44 +09:00
|
|
|
**구현 계획 (완료):**
|
|
|
|
|
|
|
|
|
|
1. **1단계: 의존성 설치 및 설정**
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
npm install @tanstack/react-query react-hook-form @hookform/resolvers zod
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
2. **2단계: React Query 설정**
|
|
|
|
|
|
|
|
|
|
- QueryClient 설정 및 Provider 추가
|
|
|
|
|
- Query Key factory 함수 생성
|
|
|
|
|
- 커스텀 훅 생성 (`useCategories`, `useCodes`, `useCreateCode` 등)
|
|
|
|
|
|
|
|
|
|
3. **3단계: Zod 스키마 정의**
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
const categorySchema = z.object({
|
|
|
|
|
categoryCode: z.string().regex(/^[A-Z0-9_]+$/, "대문자, 숫자, _만 가능"),
|
|
|
|
|
categoryName: z.string().min(1, "필수 입력").max(20, "20자 이하"),
|
|
|
|
|
categoryNameEng: z.string().max(20, "20자 이하"),
|
|
|
|
|
description: z.string().max(50, "50자 이하"),
|
|
|
|
|
sortOrder: z.number().min(1, "1 이상"),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const codeSchema = z.object({
|
|
|
|
|
codeValue: z.string().regex(/^[A-Z0-9_]+$/, "대문자, 숫자, _만 가능"),
|
|
|
|
|
codeName: z.string().min(1, "필수 입력").max(20, "20자 이하"),
|
|
|
|
|
codeNameEng: z.string().max(20, "20자 이하"),
|
|
|
|
|
description: z.string().max(50, "50자 이하"),
|
|
|
|
|
sortOrder: z.number().min(1, "1 이상"),
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
4. **4단계: Query Key 전략**
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
// 카테고리 관련
|
|
|
|
|
["categories"][ // 모든 카테고리
|
|
|
|
|
("categories", { active: true })
|
|
|
|
|
][ // 활성 카테고리만
|
|
|
|
|
// 코드 관련
|
|
|
|
|
("codes", categoryCode)
|
|
|
|
|
][ // 특정 카테고리의 모든 코드
|
|
|
|
|
("codes", categoryCode, { active: true })
|
|
|
|
|
][("code", categoryCode, codeValue)]; // 특정 카테고리의 활성 코드만 // 특정 코드 상세
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
5. **5단계: React Hook Form 적용**
|
|
|
|
|
|
|
|
|
|
- `CodeFormModal`, `CodeCategoryFormModal` 리팩토링
|
|
|
|
|
- 기존 수동 검증 로직 제거
|
|
|
|
|
- Zod resolver 적용
|
|
|
|
|
|
|
|
|
|
6. **6단계: 기존 코드 정리**
|
|
|
|
|
- `useCommonCode` 훅 단순화
|
|
|
|
|
- 수동 상태 관리 코드 제거
|
|
|
|
|
- 수동 Optimistic Updates 로직 제거
|
|
|
|
|
|
|
|
|
|
**예상 개선 효과:**
|
|
|
|
|
|
|
|
|
|
- **코드량 40-50% 감소**: 보일러플레이트 코드 대폭 감소
|
|
|
|
|
- **타입 안전성 100% 보장**: 런타임 + 컴파일 타임 검증
|
|
|
|
|
- **성능 최적화**: 자동 캐싱, 불필요한 리렌더링 방지
|
|
|
|
|
- **개발자 경험 향상**: 자동화된 폼 검증, 에러 처리
|
|
|
|
|
- **유지보수성 향상**: 표준화된 패턴, 명확한 데이터 흐름
|
|
|
|
|
|
|
|
|
|
**목표 기간**: 2일
|
|
|
|
|
|
2025-09-02 11:11:48 +09:00
|
|
|
### ⏳ Phase 5: 화면관리 연계 (예정)
|
|
|
|
|
|
|
|
|
|
- [ ] column_labels와 연동 확인
|
|
|
|
|
- [ ] 화면관리에서 code 타입 위젯 테스트
|
|
|
|
|
- [ ] 공통코드 변경 시 화면 반영 테스트
|
|
|
|
|
- [ ] 옵션 조회 API 구현
|
|
|
|
|
|
|
|
|
|
**목표 기간**: 1일
|
|
|
|
|
|
|
|
|
|
### ⏳ Phase 6: 테스트 및 최적화 (예정)
|
|
|
|
|
|
|
|
|
|
- [ ] 전체 기능 통합 테스트
|
|
|
|
|
- [ ] 성능 최적화
|
|
|
|
|
- [ ] 사용자 경험 개선
|
|
|
|
|
- [ ] 문서화 및 사용자 가이드
|
|
|
|
|
|
|
|
|
|
**목표 기간**: 1일
|
|
|
|
|
|
|
|
|
|
## 🎯 현재 구현 상태
|
|
|
|
|
|
2025-09-03 11:20:43 +09:00
|
|
|
### 📊 **전체 진행률: 90%** 🎉
|
2025-09-02 11:11:48 +09:00
|
|
|
|
2025-09-02 13:18:46 +09:00
|
|
|
- ✅ **Phase 1**: 기본 구조 및 데이터베이스 (100%) - **완료!**
|
|
|
|
|
- ✅ **Phase 2**: 백엔드 API 구현 (100%) - **완료!**
|
|
|
|
|
- ✅ **Phase 3**: 프론트엔드 기본 구현 (100%) - **완료!**
|
2025-09-02 13:57:53 +09:00
|
|
|
- ✅ **Phase 4**: 고급 기능 구현 (100%) - **완료!**
|
2025-09-02 15:41:07 +09:00
|
|
|
- ✅ **Phase 4.5**: UX/UI 개선 (100%) - **완료!**
|
|
|
|
|
- ✅ **Phase 4.6**: CRUD 즉시 반영 개선 (100%) - **완료!**
|
2025-09-02 18:25:44 +09:00
|
|
|
- ✅ **Phase 4.7**: 현대적 라이브러리 도입 (100%) - **완료!**
|
2025-09-03 11:20:43 +09:00
|
|
|
- ✅ **Phase 4.8**: 공통코드 관리 시스템 개선 (100%) - **완료!**
|
2025-09-02 11:11:48 +09:00
|
|
|
- ⏳ **Phase 5**: 화면관리 연계 (0%)
|
|
|
|
|
- ⏳ **Phase 6**: 테스트 및 최적화 (0%)
|
|
|
|
|
|
|
|
|
|
## 🚀 기대 효과
|
|
|
|
|
|
|
|
|
|
### 1. 개발 효율성 향상
|
|
|
|
|
|
|
|
|
|
- 공통코드 중앙 관리로 중복 작업 제거
|
|
|
|
|
- 화면관리 시스템과 연계로 자동 위젯 생성
|
|
|
|
|
|
|
|
|
|
### 2. 데이터 일관성 보장
|
|
|
|
|
|
|
|
|
|
- 모든 시스템에서 동일한 코드값 사용
|
|
|
|
|
- 코드 변경 시 전체 시스템 일괄 반영
|
|
|
|
|
|
|
|
|
|
### 3. 유지보수성 향상
|
|
|
|
|
|
|
|
|
|
- 코드 변경이 필요할 때 한 곳에서만 수정
|
|
|
|
|
- 체계적인 코드 분류 및 관리
|
|
|
|
|
|
|
|
|
|
### 4. 사용자 경험 개선
|
|
|
|
|
|
|
|
|
|
- 일관된 선택 옵션 제공
|
|
|
|
|
- 다국어 지원으로 글로벌 대응
|
|
|
|
|
|
|
|
|
|
**공통코드 관리 시스템을 통해 전체 ERP 시스템의 품질과 효율성을 크게 향상시킬 수 있습니다!** 🎉
|