Spring

Flyway 도입 전 탐색해보기

recent0 2024. 12. 7. 15:01

0. 서론


이전에 Flyway를 사용해서 DB 형상 관리를 하면, 좀 더 개발에 집중할 수 있다고 생각해 도입한 기억이 있습니다. 다만 소수 인원이었기에 단순히 배포할 때, 실행해야 할 쿼리가 누락되는 걸 방지하기 위한 용도로만 사용했습니다. 이후 업무를 하면서 DDL, DML 쿼리가 생각보다 자주 발생하는 것을 느끼고 이를 팀원끼리 매번 공유하기 어려워, Flyway를 도입해 그 시간을 단축하면 좋겠다는 생각이 들었습니다. 그리하여 도입 전 Flyway에 대해 알아보고 정확히 알아보기 위해 이번 글을 작성하고자 합니다.

 

1. Flyway 알아보기


1.1 Flyway란?

  • Flyway는 DB 마이그레이션 툴로, DB 형상관리를 위한 도구
  • Git을 통해 코드 형상 관리를 하듯, Flyway를 사용해 다른 사람들과 DB 형상관리를 할 수 있음

1.2 Flyway를 통해 얻을 수 있는 장점

  • DB 형상관리를 통해 수동으로 쿼리를 공유하고 실행하지 않아 개발에 집중 가능
  • 이전에 실행되었던 쿼리들의 히스토리를 쉽게 관리할 수 있음
  • DB 마이그레이션 자동화

1.3 DB 마이그레이션이 왜 필요한가?

출처 : https://documentation.red-gate.com/flyway/getting-started-with-flyway/why-database-migrations

  • 코드 부분은 현재 Git과 같은 형상관리 툴로 관리가 잘 이루어지고 있음
  • 하지만 DB 상태에 대해서는 정확한 관리가 되고 있지 않음
  • 그렇기 때문에 각각의 환경에서 DB 관리를 수동으로 진행하거나 누락되는 경우가 종종 생김
  • 이때 Flyway를 사용해서 DB도 코드처럼 형상관리를 통해, 각각 환경에 맞게끔 DB 마이그레이션을 진행해 생산성을 높일 수 있음

 

2. Spring Boot Flyway 적용


2.1 개발환경

  • Spring Boot 3.3.6, Java 17, MySQL 8.0.36

2.2 Flyway 설정

Flyway Gradle 설정

  • Gradle Task에서 flyway를 사용하기 위해 plugins 부분을 아래와 같이 추가
plugins {
    id "org.flywaydb.flyway" version "11.0.0"
}

dependencies {
	implementation 'org.flywaydb:flyway-core'
	implementation 'org.flywaydb:flyway-mysql'
}

flyway {
	url = 'jdbc:mysql://localhost:3306/beer?useSSL=false&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true'
	user = 'root'
	password = '1234'
	baselineVersion = 1
	outOfOrder = true
}

Flyway 스크립트 파일 기본 위치 및 설정

  • Flyway가 스크립트를 실행하기 위한 기본 파일 위치는 main/resources/db/migration
  • 다만 application.yml에서 spring.flyway.locations에 value를 설정해 위치를 변경할 수 있음

2.3 Flyway Schema History

  • flyway를 통해 스키마를 관리할 때 어떤 변경 내역들이 존재했는지 확인할 수 있는 테이블
  • 작성했던 스크립트를 기반으로 version, type, description과 같은 데이터가 저장
  • 스크립트 실행 시간, 성공 여부 또한 확인할 수 있음
create table if not exists beer.flyway_schema_history
(
    installed_rank int                                 not null
        primary key,
    version        varchar(50)                         null,
    description    varchar(200)                        not null,
    type           varchar(20)                         not null,
    script         varchar(1000)                       not null,
    checksum       int                                 null,
    installed_by   varchar(100)                        not null,
    installed_on   timestamp default CURRENT_TIMESTAMP not null,
    execution_time int                                 not null,
    success        tinyint(1)                          not null
);

2.4 Flyway 컨벤션

Flyway 스크립트 네이밍

  • flyway 스크립트 네이밍 Prefix + Version + Separator + Description + suffix으로 조합됨
  • 조합한 네이밍 규칙은 아래와 같습니다

출처 : https://documentation.red-gate.com/fd/migrations-184127470.html#naming-1

Prefix

  • Prefix는 크게 V(Versioned), U(Undo), R(Repeatable)로 구분
  • V(Versioned)는 현재 버전을 새로운 버전으로 업데이트할 때 사용
  • U는 현재 버전을 이전 버전으로 되돌리고 싶은 경우
  • R은 버전 관련없이 매번 실행되어야 할 때

