본문 바로가기

Docker

Docker개발환경에서 빠르게 코드 변경사항 저장하기

개발을 하다 보면 코드 변경 사항을 빠르게 반영하고 싶지만, docker compose up --build를 사용할 때마다 빌드 시간이 너~무 오래 걸려 불편함을 느낄 때가 있다.. 팀 프로젝트를 하는데 이와같은 문제를 겪다가 빌드 시간을 단축하면서도 코드 변경 사항이 실시간으로 반영되는 개발 환경을 구축해 보았다. 

이번 포스팅에서는 그에 대한 내용을 다뤄보고자 한다.

 

해당 포스팅은 Java, springboot환경에서 실행됩니다.

문제점

  • 느린 빌드 시간: docker compose up --build를 실행할 때마다 전체 이미지를 재빌드하느라 시간이 많이 소요 됨
  • 비효율적인 개발 프로세스: 작은 코드 변경에도 빌드와 컨테이너 재시작을 반복해야 했음

목표

  • 빌드 시간 단축: 전체 이미지를 재빌드하지 않고도 코드 변경 사항을 반영하고 싶음
  • 실시간 코드 반영: 애플리케이션 코드 수정 시 즉시 변경 사항이 반영되도록 환경을 구성하고자 함.

해결 방법

1. 소스 코드 볼륨 마운트

Docker Compose에서 볼륨 마운트를 사용하여 호스트의 소스 코드를 컨테이너에 직접 연결함. 이를 통해 코드 변경 시 컨테이너 내부에도 즉시 반영되도록 설정

2. Gradle 설정 및 자동 컴파일

Gradle의 --continuous 옵션을 활용하여 소스 코드 변경 시 자동으로 재컴파일되도록 햇음. 이를 통해 애플리케이션이 최신 코드로 실행되도록 보장함

3. Spring Boot DevTools 활용

Spring Boot DevTools를 사용하여 애플리케이션이 클래스 변경 사항을 감지하고 자동으로 재시작되도록 설정

4. DevTools 폴링 모드 활성화

Docker 환경에서는 파일 시스템 이벤트가 컨테이너에 제대로 전달되지 않을 수 있으므로, DevTools의 폴링 모드를 활성화하여 변경 사항을 감지하도록 함

 

 

docker-compose.yml파일

version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:8080"
    volumes:
      - ./src:/app/src
      - ./build.gradle:/app/build.gradle
      - ./settings.gradle:/app/settings.gradle
      - ./gradle:/app/gradle
    command: >
      sh -c "./gradlew --no-daemon compileJava --continuous &
             ./gradlew --no-daemon bootRun"
    environment:
      SPRING_DEVTOOLS_RESTART_ENABLED: "true"
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql-container:3306/playground
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: 1234
    depends_on:
      - mysql-container

  mysql-container:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: 1234
      MYSQL_DATABASE: playground
    ports:
      - "3306:3306"

 

 

application.yml 파일

spring:
  devtools:
    restart:
      enabled: true
      additional-paths: /app/src
      poll-interval: 1000
      quiet-period: 500
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://mysql-container:3306/playground?serverTimezone=Asia/Seoul
    username: root
    password: 1234
  jpa:
    open-in-view: false
    database: mysql
    show-sql: true
    hibernate:
      ddl-auto: update
server:
  port: 8080

 

dockerfile

FROM openjdk:17-jdk-slim

WORKDIR /app

# Gradle 및 설정 파일 복사
COPY gradlew build.gradle settings.gradle ./
COPY gradle ./gradle

# Gradle 실행 권한 부여
RUN chmod +x gradlew

# 의존성 사전 다운로드
RUN ./gradlew clean assemble --no-daemon

# 애플리케이션 실행 포트 노출
EXPOSE 8080

# 컨테이너 실행 시 애플리케이션 구동
CMD ["./gradlew", "bootRun", "--no-daemon"]

 

그리고 build.gradle

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.4'
    id 'io.spring.dependency-management' version '1.1.4'

}

group = 'com.swyp'
version = '0.0.1-SNAPSHOT'

java {
    toolchain {
       languageVersion = JavaLanguageVersion.of(17)
    }
}

configurations {
    compileOnly {
       extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    runtimeOnly 'mysql:mysql-connector-java:8.0.33'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks.named('test') {
    useJUnitPlatform()
}
tasks.register('watch') {
    dependsOn 'classes'
    doLast {
       println 'Watching for changes...'
    }
}

tasks.named('bootRun') {
    dependsOn 'classes'
}

tasks.named('classes') {
    mustRunAfter 'clean'
}


springBoot {
    mainClass = 'com.swyp.playground.PlaygroundApplication'
}

 

 

동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.

 

완전 빠르다 헠