ERP-node/db/migrations/RUN_046_MIGRATION.md

7.1 KiB

마이그레이션 046: 채번규칙 scope_type 확장

📋 목적

메뉴 기반 채번규칙 필터링을 테이블 기반 필터링으로 전환하여 더 직관적이고 유지보수하기 쉬운 시스템 구축

주요 변경사항

  1. scope_type 값 확장: 'global', 'menu''global', 'table', 'menu'
  2. 기존 데이터 자동 마이그레이션 (global + table_nametable)
  3. 유효성 검증 제약조건 추가
  4. 멀티테넌시 인덱스 최적화

🚀 실행 방법

Docker 환경 (권장)

docker exec -i erp-node-db-1 psql -U postgres -d ilshin < db/migrations/046_update_numbering_rules_scope_type.sql

로컬 PostgreSQL

psql -U postgres -d ilshin -f db/migrations/046_update_numbering_rules_scope_type.sql

pgAdmin / DBeaver

  1. db/migrations/046_update_numbering_rules_scope_type.sql 파일 열기
  2. 전체 내용 복사
  3. SQL 쿼리 창에 붙여넣기
  4. 실행 (F5 또는 Execute)

검증 방법

1. 제약조건 확인

SELECT conname, pg_get_constraintdef(oid)
FROM pg_constraint
WHERE conrelid = 'numbering_rules'::regclass
  AND conname LIKE '%scope%';

예상 결과:

conname                               | pg_get_constraintdef
--------------------------------------|---------------------
check_scope_type                      | CHECK (scope_type IN ('global', 'table', 'menu'))
check_table_scope_requires_table_name | CHECK (...)
check_global_scope_no_table_name      | CHECK (...)
check_menu_scope_requires_menu_objid  | CHECK (...)

2. 인덱스 확인

SELECT indexname, indexdef
FROM pg_indexes
WHERE tablename = 'numbering_rules'
  AND indexname LIKE '%scope%'
ORDER BY indexname;

예상 결과:

indexname                           | indexdef
------------------------------------|----------
idx_numbering_rules_scope_menu      | CREATE INDEX ... (scope_type, menu_objid, company_code)
idx_numbering_rules_scope_table     | CREATE INDEX ... (scope_type, table_name, company_code)

3. 데이터 마이그레이션 확인

-- scope_type별 개수
SELECT scope_type, COUNT(*) as count
FROM numbering_rules
GROUP BY scope_type;

예상 결과:

scope_type | count
-----------|------
global     | X개   (table_name이 NULL인 규칙들)
table      | Y개   (table_name이 있는 규칙들)
menu       | Z개   (menu_objid가 있는 규칙들)

4. 유효성 검증

-- 이 쿼리들은 모두 0개를 반환해야 정상
-- 1) global인데 table_name이 있는 규칙 (없어야 함)
SELECT COUNT(*) as invalid_global
FROM numbering_rules
WHERE scope_type = 'global' AND table_name IS NOT NULL;

-- 2) table인데 table_name이 없는 규칙 (없어야 함)
SELECT COUNT(*) as invalid_table
FROM numbering_rules
WHERE scope_type = 'table' AND table_name IS NULL;

-- 3) menu인데 menu_objid가 없는 규칙 (없어야 함)
SELECT COUNT(*) as invalid_menu
FROM numbering_rules
WHERE scope_type = 'menu' AND menu_objid IS NULL;

모든 카운트가 0이어야 정상

5. 회사별 데이터 격리 확인 (멀티테넌시)

-- 회사별 규칙 개수
SELECT 
  company_code, 
  scope_type, 
  COUNT(*) as count
FROM numbering_rules
GROUP BY company_code, scope_type
ORDER BY company_code, scope_type;

각 회사의 데이터가 독립적으로 존재해야 함


🚨 롤백 방법 (문제 발생 시)

BEGIN;

