# INVYONE "공통코드" 테이블 현황 정리 (분석 리포트)

작성일: 2026-05-15
작성자: gbpark + Claude

> **목적**: 사용자가 "공통코드 관련 테이블이 여러 개"라고 인지하고 있는 상태에서, 실제로 어떤 테이블들이 어떤 역할로 흩어져 있는지 정확히 파악하기 위한 분석 리포트. DDL 변경/통폐합은 별도 작업.

---

## 0. 요약 (한 페이지)

INVYONE 에는 **"코드성 데이터"가 5계열 / 약 14개 테이블**로 분산되어 있음. 사용자 입장에서 "공통코드"라 부를 만한 것은 둘로 나뉨:

| 계열 | 본질 | 대표 테이블 | 관리 UI |
|---|---|---|---|
| **A. 시스템 표준 코드 (Master Code)** | 정적·전사적 코드 마스터 | `code_category`, `code_info` | `/admin/systemMng/commonCodeList` |
| **B. 테이블/컬럼별 카테고리 값** | 특정 테이블·컬럼이 가질 수 있는 값 집합 | `CATEGORY_VALUES`, `CATEGORY_COLUMN_MAPPING`, `TABLE_TYPE_COLUMNS`, `TABLE_LABELS` | `/admin/systemMng/tableMngList` 일부 + `/admin/cascading-management` 탭 |
| **C. 카테고리값 캐스케이딩** | B의 값들 사이 부모-자식 매핑 | `CATEGORY_VALUE_CASCADING_GROUP/MAPPING` | `/admin/cascading-management` (CategoryValueCascadingTab) |
| **D. 일반 캐스케이딩 5종** | 임의 테이블 사이 부모-자식·자동채움·조건·배타·일반관계 | `CASCADING_HIERARCHY_*`, `CASCADING_AUTO_FILL_*`, `CASCADING_CONDITION`, `CASCADING_MUTUAL_EXCLUSION`, `CASCADING_RELATION` | `/admin/cascading-management` 탭 6종 + `/admin/auto-fill`, `/admin/cascading-relations` (중복) |
| **E. DB 타입 분류** | 외부 DB 종류 분류 (postgresql/oracle/...) | `db_type_categories` | (현재 별도 페이지 없음 — `external_db_connections` 화면에서 참조) |

**핵심 문제점 5개**:
1. **A와 B가 둘 다 살아있고**, 일반 사용자가 어디에 코드를 등록해야 하는지 직관적이지 않음.
2. **B의 핵심 테이블 `CATEGORY_VALUES`가 두 mapper(`categoryTree.xml`, `tableCategoryValue.xml`)에서 중복 정의**됨 — namespace 만 다르고 컬럼 대소문자 표기가 갈림.
3. **테이블명 대소문자가 일관되지 않음** — `code_category`(소문자) vs `CATEGORY_VALUES`(대문자), `category_tree`(소문자) vs `CASCADING_*`(대문자).
4. **`/admin/auto-fill`, `/admin/cascading-relations` 가 `/admin/cascading-management` 의 탭과 별도 페이지로 동시 존재** — 한 기능을 두 곳에서 관리 가능 (중복 UI).
5. **C와 D의 `CASCADING_RELATION`이 의미상 겹침** — 둘 다 "부모 선택 → 자식 옵션 필터링"이지만 데이터 소스만 다름 (B 카테고리 값 vs 임의 테이블).

---

## 1. 계열별 상세

### A. 시스템 표준 코드 — `code_category` / `code_info`

**역할**: 한 회사(또는 전사 공통, `company_code = '*'`)가 정의하는 표준 코드 마스터.
예: 부서 코드 그룹("DEPT") 아래 R&D / SALES / HR 등.

| 테이블 | 핵심 컬럼 | 비고 |
|---|---|---|
| `code_category` | category_code(PK), category_name, category_name_eng, description, sort_order, is_active, menu_objid, company_code, created_*, updated_* | 코드 그룹 (소문자 snake) |
| `code_info` | (category_code, code_value) PK, code_name, code_name_eng, description, sort_order, is_active, menu_objid, company_code, **parent_code_value, depth**, created_*, updated_* | 코드 항목. **자체 계층 지원** (parent + depth) |

