Spring Boot :: Flyway 테스트 도입

JaeHwan·2023년 8월 24일
1

Spring Boot

목록 보기
4/8
post-thumbnail

💽 Flyway란?

Flyway는 데이터베이스의 형상 관리를 목적으로 하는 오픈소스 데이터베이스 마이그레이션 툴입니다.

🗨️ DB 마이그레이션이란?

처음 “DB의 마이그레이션”이라는 단어를 봤을 때는 orale -> mysql ... mariaDB -> postgresql 이런 식으로 DB 자체를 변경하는 작업으로 이해를 했습니다. 하지만 DB의 마이그레이션은 DB 스키마에 발생한 변경사항을 DB에 적용하는 것입니다. 현재 저희는 운영환경에 따라서 DB가 다르기 때문에 스키마에 변동에 따른 싱크가 문제가 생길 수도 있다고 생각합니다.

이러한 오류를 Flyway 등 DB 마이그레이션 툴이 보완해준다고 생각이 됩니다.

❓ 사용법

개발자가 직접 설정한 경로에 버전에 따른 .sql 스크립트를 작성합니다.
그러면 Flyway가 직접 해당 DB에 flyway_schema_history 라는 테이블을 만드는데 이 테이블을 통해서 개발자가 작성한 스크립트를 어느 버전까지 실행했고 그 결과가 현재의 테이블 구조와 동일한지를 비교해 가면서 싱크를 맞춰주는 작업을 하게됩니다.

즉, 지정한 경로에 스크립트만 제대로 작성한다면, Flyway는 이를 가지고 운영환경에 따라 DB가 다르더라도 스크립트의 버전을 확인하고 실행하지 않은 스크립트를 실행하면서 싱크를 맞출 겁니다. 이렇게 하면서 이를 통해 스키마 변경 관리의 번거로움과 인간 에러 가능성을 최소화할 수 있습니다.

📏 규칙

파일의 이름의 경우에는 정해진 규칙이 있습니다.

버전의 경우에는 이전의 버전보다 높아야 하고 description의 경우 직관적으로 어떤 동작을 하는 스크립트인지 알 수 있도록 작성하는 편이 좋습니다.

자세하게는 아래와 같은 규칙을 가지고 있습니다.

  • Prefix

    Prefix에는 V(Versioned), U(Undo), R(Repeatable)의 3가지 종류가 있는데요.

    V는 현재 버전을 새로운 버전으로 업데이트하는 경우, U는 현재 버전을 이전 버전으로 되돌리는 경우, R은 버전에 관계없이 매번 실행하는 경우에 사용됩니다.

  • Version

    V와 U는 버전이 명시되어야 하며, R은 버전이 명시되지 않아도 상관이 없는데요.

    Flyway의 핵심 기능인 Versioned Migrations는 마이그레이션 스크립트의 최신 버전과 현재 데이터베이스의 schema 버전을 비교하여 차이점이 있다면 마이그레이션 스크립트를 순차적으로 실행하여 최신 schema와의 격차를 좁혀나가는 방법입니다.

    때문에 마이그레이션 스크립트를 추가할 때는 항상 최신 마이그레이션 스크립트보다 큰 숫자로 버전을 설정해야 하며, 이때 사용되는 버전은 단순하게 1, 2, 3으로 나타낼 수 있도 있지만 20221204와 같은 날짜로도 명시할 수 있습니다.

  • Separator

    __ (언더바 2개)로 작성되어야 한다는 규칙이 있습니다.

  • Description

    스크립트의 내용에 맞게 자유롭게 적을 수 있으며, 단어의 구분은 _ (언더바 1개)로 하게 됩니다.

🌿 Spring Boot로 써보자! ( DB : postgresql )

🛠️ 환경설정

우선 gradle을 이용해 sentry 라이브러리를 가져옵니다.

// https://mvnrepository.com/artifact/org.flywaydb/flyway-core
	implementation group: 'org.flywaydb', name: 'flyway-core', version: '9.21.1'

