## 단가관리 (Pricing Management) - 단가 목록 페이지 (IntegratedListTemplateV2 공통 템플릿 적용) - 단가 등록/수정 폼 (원가/마진 자동 계산) - 이력 조회, 수정 이력, 최종 확정 다이얼로그 - 판매관리 > 단가관리 네비게이션 메뉴 추가 ## HR 관리 (Human Resources) - 사원관리 (목록, 등록, 수정, 상세, CSV 업로드) - 부서관리 (트리 구조) - 근태관리 (기본 구조) ## 품목관리 개선 - Radix UI Select controlled mode 버그 수정 (key prop 적용) - DynamicItemForm 파일 업로드 지원 - 수정 페이지 데이터 로딩 개선 ## 문서화 - 단가관리 마이그레이션 체크리스트 - HR 관리 구현 체크리스트 - Radix UI Select 버그 수정 가이드 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
11 KiB
11 KiB
부서관리 화면 구현 체크리스트
생성일: 2025-12-05 상태: 구현 완료 (API 연동 대기) 경로:
인사관리 > 부서관리테스트 URL:http://localhost:3000/ko/hr/department-management
0. 핵심 요구사항
트리 구조 - 무제한 깊이
회사명
├── 부서A
│ ├── 팀A-1
│ │ ├── 파트A-1-1
│ │ │ └── ... (무제한)
│ │ └── 파트A-1-2
│ └── 팀A-2
├── 부서B
│ └── 팀B-1
└── 부서C
- 깊이 제한 없음: 부서 > 하위부서 > 하위부서 > ... 무한 재귀 가능
- 재귀적 데이터 구조:
children배열로 무한 중첩 표현 - 동적 들여쓰기: depth에 따라 padding-left 계산 (
depth * 24px등) - +/- 토글: 하위 항목이 있는 모든 노드에 펼침/접힘 버튼 표시
1. 화면 분석 요약
1.1 스크린샷 기반 기능 정리
| 번호 | 구분 | 기능 | 설명 |
|---|---|---|---|
| 01 | 전체 선택 | 체크박스 | 전체 선택/해제 토글, 디폴트 해제 |
| 02 | 개별 선택 | 체크박스 | 개별 선택/해제 토글, 디폴트 해제 |
| 03 | 추가 버튼 | 상단 액션 | 관리 권한 없으면 숨김, 선택한 부서의 하위 부서 일괄 생성 |
| 04 | 삭제 버튼 | 상단 액션 | 관리 권한 없으면 숨김, "선택한 부서 N개를 삭제하시겠습니까?" Alert |
| 05 | 축소 버튼 | 트리 (-) | 클릭 시 확대 버튼으로 변경, 하위 부서 숨김 |
| 06 | 확대 버튼 | 트리 (+) | 클릭 시 축소 버튼으로 변경, 하위 부서 표시 |
| 07 | 추가 버튼 | 행 호버 | 관리 권한 없으면 숨김, 부서 추가 팝업 표시 |
| 08 | 수정 버튼 | 행 호버 | 관리 권한 없으면 숨김, 부서 수정 팝업 표시 |
| 09 | 삭제 버튼 | 행 호버 | 관리 권한 없으면 숨김, "{부서명} 부서를 삭제하시겠습니까?" Alert |
1.2 팝업 구성
| 팝업 | 구성요소 | 비고 |
|---|---|---|
| 부서 추가 | 부서명 인풋박스 + 취소/등록 버튼 | 기존 부서명 표시, 수정 가능 |
| 부서 수정 | 부서명 인풋박스 + 취소/수정 버튼 | 기존 부서명 표시 |
1.3 삭제 로직
- 삭제 시 해당 부서의 인원은 회사(기본) 인원으로 변경됨
2. 컴포넌트 구조
src/
├── app/[locale]/(protected)/hr/
│ └── department-management/
│ └── page.tsx # 페이지 진입점
│
└── components/hr/
└── DepartmentManagement/
├── index.tsx # 메인 컴포넌트
├── DepartmentStats.tsx # 전체 부서 카운트 카드
├── DepartmentToolbar.tsx # 검색 + 추가/삭제 버튼
├── DepartmentTree.tsx # 트리 구조 테이블
├── DepartmentTreeItem.tsx # 트리 행 (재귀)
├── DepartmentDialog.tsx # 추가/수정 팝업
└── types.ts # 타입 정의
3. 타입 정의
// types.ts
/**
* 부서 데이터 (무제한 깊이 재귀 구조)
* - depth: 렌더링 시 들여쓰기 계산용 (0부터 시작, 제한 없음)
* - children: 하위 부서 배열 (재귀적으로 무한 중첩 가능)
*/
interface Department {
id: number;
name: string;
parentId: number | null;
depth: number; // 깊이 (0: 최상위, 1, 2, 3, ... 무제한)
children?: Department[]; // 하위 부서 (재귀 - 무제한 깊이)
}
interface DepartmentDialogProps {
isOpen: boolean;
onOpenChange: (open: boolean) => void;
mode: 'add' | 'edit';
parentDepartment?: Department; // 추가 시 부모 부서
department?: Department; // 수정 시 대상 부서
onSubmit: (name: string) => void;
}
/**
* 트리 아이템 Props (재귀 렌더링)
*/
interface DepartmentTreeItemProps {
department: Department;
depth: number; // 현재 깊이 (들여쓰기 계산용)
isExpanded: boolean; // 펼침 상태
isSelected: boolean; // 선택 상태
onToggleExpand: (id: number) => void;
onToggleSelect: (id: number) => void;
onAdd: (parentId: number) => void;
onEdit: (department: Department) => void;
onDelete: (department: Department) => void;
expandedIds: Set<number>; // 전체 펼침 상태 (하위 재귀용)
selectedIds: Set<number>; // 전체 선택 상태 (하위 재귀용)
}
4. 구현 체크리스트
Phase 1: 기본 구조 설정
types.ts- Department 타입 및 Props 인터페이스 정의page.tsx- 라우트 페이지 생성 (/hr/department-management)index.tsx- DepartmentManagement 메인 컴포넌트 (PageLayout + PageHeader)
Phase 2: 상단 영역 구현
DepartmentStats.tsx- 전체 부서 카운트 카드 ("전체 부서 N개")DepartmentToolbar.tsx- 검색창 구현DepartmentToolbar.tsx- 선택 카운트 표시 ("총 N건 | N건 선택")DepartmentToolbar.tsx- 추가 버튼 (선택한 부서 하위에 일괄 추가)DepartmentToolbar.tsx- 삭제 버튼 (선택한 부서 일괄 삭제)
Phase 3: 트리 테이블 구현
DepartmentTree.tsx- 테이블 헤더 (전체 선택 체크박스 + 부서명 + 작업)DepartmentTreeItem.tsx- 트리 행 기본 구조 (체크박스 + 들여쓰기 + 부서명)DepartmentTreeItem.tsx- +/- 버튼 (하위 부서 펼침/접힘)DepartmentTreeItem.tsx- 호버 시 작업 버튼 표시 (추가, 수정, 삭제)DepartmentTreeItem.tsx- 재귀 렌더링 (하위 부서 표시)
Phase 4: 다이얼로그 구현
DepartmentDialog.tsx- 부서 추가 팝업 (부서명 입력 + 취소/등록)DepartmentDialog.tsx- 부서 수정 팝업 (부서명 입력 + 취소/수정)- AlertDialog - 단일 삭제 확인 ("{부서명} 부서를 삭제하시겠습니까?")
- AlertDialog - 일괄 삭제 확인 ("선택한 부서 N개를 삭제하시겠습니까?")
Phase 5: 상태 관리 & 인터랙션
- 체크박스 선택 상태 관리 (selectedIds: Set)
- 전체 선택/해제 로직 구현
- 트리 펼침/접힘 상태 관리 (expandedIds: Set)
- 검색 필터링 로직 구현 (TODO: 추후 구현)
- 목업 데이터 생성 및 연동
Phase 6: 스타일 및 마무리
- 기존 프로젝트 디자인 패턴 적용 확인
- 반응형 레이아웃 확인 (모바일/데스크톱)
- 호버 애니메이션 및 트랜지션 확인
- 접근성 확인 (키보드 네비게이션) (TODO: 추후 보완)
5. 디자인 기준 (기존 프로젝트 패턴)
5.1 레이아웃
- 페이지 패딩:
p-4 md:p-6 - 섹션 간격:
space-y-4 md:space-y-6 - 카드:
border rounded-lg p-4
5.2 컴포넌트
| 요소 | 컴포넌트 | 비고 |
|---|---|---|
| 페이지 래퍼 | PageLayout |
maxWidth="full" |
| 헤더 | PageHeader |
icon + title + description |
| 카드 | Card |
전체 부서 카운트 |
| 입력창 | Input |
검색, 부서명 |
| 버튼 | Button |
variant: default, outline, ghost, destructive |
| 체크박스 | Checkbox |
전체/개별 선택 |
| 팝업 | Dialog |
추가/수정 |
| 확인창 | AlertDialog |
삭제 확인 |
5.3 아이콘 (lucide-react)
Building2- 페이지 아이콘Search- 검색Plus- 추가Trash2- 삭제Pencil- 수정ChevronRight/ChevronDown- 트리 펼침/접힘Minus- 트리 접힘 버튼
5.4 인터랙션
- 행 호버:
hover:bg-gray-50 transition-colors - 작업 버튼: 호버 시에만 표시 (
opacity-0 group-hover:opacity-100)
6. 목업 데이터
/**
* 무제한 깊이 트리 구조 목업 데이터
* - depth 0: 회사
* - depth 1: 부서
* - depth 2: 팀
* - depth 3+: 파트, 셀, ... (무제한)
*/
const mockDepartments: Department[] = [
{
id: 1,
name: '회사명',
parentId: null,
depth: 0,
children: [
{
id: 2,
name: '경영지원본부',
parentId: 1,
depth: 1,
children: [
{
id: 4,
name: '인사팀',
parentId: 2,
depth: 2,
children: [
{
id: 7,
name: '채용파트',
parentId: 4,
depth: 3,
children: [
{ id: 10, name: '신입채용셀', parentId: 7, depth: 4, children: [] },
{ id: 11, name: '경력채용셀', parentId: 7, depth: 4, children: [] },
]
},
{ id: 8, name: '교육파트', parentId: 4, depth: 3, children: [] },
]
},
{ id: 5, name: '총무팀', parentId: 2, depth: 2, children: [] },
]
},
{
id: 3,
name: '개발본부',
parentId: 1,
depth: 1,
children: [
{ id: 6, name: '프론트엔드팀', parentId: 3, depth: 2, children: [] },
{ id: 9, name: '백엔드팀', parentId: 3, depth: 2, children: [] },
]
},
]
}
];
/**
* 전체 부서 수 계산 유틸리티 (재귀)
*/
const countAllDepartments = (departments: Department[]): number => {
return departments.reduce((count, dept) => {
return count + 1 + (dept.children ? countAllDepartments(dept.children) : 0);
}, 0);
};
/**
* 모든 부서 ID 추출 유틸리티 (재귀 - 전체 선택용)
*/
const getAllDepartmentIds = (departments: Department[]): number[] => {
return departments.flatMap(dept => [
dept.id,
...(dept.children ? getAllDepartmentIds(dept.children) : [])
]);
};
7. 참고 파일
| 파일 | 참고 내용 |
|---|---|
src/components/items/ItemMasterDataManagement.tsx |
전체 구조 패턴 |
src/components/organisms/PageHeader.tsx |
헤더 컴포넌트 |
src/components/organisms/PageLayout.tsx |
레이아웃 컴포넌트 |
src/components/ui/dialog.tsx |
다이얼로그 사용법 |
src/components/ui/alert-dialog.tsx |
확인 다이얼로그 |
src/components/ui/checkbox.tsx |
체크박스 |
8. 진행 로그
| 날짜 | 작업 내용 | 상태 |
|---|---|---|
| 2025-12-05 | 계획서 작성 | 완료 |
| 2025-12-05 | Phase 1: 기본 구조 (types, page, index) | 완료 |
| 2025-12-05 | Phase 2: 상단 영역 (Stats, Toolbar) | 완료 |
| 2025-12-05 | Phase 3: 트리 테이블 (Tree, TreeItem) | 완료 |
| 2025-12-05 | Phase 4: 다이얼로그 (Dialog, AlertDialog) | 완료 |
| 2025-12-05 | Phase 5: 상태 관리 (선택, 펼침, 추가/수정/삭제) | 완료 |
| 2025-12-05 | 빌드 테스트 | 성공 |
9. 이슈 및 메모
- API 연동 없이 화면만 먼저 구현 (목업 데이터 사용)
- 권한 체크 로직은 추후 API 연동 시 구현 예정
- 삭제 시 인원 이동 로직은 백엔드 API 의존