**연관 코드**:
- mapper: `backend-spring/src/main/resources/mapper/commonCode.xml` (458줄)
- service: `CommonCodeService.java`
- controller: `CommonCodeController.java`
- frontend type: `frontend/types/commonCode.ts`
- frontend schema: `frontend/lib/schemas/commonCode.ts`
- UI 진입점: `frontend/app/(main)/admin/systemMng/commonCodeList/page.tsx`
- 관련 컴포넌트: `frontend/components/admin/CodeDetailPanel.tsx`, `CodeFormModal.tsx`, `CategoryItem.tsx`, `SortableCodeItem.tsx`
- 쿼리 훅: `frontend/hooks/queries/useCategories.ts`, `useCodes.ts`, `useCategoriesInfinite.ts`, `useCodesInfinite.ts`

**API 메서드** (commonCode.xml 기준):
- 카테고리: `getCommonCodeCategoryList/Cnt/Info`, `insert/update/deleteCommonCodeCategory`
- 코드: `getCommonCodeList/Cnt/Info`, `insert/update/deleteCommonCode`, `updateCommonCodeSortOrder`
- 계층: `getCommonCodeHierarchicalList`, `getCommonCodeTreeList`, `getCommonCodeOptionList`, `getCommonCodeParentDepth`, `getCommonCodeChildrenCnt`
- 중복: `getCommonCodeDuplicateCnt`, `getCommonCodeDuplicateByField`

---

### B. 테이블/컬럼별 카테고리 값 (Per-Table-Column Category)

**역할**: 임의 테이블의 특정 컬럼이 "category" 타입 입력일 때, 그 컬럼이 가질 수 있는 값 집합을 사용자가 관리.
A와 다른 점 — A는 "코드 그룹 단위", B는 "**테이블+컬럼 단위**"로 묶임.

| 테이블 | 핵심 컬럼 | 비고 |
|---|---|---|
| `CATEGORY_VALUES` ⚠️대소문자 혼재 | VALUE_ID(PK, auto), TABLE_NAME, COLUMN_NAME, VALUE_CODE, VALUE_LABEL, VALUE_ORDER, **PARENT_VALUE_ID, DEPTH, PATH** (MPTT 스타일), DESCRIPTION, **COLOR, ICON**, IS_ACTIVE, IS_DEFAULT, COMPANY_CODE, MENU_OBJID, created/updated | 트리 지원 + 시각 속성(color/icon) |
| `CATEGORY_COLUMN_MAPPING` | MAPPING_ID(PK), TABLE_NAME, LOGICAL_COLUMN_NAME, PHYSICAL_COLUMN_NAME, MENU_OBJID, COMPANY_CODE, DESCRIPTION | 논리 컬럼명 ↔ 물리 컬럼명 매핑 (메뉴별). UPSERT 키: (table_name, logical_column_name, menu_objid, company_code) |
| `TABLE_TYPE_COLUMNS` | TABLE_NAME, COLUMN_NAME, COLUMN_LABEL, INPUT_TYPE(='category'), DISPLAY_ORDER, ... | 어떤 컬럼이 category 입력인지 표시 |
| `table_labels` | table_name, table_label | 테이블 라벨 (디스플레이용) |

**관련 mapper / service / controller / 페이지**:
- mapper 1: `categoryTree.xml` (182줄, namespace=`categoryTree`, 컬럼 **소문자**)
- mapper 2: `tableCategoryValue.xml` (470줄, namespace=`tableCategoryValue`, 컬럼 **대문자**) ★ 같은 테이블 두 mapper
- service: `CategoryTreeService.java`, `TableCategoryValueService.java` (둘 다 별도 존재)
- controller: `CategoryTreeController.java`, `TableCategoryValueController.java`
- UI: `frontend/app/(main)/admin/systemMng/tableMngList/page.tsx` (테이블 관리 안에서 category 컬럼 다룸)
- 컴포넌트: `frontend/components/admin/cascading-management/tabs/HierarchyColumnTab.tsx`
- 공용 컴포넌트: `frontend/components/common/HierarchicalCodeSelect.tsx`, `MultiColumnHierarchySelect.tsx`

**`CATEGORY_VALUES` 사용처 차이**:
- `categoryTree.xml`: 트리 노드 조회/생성/이동, path 갱신, 컬럼 라벨 조인
- `tableCategoryValue.xml`: 메뉴 컨텍스트(MENU_OBJID) + 컬럼 매핑 + 사용 건수 조회 + 재귀 CTE 자식 수집