-- 제약조건 제거
ALTER TABLE numbering_rules DROP CONSTRAINT IF EXISTS check_scope_type;
ALTER TABLE numbering_rules DROP CONSTRAINT IF EXISTS check_table_scope_requires_table_name;
ALTER TABLE numbering_rules DROP CONSTRAINT IF EXISTS check_global_scope_no_table_name;
ALTER TABLE numbering_rules DROP CONSTRAINT IF EXISTS check_menu_scope_requires_menu_objid;

-- 인덱스 제거
DROP INDEX IF EXISTS idx_numbering_rules_scope_table;
DROP INDEX IF EXISTS idx_numbering_rules_scope_menu;

-- 데이터 롤백 (table → global)
UPDATE numbering_rules
SET scope_type = 'global'
WHERE scope_type = 'table';

-- 기존 제약조건 복원
ALTER TABLE numbering_rules
ADD CONSTRAINT check_scope_type 
CHECK (scope_type IN ('global', 'menu'));

-- 기존 인덱스 복원
CREATE INDEX IF NOT EXISTS idx_numbering_rules_table 
ON numbering_rules(table_name, column_name);

COMMIT;

📊 마이그레이션 내용 상세

변경 사항

항목 변경 전 변경 후
scope_type 값 'global', 'menu' 'global', 'table', 'menu'
유효성 검증 없음 table/global/menu 타입별 제약조건 추가
인덱스 (table_name, column_name) (scope_type, table_name, company_code)
(scope_type, menu_objid, company_code)
데이터 global + table_name table 타입으로 자동 변경

영향받는 데이터

-- 자동으로 변경되는 규칙 조회
SELECT 
  rule_id,
  rule_name,
  scope_type as old_scope_type,
  'table' as new_scope_type,
  table_name,
  company_code
FROM numbering_rules
WHERE scope_type = 'global' 
  AND table_name IS NOT NULL;

⚠️ 주의사항

  1. 백업 필수: 마이그레이션 실행 전 반드시 데이터베이스 백업
  2. 트랜잭션: 전체 마이그레이션이 하나의 트랜잭션으로 실행됨 (실패 시 자동 롤백)
  3. 성능: 규칙이 많으면 실행 시간이 길어질 수 있음 (보통 1초 이내)
  4. 멀티테넌시: 모든 회사의 데이터가 안전하게 마이그레이션됨
  5. 하위 호환성: 기존 기능 100% 유지 (자동 변환)

🔍 문제 해결

제약조건 충돌 발생 시

-- 문제가 되는 데이터 확인
SELECT rule_id, rule_name, scope_type, table_name, menu_objid
FROM numbering_rules
WHERE 
  (scope_type = 'table' AND table_name IS NULL)
  OR (scope_type = 'global' AND table_name IS NOT NULL)
  OR (scope_type = 'menu' AND menu_objid IS NULL);

-- 수동 수정 후 다시 마이그레이션 실행

인덱스 생성 실패 시

-- 기존 인덱스 확인
SELECT indexname, indexdef
FROM pg_indexes
WHERE tablename = 'numbering_rules';

-- 충돌하는 인덱스 삭제 후 다시 실행
DROP INDEX IF EXISTS <충돌하는_인덱스명>;

📈 성능 개선 효과

Before (기존)

-- 단일 인덱스: (table_name, column_name)
-- company_code 필터링 시 Full Table Scan 가능성

After (변경 후)

-- 복합 인덱스: (scope_type, table_name, company_code)
-- 멀티테넌시 쿼리 성능 향상 (회사별 격리 최적화)
-- WHERE 절과 ORDER BY 절 모두 인덱스 활용 가능

예상 성능 향상: 회사별 규칙 조회 시 3-5배 빠름


📞 지원

  • 작성자: 개발팀
  • 작성일: 2025-11-08
  • 관련 문서: /채번규칙_테이블기반_필터링_구현_계획서.md
  • 이슈 발생 시: 롤백 스크립트 실행 후 개발팀 문의

다음 단계

마이그레이션 완료 후:

  1. 검증 쿼리 실행
  2. 백엔드 API 수정 (Phase 2)
  3. 프론트엔드 수정 (Phase 3-5)
  4. 통합 테스트

마이그레이션 준비 완료! 🚀