flyway는 DB 형상관리 오픈소스 툴이다. 즉, db schema가 변경이 발생할 때 flyway라는 툴로 관리를 할 수 있는 것이다.
이번 포스팅에서는 flyway 적용 방법에 대해 아주 간단히 알아볼 것이다.
Flyway 사용 이유
- 배포 환경(prod)에서는 ddl-auto를 create, update, create-drop을 지양해야 한다. 배포된 db의 데이터들이 모두 증발될 수 있기 때문이다. 따라서, db schema 변경이 있을 때에 우리가 직접 배포 환경 db에 접속하여 schema를 작성해주어야 한다. 이는 매우 번거로우며 위험성 또한 높다. Flyway를 사용하면 이러한 번거로운 작업이 사라지게 된다.
- 변경된 스키마를 한눈에 확인할 수 있어 스키마 버전 관리에 용이해진다.
flyway를 사용하게 되면 Flyway DB 스크립트에만 의존하게 된다. 따라서, 변경되는 schema를 flyway 마이그레이션 스크립트로 잘 작성해주기만 하면 ddl-auto를 validated로 해도 되는 것이다.
환경세팅
- Spring Boot 2.6.9
- Gradle
- MySQL
- JPA
build.gradle
implementation 'org.flywaydb:flyway-core'
application.yml
spring:
flyway:
enabled: true
baseline-on-migrate: true
참고로, 로컬에서는 flyway를 적용하지 않고 운영 서버에서만 flyway를 적용하고 싶을 수 있다. 실제로 지금 flyway를 적용중인 프로젝트에서도 로컬 환경에서는 db 컬럼 변경이 꽤 많을 것으로 예상돼 flyway enabled: false로, 배포 서버의 WAS에서는 flyway enabled: true로 해놓았다. 브랜치 전략과 CD 툴을 잘 이용하여 관리해주도록 하자.
스크립트 작성
이제 Flyway 마이그레이션 스크립트를 작성해주도록 하자.
스크립트 .sql 파일명은 반드시 아래 규칙을 지켜주어야 한다.
- V(Versioned): 현재버전을 새로운 버전으로 업데이트할 때
- U(Undo): 현재 버전을 이전버전으로 되돌릴 때
- R(Repeatable): 버전 관계 없이 매번 실행할 때
예를 들어 1.0.0 버전의 엔티티에 해당되는 베이스라인 스크립트를 작성한다면 V1__hello.sql 과 같이 대문자 V와 함께 버전을 작성해주고 언더바를 2개 넣어주도록 하자. 소문자 v로 작성하거나, _를 2개가 아닌 1개만 작성한다면 제대로 인식하지 못하므로 주의하자. 또한, 해당 스크립트는 resources/db/migration 디렉토리에 넣어주도록 하자.
베이스라인 스크립트를 생성해보자. 해당 스키마에는 아래와 같은 스키마들이 들어갈 것이다.
create table member (
member_id bigint not null auto_increment,
created_at datetime not null,
last_modified_at datetime not null,
email varchar(255) not null,
platform varchar(255) not null,
platform_id varchar(255) not null,
username varchar(255) not null,
primary key (member_id)
) engine=InnoDB default charset utf8mb4;
.
.
.
이제 애플리케이션을 실행하면 아래와 같은 결과가 나온다.
위와 같이 flyway_schema_history가 생성됐고, 작성한 스크립트들이 모두 나오면 성공이다.
변경되는 스크립트 작성
엔티티가 변경돼, 추가로 변경되는 스크립트를 작성할 때에는 기존 스크립트를 건드리지 말고 변경되는 엔티티 부분에 해당되는 스키마만 새로 만들어서 작성해주면 된다.
회원의 나이에 대한 컬럼이 추가된다고 해보자.
따라서 db/migration 디렉토리에 새로운 스크립트를 추가해주었다.
주의해야할 점은, 기존에 존재하는 최신 버전보다 높은 숫자의 버전으로 만들어주어야 한다.
중간에 수정하고 추가한답시고, 기존에 존재하는 최신 버전보다 낮은 버전으로 만들고 추가하면 무시된다. 또, 기존 스크립트를 수정할 경우 에러가 발생하게 된다.
V0.3.1__add_age.sql에는 변경되는 스키마만 작성해주도록 하자.
V0.3.1__add_age.sql
alter table member ADD COLUMN age int(20);
만약 엔티티와 스크립트가 불일치하면 컴파일 단에서 미리 에러를 확인할 수 있다.
Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: wrong column type encountered in column [age] in table [member]; found [bigint (Types#BIGINT)], but expecting [integer (Types#INTEGER)]
따라서 배포 환경에 스크립트를 직접 적용할 때에 비해 훨씬 안전하다는 것을 확인할 수 있다.
추가로 작성한 flyway 마이그레이션 스크립트가 잘 반영된 것을 확인할 수 있다.
트러블슈팅
42000, 42001 에러
실행 중 기존 테이블과 스키마가 중복되는 부분이 있다면 에러가 발생할 수 있다.
- flyway_schema_history 테이블이 없거나 비어있다면 V1부터 차례로 모두 실행한다.
- flyway_schema_history 테이블에 V1, V2가 있다면 다음실행시 V3부터 실행된다.
해결방안
- 스키마가 잘못됐을 수 있다. 변경되는 부분만 잘 작성했는지 확인해보자.
- 로컬DB인 경우 다른 테이블, 혹은 flyway_schema_history 를 삭제 후 다시 실행해보자
주의점
에러 원인이 다양함에도 불구하고 42000, 42001 에러로 퉁쳐서 나오는 경우가 많은 듯하니 주의하자.
참고
- https://youtu.be/pxDlj5jA9z4
- https://flywaydb.org/documentation/getstarted/how
- https://github.com/woowacourse-teams/2022-nae-pyeon/wiki/Flyway%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0
- https://newwisdom.tistory.com/m/79
도움을 준 사람
- 우아한테크코스 4기 BE 제로 (github: https://github.com/asebn1)