⚠️ **둘 다 살아있는 채로 사용 중**. 같은 테이블에 두 가지 다른 작업 흐름이 존재.

---

### C. 카테고리값 캐스케이딩 — `CATEGORY_VALUE_CASCADING_GROUP/MAPPING`

**역할**: B의 카테고리 값들 사이에 부모-자식 매핑.
예: "거래처 분류(parent) → 결제 방식(child)" 매핑. 거래처가 A이면 결제는 카드/현금만, B이면 어음/현금만.

| 테이블 | 핵심 컬럼 |
|---|---|
| `CATEGORY_VALUE_CASCADING_GROUP` | GROUP_ID(PK), RELATION_CODE, RELATION_NAME, DESCRIPTION, PARENT_TABLE_NAME, PARENT_COLUMN_NAME, PARENT_MENU_OBJID, CHILD_TABLE_NAME, CHILD_COLUMN_NAME, CHILD_MENU_OBJID, CLEAR_ON_PARENT_CHANGE, SHOW_GROUP_LABEL, EMPTY_PARENT_MESSAGE, NO_OPTIONS_MESSAGE, COMPANY_CODE, IS_ACTIVE, created/updated |
| `CATEGORY_VALUE_CASCADING_MAPPING` | MAPPING_ID(PK), GROUP_ID(FK), PARENT_VALUE_CODE, PARENT_VALUE_LABEL, CHILD_VALUE_CODE, CHILD_VALUE_LABEL, DISPLAY_ORDER, COMPANY_CODE, IS_ACTIVE, CREATED_DATE |

- mapper: `categoryValueCascading.xml` (179줄)
- service: `CategoryValueCascadingService.java`
- controller: `CategoryValueCascadingController.java`
- UI: `frontend/app/(main)/admin/cascading-management/tabs/CategoryValueCascadingTab.tsx`

---

### D. 일반 캐스케이딩 5종 — `CASCADING_*`

**역할**: B/C 와 달리 임의 테이블 사이의 관계를 관리. 카테고리값에 한정되지 않음.

| # | 테이블 | 역할 | mapper | UI 탭 |
|---|---|---|---|---|
| D-1 | `CASCADING_HIERARCHY_GROUP` + `CASCADING_HIERARCHY_LEVEL` | 다단 계층 (회사-부서-팀 or BOM 부모-자식). 모드 2가지: SELF_REF (단일 테이블 자기참조) / BOM (조인 테이블) | cascadingHierarchy.xml (219줄) | HierarchyTab |
| D-2 | `CASCADING_AUTO_FILL_GROUP` + `CASCADING_AUTO_FILL_MAPPING` | 한 필드 선택 시 다른 필드 자동 채움 (예: 거래처 코드 → 거래처명, 사업자번호, 주소 자동) | cascadingAutoFill.xml (128줄) | AutoFillTab + 별도 `/admin/auto-fill` |
| D-3 | `CASCADING_CONDITION` | 조건부 캐스케이딩 룰 (CONDITION_FIELD/OPERATOR/VALUE 등) | cascadingCondition.xml (100줄) | ConditionTab |
| D-4 | `CASCADING_MUTUAL_EXCLUSION` | 상호 배타 (필드 A 선택 시 B 비활성) | cascadingMutualExclusion.xml (145줄) | MutualExclusionTab |
| D-5 | `CASCADING_RELATION` | 일반 부모-자식 테이블 조회 룰 (PARENT_TABLE/COLUMN → CHILD_TABLE/FILTER_COLUMN). C와 데이터 소스만 다름 (임의 테이블) | cascadingRelation.xml (160줄) | CascadingRelationsTab + 별도 `/admin/cascading-relations` |

