You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
애플리케이션에 내장 톰캣을 라이브러리로 포함했다. 이 코드를 어떻게 빌드하고 배포하는지 알아보자.
자바의 main() 메서드를 실행하기 위해서는 jar 형식으로 빌드해야 한다.
jar 안에는 META-INF/MAINFEST.MF 파일에 실행할 main() 메서드의 클래스를 지정해주어야한다.
실습해보자.
빌드 해주기
명령어 : ./gradlew clean builder
빌드된 것을 조회
자바를 실행
명령어 : java -jar embed-0.0.1-SNAPSHOT.jar
아래와 같이 에러가 뜬다 왜 그런걸까 ?
압축해제
빌드한 jar 파일의 압축을 풀어서 MANIFEST.MF내용확인하기
명령어 : jar -xvf embed-0.0.1-SNAPSHOT.jar
압축 푼 결과물
META-INF
MANIFEST.MF
hello
servlet
HelloServlet.class
embed
EmbedTomcatSpringMain.class
EmbedTomcatServletMain.class
spring
HelloConfig.class
HelloController.class
MANIFEST.MF 안에 내용 보기
위에 질문에 답한다.
지금 경로에는 스프링프레임워크도 없고 스프링라이브러리나 내장 톰켓 라이브러리가 보이지 않는다.
✅ jar파일은 jar파일을 포함할 수 없다.
WAR와 다르게 JAR파일은 내부에 라이브러리 역할을 하는 JAR 파일을 포함할 수 없다. 포함한다고 해도 인식이 안된다. Jar파일 스펙의 한계….이다. WAR는 웹 애플리케이션 서버(WAS) 위에서만 실행할 수 있다.
대안으로는 라이브러리 jar파일을 모두 구해서 MANIFEST 파일에 해당 경로를 적어주면 인식이 되지만 너무 효율적이지 않다. Jar파일 안에 jar 파일을 포함할 수 없기 때문에 라이브러리 역할을 하는 jar 파일도 항상 함께 가지고 다녀야 한다. → 이 방법은 권장하지 않음
6️⃣ 내장 톰캣5 - 빌드와 배포2
✅ FatJar
대안으로 fat jar 또는 uber jar라고 불리는 방법이 있다.
Jar안에는 Jar를 포함할 수 없지만 클래스는 얼마든지 포함이 가능하다.
라이브러리에 사용되는 jar를 풀면 → class생성 → class를 뽑아서 새로 만드는 jar에 포함하는 것이다.
이렇게 하면 수 많은 라이브러리에서 나오는 class 때문에 뚱뚱한(fat) jar가 탄생한다. 따라서 Fat Jar라고 부르는 것이다.
build.gradle - buildFatJar
//Fat Jar 생성
task buildFatJar(type: Jar) {
manifest {
attributes 'Main-Class': 'hello.embed.EmbedTomcatSpringMain'
}
duplicatesStrategy =DuplicatesStrategy.WARN
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
Fat Jar를 빌드하기
명령어 : ./gradlew clean buildFatJar
확인하기
Fat Jar 용량 확인 하기
명령어 : ls -arlth
용량 : 10M
java 실행
정상 실행
Fat Jar 자바 실행하기
명령어 :jar -xvf embed-0.0.1-SNAPSHOT.jar
엄청 많이 풀린다.
✅ Fat Jar 정리
장점
Fat Jar 덕분에 하나의 jar 파일에 필요한 라이브러리들을 내장할 수 있게 되었다.
내장 톰켓 라이브러리를 jar 내부에 있게 되었다.
덕분에 하나의 jar 파일로 배포부터, 웹 서버 설치+실행까지 모든 것을 단순화 할 수 있다.
✅WAR 단점과 해결
톰켓 같은 WAS를 별도로 설치해야 한다.
해결 : WAS를 별도로 설치하지 않아도 된다. 톰캣 같은 WAS가 라이브러리로 JAR 내부에 포함되어 있다.
개발 환경 설정이 복잡하다.
단순한 자바라면 별도의 설정을 고민하지 않고, main() 메서드만 실행하면 된다.
웹 애플리케이션은 WAS를 연동하기 위한 복잡한 설정이 들어간다.
해결: IDE에 복잡한 WAS 설정이 필요하지 않다. 단순히 main() 메서드만 실행하면 된다.
배포 과정이 복잡하다. WAR를 만들고 이것을 또 WAS에 전달해서 배포해야 한다.
해결: 배포 과정이 단순하다. JAR를 만들고 이것을 원하는 위치에서 실행만 하면 된다.
톰캣의 버전을 업데이트 하려면 톰캣을 다시 설치해야 한다.
해결: gradle에서 내장 톰캣 라이브러리 버전만 변경하고 빌드 후 실행하면 된다.
✅ Fat Jar의 단점
어떤 라이브러리가 포함되어 있는지 확인하기 어렵다.
모두 class로 풀려있으니 어떤 라이브러리가 사용되고 있는지 추적하기 어렵다
파일명 중복을 해결할 수 없다.
클래스나 리소스 명이 같은 경우 하나를 포기해야 한다. 이것은 심각한 문제를 발생한다.
예) 서블릿 컨테이너 초기화에서 학습한 부분에서
META-INF/services/jakarta.servlet.ServletContainerInitializer 이 파일이 여러 라이브러리(jar )에 있을 수 있다.
A 라이브러리와 B 라이브러리 둘다 해당 파일을 사용해서 서블릿 컨테이너 초기화를 시도한다. 둘다. 해당 파일을 jar 안에 포함한다.
Fat Jar 를 만들면 파일명이 같으므로 A , B 라이브러리가 둘다 가지고 있는 파일 중에 하나의 파일만 선택된다. 결과적으로 나머지 하나는 포함되지 않으므로 정상 동작하지 않는다
패키지 위치가 중요한 이유는 @MySpringBootApplication에 컴포넌트 스캔이 추가되어 있는데, 컴포넌트 스캔의 기본 동작은 해당 애노테이션이 붙은 클래스의 현재 피키지부터 하위 패키지를 컴포넌트 스캔의 대상으로 사용하기 때문이다.
애노테이션이 붙은 hello.MySpringBootMain 클래스의 패키지 위치는 hello이므로 그 하위의 hello.spring.HelloController를 컴포넌트 스캔한다.
MySpringApplication.run(설정 정보, args) 이렇게 한줄로 실행하면 된다.
이 기능을 사용하는 개발자는 @MySpringBootApplication 애노테이션과 MySpringApplication.run() 메서드만 기억하면 된다.
이렇게 하면 내장 톰캣 실행, 스프링 컨테이너 생성, 디스패처 서블릿, 컴포넌트 스캔까지 모든 기능이 한번에 편리하게 동작함.
지금까지 만든 것을 라이브러리로 만들어서 배포한다면? 그것이 바로 스프링 부트이다.
8️⃣ 스프링 부트와 웹 서버 - 프로젝트 생성
프로젝트설정순서
boot-start 의 폴더 이름을 boot로 변경하자.
프로젝트임포트
plugins {
id 'java'
id 'org.springframework.boot' version '3.0.2'
id 'io.spring.dependency-management' version '1.1.0'
}
group ='hello'
version ='0.0.1-SNAPSHOT'
sourceCompatibility ='17'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
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 내부에 같은 경로의 파일이 있어도 둘다 인식할 수 있다.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
섹션 4. 스프링 부트와 내장 톰켓
1️⃣ WAR 배포 방식의 단점
WAR로 웹 애플리케이션을 개발하고 배포하는 과정
웹 애플리케이션과 서버를 별도로 분리되어있는 구조의 단점
2️⃣ 내장 톰캣1 - 설정
3️⃣ 내장 톰캣2 - 서블릿
4️⃣ 내장 톰캣3 - 스프링
5️⃣ 내장 톰캣4 - 빌드와 배포1
애플리케이션에 내장 톰캣을 라이브러리로 포함했다. 이 코드를 어떻게 빌드하고 배포하는지 알아보자.
자바의 main() 메서드를 실행하기 위해서는 jar 형식으로 빌드해야 한다.
jar 안에는 META-INF/MAINFEST.MF 파일에 실행할 main() 메서드의 클래스를 지정해주어야한다.
실습해보자.
압축 푼 결과물
MANIFEST.MF 안에 내용 보기
위에 질문에 답한다.
지금 경로에는 스프링프레임워크도 없고 스프링라이브러리나 내장 톰켓 라이브러리가 보이지 않는다.
✅ jar파일은 jar파일을 포함할 수 없다.
6️⃣ 내장 톰캣5 - 빌드와 배포2
✅ FatJar
대안으로 fat jar 또는 uber jar라고 불리는 방법이 있다.
Jar안에는 Jar를 포함할 수 없지만 클래스는 얼마든지 포함이 가능하다.
라이브러리에 사용되는 jar를 풀면 → class생성 → class를 뽑아서 새로 만드는 jar에 포함하는 것이다.
이렇게 하면 수 많은 라이브러리에서 나오는 class 때문에 뚱뚱한(fat) jar가 탄생한다. 따라서 Fat Jar라고 부르는 것이다.
build.gradle - buildFatJar
✅ Fat Jar 정리
✅WAR 단점과 해결
main()메서드만 실행하면 된다.main()메서드만 실행하면 된다.✅ Fat Jar의 단점
META-INF/services/jakarta.servlet.ServletContainerInitializer이 파일이 여러 라이브러리(jar)에 있을 수 있다.A라이브러리와B라이브러리 둘다 해당 파일을 사용해서 서블릿 컨테이너 초기화를 시도한다. 둘다. 해당 파일을jar안에 포함한다.Fat Jar를 만들면 파일명이 같으므로A,B라이브러리가 둘다 가지고 있는 파일 중에 하나의 파일만 선택된다. 결과적으로 나머지 하나는 포함되지 않으므로 정상 동작하지 않는다7️⃣ 편리한 부트 클래스 만들기
configClass : 스프링 설정을 파라미터로 전달받는다.
args : main(args)를 전달 받아서 사용한다.
tomcat.start() : 발생하는 예외는 잡아서 런타임 예외로 변경
MySpringBootApplication
컴포넌트 스캔 기능이 추가된 단순한 애노테이션이다.
시작할 때 이 애노테이션을 붙여서 사용하면 된다.
MySpringBootMain
패키지 위치가 중요 → Hello에 있음
패키지 위치가 중요한 이유는 @MySpringBootApplication에 컴포넌트 스캔이 추가되어 있는데, 컴포넌트 스캔의 기본 동작은 해당 애노테이션이 붙은 클래스의 현재 피키지부터 하위 패키지를 컴포넌트 스캔의 대상으로 사용하기 때문이다.
애노테이션이 붙은 hello.MySpringBootMain 클래스의 패키지 위치는 hello이므로 그 하위의 hello.spring.HelloController를 컴포넌트 스캔한다.
MySpringApplication.run(설정 정보, args)이렇게 한줄로 실행하면 된다.@MySpringBootApplication애노테이션과MySpringApplication.run()메서드만 기억하면 된다.지금까지 만든 것을 라이브러리로 만들어서 배포한다면? 그것이 바로 스프링 부트이다.
8️⃣ 스프링 부트와 웹 서버 - 프로젝트 생성
plugins { id 'java' id 'org.springframework.boot' version '3.0.2' id 'io.spring.dependency-management' version '1.1.0' } group = 'hello' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' } tasks.named('test') { useJUnitPlatform() }9️⃣ 스프링 부트와 웹 서버 - 실행 과정
스프링 부트를 실행할 때는 자바
main()메서드에서SpringApplication.run()을 호출해주면 된다.
여기에 메인 설정 정보를 넘겨주는데, 보통
@SpringBootApplication애노테이션이 있는 현재 클래스를 지정해주면 된다.참고로 현재 클래스에는
@SpringBootApplication애노테이션이 있는데, 이 애노테이션 안에는 컴포넌트 스캔을 포함한 여러 기능이 설정되어 있다. 기본 설정은 현재 패키지와 그 하위 패키지 모두를 컴포넌트 스캔한다.
이 단순해 보이는 코드 한줄 안에서는 수 많은 일들이 발생하지만 핵심은 2가지다.
🔟 스프링 부트와 웹 서버 - 빌드와 배포
✅JAR를 푼 결과
결과 : FatJar가 아니다!!
다음 챕터에서 스프링부트의 Jar를 알아보자
1️⃣1️⃣ 스프링 부트 실행 가능 Jar
Fat Jar는 하나의 Jar 파일에 라이브러리의 클래스와 리소스를 모두 포함했다. 그래서 실행에 필요한 모든 내용을 하나의 JAR로 만들어서 배포하는 것이 가능했다. 하지만 Fat Jar는 다음과 같은 문제를 가지고 있다.
Fat Jar의 단점
A라이브러리와B라이브러리 둘다 해당 파일을 사용해서 서블릿 컨테이너 초기화를 시도한다. 둘다. 해당 파일을jar안에 포함한다.Fat Jar를 만들면 파일명이 같으므로A,B둘중 하나의 파일만 선택된다. 결과적으로 나머지는 정상 동작하지 않는다.실행 가능 Jar
스프링 부트는 이런 문제를 해결하기 위해 jar 내부에 jar를 포함할 수 있는 특별한 구조의 jar를 만들고 동시에 만든 jar를 내부 jar를 포함해서 실행할 수 있게 했다. 이것을 실행 가능 Jar(Executable Jar)라 한다. 이 실행 가능 Jar를 사용하면 다음 문제들을 깔끔하게 해결할 수 있다.
a.jar,b.jar내부에 같은 경로의 파일이 있어도 둘다 인식할 수 있다.참고로 실행 가능 Jar는 자바 표준은 아니고, 스프링 부트에서 새롭게 정의한 것이다.
지금부터 실행 가능 Jar를 자세히 알아보자.
✅실행 가능 Jar 내부 구조
✅ Jar 실행 정보
Main-Class
JarLauncher는 스프링 부트가 빌드시에 넣어준다.org/springframework/boot/loader/JarLauncher에 실제로 포함되어 있다.
스프링 부트는 jar 내부에 jar를 읽어들이는 기능이 필요하다. 또 특별한 구조에 맞게 클래스 정보도 읽어들여야 한다. 바로JarLauncher가 이런 일을 처리해준다. 이런 작업을 먼저 처리한 다음 Start-Class:에 지정된main()을 호출한다.
Start-Class: 우리가 기대한main()이 있는hello.boot.BootApplication가 적혀있다.
기타: 스프링 부트가 내부에서 사용하는 정보들이다.
참고:Main-Class를 제외한 나머지는 자바 표준이 아니다. 스프링 부트가 임의로 사용하는 정보이다.
✅ 스프링 부트 로더
org/springframework/boot/loader 하위에 있는 클래스들이다. JarLauncher 를 포함한 스프링 부트가 제공하는 실행 가능 Jar를 실제로 구동시키는 클래스들이 포함되어 있다. 스프링 부트는 빌드시에 이 클래스들을 포함해서 만들어준다.
BOOT-INF
classes: 우리가 개발한 class 파일과 리소스 파일lib: 외부 라이브러리classpath.idx: 외부 라이브러리 모음layers.idx: 스프링 부트 구조 정보WEB-INF라는 내부 폴더에 사용자 클래스와 라이브러리를 포함하고 있는데, 실행 가능 Jar도 그 구조를 본따서 만들었다. 이름도 유사하게BOOT-INF이다.JarLauncher를 통해서 여기에 있는classes와lib에 있는 jar 파일들을 읽어들인다.실행 과정 정리
java -jar xxx.jar
MANIFEST.MF 인식
JarLauncher.main() 실행
BOOT-INF/classes/ 인식
BOOT-INF/lib/ 인식
Beta Was this translation helpful? Give feedback.
All reactions