Flyway - DB Migration Tool

콜트·2022년 1월 7일
0

Flyway & Liquibase

Java 진영에서의 Database Migration Tool에는 대표적으로 Flyway, Liquibase가 있다.

두 가지 도구 모두 데이터베이스 리팩토링 및 버전 관리를 위한 도구이며, 스프링 부트에서 공식적으로 자동 설정을 지원해준다. 그리고 둘 다 오픈소스이며 무료, 유료 버전이 있다.

위 사진은 Spring Initialzr 에서 Dependencies를 확인한 모습이다.

Flyway

Flyway by Redgate - Database Migration Made Easy.
GitHub - flyway/flyway: Flyway by Redgate

  • 순수 SQL 또는 Java 코드로 작성된 스크립트를 기반으로 한다.
  • Maven, Gradle 플러그인 및 CLI 를 지원한다.

Liquibase

Liquibase | Open Source Version Control for Your Database
GitHub - liquibase/liquibase: Main Liquibase Source

  • SQL, XML, YAML, JSON으로 작성할 수 있는 변경 로그 및 변경 집합 파일을 기반으로 한다.
  • Maven, Gradle 플러그인 및 CLI 를 지원한다.

Flyway vs Liquibase

아래는 Liquibase 공식 홈페이지에 게시된 기능 비교 사진이다.
Liquibase vs. Flyway 참고.

  • Flyway는 주로 SQL을 이용하여 데이터베이스 변경사항을 정의하지만 Liquibase는 JSON, YAML 등으로 제공되는 추상화를 누릴 수 있는 이점이 있다.
  • Flyway는 Java 코드를 이용해서 데이터베이스를 변경하는 것이 가능하므로(API 지원) Java 진영에서 기본적으로 많이 선택된다. BLOB, CLOB 변경 또는 고급 대량 데이터 변경과 같은 복잡한 마이그레이션을 지원한다.
  • Liquibase는 추상화를 제공하므로 서로 다른 기본 데이터베이스 기술이 서로 다른 환경에서 구동되는 경우에 적합하다.
  • Liquibase는 무료 버전에서도 diff를 생성할 수 있지만 Flyway는 diff를 생성할 수 없다(IntelliJ에서 사용할 수 있는 JPA Buddy 라는 플러그인을 이용하면 Flyway도 diff를 생성할 수 있다).
  • Flyway는 유료 버전에서만 롤백을 지원하지만 Liquibase는 무료 버전에서도 롤백을 지원하며, 유료 버전에서는 좀 더 다양한 기능을 지원하는 형태이다.
  • Flyway는 선형적으로 버전을 관리한다. 즉, 변경 사항의 순서가 마이그레이션 스크립트 이름에 따라 다르다. 즉, 스크립트 파일의 명명규칙을 따라야 한다. Liquibase는 고유한 식별 체계를 사용하며 변경 로그를 이용하여 데이터베이스 변경 순서를 쉽게 관리할 수 있다.

기술 선택

Migration Tool을 사용하게 된다면 Flyway를 사용할 예정이며, 이유는 다음과 같다.

  • 언뜻 살펴보면 추상화와 다양한 기능을 제공하는 Liquibase가 훨씬 좋아보이지만, 그렇게까지 많은 기능이 필요하지 않은 경우 오히려 오버 엔지니어링이 될 수 있으며 이를 위해 필요한 시간이 부담일 수 있다.
  • Liquibase에서 제공하는 JSON, YAML 등 추상화된 문법으로 변경 이력을 관리할 수 있다는 건 분명 매력적이지만, SQL을 기본으로 사용하는 Flyway가 적응하기 쉬울 것이다. JPA로 Entity를 작성한다면 누구나 DB 구조를 변경할 수 있다. 따라서 쉽게 적응할 수 있는 것이 가장 중요하다.
  • SQL로 파일을 작성해야 하는 불편함이 있을 수 있으나, JPA의 ddl-auto 를 사용하고 있으므로 show_sql 옵션을 통해 확인할 수 있는 DDL을 그대로 사용하면 되기 때문에 이 부분은 충분히 완화될 수 있다(후술할 Plugin의 도움을 받으면 더 쉽게 할 수 있다!).
  • Flyway가 공식 문서가 좀 더 상세히 작성되어 있다.

Flyway 설정 및 사용방법

Flyway?

DB Schema를 버전관리할 수 있도록 만들어진 툴이며, 개요와 동작 방식은 아래의 공식 문서에 잘 설명되어 있다.

Why Database Migrations
How Flyway Works

