백엔드 프로젝트를 만들다 보면 테이블을 만들고, 컬럼을 추가하고, 인덱스를 붙이는 일이 계속 생긴다.
처음에는 DB에 직접 접속해서 SQL을 실행해도 괜찮아 보인다.
CREATE TABLE users (
id BIGINT PRIMARY KEY,
name VARCHAR(100)
);
하지만 프로젝트가 커지거나 환경이 늘어나면 문제가 생긴다.
이 문제를 해결하기 위해 사용하는 도구가 Flyway다.
Flyway는 DB schema 변경 이력을 관리하는 migration 도구다.
코드 변경 이력을 Git으로 관리하듯이,
DB 변경 이력을 SQL 파일로 관리하게 해준다.
Spring Boot 프로젝트에서는 보통 다음 위치에 migration SQL 파일을 둔다.
src/main/resources/db/migration/
예를 들면 이런 식이다.
src/main/resources/db/migration/V1__create_birth_profiles.sql
src/main/resources/db/migration/V2__create_counseling_sessions.sql
src/main/resources/db/migration/V3__add_prompt_version.sql
파일 이름에는 규칙이 있다.
V번호__설명.sql
예:
V1__create_users.sql
V2__add_email_to_users.sql
Flyway는 이 파일들을 버전 순서대로 실행한다.
Spring Boot 애플리케이션이 실행될 때 Flyway가 함께 실행되도록 설정할 수 있다.
앱이 뜰 때 Flyway는 다음 과정을 수행한다.
즉, 이미 실행된 migration은 다시 실행하지 않는다.
예를 들어 DB에 V1만 적용되어 있고 프로젝트에는 V1, V2, V3가 있다면, Flyway는 V2, V3만 실행한다.
적용된다.
Spring Boot 애플리케이션을 Docker 이미지로 빌드하면, migration SQL 파일도 애플리케이션 리소스에 포함된다.
예를 들어 이미지 안에 다음 파일들이 들어 있다고 하자.
db/migration/V1__create_birth_profiles.sql
db/migration/V2__create_sessions.sql
이 이미지를 dev 환경에서 실행하면 dev DB에 migration이 적용된다.
staging 환경에서 실행하면 staging DB에 migration이 적용된다.
prod 환경에서 실행하면 prod DB에 migration이 적용된다.
중요한 점은 migration 상태는 DB별로 따로 관리된다는 것이다.
같은 Docker 이미지를 사용하더라도 연결하는 DB가 다르면, 각 DB의 flyway_schema_history를 기준으로 migration이 적용된다.
dev app -> dev PostgreSQL -> dev DB의 flyway_schema_history
staging app -> staging PostgreSQL -> staging DB의 flyway_schema_history
prod app -> prod PostgreSQL -> prod DB의 flyway_schema_history
Gradle 의존성은 다음과 같이 추가할 수 있다.
implementation 'org.flywaydb:flyway-core'
implementation 'org.flywaydb:flyway-database-postgresql'
PostgreSQL을 사용하는 경우 flyway-database-postgresql을 함께 추가한다.
설정 예시는 다음과 같다.
spring:
datasource:
url: jdbc:postgresql://postgres:5432/app
username: app
password: app
flyway:
enabled: true
이렇게 설정하면 Spring Boot 애플리케이션이 실행될 때 Flyway가 자동으로 migration을 수행한다.
-- src/main/resources/db/migration/V1__create_birth_profiles.sql
CREATE TABLE birth_profiles (
id UUID PRIMARY KEY,
birth_date DATE NOT NULL,
birth_time TIME,
calendar_type VARCHAR(20) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
다음 변경이 필요하면 새 파일을 추가한다.
-- src/main/resources/db/migration/V2__create_counseling_sessions.sql
CREATE TABLE counseling_sessions (
id UUID PRIMARY KEY,
birth_profile_id UUID NOT NULL REFERENCES birth_profiles(id),
topic VARCHAR(50) NOT NULL,
prompt TEXT NOT NULL,
response TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
기존 V1 파일을 수정하는 대신, V2 파일을 추가하는 것이 중요하다.
이미 다른 환경에 적용된 migration 파일을 수정하면 환경 간 schema 이력이 꼬일 수 있다.
개인 프로젝트나 MVP 단계에서는 앱 시작 시 Flyway가 자동으로 migration을 실행하게 두는 방식이 편하다.
Docker image 실행
-> Spring Boot app 시작
-> Flyway migration 자동 적용
-> app 구동
하지만 운영 환경에서는 더 조심해야 한다.
운영 DB에 schema 변경이 들어가는 일은 위험할 수 있기 때문에, 실무에서는 migration을 애플리케이션 시작과 분리하기도 한다.
예를 들면:
1. 배포 파이프라인에서 migration job 실행
2. migration 성공 확인
3. 새 애플리케이션 버전 배포
이 방식은 조금 더 번거롭지만, DB 변경 실패와 애플리케이션 배포 실패를 분리해서 볼 수 있다.
Flyway는 DB 변경 이력을 코드처럼 관리하게 해주는 도구다.
Spring Boot와 함께 사용하면 애플리케이션 실행 시 migration을 자동 적용할 수 있고, Docker로 다른 환경에 배포해도 각 환경의 DB 상태에 맞게 필요한 migration만 실행할 수 있다.
개인 프로젝트에서는 앱 시작 시 자동 migration으로 시작해도 충분하다.
운영 환경에서는 배포 파이프라인에서 migration을 별도 단계로 분리하는 방식을 고려할 수 있다.
핵심은 이것이다.
테이블 변경을 DB에 직접 손으로 반영하지 말고, migration 파일로 남기자.
그래야 로컬, 개발, 운영 환경의 DB schema를 일관되게 관리할 수 있다.
| Builder pattern과 @Jacksonized (0) | 2023.05.07 |
|---|---|
| 자바 Vo -> Entity 리스트 변환 : stream, map, collect & 람다식 (0) | 2023.04.10 |
| [Spring Boot] 테스트 클래스와 @Transactional 어노테이션 (1) | 2023.03.05 |
| [Spring boot] 테스트 케이스 작성하기 (0) | 2023.02.22 |
| [Spring Boot] Entity와 Repository (0) | 2023.02.20 |