Files
invyone/db/migrations/RUN_088_MIGRATION.md
johngreen c350ebe86a feat(부서관리): 다중 결재/부서 관리자 + 조직장 (DEPT_MANAGERS 매핑 테이블)
- 마이그레이션 V022/RUN_088: DEPT_MANAGERS 신규 (role: approval/dept/org_leader, PK 3-tuple, FK CASCADE)
- StartupSchemaMigrator 에 V022 idempotent CREATE 추가 → 테넌트 DB 자동 동기화
- mapper.xml: SELECT 에 3 json_agg ::TEXT 컬럼 추가, insertDeptManagers + deleteDeptManagersByDeptAndRole 신규
- service: parseManagersJson + syncManagers (delete-all + insert-all, 최대 10명, 역할 한글 메시지)
- frontend: types 3 필드, DeptDetailDraft 확장, ManagerChipsField (chip+UserSearchModal 재사용), 조직장 Row 신규

기존 DEPT_INFO.APPROVAL_MANAGER / DEPT_MANAGER 단일 컬럼은 호환을 위해 유지.
2026-05-14 15:19:50 +09:00

5.5 KiB

088 마이그레이션 — DEPT_MANAGERS 테이블 추가 (다중 관리자 + 조직장)

작성일: 2026-05-14 작성자: johngreen 관련: RPS 더존 ERP UJA1040 레퍼런스 대비 누락 기능 (A 단계 — 다중 관리자 + 조직장)

목적

부서별로 결재 관리자 / 부서 관리자 / 조직장을 각각 다중 등록 (최대 10명) 할 수 있도록 매핑 테이블 신설.

  • 기존 DEPT_INFO.APPROVAL_MANAGER / DEPT_INFO.DEPT_MANAGER 컬럼은 단일 user_id 만 저장 가능
  • 신규 DEPT_MANAGERS 매핑 테이블이 SoT(source of truth). ROLE 컬럼으로 3 종류 구분
    • approval = 결재 관리자 (자동 결재라인 등록 시 호출)
    • dept = 부서 관리자 (행정 책임자)
    • org_leader = 조직장 (본인 부서 + 하위 부서의 경비/근태 조회·승인 권한)
  • 기존 단일 컬럼은 호환 위해 일단 유지. 향후 cleanup PR 에서 제거 예정

스키마

DEPT_MANAGERS (신규)

컬럼 타입 제약 설명
DEPT_CODE VARCHAR(1024) NOT NULL, FK → DEPT_INFO ON DELETE CASCADE 부서 코드
USER_ID VARCHAR(50) NOT NULL 사용자 ID
ROLE VARCHAR(20) NOT NULL, CHECK approval | dept | org_leader
SORT_ORDER INTEGER NOT NULL DEFAULT 1 표시 순서
CREATED_AT TIMESTAMP NOT NULL DEFAULT NOW() 등록 시각

PK: (DEPT_CODE, USER_ID, ROLE) — 같은 사용자가 같은 부서에 같은 role 로 중복 등록 차단. 인덱스: (DEPT_CODE, ROLE, SORT_ORDER) — 부서별 role 조회 + 정렬 가속.

SQL

-- =================================================================
-- 088: DEPT_MANAGERS 테이블 (idempotent)
-- =================================================================

CREATE TABLE IF NOT EXISTS DEPT_MANAGERS (
    DEPT_CODE   VARCHAR(1024) NOT NULL,
    USER_ID     VARCHAR(50)   NOT NULL,
    ROLE        VARCHAR(20)   NOT NULL,
    SORT_ORDER  INTEGER       NOT NULL DEFAULT 1,
    CREATED_AT  TIMESTAMP     NOT NULL DEFAULT NOW(),
    PRIMARY KEY (DEPT_CODE, USER_ID, ROLE),
    CONSTRAINT chk_dept_managers_role
        CHECK (ROLE IN ('approval', 'dept', 'org_leader')),
    CONSTRAINT fk_dept_managers_dept
        FOREIGN KEY (DEPT_CODE) REFERENCES DEPT_INFO(DEPT_CODE) ON DELETE CASCADE
);

