
외부 JAR를 Spring Boot에서 사용한다는 것은, 어느 시점의 classpath에 그 JAR를 올릴지 선택하는 문제입니다. 시점마다 목적과 설계 포인트가 달라지며, 특히 Docker 환경에서는 “언제, 어디에 복사해서 classpath에 태울지”를 명확히 하는 것이 중요합니다. 아래에서는
libs/external-logging-1.0.0.jar를 예시로 시점별로 정리해 보겠습니다.
컴파일 시점에는 소스 코드가 외부 JAR 안의 클래스와 메서드를 import 할 수 있어야 합니다. Gradle에서는 일반 라이브러리처럼 implementation files("libs/external-logging-1.0.0.jar") 형태로 의존성을 추가합니다.
특징
컴파일 에러 없이 해당 JAR의 타입·메서드를 직접 사용합니다.
Spring Boot Gradle/Maven 플러그인을 사용할 경우, 보통 패키징 시점에 fat JAR(BOOT-INF/lib) 안에 함께 포함됩니다.
버전이 자주 바뀌지 않는 공통 라이브러리에 적합합니다.
fat jar는 “내 코드랑 필요한 라이브러리를 전부 한 덩어리 JAR 하나에 다 넣어 둔 실행용 최종 JAR 형태”라고 보시면 됩니다.
예시 (build.gradle)
dependencies {
implementation files('libs/external-logging-1.0.0.jar')
}
Docker 예시
# 1단계: 빌드 (컴파일+패키징)
FROM gradle:8-jdk AS builder
WORKDIR /workspace
COPY . .
RUN gradle clean bootJar
# 2단계: 런타임
FROM eclipse-temurin:17-jre
WORKDIR /app
# 빌드 결과물만 복사 (외부 JAR는 fat JAR 안에 이미 포함)
COPY --from=builder /workspace/build/libs/my-app-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-jar","/app/app.jar"]
이 패턴에서는 외부 JAR가 개발 환경과 빌드 컨테이너의 파일 시스템에 존재하고, 최종 app.jar 내부 BOOT-INF/lib에도 포함된다는 점이 핵심입니다.
여기서 빌드 시점은 gradle build 또는 mvn package로 실행 가능한 하나의 JAR를 만드는 패키징 단계입니다. 이때 “어떤 JAR를 실행 JAR 내부에 넣을지” 혹은 “외부 폴더로 뺄지”를 결정합니다.
컴파일 의존성에 올려둔 외부 JAR는 Spring Boot 플러그인이 기본 설정이라면 자동으로 BOOT-INF/lib에 패키징합니다.
java -jar app.jar 한 줄로 실행이 가능합니다.app.jar만 복사하면 됩니다./opt/projects/my-app/
build.gradle
src/...
libs/
external-logging-1.0.0.jar
/opt/projects/my-app-docker/
DockerfileFROM eclipse-temurin:17-jre
WORKDIR /app
COPY build/libs/my-app-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-jar","/app/app.jar"]실행 JAR에는 기본 의존성만 담고, 일부 JAR는 파일 시스템 상의 별도 디렉터리에 두고 loader.path 등으로 런타임 classpath에 추가하는 패턴입니다.
import합니다.BOOT-INF/lib에서 제외하고, 별도 디렉터리(/app/ext-libs)에 복사합니다.loader.path로 지정해 classpath에 올립니다./app/app.jar
/app/ext-libs/external-logging-1.0.0.jarFROM eclipse-temurin:17-jre
WORKDIR /app
# 1) 앱 JAR 복사
COPY build/libs/my-app-0.0.1-SNAPSHOT.jar app.jar
# 2) 외부 JAR를 이미지 안의 별도 디렉터리에 복사
COPY libs/external-logging-1.0.0.jar ext-libs/external-logging-1.0.0.jar
# 3) 외부 JAR 디렉터리를 loader.path 로 추가
ENTRYPOINT ["java",
"-Dloader.path=ext-libs/",
"-Dloader.main=com.example.MyApplication",
"-cp","app.jar",
"org.springframework.boot.loader.PropertiesLauncher"]이 방식의 핵심은 “외부 JAR 교체를 앱 재빌드 없이 하고 싶다”는 요구를 만족시키는 것입니다.
런타임 시점은 이미 만들어진 Spring Boot 실행 JAR를 띄울 때, 프로세스 실행 명령에서 외부 JAR를 classpath에 추가하는 단계를 의미합니다.
PropertiesLauncher + loader.path 사용.URLClassLoader 등을 이용해 특정 디렉터리의 JAR를 동적으로 로딩하는 플러그인 구조./app/app.jar
/app/ext-libs/external-logging-1.0.0.jarPropertiesLauncher 사용)java \
-cp app.jar \
-Dloader.path=ext-libs/ \
-Dloader.main=com.example.MyApplication \
org.springframework.boot.loader.PropertiesLauncher또는 단순히 JVM classpath를 이용해 외부 JAR를 포함하는 형태도 가능합니다.
java -classpath "app.jar:ext-libs/*" com.example.MyApplication
| 시점 | classpath 기준 위치 예시 | 코드에서 import 가능한가? | app.jar 안에 포함 여부 | 주 사용 목적 |
|---|---|---|---|---|
| 컴파일 시점 | Gradle/Maven 의존성(implementation, api) | 가능 (O) | 보통 포함되도록 설정되는 경우가 많음 | 일반 라이브러리 의존, 강한 타입 의존 |
| 빌드 시점 | BOOT-INF/lib에 넣을지 vs 외부 디렉터리로 뺄지 | 새로 결정 X (컴파일 때 이미 결정) | 플러그인 설정·Docker COPY로 포함/미포함 제어 | fat JAR 구성, app.jar + ext-libs 구조 |
| 런타임 시점 | loader.path(/ext-libs 등), 커스텀 ClassLoader | 직접 import는 보통 X | 보통 실행 JAR 밖에 두고 필요 시 로딩 | 플러그인, 고객별 모듈, 교체 가능한 확장 |
import 가능 여부는 오직 “컴파일 시점에 그 JAR가 의존성으로 classpath에 올라가 있었는가”에 의해 결정됩니다.
Spring Boot 프로젝트에 외부 JAR를 “넣어야 하는” 상황에서는 다음 기준으로 선택하는 것이 좋습니다.
loader.path 또는 플러그인 구조로 외부 JAR를 올리는 패턴이 더 적합합니다.Docker에서는 “이미지 빌드 시점(복사 위치)”과 “컨테이너 실행 시점(java 옵션)”을 구분해 생각하면, 각 시점별 설계가 훨씬 명확해집니다.