차근차근 Modern Spring Boot 3 기초 (3) Flyway를 통한 DDL 관리

Merge SimpsonMerge Simpson
5 min read

Gradle 프로젝트

우선 flyway를 적용하기 위하여 우리 프로젝트의 의존성 라이브러리 관리 방식을 이해해야 합니다.

우리는 빌드 도구로 gradle을 선택했습니다. 우리가 이번 프로젝트에서 gradle을 다루기 위해 눈여겨 봐야 할 파일들은 다음과 같습니다.

  • build.gradle.kts: 가장 많은 작업을 하게 되는 파일입니다. 우리는 이곳에 의존성 라이브러리를 나열하고 관리할 수 있으며, 각종 스크립트를 작성해 둘 수 있습니다.

  • settings.gradle.kts: 이번 실습에서는 거의 다루지 않아도 됩니다. 모듈을 관리하거나, 기초가 되는 일부 명령을 작성해 두는 데에 사용합니다.

  • gradle.properties(기본 생성이 아님): 이 파일은 지금 프로젝트에 포함되어 있지 않습니다. 이 파일에는 우리가 build.gradle(build.gradle.kts)이나 settings.gradle 등에서 사용하는 변수 목록을 작성해 둘 수 있습니다.

의존성 라이브러리 작성

build.gradle.kts 파일에 기존 내용 대신 다음 내용을 입력합니다.

plugins {
    java
    id("org.springframework.boot") version "3.3.1"
    id("io.spring.dependency-management") version "1.1.5"
}

group = "com.example"
version = "0.0.1-SNAPSHOT"

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
        sourceCompatibility = JavaVersion.VERSION_21
        targetCompatibility = JavaVersion.VERSION_21
    }
}

