JPA 자동 스키마 관리에서 Flyway 기반 DB 마이그레이션

2026. 2. 3. 14:49·Backend/Trouble Shooting

 

배경

프로젝트 초기에는 개발 속도를 우선시해
JPA의 ddl-auto: update와 Java 기반 데이터 초기화 방식을 사용했다.

이 방식은 빠른 개발에는 유리했지만,
서비스 출시를 고려하면서 다음과 같은 한계를 명확히 인식하게 되었다.

  • 스키마 변경이 언제, 왜 발생했는지 추적하기 어려움
  • 애플리케이션 재기동 시 DB 상태가 달라질 수 있음
  • 개발 환경과 운영 환경의 데이터 경계가 흐려짐

이 시점부터 데이터베이스를
“프레임워크가 대신 관리해주는 영역”이 아니라
“서비스 안정성을 좌우하는 핵심 시스템”으로 보게 되었다.


문제 정의

겉으로 드러난 문제는 ddl-auto 설정이었지만,
본질적인 문제는 다음 질문으로 정리되었다.

“DB 스키마 변경에 대한 책임은 어디에 있어야 하는가?”

  • JPA의 자동 스키마 생성에 위임할 것인가
  • 아니면 애플리케이션 레벨에서 명시적으로 관리할 것인가

운영 환경에서는

  • 한 번의 잘못된 스키마 변경이
  • 데이터 유실이나 서비스 장애로 이어질 수 있다.

따라서 변경 이력 추적 가능성, 재현 가능성, 환경 간 일관성을 기준으로
스키마 관리 전략을 재설계하기로 결정했다.


해결 전략

1️⃣ 스키마 변경 책임의 분리

  • JPA
    • 역할: 엔티티 ↔ DB 스키마 간 정합성 검증
    • 설정: ddl-auto: validate
  • Flyway
    • 역할: 실제 스키마 변경의 단일 진실 공급원(Single Source of Truth)

이 구조를 통해

“엔티티는 스키마를 설명하고,
스키마 변경은 SQL로만 발생한다”
는 명확한 책임 분리를 만들었다.

 

2️⃣ 마이그레이션 파일 기반 버전 관리

모든 DDL/DML 변경을
버전이 명시된 SQL 파일로 관리했다.

  • V1__init.sql : 테이블 생성
  • V2__insert_basic_data.sql : 서비스 필수 기초 데이터
  • V3__insert_reference_data.sql : 정적 참조 데이터
  • 개발 환경 전용 마이그레이션 분리

이를 통해 데이터베이스도 코드처럼
“어디까지 적용되었는지 설명 가능한 상태”가 되었다.

3️⃣ 환경 분리 전략

  • 공통 마이그레이션: 개발 / 운영 공통
  • 개발 전용 마이그레이션: 테스트 데이터만 포함
  • 운영 환경:
    • 테스트 데이터 미적용
    • 기존 DB 적용 시 baseline-on-migrate 사용

4️⃣ 엔티티-DB 매핑의 명시화

Flyway + validate 조합을 사용하면서
Hibernate의 암묵적 규칙에 의존하는 것이 얼마나 위험한지 체감했다.

이에 따라 모든 엔티티에 다음을 명시했다.

  • @Table(name = "...")
  • @Column(name = "...")
  • @GeneratedValue(strategy = GenerationType.IDENTITY)

또한 물리 네이밍 전략을 명확히 지정해
Hibernate가 예측 불가능한 컬럼명을 생성하지 않도록 했다.

👉 이 과정은 단순 설정 변경이 아니라
ORM 추상화의 한계를 의식적으로 인지하는 계기가 되었다.


구현 과정에서 겪은 주요 이슈

▪ 컬럼 네이밍 전략 불일치

  • SQL은 camelCase
  • Hibernate 기본 전략은 snake_case

➡️ 해결:

  • 명시적 매핑 + Naming Strategy 통일

▪ @Lob 사용 시 컬럼 타입 불일치

  • ORM 기대 타입과 실제 SQL 타입 불일치로 validation 실패

