spring boot 를 시작하면서 한가지 불편함을 겪었다. 바로 컴파일 언어와 인터프리터 언어 간 차이 때문에 발생하는 이슈였다. python 의 경우 컴파일을 거치지 않고 실행되는 반면, java 의 경우 컴파일을 한 후 jar 파일을 만들어 실행한다. 이 때문에 도커 개발환경에서 django 의 경우 내 디렉토리를 마운팅해주면 코드를 수정할 때마다 서버가 재시작되어 바로 반영 가능하다. 그러나 java 의 경우 내가 코드를 수정하고 컴파일하여 새로운 jar 파일을 토대로 서버를 재시작해야되기 때문에 쉽지 않다. 이와 관련하여 어떻게 해결해야할 지 구글링 및 스프링 문서를 뒤져봤고 해결한 방법을 정리하고자 한다.
spring Boot Application을 개발하고 디버깅을 하는데 도움이 되는 도구 모음을 의미한다. 개발 단계에서 자동으로 다시 로드되는 기능을 제공하여 개발자가 변경 사항을 신속하게 확인할 수 있도록 도움을 준다. 또한 라이브 리로딩, 프로퍼티 변경 감지 및 자동 재시작과 같은 기능도 제공한다.
intellij IDE 에서 spring boot devtools 관련하여 설정 및 관리가 가능하다. 그러나 나는 직접 devtools 를 관리하여 docker container 단에서 설정하기를 원했고, 이와 관련된 레퍼런스를 조사하여 다음과 같이 코드를 추가해주었다.
#!/bin/bash
# Dockerfile 에서 ENTRYPOINT ["./entrypoint.sh"] 로 실행된다
start_server() {
(sleep 30; ./gradlew buildAndReload --continuous -PskipDownload=true -x Test) &
./gradlew bootRun -PskipDownload=true
}
start_server
직접 정의한 buildAndReload
task 를 백그라운드로 돌려주고 빌드 시 패키지 다운로드와 테스트를 스킵하도록 처리해준다.
# devtools
spring.devtools.restart.enabled = true # restart 활성화
spring.devtools.restart.additional-paths = .
spring.devtools.livereload.enabled = true # livereload 활성화
spring.devtools.restart.trigger-file = .restart # 트리거 파일로서 해당 파일이 수정되면 서버가 재시작된다
version: '3.9'
services:
db:
container_name: postgresql
image: postgres:15-alpine
...
backend:
build:
context: ./backend
dockerfile: Dockerfile
target: development # not production
container_name: backend
environment:
- ...
depends_on:
db:
condition: service_healthy
ports:
- 8080:8080
restart: always
volumes:
- ./backend:/home/deploy/app/backend
volume 설정에 ./backend:/home/deploy/app/backend
와 같이 넣어줌으로써 내 코드를 마운트해주었다. 이로서 내가 코드 수정하면 컨테이너 내 코드도 같이 수정되도록 한다.
...
dependencies {
...
developmentOnly "org.springframework.boot:spring-boot-devtools"
}
...
task getDeps(type: Copy) {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from configurations.compileClasspath into "libs/"
from configurations.runtimeClasspath into "libs/"
}
task buildAndReload {
dependsOn build
mustRunAfter build // buildAndReload must run after the source files are built into class files using build task
doLast {
new File(".", ".restart").text = "${System.currentTimeMillis()}" // update trigger file in root folder for hot reload
}
}
getDeps
task 의 경우 변경된 파일을 반영한다. 그리고 buildAndReload
task 를 통해 빌드하면 .restart 파일도 수정하도록 한다.