devcsb / flyway

[2022년 유스콘 - flyway로 DB 형상관리 시작하기] Hands On Lab Session 진행을 위한 예제 프로젝트입니다.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

YOUTHCON'22 - Flyway로 DB 형상관리 시작하기

📌 실습 진행 환경

  • Java 11
  • SpringBoot 2.7.7
  • Gradle 7.6
  • H2 database (2.1.212)
  • IntelliJ IDEA

📝 실습을 마치고 나면...

💯 Flyway를 활용하여 DB 스키마를 형상관리하고, 마이그레이션을 자동화하는 방법에 익숙해질 수 있습니다.

👨 이런 분들이라면, 더욱 도움이 되실거에요

☑️ DB 스키마 변경을 수작업으로 하고 계시는 분

☑️ 개발 환경이 달라질 때마다 반복되는 마이그레이션 작업에 지치신 분

☑️ 반복 작업을 싫어하고 자동화를 좋아하시는 분

🔎 실습 시나리오

  • Flyway Library를 Application에 연동하고, Flyway Gradle Plugin 의존성 추가하기

  • Migration File을 작성하여, DB 스키마를 변경해보기

  • 기존 데이터가 존재하는 상황에서 Baseline을 설정해보기

  • Community Version(무료 버전)에서 Rollback 해보기

  • 로컬환경에서만 Seed Data가 삽입되도록 Repeatable Migration 실행해보기

🚋 그럼 같이 시작해봅시다!

1. Flyway를 적용할 프로젝트를 열고, 아래와 같이 Flyway 라이브러리 의존성과 Gradle Plugin을 추가합니다.

build.gradle

plugins {
	id 'java'
	id 'org.springframework.boot' version '2.7.7'
	id 'io.spring.dependency-management' version '1.0.15.RELEASE'
	id "org.flywaydb.flyway" version "9.8.1" // flyway gradle plugin 의존성 추가
}

group = 'bong'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
	mavenCentral()
}

/* flyway gradle plugin에서 사용하는 설정값 블록 */
flyway {
	url = 'jdbc:h2:~/flyway'
	user = 'sa'
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.boot:spring-boot-starter-jdbc'
	runtimeOnly 'com.h2database:h2'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'

	implementation 'org.flywaydb:flyway-core' // Application 에 flyway library 의존성 추가

}

tasks.named('test') {
	useJUnitPlatform()
}

application.yml

spring:
  h2:
    console:
      enabled: true // h2 웹 콘솔 사용 설정

  datasource:
    url: jdbc:h2:~/flyway
    username: sa
    password:
    driver-class-name: org.h2.Driver

  flyway:
    enabled: true # 기본값 = true 이지만, flyway를 사용하고 있음을 명시해줍니다.

2. Migration File을 작성하여, 여러가지 방법으로 Migration을 수행해봅시다.

  1. Migration File이 위치하는 기본 경로에 맞게, 폴더를 생성합니다.

    src/main/resources/db/migration
  2. Naming Convention을 준수하여 Migration File을 작성합니다.

    V1__create_member_table.sql

    DROP TABLE IF EXISTS member;
    
    CREATE TABLE member
    (
       id   BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
       name VARCHAR(100) NOT NULL
    )
  3. application을 구동시킵니다.

    콘솔 로그를 보시면, 아래 사진과 같이, 정상적으로 Migration 작업이 수행된 것을 확인하실 수 있습니다.

flyway 반영로그

  1. h2 웹 콘솔을 이용하여, db를 직접 조회해봅니다.
http://localhost:8080/h2-console/

애플리케이션이 실행 중인 상태에서, 웹 브라우저에서 위의 주소로 접근하면, h2 웹 콘솔을 사용할 수 있습니다.
JDBC URL 란에 jdbc:h2:~/flyway 경로를 입력하고, 연결 버튼을 눌러, DB에 접근하여 member 테이블이 생긴 것을 확인해봅니다.

지금까지 우리는 애플리케이션에 Flyway Library 의존성을 추가하여, 애플리케이션 부트스트랩 단계에서 Migration 작업을 수행시켜봤습니다.
이 방식은 배포환경에서 유용하게 사용할 수 있습니다.

하지만, Application을 재시작 시키지 않고 Migration 작업만 해야하는 상황도 존재합니다. 이를 위해서 Flyway Gradle Plugin을 이용하여 Migration 작업을 진행해보겠습니다.

  1. V2에 해당하는 Migration File을 만듭니다.

    V2__insert_into_member.sql

    INSERT INTO member(name) VALUES ('최수봉');

💣주의사항

우리는 실습의 편의상, 로컬에 별도의 dbms 설치 없이 h2 database를 Embedded 모드로 사용 중입니다.
h2 DB를 embedded 모드(파일 모드)로 사용하는 경우, 멀티 커넥션이 허용되지않아 db접근에 문제가 발생할 수 있습니다.
그러므로, 이번 실습에서는 실행 중인 어플리케이션을 먼저 종료한 다음, Gradle plugin을 통해 Migration 작업을 하도록 합니다.

  1. Gradle plugin으로 Migration을 수행합니다.

    아래 사진과 같이, IntelliJ 우측 상단 Gradle 탭-프로젝트명-Tasks-flyway 패키지를 보시면
    여러 가지 Flyway 명령어를 GUI 환경에서 사용할 수 있도록 제공하고 있습니다.
    여기서 flywayMigrate를 더블클릭하여, 마이그레이션 작업을 실행시킵니다.

    이는, 프로젝트 루트 경로에서 ./gradlew flywayMigrate 명령어를 수행한 것과 같습니다.

  2. Migration이 정상적으로 수행된 것을 확인할 수 있습니다. db입력

