# 부서관리 버그 헌팅 통합 요약 (2026-05-08)

4개 도메인 병렬 분석 결과. 상세 리포트는 별도 파일 참조.

- [Frontend (page.tsx)](2026-05-08-부서관리-버그헌팅-frontend.md)
- [Backend (Controller/Service)](2026-05-08-부서관리-버그헌팅-backend.md)
- [SQL/Mapper](2026-05-08-부서관리-버그헌팅-sql.md)
- [UX Edge Cases](2026-05-08-부서관리-버그헌팅-ux.md)

---

## 🔴 CRITICAL (즉시 수정)

| # | 위치 | 한 줄 |
|---|---|---|
| C1 | `DepartmentService.java:356-370` | **setPrimaryDept 데이터 손상** — 사용자가 소속되지 않은 부서로 호출 시 다른 부서 primary 만 해제되고 새 primary 미설정 → 사용자가 어떤 부서도 primary 가 아닌 invariant 깨진 상태로 commit |
| C2 | `DepartmentController.java:234-245` | **searchUsers 회사 격리 누락** — `userCompanyCode` 가드 없음. 임의 사용자가 다른 회사 사용자 목록 검색 가능 → 멀티테넌시 침해 |
| C3 | `DepartmentService.java:107` + `:251` | **parent_dept_code cross-tenant** — 존재/회사/삭제 검증 전혀 없음. update 의 verifyParentCycle 도 회사 격리 검증 없음. 다른 회사 부서를 부모로 지정 가능 |

## 🟠 HIGH

