Fat Jar는 하나의 Jar 파일에 라이브러리의 클래스와 리소스를 모두 포함했다. 그래서 실행에 필요한 모든 내용을 하나의 JAR로 만들어서 배포하는 것이 가능했다. Fat Jar는 명확한 문제를 가지고 있다.
Fat Jar의 단점
- 어떤 라이브러리가 포함되어 있는지 확인하기 어렵다.
- 모두 class 로 풀려있으니 어떤 라이브러리가 사용되고 있는지 추적하기 어렵다.
- 파일명 중복을 해결할 수 없다.
- 클래스나 리소스 명이 같은 경우 하나를 포기해야 한다. 이것은 심각한 문제를 발생한다. 예를 들어서 서블릿 컨테이너 초기화에서 학습한 부분을 떠올려 보자.
- META-INF/services/jakarta.servlet.ServletContainerInitializer 이 파일이 여러 라이브러리( jar )에 있을 수 있다.
- A 라이브러리와 B 라이브러리 둘다 해당 파일을 사용해서 서블릿 컨테이너 초기화를 시도한다. 둘다 해당 파일을 jar 안에 포함한다.
- Fat Jar 를 만들면 파일명이 같으므로 A , B 둘중 하나의 파일만 선택된다. 결과적으로 나머지는 정상 동작하지 않는다.
실행 가능 Jar
스프링 부트는 이런 문제를 해결하기 위해 jar 내부에 jar를 포함할 수 있는 특별한 구조의 jar를 만들고 동시에 만든 jar를 내부 jar를 포함해서 실행할 수 있게 했다.
이것을 실행 가능 Jar(Executable Jar)라 한다. 이 실행 가능 Jar를 사용하면 다음 문제들을 깔끔하게 해결할 수 있다.
- 문제: 어떤 라이브러리가 포함되어 있는지 확인하기 어렵다.
- 해결: jar 내부에 jar를 포함하기 때문에 어떤 라이브러리가 포함되어 있는지 쉽게 확인할 수 있다.
- 문제: 파일명 중복을 해결할 수 없다.
- 해결: jar 내부에 jar를 포함하기 때문에 a.jar , b.jar 내부에 같은 경로의 파일이 있어도 둘다 인식할 수 있다.
참고로 실행 가능 Jar는 자바 표준은 아니고, 스프링 부트에서 새롭게 정의한 것이다.
실행 가능 Jar 내부 구조
- boot-0.0.1-SNAPSHOT.jar
- META-INF
- MANIFEST.MF
- META-INF
- org/springframework/boot/loader
- JarLauncher.class : 스프링 부트 main() 실행 클래스
- BOOT-INF
- classes : 우리가 개발한 class 파일과 리소스 파일
- hello/boot/BootApplication.class
- hello/boot/controller/HelloController.class
- ...
- lib : 외부 라이브러리
- spring-webmvc-6.0.4.jar
- tomcat-embed-core-10.1.5.jar
- ...
- classpath.idx : 외부 라이브러리 모음
- layers.idx : 스프링 부트 구조 정보
- classes : 우리가 개발한 class 파일과 리소스 파일
Jar 실행 정보
java -jar xxx.jar를 실행하게 되면 우선 META-INF/MANIFEST.MF 파일을 찾는다. 그리고 여기에 있는 Main-Class를 읽어서 main() 메서드를 실행하게 된다. 스프링 부트가 만든 MANIFEST.MF를 확인해 보자..

- Main-Class
- 우리가 기대한 main() 이 있는 hello.boot.BootApplication 이 아니라 JarLauncher 라는 전혀 다른 클래스를 실행하고 있다.
- JarLauncher 는 스프링 부트가 빌드시에 넣어준다. "org/springframework/boot/loader/JarLauncher" 에 실제로 포함되어 있다.
- 스프링 부트는 jar 내부에 jar를 읽어들이는 기능이 필요하다. 또 특별한 구조에 맞게 클래스 정보도 읽어들여야 한다. 바로 JarLauncher 가 이런 일을 처리해준다. 이런 작업을 먼저 처리한 다음 Start-Class: 에 지정된 main() 을 호출한다.
- Start-Class : 우리가 기대한 main() 이 있는 hello.boot.BootApplication 가 적혀있다.
- 기타: 스프링 부트가 내부에서 사용하는 정보들이다.
- Spring-Boot-Version : 스프링 부트 버전
- Spring-Boot-Classes : 개발한 클래스 경로
- Spring-Boot-Lib : 라이브러리 경로
- Spring-Boot-Classpath-Index : 외부 라이브러리 모음
- Spring-Boot-Layers-Index : 스프링 부트 구조 정보
- 참고: Main-Class 를 제외한 나머지는 자바 표준이 아니다. 스프링 부트가 임의로 사용하는 정보이다.
스프링 부트 로더
org/springframework/boot/loader 하위에 있는 클래스들이다.
JarLauncher를 포함한 스프링 부트가 제공하는 실행 가능 Jar를 실제로 구동시키는 클래스들이 포함되어 있다. 스프링 부트는 빌드 시에 이 클래스들을 포함해서 만들어준다.
BOOT-INF
- 구조
- classes : 우리가 개발한 class 파일과 리소스 파일
- lib : 외부 라이브러리
- classpath.idx : 외부 라이브러리 모음
- layers.idx : 스프링 부트 구조 정보
- WAR구조는 WEB-INF 라는 내부 폴더에 사용자 클래스와 라이브러리를 포함하고 있는데, 실행 가능 Jar도 그 구조를 본따서 만들었다. 이름도 유사하게 BOOT-INF 이다.
- JarLauncher 를 통해서 여기에 있는 classes 와 lib 에 있는 jar 파일들을 읽어들인다.
실행 과정 정리
- java -jar xxx.jar
- MANIFEST.MF 인식
- JarLauncher.main() 실행
- BOOT-INF/classes/ 인식
- BOOT-INF/lib/ 인식
- BootApplication.main() 실행
참고
실행 가능 Jar가 아니라, IDE에서 직접 실행할 때는 BootApplication.main()을 바로 실행한다.
IDE가 필요한 라이브러리를 모두 인식할 수 있게 도와주기 때문에 JarLauncher 가 필요하지 않다.
'스프링 부트(핵심 원리와 활용)' 카테고리의 다른 글
| Ch03. 스프링 부트 스타터와 라이브러리 관리 - 스프링 부트 라이브러리 버전 관리 (0) | 2023.03.07 |
|---|---|
| Ch03. 스프링 부트 스타터와 라이브러리 관리 - 라이브러리 직접 관리 (0) | 2023.03.07 |
| Ch02. 스프링 부트와 내장 톰캣 - 스프링 부트와 웹 서버 (0) | 2023.03.05 |
| Ch02. 스프링 부트와 내장 톰캣 - 편리한 부트 클래스 만들기 (0) | 2023.03.05 |
| Ch02. 스프링 부트와 내장 톰캣 - 내장 톰캣 (0) | 2023.03.03 |