Java 진영에서의 Database Migration Tool에는 대표적으로 Flyway, Liquibase가 있다.
두 가지 도구 모두 데이터베이스 리팩토링 및 버전 관리를 위한 도구이며, 스프링 부트에서 공식적으로 자동 설정을 지원해준다. 그리고 둘 다 오픈소스이며 무료, 유료 버전이 있다.
위 사진은 Spring Initialzr 에서 Dependencies를 확인한 모습이다.
Flyway by Redgate - Database Migration Made Easy.
GitHub - flyway/flyway: Flyway by Redgate
Liquibase | Open Source Version Control for Your Database
GitHub - liquibase/liquibase: Main Liquibase Source
아래는 Liquibase 공식 홈페이지에 게시된 기능 비교 사진이다.
Liquibase vs. Flyway 참고.
Migration Tool을 사용하게 된다면 Flyway를 사용할 예정이며, 이유는 다음과 같다.
DB Schema를 버전관리할 수 있도록 만들어진 툴이며, 개요와 동작 방식은 아래의 공식 문서에 잘 설명되어 있다.
Why Database Migrations
How Flyway Works
요약하자면, DB에 버전 관리 전용 테이블을 하나 만들고 Schema가 변경될 때마다 해당 테이블에 DB Schema 버전 관리에 대한 정보를 저장하는 방식이다. 이는 선형적으로 이루어진다.
현재 설정에 사용된 Spring Boot 프로젝트는 Spring Initializr에서 현재 날짜 기준으로 Flyway 의존성을 추가해서 Kotlin 프로젝트로 만들었다. 아래 사진과 같은 버전 상태이다.
먼저, flyway에 대한 의존성을 build.gradle.kts 파일에 추가해준다.
dependencies {
// ...
implementation("org.flywaydb:flyway-core")
// ...
}
Spring Boot는 Flyway에 대해서 AutoConfiguration 을 지원해준다. 따라서 application.yml에 간단하게 설정을 추가해서 Bean으로 등록해줄 수 있다.
baseline-on-migrate
옵션은 기존에 사용하던 DB Schema(그 중에서도 flyway_schema_history
테이블)가 존재한다면 false, 그게 아니라면 true로 설정하도록 한다. 본 프로젝트는 시범 적용이고 flyway를 처음 적용하므로 아래와 같이 true로 설정했다.
spring:
flyway:
enabled: true
baseline-on-migrate: true
이때, application.yml에 JPA의 ddl-auto 옵션을 사용하고 있다면 none 또는 validate로 사용하도록 한다.
flyway와 hibernate의 ddl-auto의 실행순서는 flyway가 먼저인데, flyway Migration Script에 선언된 DDL이 먼저 실행된 이후, hibernate의 ddl-auto가 실행될 때 already exists ' ' table
과 같은 에러가 발생하면서 프로젝트 구동이 실패하기 때문이다(둘 중에 하나만 적용이 되어야 하는듯 한데, 아직 JPA의 ddl-auto: update 와 함께 사용하는 방법은 찾지 못했다).
만약 flyway Bean을 직접 선언해서 사용할 것이라면 enabled 는 false로 설정한다.
application.yml 에서 spring.flyway.enabled 를 false로 설정했다면 Flyway 객체를 직접 Bean으로 등록해야 한다. 아래처럼 설정 파일을 작성하여 Flyway 객체를 Bean으로 등록한다. 마찬가지로 다양한 설정을 직접 줄 수 있다.
@Configuration
class FlywayConfiguration(val dataSource: DataSource) {
@Bean
fun flyway(): Flyway {
return Flyway.configure()
.dataSource(dataSource)
.baselineOnMigrate(true)
.load()
}
}
flyway는 Migration Script를 작성하고 해당 Script를 통해서 버전을 관리한다.
그에 따라 Migration Script를 반드시 작성해주어야 하는데, 변경 사항을 SQL로 작성하면 된다.
아무 변경사항이 없더라도 버전 관리를 하고싶다면 Migration Script는 반드시 존재해야 한다(아무 내용도 입력하지 않으면 된다).
Flyway는 기본적으로 Migration Script의 파일명을 기준으로 버전 관리 내용을 채워넣는다. 그에 따라, 파일명에는 규칙이 있다. 아래는 공식 문서에서 가져온 Naming 규칙이며, 이를 그대로 따르도록 한다. 물론, 이는 설정을 통해 커스터마이징할 수 있다.
참고로, 파일 이름이 해당 마이그레이션의 설명이 된다.
Migration Script를 작성하는 위치의 기본값은 classpath:db/migration 이다. 해당 위치에 Schema가 변경될 때마다 새롭게 작성해주면 된다. 아래는 각각의 Script의 내용이다.
사용방법은 간단하다. Migration Script를 제대로 작성한 뒤, 프로젝트를 실행하기만 하면 DB에 Script에 작성한 변경점이 반영될 것이다.
아래는 상기에 첨부한 Script에 따른 DB 변경 결과다.
기존에 존재하고 있던, 버전 관리에 반영된 Script 파일은 절대 수정하거나 삭제해서는 안된다. 새로운 변경사항이 발생하면 새롭게 Script 파일을 만들어서 추가해주어야 한다.
만약 문제가 생겼다면 flyway_schema_history
테이블을 직접 변경해야 할 것이다.
Migration Script를 매번 직접 작성하는 것은 굉장히 비효율적이며 생산성을 저하시킬 수 있다. 그래서 자동으로 작성하는 방법을 찾아보았고, 함께 소개하려고 한다.
IDE로 IntelliJ를 사용하고 있다면, JPA Buddy 라는 플러그인을 설치한다.
설치를 정상적으로 잘 했다면, 프로젝트에 맞게 설정을 해주어야 한다.
현재 프로젝트에서는 JPA Naming Strategy
로 org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
을 사용하고 있다.
하지만 JPA Buddy의 Naming Rule은 기본 값이 SpringPhysicalNamingStrategy
이다. 이걸 설정해주지 않으면 Schema 자동 생성시 문제가 발생하게 되고, 원하는대로 버전관리가 되지 않는다(이 부분에서 원인을 찾아 헤메느라 엄청나게 시간을 많이 잡아먹었다). 이는 JPA Buddy가 Hibernate를 모르기 때문에 발생하는 문제다(잘 살펴보면 case-sensitive하다는 것을 알 수 있다). 그래서 따로 설정을 해주어야 한다.
설정은 간단하다. 아래의 Tools → JPA Buddy → Database Versioning 에 naming strategy 를 프로젝트에 맞게 설정해주면 된다.
위 사진은 현재 Entity의 상태이다. 여기서 Column과 Table, 그리고 그를 기반으로 한 Migration Script를 추가해보겠다.
위 사진은 로컬 DB와 현재 프로젝트의 JPA Entity Model을 비교해서 Migration Diff 파일을 생성하는 화면이다.
새롭게 만든 Migration Script 와 그 내용이다.
이제 다시 프로젝트를 실행해보자.
위 사진과 같은 로그를 볼 수 있으며, DB를 살펴보면
첫번째 사진의 버전 이력이 두번째 사진과 같이 변한 것을 볼 수 있다.
위와 같이 마이그레이션을 스키마가 변동될 때마다 관리해서 진행해주면 된다.