**D-1 (HIERARCHY) 세부 컬럼**:
```
GROUP: GROUP_CODE, GROUP_NAME, DESCRIPTION, HIERARCHY_TYPE, MAX_LEVELS, IS_FIXED_LEVELS,
       SELF_REF_TABLE/ID_COLUMN/PARENT_COLUMN/VALUE_COLUMN/LABEL_COLUMN/LEVEL_COLUMN/ORDER_COLUMN,
       BOM_TABLE, BOM_PARENT_COLUMN, BOM_CHILD_COLUMN,
       BOM_ITEM_TABLE, BOM_ITEM_ID_COLUMN, BOM_ITEM_LABEL_COLUMN, BOM_QTY_COLUMN, BOM_LEVEL_COLUMN,
       EMPTY_MESSAGE, NO_OPTIONS_MESSAGE, LOADING_MESSAGE, COMPANY_CODE, IS_ACTIVE, ...
LEVEL: GROUP_CODE, COMPANY_CODE, LEVEL_ORDER, LEVEL_NAME, LEVEL_CODE,
       TABLE_NAME, VALUE_COLUMN, LABEL_COLUMN, PARENT_KEY_COLUMN,
       FILTER_COLUMN, FILTER_VALUE, ORDER_COLUMN, ORDER_DIRECTION,
       PLACEHOLDER, IS_REQUIRED, IS_SEARCHABLE, IS_ACTIVE, ...
```

**D-2 (AUTO_FILL) 세부 컬럼**:
```
GROUP:   GROUP_CODE, GROUP_NAME, DESCRIPTION,
         MASTER_TABLE, MASTER_VALUE_COLUMN, MASTER_LABEL_COLUMN,
         COMPANY_CODE, IS_ACTIVE, ...
MAPPING: GROUP_CODE, COMPANY_CODE, SOURCE_COLUMN, TARGET_FIELD, TARGET_LABEL,
         IS_EDITABLE, IS_REQUIRED, DEFAULT_VALUE, SORT_ORDER
```

**D-5 (RELATION) 세부 컬럼**:
```
RELATION_CODE, RELATION_NAME, DESCRIPTION,
PARENT_TABLE, PARENT_VALUE_COLUMN, PARENT_LABEL_COLUMN,
CHILD_TABLE, CHILD_FILTER_COLUMN, CHILD_VALUE_COLUMN, CHILD_LABEL_COLUMN,
CHILD_ORDER_COLUMN, CHILD_ORDER_DIRECTION,
EMPTY_PARENT_MESSAGE, NO_OPTIONS_MESSAGE, LOADING_MESSAGE,
CLEAR_ON_PARENT_CHANGE, COMPANY_CODE, IS_ACTIVE, ...
```

---

### E. DB 타입 분류 — `db_type_categories`

**역할**: 외부 DB 연결(`external_db_connections`)에서 사용하는 DB 종류 분류.
**시스템 시드값 5개**: postgresql, oracle, mysql, mariadb, mssql (INSERT 기본값 mapper 안에 하드코딩).

| 테이블 | 핵심 컬럼 |
|---|---|
| `db_type_categories` | type_code(PK), display_name, icon, color, sort_order, is_active, created/updated |
| `external_db_connections` (참조) | id, db_type(FK→type_code), is_active, ... |

- mapper: `dbTypeCategory.xml` (106줄)
- service: `DbTypeCategoryService.java`
- controller: `DbTypeCategoryController.java`
- UI: 직접 페이지 없음. 외부 DB 연결 페이지에서 옵션으로만 노출되는 것으로 보임 (`/admin/automaticMng/exconList` 추정)

**다른 4계열과 성격이 다름** — 사용자 정의 코드가 아니라 시스템 enum. 굳이 "공통코드"로 묶지 않아도 됨.

---

## 2. 중복/혼란 지점 (★)

### 2.1 같은 테이블 두 mapper 중복 (B)
- `CATEGORY_VALUES` 를 `categoryTree.xml` (소문자) + `tableCategoryValue.xml` (대문자) 가 동시에 다룸.
- 둘 다 INSERT/UPDATE 까지 갖고 있어 어느 쪽으로 들어오느냐에 따라 케이스 표기가 갈릴 수 있음.
- **확인 필요**: 실제 두 mapper의 INSERT 가 서로 다른 컬럼 집합을 쓰는지(예: `MENU_OBJID`는 tableCategoryValue 만 채움) 케이스 분석 필요.

### 2.2 UI 페이지 중복 (D-2, D-5)
- `/admin/auto-fill/page.tsx` ↔ `/admin/cascading-management` 의 `AutoFillTab.tsx`
- `/admin/cascading-relations/page.tsx` ↔ `/admin/cascading-management` 의 `CascadingRelationsTab.tsx`
- 둘 다 같은 DB 테이블을 건드림. 한 곳에서 수정 가능.