Version

  • version은 dots나 underscore를 통해 자유롭게 작성할 수 있음
  • 다만 version을 작성할 때는 이전 버전보다 높게 작성해야 함
  • 예를 들어 version을 1.0 이후에 1.1이나 2.0과 같이 작성해야 Flyway가 스크립트를 적용함

Seperator, Description, Suffix

  • Seperator는 무조건 언더스코어를 두 번 적어서 분리해야 함
  • Description은 스크립트 내용을 설명하도록 작성할 수 있고, underscore나 space로 단어들을 분리할 수 있음
  • Suffix는 단순히 sql 파일임을 명시하면 됨

 

3. Spring Boot Flyway 활용


3.1 Prefix에 따른 스크립트 작성

V(Versioned) 

V1 적용

  • V1 Script에서 테이블을 생성하도록 실행
  • flyway schema history를 확인했을 때 정상적으로 실행된 것을 확인

 

V1 Migration 이후 V0.1 Migration 적용 여부 확인

  • V1로 Migration을 진행한 후 아래와 같이 V0.1 script를 만들어 어플리케이션 서버를 실행
  • 실행 콘솔을 확인해 보면 Version이 더 낮아서 무시되는 script가 있음을 flyway가 검증하는 것을 확인

R(Repetable)

  • 어플리케이션 서버를 실행할때마다 스크립트가 실행되는 것을 확인(R__insert_test_data.sql이 지속적으로 추가됨)

 

4. 여러 DB에서 Flyway 적용


하나의 DB가 아닌 여러 개의 DB나 Schema를 사용하는 경우에는 어떻게 Flyway를 적용할 수 있는지에 대해서도 알아보았습니다.

4.1 Datasource 설정

  • 아래와 같은 DB에 Schema를 다르게 하여 2개의 DataSource를 빈으로 등록
@Configuration
public class DataSourceConfig {

    @Primary
    @Bean(name = "primaryDataSource")
    public DataSource primaryDataSource() {
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setJdbcUrl("jdbc:mysql://localhost:3306/beer?useSSL=false&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true");
        hikariConfig.setDriverClassName("com.mysql.cj.jdbc.Driver");
        hikariConfig.setUsername("root");
        hikariConfig.setPassword("1234");
        return new HikariDataSource(hikariConfig);
    }

    @Bean(name = "secondaryDataSource")
    public DataSource secondaryDataSource() {
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setJdbcUrl("jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true");
        hikariConfig.setDriverClassName("com.mysql.cj.jdbc.Driver");
        hikariConfig.setUsername("root");
        hikariConfig.setPassword("1234");
        return new HikariDataSource(hikariConfig);
    }
}

4.2 Flyway 설정 및 migrate 실행

  • 이후 각 Schema에 맞게끔 flyway가 동작하게끔 Datasource, script locations, table을 설정
  • 어플리케이션 서버를 실행시킬 때 Migrate 되도록 migrate() 메서드 호출
@Configuration
public class FlywayConfig {

    @Primary
    @Bean(name = "primaryFlyway")
    public Flyway primaryFlyway(@Qualifier("primaryDataSource") DataSource primaryDataSource) {
        Flyway flyway = Flyway.configure()
                .dataSource(primaryDataSource)
                .baselineOnMigrate(true)
                .baselineVersion("0")
                .locations("classpath:db/migration/primary")
                .table("flyway_schema_history_primary")
                .load();
        flyway.migrate();
        return flyway;
    }

    @Bean(name = "secondaryFlyway")
    public Flyway secondaryFlyway(@Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
        Flyway flyway = Flyway.configure()
                .dataSource(secondaryDataSource)
                .baselineOnMigrate(true)
                .baselineVersion("0")
                .locations("classpath:db/migration/secondary")
                .table("flyway_schema_history_secondary")
                .load();
        flyway.migrate();
        return flyway;
    }
}

 

4.3 Flyway Script 실행 확인

  • 어플리케이션을 실행시킨 후 각 스키마에 Flyway_schema_history가 생성되어 있음을 확인

flyway 각각 적용된것 확인

 

맺음말

앞으로 어떻게 Flyway를 적용하고 형상관리를 할지는 팀원들과 규칙을 정해서 사용할 필요가 있다고 느꼈습니다. 그리고 다음과 같은 점들을 배울 수 있었습니다.

  • Flyway 기본적인 사용법 및 사용이유
  • 여러 DB에서 Flyway 적용 및 관리

읽어주셔서 감사합니다.