스프링부트가 지원하는 데이터베이스 마이그레이션 툴과의 연동에 대해서 살펴보자.

데이터베이스 마이그레이션

데이터베이스 마이그레이션 툴은 스키마 변경이나 데이터 변경에 대한 이력을 버전관리시스템을 사용하듯이 관리할 수 있는 툴이다.

Flyway와 Liquibase라는 제품이 대표적인데, 여기서는 Flyway에 대해서만 살펴본다.

Flyway

Flyway는 기본적으로 SQL 파일을 사용한다. 먼저 의존성을 추가하자.

<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
</dependency>

다음으로 마이그레이션 디렉토리와 파일을 만들어야 한다.

디렉토리

  • db/migration 또는 db/migration/{vendor}
  • spring.flyway.locations 프로퍼티즈로 변경 가능

파일

파일명은 반드시 "V숫자__이름.sql" 와 같아야 한다.

  • V는 반드시 대문자
  • 숫자는 순차적으로 작성하며 타임스탬프를 이용하는 것을 권장
  • 숫자와 이름 사이에 언더바 두 개가 필요
drop table if exists account;
drop sequence if exists hibernate_sequence;
create sequence hibernate_sequence start with 1 increment by 1;
create table account ( id bigint not null, password varchar(255), username varchar(255) not null, email varchar(255), primary key (id) );

테스트를 위한 도메인 클래스를 생성하자.

@Entity
public class Account {

    @Id
    @GeneratedValue
    private Long id;

    private String username;

    private String password;

    private String email;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Account account = (Account) o;
        return Objects.equals(id, account.id) &&
                Objects.equals(username, account.username) &&
                Objects.equals(password, account.password);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, username, password);
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

우선 데이터베이스 스키마를 비워둔 상태에서 하이버네이트의 ddl-auto를 validate로 설정하여 검증만 하도록 한다.

spring.jpa.generate-dll=false
spring.jpa.hibernate.ddl-auto=validate

정상적인 테스트를 하기 전에, 스키마가 없는 상태이므로 하이버네이트에서 스키마 검증에 대한 에러를 발생하는지 테스트하기 위해서 V1__init.sql에서 v를 소문자로 변경한 뒤 애플리케이션을 실행해보자. Flyway에서 디렉토리가 존재하지 않으면 에러가 발생하기 때문에 resources/db/migration 폴더는 존재해야 한다.

디렉토리가 없는 경우 다음과 같은 에러로그가 출력된다.

Cannot find migrations location in: [classpath:db/migration] (please add migrations or check your Flyway configuration)

데이터베이스 스키마에 테이블이 없고, 스키마를 생성하기 위한 쿼리가 실행되지 않기 때문에 하이버네이트에서 검증을 하다가 에러가 발생한다.

Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [account]

다시 v를 대문자로 변경하여 Falyway가 정상적으로 해당 쿼리를 실행할 수 있도록 해보자. 애플리케이션을 실행하면 컨텍스트를 띄우면서 Falyway를 실행한다. Falyway가 해당 쿼리를 실행하여 스키마를 생성하고 하이버네이트가 스키마를 검증한 뒤, 에러 없이 애플리케이션이 실행된다.

DBMS에 접속헤서 스키마를 확인해보면 2개의 테이블이 생성된 것을 알 수 있다. 하나는 Flyway가 정보를 관리하는 테이블이며, 다른 하나는 Flyway 스키마 생성 쿼리를 실행하여 생성된 테이블이다.

Flyway가 정보를 관리하는 flyway_schema_history 테이블에서 버전, 타입, 이름, 성공 여부 등을 알 수 있다.

이번에는 새로운 컬럼을 추가해보자.

package jch.inflearn.springbootjpa.account;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.util.Objects;

@Entity
public class Account {

    @Id
    @GeneratedValue
    private Long id;

    private String username;

    private String password;

    private String email;

    // add
    private boolean active;

    //... getter, setter 등
    
    // add
    public boolean isActive() {
        return active;
    }

    // add
    public void setActive(boolean active) {
        this.active = active;
    }
}

ddl-auto가 validate로 설정되었기 때문에 하이버네이트에서 매핑이 되지 않는 컬럼에 대한 검증 에러가 발생한다.

이를 해결하기 위해서는 새로운 sql 파일을 추가해야한다. 기존의 V1__init.sql는 이미 적용이 된 상태이기 때문에 절대 수정하거나 삭제해서는 안된다.

V2__add_active.sql 라는 이름의 파일을 생성하자.

ALTER TABLE account ADD COLUMN active BOOLEAN;

다시 애플리케이션을 실행하면 Flyway가 두번째 스크립트를 적용하고 하이버네이트가 검증을 완료하여 이상이 없으므로 정상적으로 실행된다. DBMS로 접속해서 스키마를 확인해보자. account 테이블에는 active 컬럼이 추가되었고, flyway_schema_history 테이블에는 이력이 추가된 것을 알 수 있다.

Flyway에서 사용하는 스크립트 파일에는 스키마 관리 뿐 아니라 데이터도 추가/수정/삭제할 수 있다.

해당 포스팅은 스프링 부트 개념과 활용 강의 내용을 토대로 작성하였습니다.