CREATE INDEX IF NOT EXISTS idx_dept_managers_role
    ON DEPT_MANAGERS (DEPT_CODE, ROLE, SORT_ORDER);

부팅 시 StartupSchemaMigrator 가 메타 DB + 모든 활성 테넌트 DB 에 동일 DDL 을 IF NOT EXISTS 로 적용하므로 일반적으로는 별도 수동 실행이 필요 없음.

사전 점검

-- A. 테이블 사전 상태
SELECT table_name FROM information_schema.tables WHERE table_name = 'dept_managers';
-- 빈 결과여야 정상. 이미 있으면 CREATE 의 IF NOT EXISTS 가 안전.

-- B. DEPT_INFO 행수 (FK 영향 범위)
SELECT COUNT(*) FROM DEPT_INFO;

사후 검증

-- C. 테이블 추가 확인
SELECT column_name, data_type, character_maximum_length
  FROM information_schema.columns
 WHERE table_name = 'dept_managers'
 ORDER BY ordinal_position;
-- 기대: 5 행 (DEPT_CODE/USER_ID/ROLE/SORT_ORDER/CREATED_AT)

-- D. CHECK 제약 확인
SELECT constraint_name, check_clause FROM information_schema.check_constraints
 WHERE constraint_name = 'chk_dept_managers_role';
-- 기대: ROLE IN ('approval', 'dept', 'org_leader')

-- E. FK 동작 확인 (테스트)
BEGIN;
INSERT INTO DEPT_MANAGERS (DEPT_CODE, USER_ID, ROLE)
VALUES ('NON_EXISTENT_DEPT', 'tester', 'approval');
-- 기대: FK 위반 에러 (foreign key constraint "fk_dept_managers_dept")
ROLLBACK;

실행

# 1) 메타 DB
psql -h <host> -U postgres -d invyone -f RUN_088.sql

# 2) 각 테넌트 DB (StartupSchemaMigrator 가 부팅 시 자동 적용하므로 통상 생략 가능)
for db in $(psql -tA -d invyone -c "SELECT db_name FROM company_mng WHERE db_status='active'"); do
  echo "=== $db ==="
  psql -h <host> -U postgres -d "$db" -f RUN_088.sql
done

롤백

-- DEPT_MANAGERS 테이블 제거 (저장된 다중 관리자 매핑 함께 삭제됨)
DROP INDEX IF EXISTS idx_dept_managers_role;
DROP TABLE IF EXISTS DEPT_MANAGERS;

롤백 후엔 백엔드/프론트가 단일 APPROVAL_MANAGER / DEPT_MANAGER 컬럼만 사용하는 이전 동작으로 자연스럽게 복귀 (호환 컬럼 유지하기 때문).

적용 환경 체크리스트

  • 로컬 docker naengangi-pg (관련 없음 — invyone DB 는 wace/운영에만 존재)
  • wace 개발서버 PostgreSQL
  • 운영 메타 DB (invyone)
  • 운영 각 테넌트 DB (loop or 부팅 시 자동)

관련 코드

  • Flyway: backend-spring/src/main/resources/db/migration/V022__create_dept_managers.sql
  • StartupSchemaMigrator: backend-spring/src/main/java/com/erp/migration/StartupSchemaMigrator.java (마지막 항목으로 추가)
  • Mapper: backend-spring/src/main/resources/mapper/department.xml
    • selectDepartments / selectDepartmentByCode 의 SELECT 절에 APPROVAL_MANAGERS/DEPT_MANAGERS/ORG_LEADERS json_agg 컬럼 추가
    • 신규 query: insertDeptManagers, deleteDeptManagersByDept
  • Service: DepartmentService.java
    • createDepartment / updateDepartment 가 body 의 approval_managers[]/dept_managers[]/org_leaders[] 배열을 DEPT_MANAGERS 에 sync (트랜잭션, 최대 10명 검증)
  • Frontend: frontend/app/(main)/admin/userMng/deptMngList/page.tsx
    • BasicInfoForm 에 다중 chip UI + ManagerPicker 모달