### 2.3 의미 중복 (C ↔ D-5)
- C `CATEGORY_VALUE_CASCADING_GROUP` 과 D-5 `CASCADING_RELATION` 의 데이터 모델이 거의 같음 (parent_table/column → child_table/column + 메시지/IS_ACTIVE).
- 차이는 "**매핑 저장 방식**"뿐:
  - C: 매핑 테이블(`CATEGORY_VALUE_CASCADING_MAPPING`)에 (parent_value_code, child_value_code) 명시 저장 → **사용자가 매핑 직접 정의**
  - D-5: 매핑 없음. 자식 테이블의 FILTER_COLUMN 으로 **실시간 조회** (즉 자식 테이블 자체가 부모 키를 컬럼으로 갖고 있음)
- 즉 **C는 명시적 매핑, D-5는 외래키 기반**. 다른 목적이지만 UI 상으로는 헷갈리기 쉬움.

### 2.4 A의 트리 ↔ B의 트리 중복
- `code_info.parent_code_value + depth` 로 **A 자체로 트리 코드 관리 가능**.
- 동시에 B `CATEGORY_VALUES.parent_value_id + depth + path` 도 트리 지원.
- 두 트리 시스템이 별도로 존재 — 사용자 입장에서 "트리 코드를 어디에 만들지" 헷갈림.

### 2.5 테이블 케이스 일관성 부족
- 소문자 snake: `code_category`, `code_info`, `category_tree`(미사용 흔적?), `category_values`(`categoryTree.xml`에서), `table_labels`, `table_type_columns`, `db_type_categories`, `external_db_connections`
- UPPER_SNAKE: `CATEGORY_VALUES`(`tableCategoryValue.xml`에서), `CATEGORY_COLUMN_MAPPING`, `TABLE_TYPE_COLUMNS`, `CASCADING_*`, `CATEGORY_VALUE_CASCADING_*`
- CLAUDE.md 백엔드 규칙은 **SQL에서 UPPER_SNAKE** 강제 → 소문자 mapper들은 규칙 위반. (실제 DDL이 무엇인지는 PG는 비호환적으로 lowercase로 저장되므로 SELECT 는 둘 다 동작)

### 2.6 `category_tree` 의 정체
- `tableCategoryValue.xml` line 277 의 재귀 CTE 임시 이름이 `category_tree` 임 (실제 테이블 아님, 변수명 충돌).
- `categoryTree.xml` 의 namespace 이름과 헷갈리기 쉬움. 실제 테이블 이름은 `category_values`.

---

## 3. 코드 자산 요약 (mapper / service / controller / UI)

| 계열 | mapper (.xml) | Service.java | Controller.java | UI |
|---|---|---|---|---|
| A | commonCode | CommonCodeService | CommonCodeController | /admin/systemMng/commonCodeList |
| B | categoryTree, tableCategoryValue | CategoryTreeService, TableCategoryValueService | CategoryTreeController, TableCategoryValueController | /admin/systemMng/tableMngList, /admin/cascading-management(HierarchyColumnTab) |
| C | categoryValueCascading | CategoryValueCascadingService | CategoryValueCascadingController | /admin/cascading-management(CategoryValueCascadingTab) |
| D-1 | cascadingHierarchy | CascadingHierarchyService | CascadingHierarchyController | /admin/cascading-management(HierarchyTab) |
| D-2 | cascadingAutoFill | CascadingAutoFillService | CascadingAutoFillController | /admin/cascading-management(AutoFillTab) + /admin/auto-fill ⚠️중복 |
| D-3 | cascadingCondition | CascadingConditionService | CascadingConditionController | /admin/cascading-management(ConditionTab) |
| D-4 | cascadingMutualExclusion | CascadingMutualExclusionService | CascadingMutualExclusionController | /admin/cascading-management(MutualExclusionTab) |
| D-5 | cascadingRelation | CascadingRelationService | CascadingRelationController | /admin/cascading-management(CascadingRelationsTab) + /admin/cascading-relations ⚠️중복 |
| E | dbTypeCategory | DbTypeCategoryService | DbTypeCategoryController | (전용 페이지 없음) |
| 부속 | codeMerge | CodeMergeService | CodeMergeController | (확인 안 됨 — 텍스트 검색으로 컬럼명을 조건으로 테이블 찾는 유틸. UI 진입점 추적 필요) |

**총 컨트롤러 12개 / 서비스 12개 / mapper 11개 / 약 14개 테이블**.

---

## 4. 추후 정리할 때 고려할 옵션 (참고용, 결정 X)