gradle 설치 및 gradle 싱크 작업이 끝났다면 .yml 환경설정 파일을 수정해줍니다.

application.yml ( 공통 yaml 파일에 넣어줍니다.)

flyway:
    enabled: true
    baseline-on-migrate: true
    locations: classpath:/db/migration ( 이 경로는 resource/db/migration 입니다.)
    baseline-version: 1

그 후 운영환경에 맞게 DB를 설정해줍니다.

jpa:
	hibernate:
      ddl-auto: update -> validate ( 로 수정 )

flyway:
    user: DB 접근 계정
    password: DB 접근 계정
  • spring.jpa.hibernate.ddl-auto: validate
    ddl-auto 를 validate 로 수정하는 이유는 validate 의 경우 DB의 테이블과 java 로 작성된 entity 의 적합성이 맞는지 검사하고 적합하지 않을 경우 프로젝트 실행 자체를 막기 때문에, 조금 더 DB 스키마에 대한 안정성 및 코드와의 싱크가 높아집니다.

  • spring.flyway.enabled: true

    해당 설정은 기본 값이 true이지만 직관적으로 명시할 수 있도록 추가하였습니다.

  • spring.flyway.baseline-on-migrate: true

    히스토리 테이블인 flyway_schema_history가 생성되어 있는 경우 false로 설정하며, true로 설정하면 히스토리 테이블이 없을 경우에 생성이 됩니다. default 값은 false입니다.

  • spring.flyway.locations: classpath:/db/migration

    default 위치는 main/resources/db/migration이며, 즉 resources 패키지에 db/migration 패키지를 만들고 해당 패키지 안에 스크립트 파일을 추가하면 됩니다. 물론 해당 설정을 통해 flyway 적용을 원하는 커스텀 패키지로 설정할 수도 있습니다.

  • spring.flyway.baseline-version: 1

    히스토리 테이블이 없을 경우 히스토리 테이블을 생성하면서 baseline 정보가 입력되는데 그때 시작되는 버전 정보입니다.

👨🏻‍💻 코드 추가

코드의 경우 작성된 java의 코드에 추가할 필요는 없지만, 개발자가 설정한 경로에 sql 스크립트 파일이 있어야합니다. ( 테스트로 인한 여러 버전의 파일들…)

실제 DB의 경우에는 스키마의 수정이 많이 일어날 수록 불안정하기 때문에 위와 같이 많은 파일이 생성되지는 않을 것이라 생각합니다.

아래가 자바 코드라면 sql 스크립트를

public class User extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long seq;

    @Column(columnDefinition = "varchar(100) NOT NULL")
    private String username;

    @JsonIgnore
    @Column(columnDefinition = "varchar(255) NOT NULL")
    private String password;

    @Column(columnDefinition = "varchar(100) NOT NULL")
    private String nickName;

    @Column(columnDefinition = "varchar(255)")
    private String phone;

    @Column(columnDefinition = "varchar(255)")
    private String crn;

    public void deleteUser(String crn) {
        this.crn = crn;
    }
}

아래와 같이 작성하여 지정한 경로에 저장해 줍니다.

CREATE TABLE tb_user (
    seq BIGSERIAL PRIMARY KEY,
    username VARCHAR(100) NOT NULL,
    password VARCHAR(255) NOT NULL,
    nickName VARCHAR(100) NOT NULL,
    phone VARCHAR(255),
    crn VARCHAR(255),
    createdDTime TIMESTAMP,
    modifiedDTime TIMESTAMP,
    status INT DEFAULT 1
);

🖼️ 결과


😮 참조

https://wildeveloperetrain.tistory.com/210

https://documentation.red-gate.com/fd/quickstart-gradle-184127577.html

https://velog.io/@dhk22/DB-DB-migration-Springboot-Flyway

profile
Flutter를 사랑하는 근데 이제 백엔드를 곁들인

0개의 댓글