# 087 마이그레이션 — BATCH_MAPPINGS.MAPPING_CONFIG JSONB 추가

작성일: 2026-05-13
작성자: hjjeong
관련: `notes/hjjeong/2026-05-12-batch-pipeline-current-state.md` (Phase 1)

## 목적

vexplor_rps 의 conditional 매핑(파이프라인) 기능을 INVYONE 으로 이식하기 위한 첫 단계.
`BATCH_MAPPINGS` 행마다 매핑 규칙(when/then/default) 을 JSONB 로 저장할 컬럼 추가.

- `mapping_type='direct'` / `'fixed'` → `MAPPING_CONFIG` 는 NULL
- `mapping_type='conditional'` → `MAPPING_CONFIG` 에 `{"rules":[{"when":"1","then":"Y"}],"default":"?"}` 형태 저장

Phase 2 (frontend ConditionalEditor + API 확장) 와 Phase 3 (Backend MappingTransformer) 가
이 컬럼을 읽고 쓰는 전제로 동작한다.

## 스키마

### BATCH_MAPPINGS ALTER

| 컬럼 | 타입 | 제약 | 설명 |
|---|---|---|---|
| `MAPPING_CONFIG` | JSONB | NULL 허용 | conditional 평가 규칙. direct/fixed 면 NULL |

저장 포맷(`mapping_type='conditional'`):

```json
{
  "rules": [
    { "when": "1", "then": "Y" },
    { "when": "0", "then": "N" }
  ],
  "default": "?"
}
```

## SQL

```sql
-- =================================================================
-- 087: BATCH_MAPPINGS.MAPPING_CONFIG JSONB 추가 (idempotent)
-- =================================================================

ALTER TABLE BATCH_MAPPINGS
    ADD COLUMN IF NOT EXISTS MAPPING_CONFIG JSONB;
```

부팅 시 `StartupSchemaMigrator` 가 메타 DB + 모든 활성 테넌트 DB 에 동일 ALTER 를
`IF NOT EXISTS` 로 적용하므로 일반적으로는 별도 수동 실행이 필요 없음.
별도 환경(콜드 백업 복원 등)에서 수동 실행이 필요할 때 위 SQL 한 줄을 그대로 사용.

## 사전 점검

```sql
-- A. 컬럼 사전 상태
SELECT column_name, data_type FROM information_schema.columns
WHERE table_name = 'batch_mappings' AND column_name = 'mapping_config';
-- 빈 결과여야 정상. 이미 있으면 ALTER 의 IF NOT EXISTS 가 안전.

-- B. 기존 데이터 행수 (마이그레이션 영향 범위 확인)
SELECT COUNT(*) FROM BATCH_MAPPINGS;
-- 컬럼만 추가하므로 기존 행은 MAPPING_CONFIG = NULL 로 유지됨.
```

## 사후 검증

```sql
-- C. 컬럼 추가 확인
SELECT column_name, data_type FROM information_schema.columns
WHERE table_name = 'batch_mappings' AND column_name = 'mapping_config';
-- 기대: data_type = 'jsonb'

-- D. JSONB 동작 확인 (테스트)
BEGIN;
UPDATE BATCH_MAPPINGS
   SET MAPPING_CONFIG = '{"rules":[{"when":"1","then":"Y"}],"default":"?"}'::jsonb
 WHERE ID = (SELECT ID FROM BATCH_MAPPINGS LIMIT 1);
SELECT MAPPING_CONFIG->'rules'->0->>'when' AS sample
  FROM BATCH_MAPPINGS
 WHERE MAPPING_CONFIG IS NOT NULL
 LIMIT 1;
-- 기대: sample = '1'
ROLLBACK;
```

## 실행

```bash
# 1) 메타 DB
psql -h <host> -U postgres -d invyone -f RUN_087.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_087.sql
done
```

`RUN_087.sql` 은 위 "SQL" 섹션의 ALTER 한 줄을 그대로 담은 파일입니다.

## 롤백

```sql
-- MAPPING_CONFIG 컬럼 제거 (저장된 conditional 규칙은 함께 삭제됨)
ALTER TABLE BATCH_MAPPINGS DROP COLUMN IF EXISTS MAPPING_CONFIG;
```

## 적용 환경 체크리스트

- [ ] 로컬 docker `naengangi-pg` (메타 + 활성 테넌트 전부)
- [ ] wace 개발서버 PostgreSQL
- [ ] 운영 메타 DB (`invyone`)
- [ ] 운영 각 테넌트 DB (loop or 부팅 시 자동)

## 관련 코드

- Flyway: `backend-spring/src/main/resources/db/migration/V021__add_batch_mappings_mapping_config.sql`
- StartupSchemaMigrator: `backend-spring/src/main/java/com/erp/migration/StartupSchemaMigrator.java` (마지막 항목)
- Mapper: `backend-spring/src/main/resources/mapper/batch.xml`
  - `getBatchMappingsByConfigId` 의 SELECT 절: `MAPPING_CONFIG::TEXT AS MAPPING_CONFIG`
  - `insertBatchMapping` 의 VALUES 절: `#{mapping_config,jdbcType=OTHER}::jsonb`
- Service: `backend-spring/src/main/java/com/erp/service/BatchService.java`
  - `syncMappings()` 가 `stringifyJsonField(row, "mapping_config")` 로 직렬화 후 INSERT
  - `attachMappings()` 가 `parseJsonField(row, "mapping_config")` 로 SELECT 결과 역직렬화