> 이 리포트는 현황 파악만 — 아래는 사용자가 정리 방향을 고민할 때 참고용 옵션.

### 옵션 1 — "공통코드" 정의를 명확히 한 후 카테고리화
- A 만 "공통코드"로 부른다 → B 는 "테이블 카테고리", C/D 는 "필드 연동(캐스케이딩)", E 는 "DB 종류"
- 메뉴/문서/네이밍을 그 분류로 통일

### 옵션 2 — `CATEGORY_VALUES` mapper 단일화
- `categoryTree.xml` vs `tableCategoryValue.xml` 중 한쪽 폐기. 또는 둘 다 같은 컬럼 케이스로 통일.
- INSERT/UPDATE 경로를 하나로 모음 (MENU_OBJID 가 NULL 인 케이스를 어떻게 처리할지 룰 필요).

### 옵션 3 — UI 중복 페이지 제거
- `/admin/auto-fill`, `/admin/cascading-relations` 둘 중 하나 폐기 (현재는 `/admin/cascading-management` 의 탭이 정식).

### 옵션 4 — C와 D-5 통합 검토
- 같은 "부모-자식 옵션 필터링"이라는 도메인 → 한 모델로 합치고 모드(`MATCHING_MODE = 'EXPLICIT' | 'FK'`)로 분기.
- 단 마이그레이션 비용/UI 변경 비용 큼.

### 옵션 5 — 테이블 케이스 통일
- INVYONE 백엔드 규칙은 SQL UPPER_SNAKE → mapper 의 모든 SQL 을 UPPER_SNAKE 로 정렬.
- 실제 PG 의 테이블명은 어차피 lower-case (인용부호 안 쓰면) 라 동작에는 영향 없음. 가독성만 차이.

---

## 5. 다음 단계로 확인 필요한 것

- [ ] **DDL 직접 확인**: `db/migrations/` 와 `backend-spring/src/main/resources/db/migration/` 에 `code_category / code_info / CATEGORY_VALUES / CATEGORY_COLUMN_MAPPING / CASCADING_*` 의 생성 SQL이 없음 → 기존 운영 DB(`invyone`)에서 직접 `\d` 로 확인 필요.
- [ ] **CodeMergeService 의 정체**: codeMerge.xml 은 `information_schema` 만 쿼리 → 어떤 정리/마이그레이션 작업용인지 service 코드 확인.
- [ ] **C와 D-5 의 데이터 양 비교**: 실제 회사 데이터로 어느 쪽이 얼마나 쓰이는지 → 통폐합 판단 근거.
- [ ] **UI 중복 페이지 (`/admin/auto-fill`, `/admin/cascading-relations`)** — 어느 쪽이 메인 메뉴에 노출되는지 (`menu_info`), 사용자가 양쪽 다 들어갈 수 있는 상태인지 확인.

---

## 참고 파일 / 위치

- Backend mapper: `backend-spring/src/main/resources/mapper/{commonCode,categoryTree,tableCategoryValue,categoryValueCascading,cascadingHierarchy,cascadingAutoFill,cascadingCondition,cascadingMutualExclusion,cascadingRelation,dbTypeCategory,codeMerge}.xml`
- Backend service: `backend-spring/src/main/java/com/erp/service/{CommonCode,CategoryTree,TableCategoryValue,CategoryValueCascading,CascadingHierarchy,CascadingAutoFill,CascadingCondition,CascadingMutualExclusion,CascadingRelation,DbTypeCategory,CodeMerge}Service.java`
- Backend controller: 위와 동일한 prefix 의 Controller
- Frontend types: `frontend/types/commonCode.ts`
- Frontend schema: `frontend/lib/schemas/commonCode.ts`
- Frontend UI: `frontend/app/(main)/admin/systemMng/commonCodeList/`, `/tableMngList/`, `/admin/cascading-management/`, `/admin/cascading-relations/`, `/admin/auto-fill/`
- Frontend hooks: `frontend/hooks/queries/{useCategories,useCodes,useCategoriesInfinite,useCodesInfinite}.ts`
- 공용 컴포넌트: `frontend/components/common/{HierarchicalCodeSelect,MultiColumnHierarchySelect}.tsx`, `frontend/components/admin/{CodeDetailPanel,CodeFormModal,CodeCategoryFormModal,CategoryItem,SortableCodeItem}.tsx`
