hgzero/meeting/section_id.md
2025-10-31 14:35:53 +09:00

6.5 KiB

Meeting 서비스 section_id 이슈 해결 기록

문제 상황

발생한 에러

Caused by: org.postgresql.util.PSQLException: ERROR: column "section_id" of relation "minutes_sections" contains null values

에러 원인

  • JPA Entity인 MinutesSectionEntitysection_id를 Primary Key로 정의
  • 실제 데이터베이스 테이블에는 section_id 컬럼이 존재하지 않음
  • JPA가 ddl-auto: update 모드로 NOT NULL 제약조건을 추가하려고 시도했으나 실패

해결 과정

1단계: 문제 분석

  • 마이그레이션 파일 확인: V1이 없고 V2, V3만 존재
  • JPA 설정 확인: ddl-auto: update 모드 사용 중
  • Entity 분석: MinutesSectionEntity.section_id는 Primary Key로 정의됨

2단계: 해결 방안 선택

사용자 선택:

  • 방안 1: 기존 데이터 정리 후 스키마 재구성
  • 옵션 B: Foreign Key 제약조건 제거하여 기존 데이터 보존

3단계: Flyway 마이그레이션 설정

build.gradle 수정

// Flyway 의존성 추가
implementation 'org.flywaydb:flyway-core'
runtimeOnly 'org.flywaydb:flyway-database-postgresql'

application.yml 수정

jpa:
  hibernate:
    ddl-auto: validate  # update → validate로 변경

# Flyway 설정 추가
flyway:
  enabled: true
  baseline-on-migrate: true
  baseline-version: 0
  validate-on-migrate: true

4단계: 마이그레이션 파일 생성

V1__create_initial_schema.sql

  • 모든 초기 테이블 생성
  • Foreign Key 제약조건 주석 처리 (옵션 B 선택)
    • fk_todos_minutes: todos 테이블의 minutes_id 외래키
    • fk_sessions_meetings: sessions 테이블의 meeting_id 외래키
-- 외래키 제약조건 비활성화 예시
-- ALTER TABLE todos DROP CONSTRAINT IF EXISTS fk_todos_minutes;
-- ALTER TABLE todos ADD CONSTRAINT fk_todos_minutes
--     FOREIGN KEY (minutes_id) REFERENCES minutes(minutes_id);

V2__create_meeting_participants_table.sql

  • V1에 통합되어 no-op 처리
SELECT 1; -- No-op statement

V4__fix_missing_columns.sql

  • minutes_sections 테이블에 section_id 컬럼 추가
  • 시퀀스 생성 후 기존 데이터에 자동 ID 할당
-- 1. 시퀀스 생성
CREATE SEQUENCE IF NOT EXISTS minutes_sections_temp_seq;

-- 2. section_id 컬럼 추가
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns
                   WHERE table_name = 'minutes_sections' AND column_name = 'section_id') THEN
        -- 임시 컬럼 추가
        ALTER TABLE minutes_sections ADD COLUMN temp_section_id VARCHAR(50);

        -- 기존 데이터에 ID 생성
        UPDATE minutes_sections
        SET temp_section_id = 'section-' || nextval('minutes_sections_temp_seq'::regclass)
        WHERE temp_section_id IS NULL;

        -- 기존 Primary Key 제거
        ALTER TABLE minutes_sections DROP CONSTRAINT IF EXISTS minutes_sections_pkey;

        -- 컬럼명 변경
        ALTER TABLE minutes_sections RENAME COLUMN temp_section_id TO section_id;
        ALTER TABLE minutes_sections ALTER COLUMN section_id SET NOT NULL;

        -- 새로운 Primary Key 설정
        ALTER TABLE minutes_sections ADD PRIMARY KEY (section_id);
    END IF;
END $$;

5단계: 실행 프로파일 수정

.run/meeting-service.run.xml

<entry key="JPA_DDL_AUTO" value="validate" />

발생한 에러들과 해결

에러 1: PostgreSQL 인증 실패

  • 증상: psql 직접 연결 실패
  • 해결: Flyway 마이그레이션 사용으로 전환

에러 2: 컬럼 존재하지 않음 (42703)

  • 증상: V1 마이그레이션의 DELETE 문에서 존재하지 않는 컬럼 참조
  • 해결: DELETE 문 제거, CREATE TABLE IF NOT EXISTS 활용

에러 3: Foreign Key 제약조건 위반 (23503)

  • 증상: ERROR: insert or update on table "todos" violates foreign key constraint "fk_todos_minutes"
  • 해결: 사용자 선택(옵션 B)에 따라 Foreign Key 제약조건 주석 처리

에러 4: 인덱스 중복 (42P07)

  • 증상: V2 마이그레이션이 이미 존재하는 테이블/인덱스 생성 시도
  • 해결: V2를 no-op으로 변경 (SELECT 1)

에러 5: section_id 컬럼 누락

  • 증상: Schema-validation: missing column [section_id] in table [minutes_sections]
  • 해결: V4 마이그레이션 생성하여 컬럼 추가

에러 6: 시퀀스 존재하지 않음 (42P01)

  • 증상: V4에서 시퀀스를 생성 전에 사용 시도
  • 해결: 시퀀스 생성을 DO 블록 앞으로 이동

에러 7: 포트 사용 중

  • 증상: 8082 포트가 이미 사용 중
  • 해결: lsof -ti:8082 | xargs -r kill -9로 프로세스 종료

최종 결과

서비스 정상 실행 확인

curl http://localhost:8082/actuator/health

응답:

{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "PostgreSQL",
        "validationQuery": "isValid()"
      }
    },
    "diskSpace": {"status": "UP"},
    "ping": {"status": "UP"},
    "redis": {"status": "UP"}
  }
}

마이그레이션 적용 결과

  • V1: 초기 스키마 생성 (FK 제약조건 제외)
  • V2: no-op (V1에 통합)
  • V3: 기존 유지
  • V4: section_id 컬럼 추가

주요 변경 파일

  1. meeting/src/main/resources/db/migration/V1__create_initial_schema.sql (생성)
  2. meeting/src/main/resources/db/migration/V2__create_meeting_participants_table.sql (수정)
  3. meeting/src/main/resources/db/migration/V4__fix_missing_columns.sql (생성)
  4. meeting/src/main/resources/application.yml (수정)
  5. meeting/.run/meeting-service.run.xml (수정)
  6. build.gradle (수정)

교훈

  1. Flyway를 초기부터 사용: JPA의 ddl-auto: update는 프로덕션 환경에서 위험
  2. Baseline 마이그레이션 전략: 기존 스키마를 버전 0으로 인정하고 점진적 수정
  3. 데이터 보존 vs 무결성: 비즈니스 요구사항에 따라 Foreign Key 제약조건 선택적 적용
  4. 마이그레이션 테스트: 각 마이그레이션은 독립적으로 실행 가능해야 함
  5. 환경 변수 관리: ddl-auto 설정은 환경별로 다르게 관리 필요

향후 개선 사항

  1. Foreign Key 제약조건 재검토

    • 데이터 정리 후 제약조건 활성화 검토
    • 참조 무결성 확보 방안 논의
  2. 마이그레이션 전략 수립

    • 개발/스테이징/프로덕션 환경별 마이그레이션 절차
    • 롤백 계획 수립
  3. 모니터링 강화

    • 데이터베이스 스키마 변경 추적
    • 마이그레이션 실패 알림 설정