| # | 위치 | 한 줄 |
|---|---|---|
| H1 | `page.tsx:658` (회사 Select) | 회사 변경 시 `selectedCode`/`draft`/`isNewMode` 초기화 안 됨 → 다른 회사 부서가 우측 패널에 stale 노출 + 잘못된 회사 코드로 저장 위험 |
| H2 | `page.tsx:403` `handleMove` | `Promise.all` 로 N개 PUT — 일부 실패 시 부분 업데이트 영구 잔존 (catch 에서 loadDepartments 미호출). 트랜잭션 없음 |
| H3 | `page.tsx:231-246` 검색 필터 | 자식 매칭/부모 미매칭 시 트리 구조가 깨져 부서 안 보임 (broken tree) |
| H4 | `department.xml:160-179` | **updateDepartment 의 WHERE 에 `DELETED_AT IS NULL` 누락** — 삭제된 부서도 update 통과 후 controller 가 404 반환 → DB 는 변경되었는데 사용자는 실패로 인식 (silent corruption) |
| H5 | `DepartmentService.java:131` | **updateDepartment 부서명 중복 검증 없음** — create 에는 있지만 update 에 없어 동일 이름 active 부서 두 개 공존 가능 |
| H6 | `DepartmentService.java:210` `restoreDepartment` | 복구 시 동일 이름 active 부서 충돌 검증 없음 → 100% 재현되는 중복 이름 공존 |
| H7 | `DepartmentController.java:135` `deleteDepartment` | COMPANY_ADMIN 이 글로벌 부서 (`*`) 삭제 가능 → SUPER_ADMIN 전용 가드 필요 |
| H8 | `DepartmentService.java:85-99` `createDepartment` | 사용자 명시 dept_code 가 정규식 위반 시 silent fallback → 자동 코드로 발행되지만 사용자는 알지 못함 |
| H9 | `DepartmentController.java:234` `searchUsers` | role 검사 없음 — 일반 USER 가 회사 내 사용자 enumeration 가능 |
| H10 | `page.tsx:113` `emptyDraft` | `start_date` 가 `new Date().toISOString().slice(0,10)` 으로 고정 — UI 에서 hidden 인데도 today 가 강제 저장됨. 일괄등록도 동일 |
| H11 | DEPT_INFO 스키마 (mapper 외 확인 필요) | `(COMPANY_CODE, dept_name)` partial UNIQUE 인덱스 부재 가능성 → race 시 중복 부서명 공존 (#H5/H6 의 근본 방어선) |

## 🟡 MEDIUM

| # | 위치 | 한 줄 |
|---|---|---|
| M1 | `page.tsx:219-228` 멤버 fetch | AbortController/cancellation flag 없음 → 빠른 부서 전환 시 stale 멤버 데이터 표시 |
| M2 | `page.tsx:503` `handleDelete` | `selectedCode` 를 클로저로 캡처 안 함 → 다이얼로그 열린 상태에서 다른 부서 선택되면 엉뚱한 부서 삭제 위험 |
| M3 | `page.tsx:299` `handleClearDetail` | `isDirty` 무시. X 버튼 클릭 시 미저장 변경 즉시 폐기 |
| M4 | `page.tsx:997` 변경이력 모달 | API 호출 없음 — 항상 "이력 없음" 표시. 기능 미구현 |
| M5 | `DepartmentService.java:96` `selectNextDeptNumber` | `MAX+1` 비원자적 → 동시 생성 시 PK 충돌 5xx (catch 누락) |
| M6 | `DepartmentService.java:181` `deleteDepartment` | 활성 자식만 카운트 → 자식 단독 복구 시 부모 deleted 차단되는 UX trap |
| M7 | `DepartmentService.java:283` `searchUsers` | LIKE 와일드카드 `%`/`_` 미이스케이프 → 동작 이상 + enumeration |
| M8 | `DepartmentService.java:107`+ | `nullIfBlank` 만 적용. 선행/후행 공백 보존 → DB 에 공백 포함 코드 저장 가능 |
| M9 | `department.xml:150-174` | `#{date}::date` cast 시 잘못된 형식 → SQLException 5xx |
| M10 | DEPT_INFO `*` 회사 정책 | 글로벌 부서 read 일반 user 허용 / write 권한 정책 모호 |
| M11 | `DepartmentController.java:353` super 식별 | `company_code='*'` 우연 충돌 시 super 권한 부여 — provisioning 가드 필수 |

## 🟢 LOW

| # | 위치 | 한 줄 |
|---|---|---|
| L1 | `DepartmentController.java:271-272` | dead code (동일 키 두 번 lookup) |
| L2 | `DepartmentService.java:324` `removeDeptMember` | primary 자동 승격 race 가능성 (낮음) |
| L3 | `page.tsx:920` picker "최상위로" 선택 | UX 미구현 — `handleConfirmMoveTo(null)` 트리거 경로 없음 |
| L4 | `page.tsx:957` bulk register | 실패 부서 코드/사유 표시 없음 (성공/실패 카운트만) |
| L5 | `page.tsx:1469` 멤버 패널 | 멤버 추가/제거 UI 미구현. API 만 존재 |
| L6 | `page.tsx:249` `expandAll` + 검색 | 검색 후 expandAll → 검색 해제 시 부분 펼침 상태 |
| L7 | `department.xml:213` selectDeptMembers INNER JOIN | USER_INFO 삭제 시 orphan 멤버 표시 안 됨 |
| L8 | `department.xml:192` deleteUserDeptByDeptCode | dead query |
| L9 | `department.xml:266` selectFirstUserDept | 동일 NOW() 다중 row 시 비결정적 |

---

## 추천 수정 순서

**Phase 1 (긴급/보안)**: C1, C2, C3, H7
**Phase 2 (데이터 무결성)**: H4, H5, H6, H11, H1
**Phase 3 (UX/안정성)**: H2, H3, H8, H10, M1~M3
**Phase 4 (기능 보강)**: M4, L4, L5, L3
**Phase 5 (정리)**: L1, L7~L9, M5~M11

## 도메인별 통계

- Frontend: 진짜 8 / 잠재 6 / 오탐 1
- Backend: CRITICAL 3 + HIGH 7 + MEDIUM 7 + LOW 4
- SQL: HIGH 2 + MEDIUM 4 + LOW 4 + OK 4
- UX: 진짜 6 + 잠재 10 + OK 4
