조직도 다량등록 (Organization Bulk Registration)
조직도 관리 화면에서 부서를 한 건씩 등록하는 대신, 스프레드시트에서 복사한 줄이나 CSV 파일로 여러 부서를 한 번에 등록합니다. 저장 전에 모든 줄을 중앙 원장과 대조해 판정하고, 등록할 수 없는 줄이 하나라도 있으면 아무것도 저장하지 않습니다.
화면 흐름
조직도 관리탭 → 조직도 트리 헤더의다량 등록.- 표에서 복사한 줄을 붙여넣거나
CSV 파일 불러오기로.csv·.tsv·.txt파일을 읽습니다. 파일은 UTF-8(BOM 포함)을 먼저 시도하고, 실패하면 CP949(EUC-KR)로 다시 읽습니다. 두 방식으로도 글자가 깨지면 등록하지 않고 파일 저장 형식을 안내합니다. - 줄별 판정 표에서
Dept Code·부서명·상위·판정을 확인합니다. 판정이 모두등록 가능일 때만N건 등록버튼이 활성화됩니다. - 등록이 끝나면 조직도 트리를 즉시 갱신하고
N개 부서를 다량 등록했습니다.를 알립니다.
엑셀 저장 형식
| 저장 방식 | 결과 |
|---|---|
CSV UTF-8(쉼표로 분리) | 그대로 사용 |
CSV(쉼표로 분리) (한국어 엑셀 기본, CP949) | 자동 판독 |
텍스트(탭으로 분리) (.txt·.tsv) | 자동 판독(탭 구분) |
| 부서명에 쉼표가 있고 따옴표로 감싸지 않은 파일 | 열이 밀려 상위 부서 판정이 어긋나므로 미리보기에서 먼저 확인 |
입력 형식
한 줄이 한 부서이며 열 순서는 Dept Code, 부서명, 상위 Dept Code(선택), 활성(선택)입니다.
csv
PRODUCT,제품기획실,,
PRODUCT_SERVICE,제품서비스팀,PRODUCT,Y
PLATFORM,플랫폼사업부,,
PORTAL_CONSOLE,포털콘솔팀,PLATFORM,N| 규칙 | 내용 |
|---|---|
| 구분자 | 쉼표 또는 탭. 줄마다 판단하며, "연구, 개발팀"처럼 따옴표로 감싼 값의 쉼표는 값으로 보존 |
| 건너뛰는 줄 | 빈 줄, #로 시작하는 주석, 첫 줄의 머리글(Dept Code·부서명·code·name 등) |
| Dept Code | 앞뒤 공백 제거 후 대문자로 정규화, 공백 없는 2~64자 |
| 상위 Dept Code | 비우면 최상위 부서. 내부 id가 아니라 Dept Code로 지정하며 원장 또는 같은 입력 안에서 찾습니다 |
| 활성 | Y/N·true/false·활성/비활성·1/0. 비우면 활성 |
| 상한 | 한 번에 DEPARTMENT_BULK_MAX_ROWS(500)건 |
저장 전 판정
브라우저와 서버가 shared/departmentBulk.ts의 같은 순수 함수를 사용합니다.
| 판정 | 의미 |
|---|---|
READY | 등록 가능. 상위 부서가 같은 입력에 있으면 그 줄을 먼저 저장하도록 정렬 |
SKIPPED_EXISTING | 기등록 코드이며 이미 등록된 Dept Code는 제외를 켠 경우에만 제외 처리 |
DUPLICATE_IN_INPUT | 같은 입력 안에서 Dept Code 중복(먼저 나온 줄 번호를 안내) |
DUPLICATE_IN_DIRECTORY | 원장에 이미 있는 Dept Code |
MISSING_PARENT | 상위 Dept Code가 원장·입력에 없거나 형식이 잘못됨 |
CYCLIC_PARENT | 입력 안에서 상위 지정이 순환 |
INVALID_CODE·INVALID_NAME·INVALID_ACTIVE | 코드·부서명·활성 값 형식 오류 |
서버 계약
| 항목 | 값 |
|---|---|
| 라우트 | directory.bulkCreateDepartments (adminProcedure) |
| 입력 | directoryDepartmentBulkInput — departments[](1~500), skipExisting(기본 false) |
| 구현 | createDirectoryDepartmentsBulk (server/directory.ts) |
| 반환 | { created: [{ id, deptCode, name, parentId }], skippedCount } |
| 저장 | 단일 트랜잭션. 부모가 먼저 오도록 정렬한 뒤 줄마다 nextSiblingDisplayOrder로 형제 노출 순서를 부여합니다 |
| 실패 | 등록 불가 줄이 있으면 BAD_REQUEST와 함께 앞선 3건의 줄 번호·사유를 돌려주고 아무것도 저장하지 않습니다(부분 반영 없음) |
deptCode는 directory_departments_code_unique unique 인덱스를 사용하므로, 동시 등록 경합은 DB 제약에서 차단됩니다.
검증
bash
pnpm check && pnpm testserver/departmentBulk.test.ts— 구분자·머리글·따옴표·활성 값 해석, 부모 우선 정렬, 중복·순환·상위 누락·형식 오류, 500건 초과, zod 입력 계약client/src/lib/departmentBulkDraft.test.ts— 미리보기 요약 문구, 제출 가능 조건, 표시 줄 상한, 상태 라벨, CSV 파일 해석(BOM·CRLF·CP949 폴백·판독 불가 거부)
로컬 dev DB 검증(2026-09-22): 상위 Dept Code 기반 계층 연결, 최상위·형제 노출 순서(100 → 200), isActive 반영, 기등록 코드 차단·제외 옵션, 순환·상위 누락 차단, 라우트 인증(UNAUTHORIZED)과 계약 거부 경로를 확인하고 검증용 부서는 정리했습니다.
남은 선택 작업
- 감사 로그: 단건
createDepartment과 동일하게 생성 감사 기록은 남기지 않습니다. 다량등록도 감사 대상으로 삼으려면DEPARTMENT엔터티에 일괄 생성 action을 추가하는 별도 작업이 필요합니다. - 표에서 직접 붙여넣기 어려운 환경을 위한 파일 업로드 서버 처리(현재는 브라우저에서 읽습니다)와 등록 결과 보고서 내려받기는 아직 없습니다.