configurations {
    compileOnly {
        extendsFrom(configurations.annotationProcessor.get())
    }
    configureEach {
        // exclude LOGBACK
        exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging")
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-validation")
    implementation("org.springframework.boot:spring-boot-starter-log4j2")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    developmentOnly("org.springframework.boot:spring-boot-devtools")
    annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")

    // DB
    runtimeOnly("org.postgresql:postgresql")

    // flyway
    implementation("org.flywaydb:flyway-core:9.22.3")

    // lombok
    compileOnly("org.projectlombok:lombok")
    annotationProcessor("org.projectlombok:lombok")
}

tasks.withType<Test> {
    useJUnitPlatform()
}

위 내용 중 dependencies에 나열한 항목이 이 프로젝트에 적용되는 의존성 라이브러리 목록입니다.

위 내용 중 flyway의 의존성 라이브러리를 추가한 부분은 다음과 같습니다.

dependencies {
    // ...

    // flyway
    implementation("org.flywaydb:flyway-core:9.22.3")
}
  • MVN Repository(https://mvnrepository.com/)에서 검색하여 의존성 라이브러리를 추가할 수 있습니다. (이번에는 제가 검색해서 갖고 왔습니다.)

  • 현 시점 기준으로 Flyway는 10 버전까지 있습니다. (위에 우리가 적용 중인 것은 9 버전)

    • PostgreSQL 14 버전에 적용 가능한 Flyway 버전은 최대 9 버전까지입니다.

    • 만약 DB 접속 정보가 정확함에도 Flyway 연결 오류가 발생한다면, 실수로 10 버전 이상으로 작성하였거나 버전 입력을 생략하여 10 버전 이상이 적용된 것일 수 있습니다.

Sync Gradle (Refresh Gradle)

만약 settings.gradle 또는 build.gradle 등을 수정했다면, 인텔리제이는 화면의 우상단에 코끼리 버튼(Load Gradle Changes 버튼)을 보여 줄 것입니다.

이 버튼을 누르면 우리가 추가한 내용이 반영됩니다. 반드시 이 버튼을 누르는 것을 기억하십시오. 만약 이 버튼을 누르지 않고 오른쪽 닫기 버튼을 눌러 닫았다면, 화면 어딘가에서 gradle 탭을 찾아야 합니다.

이곳에서 새로고침(🔄) 버튼을 누릅니다. 또는 다음처럼 프로젝트 단위로 Reload gradle projectRefresh Gradle Dependencies를 선택할 수 있습니다. (project 탭이 아니라 gradle 탭임.)

확인을 위해 빌드 탭을 찾아서 열어 보세요. 화면의 UI는 저와 다를 수 있습니다. 저는 자리에 위 의존성 라이브러리들이 설치되어 있기 때문에 메시지가 짧습니다. 확인해야 하는 BUILD SUCCESSFUL 메시지는 동일하게 작성되어 있을 것입니다.

오타가 없고 인터넷에 잘 연결되어 있다면 BUILD SUCCESSFUL 메시지를 확인할 수 있을 것입니다.

스프링 부트 프로젝트

Application 구성 속성

우선 src/main/resources에 있는 application.properties 파일을 application.yml로 이름을 변경합니다. 그렇게 함으로써 우리가 훨씬 다루기 편합니다.

리팩터 버튼(Refactor)을 눌러 변경을 완료합니다.

DB 접속 정보, 커넥션 풀, Flyway 설정 입력

spring:
  application.name: example-springboot3-webmvc

  datasource:
    driver-class-name: org.postgresql.Driver
    url: ${POSTGRES_URL:jdbc:postgresql://localhost:5442/demo}
    username: ${POSTGRES_USERNAME:root}
    password: ${POSTGRES_PASSWORD:root}

    # connection pool
    hikari:
      connection-timeout: 30_000
      idle-timeout: 60_000
      max-lifetime: 1_800_000
      maximum-pool-size: 300
      minimum-idle: 5
      leak-detection-threshold: 2000

  flyway:
    baseline-on-migrate: true # flyway schema history 테이블이 없다면 생성한다.

DB 접속 정보(데이터 소스)

  • ${환경변수}는 환경 변수를 읽어서 사용합니다.

  • ${환경변수:기본값}은 해당 환경 변수가 없을 때 기본값을 사용합니다. 환경 변수가 있다면 기본값은 무시됩니다.

커넥션 풀(데이터소스의 하위 속성)

  • 다음 설명이 아직 어렵다면 지금 바로 이해할 필요는 없습니다. 나중에 따로 설명할 일이 거의 없을 거라 생각해서 미리 설명해 두었습니다.

  • DB에 접속할 때마다 커넥션을 생성하고 작업을 마칠 때마다 커넥션을 종료하는 동작이 주요 로직 흐름에서 비용(cost)이 되지 않도록, 미리 커넥션 풀(connection pool)이라는 곳에 커넥션들을 생성해 두어 관리하고, 로직에 커넥션이 필요하면 그때그때 임대해 쓰는 방식입니다.

Flyway 설정

  • Flyway는 처음 사용할 때 flyway schema history라는 테이블을 생성해야 합니다.

  • baseline-on-migrate 옵션을 true로 변경하면, flyway schema history 테이블이 없을 때 그 테이블을 생성합니다.

이제 Flyway를 사용할 준비가 되었습니다.

(참고) 파일 인코딩 변경

application.yml 파일의 기본 인코딩이 UTF-8로 되어 있지 않다면, 다음처럼 마우스를 올려 변경할 수 있습니다. Change file encoding을 클릭하고, 목록에서 UTF-8을 선택해 줍니다.

Convert를 누르는 것이 가장 좋습니다. (보이는 문자를 유지한 상태로 인코딩을 자동으로 맞춰 줍니다.)

Flyway DDL 버전 파일 작성

우리는 앞서 Flyway 의존성 추가(dependencies)와 Flyway 구성 속성 작성(application.yml)을 완료했습니다.

이제 Flyway의 다른 구성 속성을 변경하지 않는다면, 다음 경로에 DDL 파일을 작성하면 됩니다.

  • 경로: src/main/resources/db/migration

  • 위 경로 중 db/migration 폴더는 우리가 만들어야 합니다.
    (인텔리제이에서는 db.migration처럼 보일 수 있습니다. 실제 폴더 이름을 db.migration으로 만들지 마세요. 육안으로 보이는 것과 다르고, 이름 바꾸기 등을 통해 확인할 수 있습니다.)

(참고) DDL, DML이란?

  • DDL: 스키마를 구축할 때 사용하는 SQL입니다. CREATE, ALTER, DROP 등입니다. 테이블을 만들 때 사용한다고 이해하면 됩니다.

  • DML: 테이블에 데이터를 넣고, 조회하고, 수정하고, 삭제하는 동작을 위한 SQL입니다. 테이블을 이용할 때 사용한다고 이해하면 됩니다.

서비스를 만들고 운영하는 관점에서 보면, DDL은 서비스를 만드는 동안에 사용되지만, DML은 서비스 배포 후에도 사용자의 요청을 처리하는 데에 활발하게 사용됩니다. 즉, 운영 내내 사용됩니다.

테이블을 만들고(DDL) → 사용하는(DML) 것입니다.

Flyway DDL 파일 이름 규칙

  • V버전__설명.sql: 버전 파일입니다. 순서대로 잘 실행됩니다. 스키마 변경의 히스토리 역할을 하기 때문에 이미 운영 환경에 적용된 V 파일은 그대로 두고, 새로운 V 파일을 추가하여 사용합니다.

  • R__설명.sql: 반복 파일입니다. 수정될 때마다 실행됩니다. 더미 데이터나 시드 데이터를 삽입해 둘 때 사용할 수 있습니다.

DDL 작성

경로: src/main/resources/db/migration

파일 이름: V1_0_0__init_schema.sql

-- Enable UUID
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

경로: 위와 같음.

파일 이름: V1_0_1__add_tb_account.sql

CREATE TABLE IF NOT EXISTS account (
    id              UUID                DEFAULT uuid_generate_v4(),
    username        VARCHAR(255),
    password        VARCHAR(255),
    nickname        VARCHAR(255),

    status          VARCHAR(255),
    created_at      TIMESTAMP           DEFAULT now(),
    updated_at      TIMESTAMP,

    CONSTRAINT pk_account PRIMARY KEY (id),
    CONSTRAINT uq_account_username UNIQUE (username),
    CONSTRAINT uq_account_nickname UNIQUE (nickname)
);

애플리케이션을 실행하여 DDL이 작동하는지 확인

우리가 실행할 애플리케이션 파일은 베이스 패키지(com.example.demo)에 있습니다. 프로젝트의 초기 생성 이름에 따라 자동으로 붙은 이름이 모두 다를 수 있습니다. 저 파일을 열고 인텔리제이의 실행 버튼 또는 디버그 버튼을 눌러 실행하면 됩니다. (재생 버튼 ▶️)

이제 애플리케이션을 실행하면 위 DDL을 순서대로 실행하는 것을 로그로 확인할 수 있습니다. DB는 켜 둔 상태여야 합니다. (도커 데스크톱에서 컨테이너 단위로 끄고 켤 수 있습니다.)

또한 DBeaver 등 DB 접속 도구에서 테이블이 존재하는지 확인할 수 있습니다. DB에 접속하는 방식은 이전 글을 참고하세요. (원래 켜 둔 상태였다면 항목을 잘 선택해서 F5를 눌러 새로고침을 해 주세요.)

(참고) Flyway의 V 파일을 수정하고 싶어요.

운영 환경에 이미 적용한 파일은 수정하면 안 됩니다(평시에는.).

우리는 지금 로컬에서만 사용하고 있기 때문에, 언제든 수정할 수 있습니다. 대신 V 파일을 수정한다면, 가급적 모든 테이블을 날려 버린 후 다시 구축하는 것이 좋습니다. 애플리케이션을 실행하기만 해도 쉽게 구축할 수 있다는 것을 명심하세요.


< Prev

Docker Compose로 프로젝트별 DB 설치

Next >

Persistent Entity(JPA Entity) 만들기

0
Subscribe to my newsletter

Read articles from Merge Simpson directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Merge Simpson
Merge Simpson

Hello, I am Korean. Welcome, visitor. You are very cool. 안녕하세요, 저는 한국어입니다. 방문자여 환영한다. 당신은 매우 시원해.