➡️ 해결:

  • JPA/Hiberante가 생성한 스키마를 사실상의 기준으로 사용하고 있었기 때문에 SQL DDL이 기준이 아니라 도메인 정의가 스키마의 출발점이었다.
  • 따라서, 실제 DB 스키마를 엔티티 정의에 맞추는 방향으로 수정하였다.

▪ ID 생성 전략 누락

  • AUTO 전략 사용 시 MySQL에서 시퀀스 테이블 탐색 문제 발생

➡️ 해결:

  • IDENTITY 전략 명시
  • DB의 AUTO_INCREMENT 정책과 일치시킴

▪ Flyway Checksum mismatch

  • 이미 적용된 마이그레이션 수정으로 인한 오류

➡️ 이슈를 통해 얻은 원칙:

적용된 마이그레이션은 절대 수정하지 않는다.
변경은 항상 “새 버전”으로만 추가한다.


결과

  • DB 스키마 변경 이력이 완전히 추적 가능해짐
  • 배포 시 DB 관련 리스크 감소
  • 개발 / 운영 환경 간 데이터 오염 가능성 제거
  • 신규 인원이 프로젝트 구조를 이해하는 데 걸리는 시간 단축

특히,
SQL 마이그레이션 로그만으로 DB의 현재 상태를 설명할 수 있게 된 점이
가장 큰 구조적 개선이었다.


회고 및 학습

이번 경험을 통해 얻은 가장 중요한 교훈은 다음과 같다.

자동화는 편의를 제공할 뿐,
설계에 대한 책임을 대신 지지 않는다.

초기에는 편리했던 자동 스키마 생성이
프로젝트 규모가 커질수록 위험 요소로 변했다

이후에는

  • “지금 편한 선택”과 “운영에서 안전한 선택”을 구분해서 판단하고
  • 특히 데이터베이스와 관련된 결정은 항상 되돌릴 수 있는지를 기준으로 설계하게 되었다.

 

'Backend > Trouble Shooting' 카테고리의 다른 글

[Spring Boot] Swagger(Springdoc)에서 제네릭 공통 응답 스키마가 꼬이는 문제 해결기  (0) 2026.01.22
[SpringBoot] UTC 시간 처리, 코드가 아니라 설정으로 정리하기 (Hibernate 6 & Jackson)  (0) 2026.01.21
[SpringBoot] 검증 로직의 위치(@Valid vs Service) : 테스트 코드로 드러난 설계 문제  (0) 2026.01.14
[SpringBoot] 날짜와 시간 처리를 UTC로 표준화하기  (0) 2026.01.10
[SpringBoot] 프로젝트에 SonarCloud 연동하기: Gradle + GitHub Actions  (0) 2026.01.05
'Backend/Trouble Shooting' 카테고리의 다른 글
  • [Spring Boot] Swagger(Springdoc)에서 제네릭 공통 응답 스키마가 꼬이는 문제 해결기
  • [SpringBoot] UTC 시간 처리, 코드가 아니라 설정으로 정리하기 (Hibernate 6 & Jackson)
  • [SpringBoot] 검증 로직의 위치(@Valid vs Service) : 테스트 코드로 드러난 설계 문제
  • [SpringBoot] 날짜와 시간 처리를 UTC로 표준화하기
wlals916
wlals916
  • wlals916
    JM.devlog
    wlals916
    GitHub
  • 전체
    오늘
    어제
    • 분류 전체보기 (28)
      • Algorithms (20)
        • Problem Solving (14)
        • Language & Concept (6)
      • Backend (7)
        • Study Notes (1)
        • Trouble Shooting (6)
        • Development (0)
      • AI (0)
      • Review (1)
      • Dev Log (0)
  • 관리자

    • 글쓰기
    • 관리 페이지
    • 티스토리 홈
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 글쓰기
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    백엔드
    백준
    프로그래머스
    코딩테스트
    C++
    코테
    SpringBoot
    DevOps
    김영한
    회고
  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.6
wlals916
JPA 자동 스키마 관리에서 Flyway 기반 DB 마이그레이션
상단으로

티스토리툴바