Skip to content

Flyway를 사용하는 이유

JaeHo Kong edited this page Aug 1, 2022 · 1 revision

Flyway

1. DB Migration 필요한 것일까?

현재 우리는 배포 후 데이터를 관리해본 경험이 없다.

유지 보수 중 스키마 구조가 바뀌는 상황에 어떻게 대처할 것인가?

예제로 알아보자

  1. 기존 테이블 (SampleEntity)
@Entity
public class SampleEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
}
  1. age 필드 추가
@Entity
public class SampleEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private Integer age;
}

이 상황에서 변화를 어떻게 처리할 것인가?

방법1. DB 서버에 들어가 테이블 직접수정한다

ALTER TABLE sample_entity ADD COLUMN age integer default 0
  • 실수하기 좋다
  • 작업하기 번거롭다
  • 만약 데이터가 미리 있고, Entity쪽만 변경(age추가), DB 스키마를 수정하지 않았다면? image Caused by: java.sql.SQLSyntaxErrorException: Unknown column 'age' in 'field list' 에러가 발생한다.

방법2. Flyway 툴을 사용하자

궁극적인 목표

  1. DB에 접속해서 TABLE을 직접 건들지 않으며, age 컬럼 포함한 신규데이터를 저장한다.
  2. 이전 나이가 포함되지 않는 데이터는 나이 필드를 0으로 초기화한다.
  3. git으로 관리한다 → 파일로 관리할 수 있어야 한다.

Flyway를 사용해보자

1. 의존성 추가 build.gradle

dependencies {
    implementation('org.flywaydb:flyway-core:6.4.2')
}

2. 어플리케이션 설정 추가 : application.yml

  • data source 설정 (DB정보)

    spring:
      datasource:
        driver-class-name: org.h2.Driver
        url: jdbc:h2:tcp://localhost/~/flyway
        username: sa
        password:
  • flyway 설정

    spring:
      jpa:
        properties:
          hibernate:
    				jdbc.lob.non_contextual_creation: true
        generate-ddl: false
      flyway:
        enabled: true
  • Spring boot 2 이상의 경우 아래 설정 추가

    spring:
      flyway:
        baseline-on-migrate: true

3. init 파일 추가: resources/db/migration/

등록하려는 시점의 DB 스키마 구조를 입력해야 한다.

이 파일을 기준으로 flyway가 DB버전 관리를 하게된다.

  • 버전 관리가 어렵다면 일단 V {숫자}__{설명}. sql 으로 생각하자.

**V1__init.sql :**등록시점의 DB 스키마 구조 입력

drop table if exists sample_entity;

create table sample_entity(
      id   bigint auto_increment,
      name varchar(255),
      primary key (id)
);

엔티티 변경 적용

1. 컬럼 추가 (변경 전 ddl-auto: validate로 설정한다)

public class SampleEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private int age;   // 추가한 부분
}

2. V2__add_age.sql

ALTER TABLE sample_entity ADD COLUMN age integer default 0

실행

image image

버전관리

image

Prefix

  • V(Versioned): 현재버전을 새로운 버전으로 업데이트
  • U(Undo): 현재 버전을 이전버전으로 되돌리는 경우
  • R(Repeatable): 버전에 관계 없이 매번 실행하는 경우

Version

스크립트의 버전을 이전보다 꼭 높게 적어야 한다.

  • V와 U는 버전을 명시한다.
  • R은 버전을 명시하지 않는다. (매번 실행되기 때문이다)

Separator

무조건 __(언더바 2개)로 작성

Description

스크립트 내용에 맞게 자유롭게 적는다.

단어 구분은 _(언더바 1개)로 한다.

참고

https://www.youtube.com/watch?v=pxDlj5jA9z4

https://tecoble.techcourse.co.kr/post/2021-10-23-flyway/

[https://ecsimsw.tistory.com/entry/Flyway로-DB-Migration](https://ecsimsw.tistory.com/entry/Flyway%EB%A1%9C-DB-Migration)

[https://www.blog.ecsimsw.com/entry/Flyway-DB-마이그레이션-기존-데이터가-있는-경우](https://www.blog.ecsimsw.com/entry/Flyway-DB-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%EA%B8%B0%EC%A1%B4-%EB%8D%B0%EC%9D%B4%ED%84%B0%EA%B0%80-%EC%9E%88%EB%8A%94-%EA%B2%BD%EC%9A%B0)

트러블슈팅

테스트 환경에서의 Flyway 실패

1. h2 테스트 환경

  1. h2에서 jdbc:h2:mem:testdb;MODE=MYSQL 을 적용
  2. V1__init.sqlengine=InnoDB default charset utf8mb4; 부분에서 에러가 발생함

해결방법

  • h2 문법으로 적용하면 성공이 됨 → 스키마를 h2, MySQL 둘다 관리해야 하기 때문에 적용X
  • 로컬 test DB를 MySQL로 변경

2. MySQL 테스트 환경

  1. h2에서 MySQL로 마이그레이션 적용
  2. 테스트 격리가 안되는 현상 발생 (기존 테스트 실패)

해결방법

  • 프로덕션 코드는 build가 통과한다. → 엔티티와 DB스키마가 일치한다 → 테스트에 Flyway 스키마를 적용할 필요가 없다
    • Flyway + MySQL : 배포서버, 로컬서버 프로덕션 코드
    • h2 : 배포서버, 로컬서버 테스트 코드

3. 테스트 코드 yml 설정

  • flyway.enabled=false를 꼭 설정하자

4. 42001 에러

기존 테이블 내용과 새롭게 추가된 스키마 사이 추가되는 과정에서 중복된 내용이 있거관리되는 스키마 내용이 다를 때 에러가 발생한다.

  • drop table flyway_schema_history 를 적용, ddl auto를 validate 한 뒤 실행해보자.

5. 42000 에러

image flyway 실행예시

  • flyway_schema_history 테이블이 없거나 비어있다면 V1부터 차례로 모두 실행한다.
  • flyway_schema_history 테이블에 V1, V2가 있다면 다음실행시 V3부터 실행된다.

실행 중 기존 테이블과 스키마가 중복되는 부분이 있다면 에러가 발생할 수 있다.

→ 스키마를 변경하거나, 로컬DB인 경우 다른 테이블, flyway_schema_history 를 삭제 후 다시 실행해보자

Clone this wiki locally