746 lines
24 KiB
Markdown
746 lines
24 KiB
Markdown
|
|
# 공통코드 관리 시스템 설계 문서
|
||
|
|
|
||
|
|
## 📋 목차
|
||
|
|
|
||
|
|
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. **실시간 반영**: 공통코드 변경 시 화면에 즉시 반영
|
||
|
|
|
||
|
|
## 📅 개발 계획 및 진행상황
|
||
|
|
|
||
|
|
### ✅ Phase 1: 기본 구조 및 데이터베이스 (완료)
|
||
|
|
|
||
|
|
- [x] 데이터베이스 스키마 설계 및 생성
|
||
|
|
- [x] Prisma 스키마에 공통코드 모델 추가
|
||
|
|
- [x] code_category, code_info 테이블 생성
|
||
|
|
- [x] 기본 데이터 삽입 스크립트 작성 및 실행
|
||
|
|
|
||
|
|
**완료 내용:**
|
||
|
|
|
||
|
|
- Prisma schema.prisma에 공통코드 모델 추가
|
||
|
|
- Docker 컨테이너에서 테이블 생성 스크립트 실행
|
||
|
|
- 5개 카테고리, 22개 기본 코드 데이터 삽입 완료
|
||
|
|
|
||
|
|
### ⏳ Phase 2: 백엔드 API 구현 (예정)
|
||
|
|
|
||
|
|
- [ ] CommonCodeController 구현
|
||
|
|
- [ ] CommonCodeService 구현
|
||
|
|
- [ ] 카테고리 CRUD API 구현
|
||
|
|
- [ ] 코드 상세 CRUD API 구현
|
||
|
|
- [ ] 정렬 순서 변경 API 구현
|
||
|
|
|
||
|
|
**목표 기간**: 2일
|
||
|
|
|
||
|
|
### ⏳ Phase 3: 프론트엔드 기본 구현 (예정)
|
||
|
|
|
||
|
|
- [ ] 공통코드 관리 페이지 생성
|
||
|
|
- [ ] CodeCategoryPanel 컴포넌트 구현
|
||
|
|
- [ ] CodeDetailPanel 컴포넌트 구현
|
||
|
|
- [ ] 기본 CRUD 기능 구현
|
||
|
|
|
||
|
|
**목표 기간**: 2일
|
||
|
|
|
||
|
|
### ⏳ Phase 4: 고급 기능 구현 (예정)
|
||
|
|
|
||
|
|
- [ ] 드래그앤드롭 정렬 기능
|
||
|
|
- [ ] 검색 및 필터링 기능
|
||
|
|
- [ ] 일괄 업로드/다운로드 기능
|
||
|
|
- [ ] 코드 편집 모달 구현
|
||
|
|
|
||
|
|
**목표 기간**: 2일
|
||
|
|
|
||
|
|
### ⏳ Phase 5: 화면관리 연계 (예정)
|
||
|
|
|
||
|
|
- [ ] column_labels와 연동 확인
|
||
|
|
- [ ] 화면관리에서 code 타입 위젯 테스트
|
||
|
|
- [ ] 공통코드 변경 시 화면 반영 테스트
|
||
|
|
- [ ] 옵션 조회 API 구현
|
||
|
|
|
||
|
|
**목표 기간**: 1일
|
||
|
|
|
||
|
|
### ⏳ Phase 6: 테스트 및 최적화 (예정)
|
||
|
|
|
||
|
|
- [ ] 전체 기능 통합 테스트
|
||
|
|
- [ ] 성능 최적화
|
||
|
|
- [ ] 사용자 경험 개선
|
||
|
|
- [ ] 문서화 및 사용자 가이드
|
||
|
|
|
||
|
|
**목표 기간**: 1일
|
||
|
|
|
||
|
|
## 🎯 현재 구현 상태
|
||
|
|
|
||
|
|
### 📊 **전체 진행률: 0%**
|
||
|
|
|
||
|
|
- ⏳ **Phase 1**: 기본 구조 및 데이터베이스 (0%)
|
||
|
|
- ⏳ **Phase 2**: 백엔드 API 구현 (0%)
|
||
|
|
- ⏳ **Phase 3**: 프론트엔드 기본 구현 (0%)
|
||
|
|
- ⏳ **Phase 4**: 고급 기능 구현 (0%)
|
||
|
|
- ⏳ **Phase 5**: 화면관리 연계 (0%)
|
||
|
|
- ⏳ **Phase 6**: 테스트 및 최적화 (0%)
|
||
|
|
|
||
|
|
## 🚀 기대 효과
|
||
|
|
|
||
|
|
### 1. 개발 효율성 향상
|
||
|
|
|
||
|
|
- 공통코드 중앙 관리로 중복 작업 제거
|
||
|
|
- 화면관리 시스템과 연계로 자동 위젯 생성
|
||
|
|
|
||
|
|
### 2. 데이터 일관성 보장
|
||
|
|
|
||
|
|
- 모든 시스템에서 동일한 코드값 사용
|
||
|
|
- 코드 변경 시 전체 시스템 일괄 반영
|
||
|
|
|
||
|
|
### 3. 유지보수성 향상
|
||
|
|
|
||
|
|
- 코드 변경이 필요할 때 한 곳에서만 수정
|
||
|
|
- 체계적인 코드 분류 및 관리
|
||
|
|
|
||
|
|
### 4. 사용자 경험 개선
|
||
|
|
|
||
|
|
- 일관된 선택 옵션 제공
|
||
|
|
- 다국어 지원으로 글로벌 대응
|
||
|
|
|
||
|
|
**공통코드 관리 시스템을 통해 전체 ERP 시스템의 품질과 효율성을 크게 향상시킬 수 있습니다!** 🎉
|