개발 일기

[TroubleShooting] SpringBoot 멀티모듈 프로젝트 빌드 이슈(jar와 bootJar) 본문

TroubleShooting

[TroubleShooting] SpringBoot 멀티모듈 프로젝트 빌드 이슈(jar와 bootJar)

개발 일기장 주인 2024. 11. 22. 13:00

이슈

처음에 아래와 같은 구조로 스프링부트 멀티모듈 프로젝트를 구성했다.

그런데 기존에는 api모듈을 하나로 해서 진행했는데 장기간 개발해야하는 프로젝트여서 각 기능별로 분리해서 모듈화하기로 했다.

그런데 기존에는 api가 하나로 되어있어서 단순히 bootJar를 true, jar를 false로 해놓고 jar로 묶어 도커 이미지를 만들어 배포를 했었느데 bootJar와 jar에 대해 잘 알지 못한 상태로 개발을 진행하여 모든 api모듈들의 build.gradle을 

bootJar {enabled = true}
jar {enabled = false}

위와 같이 설정하였고 execute-app하위에 생긴 jar파일을 통해 도커 이미지를 묶었더니 domain-rds,common과 같은 의존성은 정상적으로 주입되었지만 Controller와 같은 빈들은 주입되지 않아 execute 모듈에 생성된 Jar나 그 jar파일을 통해 생성한 도커이미지를 실행했을때 타 모듈인 api모듈들에 정의된 controller를 실행시켰을때 404 NOT FOUND가 뜨는 것을 확인하였다.

정말 별것도 아닌 것에 3일동안 고민했고 그 결과 bootJar와 jar에 대해 제대로 이해하지 못하고 있었기 때문에 제대로된 jar파일 생성이 되지 않은 것이다.


해결과정

처음에 execute 디렉토리 하위에 생성된 JAR 파일을 사용하면서 의존성은 정상적으로 주입되었지만, Controller와 같은 Spring Bean들이 주입되지 않는 문제를 겪음. 이에 대해 "단지 execute 디렉토리 하위에 생성된 JAR 파일이라 의존성은 정상적으로 주입되지만, Controller와 같은 Bean들은 주입되지 않는 것인가?"라고 생각했음. 그러나 이는 잘못된 추측이었음.

 

문제는 단순히 JAR 파일의 위치나 Controller 빈이 주입되지 않는 것이 아니라, bootJar와 jar의 설정을 잘못 이해한 데 있었다.

bootJar는 실행 가능한 Spring Boot 애플리케이션을 패키징하는 데 사용되는 설정이고, jar는 일반적인 라이브러리 JAR을 패키징하는 데 사용된다는 것을 제대로 이해하지 못하고 있었다. 각 모듈에서 bootJar와 jar의 설정을 올바르게 하지 않아서 실행 가능한 JAR에 Controller와 같은 빈들이 포함되지 않았던 것이다.

기존에 domain-rds에서는 정상적으로 bootJar는 false, jar는 true로 하였고 api의 api-health-check모듈에서는 bootjJar를 true, jar를 false로 했기 때문에 execute의 jar파일을 실행시켰을때 DB는 정상적으로 연결되었고 API모듈 안에 있는 컨트롤러 빈은 제대로 등록되지 않았던 것아 404가 떴던 것이다.

그래서 execute모듈에서 모든 api모듈의 의존하여 실행을 위한 jar파일을 생성할 것이기 때문에 타 모듈은 모두 bootJar를 false로 돌려

./gradlew clean :execute:bootJar후 실행시켰더니

정상적으로 컨트롤러 빈이 등록되어 요청을 처리할 수 있게 됐다.

 

다시한번 왜 블로그를 무시하게 따라하기만하면 안되는지 깨달았다.


bootJar와 jar 구분

bootJar와 jar는 Gradle에서 JAR 파일을 생성하는 두 가지 다른 작업(task)입니다. 둘 다 애플리케이션의 JAR 파일을 빌드하지만, Spring Boot 프로젝트에서 사용하는 방식과 포함된 파일이 다릅니다.


1. jar

  • Gradle의 기본 작업으로, 표준 Java 프로젝트의 JAR 파일을 생성합니다.
  • 핵심 특징:
    • 단순히 컴파일된 클래스(*.class)와 resources 디렉토리의 파일을 포함합니다.
    • 의존성은 포함되지 않으며, 런타임에 외부에서 제공해야 합니다.
    • 이 작업은 Spring Boot 애플리케이션 실행이 목적이 아닌 경우에 적합합니다.
      (예: 라이브러리 JAR 생성)
  • 사용 예:
    • 멀티모듈 프로젝트에서 common이나 domain 같은 비실행 모듈을 빌드할 때.
    • 실행 가능한 JAR 파일이 아닌, 다른 모듈에서 사용될 라이브러리로 패키징할 때.

2. bootJar

  • Spring Boot 플러그인에서 추가한 작업으로, 실행 가능한(Spring Boot Application) JAR 파일을 생성합니다.
  • 핵심 특징:
    • Spring Boot 애플리케이션을 실행하기 위한 JAR 파일을 생성합니다.
    • 모든 의존성 라이브러리Spring Boot 런타임을 포함합니다.
    • 기본적으로 META-INF/MANIFEST.MF 파일에 애플리케이션의 진입점(Main-Class)을 지정하여 java -jar로 실행 가능하게 만듭니다.
    • 이 작업은 Spring Boot 애플리케이션 실행을 목적으로 합니다.
  • 사용 예:
    • 멀티모듈 프로젝트에서 최종 실행 애플리케이션을 패키징할 때.
    • 하나의 완전한 JAR 파일로 모든 의존성을 포함하여 배포할 때.

주요 차이점 비교

특징jarbootJar

의존성 포함 여부 포함되지 않음 모든 의존성 포함 (Fat JAR)
Spring Boot 런타임 포함 포함되지 않음 포함됨 (Spring Boot 애플리케이션 실행 가능)
목적 라이브러리 JAR, 실행 불가능 실행 가능한 JAR (애플리케이션 실행 목적)
기본 생성 위치 build/libs/<name>.jar build/libs/<name>.jar
실행 명령 실행 불가능 (java -cp 사용 필요) 실행 가능 (java -jar <name>.jar)

사용 상황

  • jar
    • 멀티모듈 프로젝트에서 실행되지 않는 모듈 (예: common, domain)을 패키징할 때.
    • 다른 모듈에서 사용할 라이브러리로 빌드할 때.
  • bootJar
    • Spring Boot 애플리케이션을 실행하기 위한 최종 JAR 파일을 생성할 때.
    • 배포용 Fat JAR 파일을 생성할 때.

함께 사용 시의 주의점

Spring Boot 프로젝트에서는 일반적으로 실행 가능한 모듈은 bootJar를 사용하고, 라이브러리로 사용되는 모듈은 jar를 사용합니다.
Gradle에서 bootJar와 jar는 동시에 활성화될 수 없으므로, 하나를 enabled = true로 설정하고 다른 하나를 enabled = false로 설정해야 합니다.