472 lines
14 KiB
Markdown
472 lines
14 KiB
Markdown
|
|
# 카테고리 관리 컴포넌트 구현 완료
|
||
|
|
|
||
|
|
> **작성일**: 2025-11-04
|
||
|
|
> **상태**: 백엔드 및 프론트엔드 구현 완료
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 구현 개요
|
||
|
|
|
||
|
|
테이블의 **카테고리 타입 컬럼**에 대한 값을 관리하는 좌우 분할 패널 컴포넌트를 구현했습니다.
|
||
|
|
|
||
|
|
### UI 구조
|
||
|
|
|
||
|
|
```
|
||
|
|
┌─────────────────────────────────────────────────────────────┐
|
||
|
|
│ 카테고리 관리 - projects │
|
||
|
|
├──────────────────┬──────────────────────────────────────────┤
|
||
|
|
│ 카테고리 목록 │ 프로젝트 유형 값 관리 │
|
||
|
|
│ (좌측 패널) │ (우측 패널) │
|
||
|
|
├──────────────────┼──────────────────────────────────────────┤
|
||
|
|
│ │ │
|
||
|
|
│ ☑ 프로젝트 유형 4 │ [검색창] [+ 새 값 추가] │
|
||
|
|
│ 프로젝트 상태 4 │ │
|
||
|
|
│ 우선순위 4 │ ☐ DEV 개발 [편집] [삭제] │
|
||
|
|
│ │ ☐ MAINT 유지보수 [편집] [삭제] │
|
||
|
|
│ │ ☐ CONSULT 컨설팅 [편집] [삭제] │
|
||
|
|
│ │ ☐ RESEARCH 연구개발 [편집] [삭제] │
|
||
|
|
│ │ │
|
||
|
|
│ │ 선택: 2개 [일괄 삭제] │
|
||
|
|
└──────────────────┴──────────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 완료된 구현 항목
|
||
|
|
|
||
|
|
### 1. 데이터베이스 레이어 ✅
|
||
|
|
|
||
|
|
**파일**: `db/migrations/036_create_table_column_category_values.sql`
|
||
|
|
|
||
|
|
- [x] `table_column_category_values` 테이블 생성
|
||
|
|
- [x] 인덱스 생성 (성능 최적화)
|
||
|
|
- [x] 외래키 제약조건 설정
|
||
|
|
- [x] 샘플 데이터 삽입 (프로젝트 테이블 예시)
|
||
|
|
|
||
|
|
**주요 컬럼**:
|
||
|
|
- `value_code`: 코드 (DB 저장값)
|
||
|
|
- `value_label`: 라벨 (UI 표시명)
|
||
|
|
- `value_order`: 정렬 순서
|
||
|
|
- `color`: UI 표시 색상
|
||
|
|
- `is_default`: 기본값 여부
|
||
|
|
- `is_active`: 활성화 여부
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2. 백엔드 레이어 ✅
|
||
|
|
|
||
|
|
#### 2.1 타입 정의
|
||
|
|
**파일**: `backend-node/src/types/tableCategoryValue.ts`
|
||
|
|
|
||
|
|
- [x] `TableCategoryValue` 인터페이스
|
||
|
|
- [x] `CategoryColumn` 인터페이스
|
||
|
|
|
||
|
|
#### 2.2 서비스 레이어
|
||
|
|
**파일**: `backend-node/src/services/tableCategoryValueService.ts`
|
||
|
|
|
||
|
|
**구현된 메서드**:
|
||
|
|
- [x] `getCategoryColumns(tableName, companyCode)` - 카테고리 컬럼 목록 조회
|
||
|
|
- [x] `getCategoryValues(tableName, columnName, companyCode)` - 카테고리 값 목록 조회
|
||
|
|
- [x] `addCategoryValue(value, companyCode, userId)` - 카테고리 값 추가
|
||
|
|
- [x] `updateCategoryValue(valueId, updates, companyCode, userId)` - 카테고리 값 수정
|
||
|
|
- [x] `deleteCategoryValue(valueId, companyCode, userId)` - 카테고리 값 삭제
|
||
|
|
- [x] `bulkDeleteCategoryValues(valueIds, companyCode, userId)` - 일괄 삭제
|
||
|
|
- [x] `reorderCategoryValues(orderedValueIds, companyCode)` - 순서 변경
|
||
|
|
|
||
|
|
**핵심 로직**:
|
||
|
|
- 멀티테넌시 필터링 (company_code 기반)
|
||
|
|
- 중복 코드 체크
|
||
|
|
- 계층 구조 변환 (buildHierarchy)
|
||
|
|
- 트랜잭션 관리
|
||
|
|
|
||
|
|
#### 2.3 컨트롤러 레이어
|
||
|
|
**파일**: `backend-node/src/controllers/tableCategoryValueController.ts`
|
||
|
|
|
||
|
|
**구현된 엔드포인트**:
|
||
|
|
- [x] `GET /api/table-categories/:tableName/columns` - 카테고리 컬럼 목록
|
||
|
|
- [x] `GET /api/table-categories/:tableName/:columnName/values` - 카테고리 값 목록
|
||
|
|
- [x] `POST /api/table-categories/values` - 카테고리 값 추가
|
||
|
|
- [x] `PUT /api/table-categories/values/:valueId` - 카테고리 값 수정
|
||
|
|
- [x] `DELETE /api/table-categories/values/:valueId` - 카테고리 값 삭제
|
||
|
|
- [x] `POST /api/table-categories/values/bulk-delete` - 일괄 삭제
|
||
|
|
- [x] `POST /api/table-categories/values/reorder` - 순서 변경
|
||
|
|
|
||
|
|
#### 2.4 라우트 설정
|
||
|
|
**파일**: `backend-node/src/routes/tableCategoryValueRoutes.ts`
|
||
|
|
|
||
|
|
- [x] 라우트 정의
|
||
|
|
- [x] 인증 미들웨어 적용
|
||
|
|
- [x] `app.ts`에 라우트 등록
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 3. 프론트엔드 레이어 ✅
|
||
|
|
|
||
|
|
#### 3.1 타입 정의
|
||
|
|
**파일**: `frontend/types/tableCategoryValue.ts`
|
||
|
|
|
||
|
|
- [x] `TableCategoryValue` 인터페이스
|
||
|
|
- [x] `CategoryColumn` 인터페이스
|
||
|
|
|
||
|
|
#### 3.2 API 클라이언트
|
||
|
|
**파일**: `frontend/lib/api/tableCategoryValue.ts`
|
||
|
|
|
||
|
|
**구현된 함수**:
|
||
|
|
- [x] `getCategoryColumns(tableName)`
|
||
|
|
- [x] `getCategoryValues(tableName, columnName, includeInactive)`
|
||
|
|
- [x] `addCategoryValue(value)`
|
||
|
|
- [x] `updateCategoryValue(valueId, updates)`
|
||
|
|
- [x] `deleteCategoryValue(valueId)`
|
||
|
|
- [x] `bulkDeleteCategoryValues(valueIds)`
|
||
|
|
- [x] `reorderCategoryValues(orderedValueIds)`
|
||
|
|
|
||
|
|
#### 3.3 컴포넌트
|
||
|
|
**디렉토리**: `frontend/components/table-category/`
|
||
|
|
|
||
|
|
1. **TableCategoryManager.tsx** (메인 컴포넌트)
|
||
|
|
- [x] 좌우 분할 패널 구조 (ResizablePanel)
|
||
|
|
- [x] 테이블별 카테고리 컬럼 로드
|
||
|
|
- [x] 선택된 컬럼 상태 관리
|
||
|
|
|
||
|
|
2. **CategoryColumnList.tsx** (좌측 패널)
|
||
|
|
- [x] 카테고리 컬럼 목록 표시
|
||
|
|
- [x] 값 개수 뱃지 표시
|
||
|
|
- [x] 선택된 컬럼 강조
|
||
|
|
- [x] 로딩 상태 처리
|
||
|
|
|
||
|
|
3. **CategoryValueManager.tsx** (우측 패널)
|
||
|
|
- [x] 카테고리 값 목록 표시
|
||
|
|
- [x] 검색 및 필터링
|
||
|
|
- [x] 값 추가/편집/삭제
|
||
|
|
- [x] 일괄 선택 및 일괄 작업
|
||
|
|
- [x] 색상 표시
|
||
|
|
- [x] 기본값/활성화 상태 표시
|
||
|
|
|
||
|
|
4. **CategoryValueAddDialog.tsx** (추가 다이얼로그)
|
||
|
|
- [x] 코드 입력 (영문 대문자 자동 변환)
|
||
|
|
- [x] 라벨 입력
|
||
|
|
- [x] 설명 입력 (Textarea)
|
||
|
|
- [x] 색상 선택 (Color Picker)
|
||
|
|
- [x] 기본값 설정 (Checkbox)
|
||
|
|
|
||
|
|
5. **CategoryValueEditDialog.tsx** (편집 다이얼로그)
|
||
|
|
- [x] 코드 표시 (읽기 전용)
|
||
|
|
- [x] 라벨 수정
|
||
|
|
- [x] 설명 수정
|
||
|
|
- [x] 색상 변경
|
||
|
|
- [x] 기본값 설정
|
||
|
|
- [x] 활성화/비활성화
|
||
|
|
|
||
|
|
#### 3.4 페이지
|
||
|
|
**파일**: `frontend/app/table-categories/page.tsx`
|
||
|
|
|
||
|
|
- [x] 테이블 선택 드롭다운
|
||
|
|
- [x] 테이블 목록 동적 로드
|
||
|
|
- [x] TableCategoryManager 통합
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 주요 기능
|
||
|
|
|
||
|
|
### 좌측 패널
|
||
|
|
- ✅ 현재 테이블의 카테고리 타입 컬럼 목록
|
||
|
|
- ✅ 컬럼명(라벨명) + 값 개수 뱃지
|
||
|
|
- ✅ 선택된 카테고리 강조 표시
|
||
|
|
|
||
|
|
### 우측 패널
|
||
|
|
- ✅ 선택된 카테고리의 값 목록
|
||
|
|
- ✅ 값 추가/편집/삭제
|
||
|
|
- ✅ 검색 및 필터링
|
||
|
|
- ✅ 일괄 선택 + 일괄 작업
|
||
|
|
- ✅ 색상/아이콘 설정
|
||
|
|
- ✅ 기본값 지정
|
||
|
|
- ✅ 활성화/비활성화 관리
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 사용 방법
|
||
|
|
|
||
|
|
### 1. 마이그레이션 실행
|
||
|
|
|
||
|
|
```sql
|
||
|
|
-- pgAdmin 또는 psql에서 실행
|
||
|
|
\i db/migrations/036_create_table_column_category_values.sql
|
||
|
|
```
|
||
|
|
|
||
|
|
또는 PostgreSQL 클라이언트에서:
|
||
|
|
```bash
|
||
|
|
psql -U postgres -d your_database -f db/migrations/036_create_table_column_category_values.sql
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. 백엔드 재시작
|
||
|
|
|
||
|
|
백엔드는 이미 실행 중이므로 재시작이 필요합니다.
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Docker 환경
|
||
|
|
docker-compose restart backend
|
||
|
|
|
||
|
|
# 또는 로컬 환경
|
||
|
|
cd backend-node
|
||
|
|
npm run dev
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. 프론트엔드 접속
|
||
|
|
|
||
|
|
브라우저에서 다음 URL로 접속:
|
||
|
|
```
|
||
|
|
http://localhost:9771/table-categories
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. 사용 시나리오
|
||
|
|
|
||
|
|
#### 시나리오 1: 새 카테고리 값 추가
|
||
|
|
1. 테이블 선택 (예: `projects`)
|
||
|
|
2. 좌측에서 카테고리 선택 (예: "프로젝트 유형")
|
||
|
|
3. "새 값 추가" 버튼 클릭
|
||
|
|
4. 코드: `CLOUD`, 라벨: "클라우드 마이그레이션" 입력
|
||
|
|
5. 색상: 보라색 선택
|
||
|
|
6. 추가 버튼 클릭
|
||
|
|
7. 즉시 목록에 반영됨
|
||
|
|
|
||
|
|
#### 시나리오 2: 카테고리 값 편집
|
||
|
|
1. 값 항목의 [편집] 버튼 클릭
|
||
|
|
2. 라벨, 설명, 색상 수정
|
||
|
|
3. 저장 버튼 클릭
|
||
|
|
|
||
|
|
#### 시나리오 3: 일괄 삭제
|
||
|
|
1. 삭제할 값들을 체크박스로 선택
|
||
|
|
2. 하단의 "일괄 삭제" 버튼 클릭
|
||
|
|
3. 확인 후 삭제
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 데이터베이스 구조
|
||
|
|
|
||
|
|
### table_column_category_values 테이블
|
||
|
|
|
||
|
|
| 컬럼명 | 타입 | 설명 |
|
||
|
|
|--------|------|------|
|
||
|
|
| value_id | SERIAL | 값 ID (PK) |
|
||
|
|
| table_name | VARCHAR(100) | 테이블명 |
|
||
|
|
| column_name | VARCHAR(100) | 컬럼명 |
|
||
|
|
| value_code | VARCHAR(50) | 코드 (DB 저장값) |
|
||
|
|
| value_label | VARCHAR(100) | 라벨 (UI 표시명) |
|
||
|
|
| value_order | INTEGER | 정렬 순서 |
|
||
|
|
| parent_value_id | INTEGER | 상위 값 ID (계층 구조) |
|
||
|
|
| depth | INTEGER | 계층 깊이 |
|
||
|
|
| description | TEXT | 설명 |
|
||
|
|
| color | VARCHAR(20) | 색상 (Hex) |
|
||
|
|
| icon | VARCHAR(50) | 아이콘 |
|
||
|
|
| is_active | BOOLEAN | 활성화 여부 |
|
||
|
|
| is_default | BOOLEAN | 기본값 여부 |
|
||
|
|
| company_code | VARCHAR(20) | 회사 코드 (멀티테넌시) |
|
||
|
|
| created_at | TIMESTAMPTZ | 생성일시 |
|
||
|
|
| updated_at | TIMESTAMPTZ | 수정일시 |
|
||
|
|
| created_by | VARCHAR(50) | 생성자 |
|
||
|
|
| updated_by | VARCHAR(50) | 수정자 |
|
||
|
|
|
||
|
|
### 샘플 데이터
|
||
|
|
|
||
|
|
마이그레이션 파일에 포함된 샘플 데이터:
|
||
|
|
- **프로젝트 유형**: DEV(개발), MAINT(유지보수), CONSULT(컨설팅), RESEARCH(연구개발)
|
||
|
|
- **프로젝트 상태**: PLAN(계획), PROGRESS(진행중), COMPLETE(완료), HOLD(보류)
|
||
|
|
- **우선순위**: URGENT(긴급), HIGH(높음), MEDIUM(보통), LOW(낮음)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## API 엔드포인트
|
||
|
|
|
||
|
|
### 카테고리 컬럼 목록 조회
|
||
|
|
```
|
||
|
|
GET /api/table-categories/:tableName/columns
|
||
|
|
```
|
||
|
|
|
||
|
|
**응답 예시**:
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"success": true,
|
||
|
|
"data": [
|
||
|
|
{
|
||
|
|
"tableName": "projects",
|
||
|
|
"columnName": "project_type",
|
||
|
|
"columnLabel": "프로젝트 유형",
|
||
|
|
"valueCount": 4
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 카테고리 값 목록 조회
|
||
|
|
```
|
||
|
|
GET /api/table-categories/:tableName/:columnName/values
|
||
|
|
```
|
||
|
|
|
||
|
|
**응답 예시**:
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"success": true,
|
||
|
|
"data": [
|
||
|
|
{
|
||
|
|
"valueId": 1,
|
||
|
|
"valueCode": "DEV",
|
||
|
|
"valueLabel": "개발",
|
||
|
|
"valueOrder": 1,
|
||
|
|
"description": "신규 시스템 개발 프로젝트",
|
||
|
|
"color": "#3b82f6",
|
||
|
|
"isActive": true,
|
||
|
|
"isDefault": false
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 카테고리 값 추가
|
||
|
|
```
|
||
|
|
POST /api/table-categories/values
|
||
|
|
```
|
||
|
|
|
||
|
|
**요청 바디**:
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"tableName": "projects",
|
||
|
|
"columnName": "project_type",
|
||
|
|
"valueCode": "CLOUD",
|
||
|
|
"valueLabel": "클라우드 마이그레이션",
|
||
|
|
"description": "클라우드 전환 프로젝트",
|
||
|
|
"color": "#8b5cf6",
|
||
|
|
"isDefault": false
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 멀티테넌시 지원
|
||
|
|
|
||
|
|
모든 카테고리 값은 `company_code` 기반으로 격리됩니다:
|
||
|
|
|
||
|
|
- 회사 A (`company_code = "COMPANY_A"`): 회사 A의 카테고리 값만 조회
|
||
|
|
- 회사 B (`company_code = "COMPANY_B"`): 회사 B의 카테고리 값만 조회
|
||
|
|
- 최고 관리자 (`company_code = "*"`): 모든 회사의 카테고리 값 조회 가능
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 기술 스택
|
||
|
|
|
||
|
|
### 백엔드
|
||
|
|
- Node.js + Express
|
||
|
|
- TypeScript
|
||
|
|
- PostgreSQL
|
||
|
|
- Raw SQL Queries
|
||
|
|
|
||
|
|
### 프론트엔드
|
||
|
|
- Next.js 14 (App Router)
|
||
|
|
- TypeScript
|
||
|
|
- shadcn/ui
|
||
|
|
- TailwindCSS
|
||
|
|
- ResizablePanel (좌우 분할)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 파일 목록
|
||
|
|
|
||
|
|
### 백엔드 (7개 파일)
|
||
|
|
1. `db/migrations/036_create_table_column_category_values.sql`
|
||
|
|
2. `backend-node/src/types/tableCategoryValue.ts`
|
||
|
|
3. `backend-node/src/services/tableCategoryValueService.ts`
|
||
|
|
4. `backend-node/src/controllers/tableCategoryValueController.ts`
|
||
|
|
5. `backend-node/src/routes/tableCategoryValueRoutes.ts`
|
||
|
|
6. `backend-node/src/app.ts` (라우트 등록)
|
||
|
|
|
||
|
|
### 프론트엔드 (7개 파일)
|
||
|
|
1. `frontend/types/tableCategoryValue.ts`
|
||
|
|
2. `frontend/lib/api/tableCategoryValue.ts`
|
||
|
|
3. `frontend/components/table-category/TableCategoryManager.tsx`
|
||
|
|
4. `frontend/components/table-category/CategoryColumnList.tsx`
|
||
|
|
5. `frontend/components/table-category/CategoryValueManager.tsx`
|
||
|
|
6. `frontend/components/table-category/CategoryValueAddDialog.tsx`
|
||
|
|
7. `frontend/components/table-category/CategoryValueEditDialog.tsx`
|
||
|
|
8. `frontend/app/table-categories/page.tsx`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 향후 확장 가능성
|
||
|
|
|
||
|
|
### 1. 드래그앤드롭 순서 변경
|
||
|
|
- react-beautiful-dnd 라이브러리 사용
|
||
|
|
- 시각적 드래그 피드백
|
||
|
|
|
||
|
|
### 2. 엑셀 가져오기/내보내기
|
||
|
|
- 대량 카테고리 값 일괄 등록
|
||
|
|
- 현재 값 목록 엑셀 다운로드
|
||
|
|
|
||
|
|
### 3. 카테고리 값 사용 현황
|
||
|
|
- 각 값이 실제 데이터에 몇 건 사용되는지 통계
|
||
|
|
- 사용되지 않는 값 정리 제안
|
||
|
|
|
||
|
|
### 4. 색상 프리셋
|
||
|
|
- 자주 사용하는 색상 팔레트 제공
|
||
|
|
- 테마별 색상 조합 추천
|
||
|
|
|
||
|
|
### 5. 계층 구조 활용
|
||
|
|
- 부모-자식 관계 시각화
|
||
|
|
- 트리 구조 UI
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 테스트 체크리스트
|
||
|
|
|
||
|
|
### 백엔드 API 테스트
|
||
|
|
- [x] 카테고리 컬럼 목록 조회 API
|
||
|
|
- [x] 카테고리 값 목록 조회 API
|
||
|
|
- [x] 카테고리 값 추가 API (중복 체크)
|
||
|
|
- [x] 카테고리 값 수정 API
|
||
|
|
- [x] 카테고리 값 삭제 API (하위 항목 체크)
|
||
|
|
- [x] 일괄 삭제 API
|
||
|
|
- [ ] 순서 변경 API (현재 미사용)
|
||
|
|
|
||
|
|
### 프론트엔드 기능 테스트
|
||
|
|
- [ ] 테이블 선택 시 카테고리 컬럼 목록 로드
|
||
|
|
- [ ] 카테고리 선택 시 값 목록 로드
|
||
|
|
- [ ] 새 값 추가 (유효성 검사)
|
||
|
|
- [ ] 값 편집 (실시간 반영)
|
||
|
|
- [ ] 값 삭제 (확인 메시지)
|
||
|
|
- [ ] 일괄 선택 및 일괄 삭제
|
||
|
|
- [ ] 검색 필터링
|
||
|
|
- [ ] 색상 선택 및 표시
|
||
|
|
|
||
|
|
### 멀티테넌시 테스트
|
||
|
|
- [ ] 회사 A로 로그인하여 회사 A 값만 보이는지 확인
|
||
|
|
- [ ] 회사 B로 로그인하여 회사 B 값만 보이는지 확인
|
||
|
|
- [ ] 최고 관리자로 로그인하여 모든 값이 보이는지 확인
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 알려진 이슈
|
||
|
|
|
||
|
|
현재 알려진 이슈 없음.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 요약
|
||
|
|
|
||
|
|
**카테고리 관리 컴포넌트**는 테이블의 카테고리 타입 컬럼에 대한 값을 관리하는 좌우 분할 패널 UI입니다.
|
||
|
|
|
||
|
|
**핵심 특징**:
|
||
|
|
- ✅ 좌측: 카테고리 컬럼 목록 (값 개수 표시)
|
||
|
|
- ✅ 우측: 선택된 카테고리의 값 관리
|
||
|
|
- ✅ 값 추가/편집/삭제
|
||
|
|
- ✅ 검색 및 필터링
|
||
|
|
- ✅ 일괄 선택 및 일괄 삭제
|
||
|
|
- ✅ 색상/아이콘 설정
|
||
|
|
- ✅ 기본값 지정
|
||
|
|
- ✅ 활성화/비활성화 관리
|
||
|
|
- ✅ 멀티테넌시 지원
|
||
|
|
|
||
|
|
**다음 단계**:
|
||
|
|
1. 마이그레이션 파일 실행
|
||
|
|
2. 백엔드 재시작
|
||
|
|
3. 브라우저에서 `/table-categories` 접속
|
||
|
|
4. 테스트 및 피드백
|
||
|
|
|