Files
sam-react-prod/claudedocs/hr/[IMPL-2025-12-05] department-management-checklist.md
byeongcheolryu 48dbba0e5f feat: 단가관리 페이지 마이그레이션 및 HR 관리 기능 추가
## 단가관리 (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>
2025-12-06 11:36:38 +09:00

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 의존