3. Flyway가 형상관리를 시작할 기준점이 될 Baseline을 잡아줍니다.

  1. 기존 데이터가 존재하는 프로젝트라는 시나리오 구성을 위해, flyway_schema_history 테이블을 삭제해줍니다.

  2. gradle plugin에서, flywayMigrate 명령어를 실행시킵니다.

    Found non-empty schema(s) "PUBLIC" but no schema history table. Use baseline() or set baselineOnMigrate to true to initialize the schema history table.

    migration 작업 실행시, 위와 같은 에러가 발생하는 것을 확인할 수 있습니다.
    연동된 데이터소스에 기존 데이터는 존재하는데, 이를 관리하는 flyway_schema_history 테이블 자체가 없는 경우에는,
    변경이력을 쌓을 시작점인 Baseline을 직접 정의해주어야 합니다.

  3. flywayBaseline 명령어로, Baseline을 잡아줍니다.

    flywayBaseline을 더블클릭하여, Baseline 생성 작업을 실행시킵니다.
    프로젝트 루트 경로에서 ./gradlew flywayBaseline 명령어를 수행한 것과 같습니다.

  4. baseline-on-migrate 설정을 이용해, 애플리케이션 부트스트랩 단계에서 Baseline 만들기

    이번에는 Gradle plugin이 아닌, 애플리케이션 구동 시점에서 Baseline을 설정하는 방법을 알아보겠습니다.

    application.yml

    flyway:
      baseline-on-migrate: true # Baseline 생성이 필요한 상황에서 migration 작업 실행시, Baseline 생성부터 하겠다는 설정

    baseline-on-migrate 설정 값이 true인 상태에서,
    애플리케이션을 구동하면, Flyway는 Baseline 생성이 필요한 상황을 스스로 판단하여,
    flyway_schema_history 테이블을 생성하고 Baseline을 생성합니다.

  5. application을 구동시킨 뒤, flyway_schema_history 테이블을 확인합니다.

    성공적으로 적용되었다면, 아래 사진과 같이 flyway_schema_history 테이블에 Baseline(기준선)이 잡힌 것을 확인할 수 있습니다. baseline

4. Community Version에서 Rollback 해보기

먼저, member 테이블에 age 컬럼을 추가하는 V3 마이그레이션을 작성합니다.

V3__add_age_to_member.sql

ALTER TABLE member ADD age INT DEFAULT 0;

정상적으로 반영된 것을 확인 할 수 있습니다.

v3 반영

이제 Rollback을 해보겠습니다.
Rollback을 하려면, 먼저 롤백 대상인 V3 마이그레이션을 보면서, 변경된 스키마 내용을 다시 되돌립니다.

아래 쿼리로, member table에 age 컬럼을 삭제합니다.

ALTER TABLE member DROP COLUMN age;

age 컬럼은 삭제되었지만, flyway_schema_history 테이블에는 기록이 남아있는 것을 확인할 수 있습니다.
flyway_schema_history 테이블에서, 버전 3에 해당하는 Row를 지워줍니다.

DELETE FROM "flyway_schema_history" WHERE "version" = 3;

delete

이렇게 스키마 구조도 이전 버전의 스키마 구조로 복구되었고, 변경이력 또한 삭제되었습니다.
이렇게해서 Rollback 작업이 모두 완료되었습니다.

5. 로컬환경에서만 Seed Data가 삽입되도록 Repeatable Migration 실행해보기

  1. application-local.yml 파일을 생성하고, location 설정을 추가합니다.

    applicaton-local.yml

    spring:
      flyway:
        enabled: true
        locations: classpath:db/migration, classpath:db/seed # 경로가 여러 개일 경우 쉼표(,)로 구분합니다.
  2. application.yml 파일에 local profile 사용을 명시하고,
    배포환경에서는 flyway가 동작하지 않도록 flyway.enabled 속성을 false로 바꿔줍니다.

    applicaton.yml

    spring:
      profiles:
        active: local # local profile 사용하기 위한 설정
    
    flyway:
      enabled: false # 운영환경에서 flyway가 동작하는 것을 방지하기 위하여 false로 설정
  3. resources/db 하위에 Repeatable Migration File만 따로 관리할 seed 폴더를 생성합니다. seed폴더

  4. 아래와 같이 Repeatable Migration File을 작성해줍니다.
    Repeatable Migration File은 File의 checksum값이 변경될 때마다 반영하는 특성을 가집니다.
    매 회 Migration마다 data를 반복적으로 seeding하기 위해서, 주석으로 timestamp를 찍어줍니다.

    R__insert_seed_data_into_member_.sql

    -- ${flyway:timestamp}  # repeatable 파일이 매번 반영되도록 주석으로 timestamp를 찍는다.
    INSERT INTO member(name) VALUES ('토비');

지금까지 다양한 시나리오에서 Flyway를 이용하여 DB 스키마를 코드로 관리하는 Hands-On-Lab Session을 진행했습니다. 이번 실습만 잘 따라오셨다면, 여러분들은 이미 Flyway를 실무에 적용하여 사용하기에 충분한 수준이 된 것입니다.

2022년의 마지막 날, 마지막 발표까지, 긴 시간 Hands-On-Lab Session에 참가해주셔서 정말 감사합니다!

BONUS

💻 CLI 환경에서 Flyway 명령어를 사용하고 싶다면...

아래 블로그에 Flyway CLI Tool을 설치하고, 간단히 사용하는 방법을 정리해두었으니 참고해주세요. https://devcamus.tistory.com/29

About

[2022년 유스콘 - flyway로 DB 형상관리 시작하기] Hands On Lab Session 진행을 위한 예제 프로젝트입니다.


Languages

Language:Java 100.0%