요약하자면, DB에 버전 관리 전용 테이블을 하나 만들고 Schema가 변경될 때마다 해당 테이블에 DB Schema 버전 관리에 대한 정보를 저장하는 방식이다. 이는 선형적으로 이루어진다.

설정

프로젝트 환경

현재 설정에 사용된 Spring Boot 프로젝트는 Spring Initializr에서 현재 날짜 기준으로 Flyway 의존성을 추가해서 Kotlin 프로젝트로 만들었다. 아래 사진과 같은 버전 상태이다.

1. build.gradle.kts

먼저, flyway에 대한 의존성을 build.gradle.kts 파일에 추가해준다.

dependencies {
    // ...
	implementation("org.flywaydb:flyway-core")
    // ...
}

2. application.yml

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 와 함께 사용하는 방법은 찾지 못했다).

2-1. FlywayConfiguration

만약 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는 반드시 존재해야 한다(아무 내용도 입력하지 않으면 된다).

Migration Script Naming Rule

Flyway는 기본적으로 Migration Script의 파일명을 기준으로 버전 관리 내용을 채워넣는다. 그에 따라, 파일명에는 규칙이 있다. 아래는 공식 문서에서 가져온 Naming 규칙이며, 이를 그대로 따르도록 한다. 물론, 이는 설정을 통해 커스터마이징할 수 있다.

참고로, 파일 이름이 해당 마이그레이션의 설명이 된다.

Flyway Migrations 참고.

Script Locations

Migration Script를 작성하는 위치의 기본값은 classpath:db/migration 이다. 해당 위치에 Schema가 변경될 때마다 새롭게 작성해주면 된다. 아래는 각각의 Script의 내용이다.

결과

사용방법은 간단하다. Migration Script를 제대로 작성한 뒤, 프로젝트를 실행하기만 하면 DB에 Script에 작성한 변경점이 반영될 것이다.
아래는 상기에 첨부한 Script에 따른 DB 변경 결과다.

주의사항!

기존에 존재하고 있던, 버전 관리에 반영된 Script 파일은 절대 수정하거나 삭제해서는 안된다. 새로운 변경사항이 발생하면 새롭게 Script 파일을 만들어서 추가해주어야 한다.
만약 문제가 생겼다면 flyway_schema_history 테이블을 직접 변경해야 할 것이다.

JPA Entity를 기반으로 한 Migration Diff Script 자동 작성

Migration Script를 매번 직접 작성하는 것은 굉장히 비효율적이며 생산성을 저하시킬 수 있다. 그래서 자동으로 작성하는 방법을 찾아보았고, 함께 소개하려고 한다.

JPA Buddy

IDE로 IntelliJ를 사용하고 있다면, JPA Buddy 라는 플러그인을 설치한다.

설치를 정상적으로 잘 했다면, 프로젝트에 맞게 설정을 해주어야 한다.

현재 프로젝트에서는 JPA Naming Strategyorg.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl 을 사용하고 있다.

하지만 JPA Buddy의 Naming Rule은 기본 값이 SpringPhysicalNamingStrategy 이다. 이걸 설정해주지 않으면 Schema 자동 생성시 문제가 발생하게 되고, 원하는대로 버전관리가 되지 않는다(이 부분에서 원인을 찾아 헤메느라 엄청나게 시간을 많이 잡아먹었다). 이는 JPA Buddy가 Hibernate를 모르기 때문에 발생하는 문제다(잘 살펴보면 case-sensitive하다는 것을 알 수 있다). 그래서 따로 설정을 해주어야 한다.

Naming Strategy 설정

설정은 간단하다. 아래의 Tools → JPA Buddy → Database Versioning 에 naming strategy 를 프로젝트에 맞게 설정해주면 된다.

JPA 기반 DB Schema Diff 자동 생성

위 사진은 현재 Entity의 상태이다. 여기서 Column과 Table, 그리고 그를 기반으로 한 Migration Script를 추가해보겠다.

위 사진은 로컬 DB와 현재 프로젝트의 JPA Entity Model을 비교해서 Migration Diff 파일을 생성하는 화면이다.

새롭게 만든 Migration Script 와 그 내용이다.

결과

이제 다시 프로젝트를 실행해보자.

위 사진과 같은 로그를 볼 수 있으며, DB를 살펴보면

첫번째 사진의 버전 이력이 두번째 사진과 같이 변한 것을 볼 수 있다.
위와 같이 마이그레이션을 스키마가 변동될 때마다 관리해서 진행해주면 된다.

profile
개발 블로그이지만 꼭 개발 이야기만 쓰라는 법은 없으니, 그냥 쓰고 싶은 내용이면 뭐든 쓰려고 합니다. 코드는 깃허브에다 작성할 수도 있으니까요.

0개의 댓글