본문 바로가기
Spring Boot, JPA

Spring - WAR, JAR, Executable JAR

by suhsein 2024. 11. 13.
728x90

인프런 김영한 강사님 강의를 듣고 정리한 내용입니다.

WAR

Web Application Archive

WAR는 WAS(Web Application Server)에 배포할 때 사용하는 압축 파일이다. WAS 위에서 동작하고, HTML과 같은 정적 리소스와 클래스 파일을 모두 포함한다. 그리고 JAR에 비해서 구조가 더 복잡하다.

WAR 구조

jar -xvf 파일명.war로 압축을 풀어서 구조를 확인할 수 있다.

  • WEB-INF
    • classes : 실행 클래스 모음(자바 클래스)
    • lib : 라이브러리 모음
    • web.xml : 웹 서버 배치 설정 파일(생략 가능)
  • 그 외 정적 리소스 (HTML, css ...)

라이브러리 모음에서 외부 라이브러리를 jar 형식으로 포함시킬 수 있다.

빌드와 배포

빌드

프로젝트 폴더에서
./gradlew clean build
명령어로 빌드한다.

프로젝트 폴더 경로/build/libs 하위에 .war 형식의 파일이 생성된다.

배포

생성된 war 파일을 배포하려면 복사한 후,
톰캣 설치 경로/webapps 하위에 붙여넣는다.
그리고 파일명을 ROOT로 변경한다. (ROOT.war 형식)

그리고 톰캣 서버를 실행하면, war 파일이 실행된다.

WAR의 단점

  1. WAS의 별도 설치가 필요하다
  2. WAR 파일 빌드 후에 WAS에 배포해야 한다.
  3. 개발 환경 설정이 복잡하다.
    JAR는 별도 설정 없이 main()만 실행하면 된다.
  4. 사용하는 WAS(ex. 톰캣)의 버전 변경을 위해 WAS를 재설치해야한다.

해결 방법
=> 톰캣을 내장시키자! 라이브러리처럼

라이브러리처럼 WAS를 내장시켜 사용하려면, WAR를 사용할 수 없다. 이후에 설명할 실행가능한 JAR로 톰캣을 내장시켜서 사용할 수 있다.

JAR

Java Archive

압출 파일로, 여러 클래스 리소스의 묶음이다.
JAR 내부의 META-INF/MANIFEST.MF 파일에서 Main-Class에 실행할 main() 메서드의 클래스를 지정해줘야 한다. jar 파일의 실행 시에 지정해둔 클래스의 main() 메서드가 실행된다.

다음과 같이 Main-Class에 클래스를 지정한다.

Main-Class: hello.embed.EmbedTomcatSpringMain
  1. JVM 위에서 직접 실행하거나
  2. 다른 곳에서 사용하는 라이브러리로 제공한다.

구조

역시 jar -xvf 파일명.jar로 압축을 풀어서 구조를 확인할 수 있다.

  • META-INF
    • MANIFEST.MF
  • 클래스 디렉토리
    • 클래스들

외부 라이브러리를 포함하지 않은 클래스 코드들만이 압축되어 저장된다.

빌드

프로젝트 폴더에서
./gradlew clean buildJar
명령어로 빌드한다.

{프로젝트 폴더 경로}/build/libs 하위에 .jar 형식의 파일이 생성된다.

문제점

외부 라이브러리를 사용하는 경우에는 IDE에서 jar 파일을 돌리는 게 아니고서야 파일 안에 라이브러리들을 포함시켜야만 한다.(빌드-배포의 경우)

하지만 자바 표준에서는 jar 파일 안에 다른 jar 파일들을 포함시키는 것이 불가능하다.

Fat Jar

기존의 jar에서 외부 라이브러리 없이 실행이 불가능한 문제를 해결하고자 처음에 Fatjar를 사용했다.

Fatjar를 사용하면 외부 라이브러리의 jar 파일들을 모두 class로 풀어서 저장한다.

빌드

프로젝트 폴더에서
./gradlew clean buildFatJar
명령어로 빌드한다.

{프로젝트 폴더 경로}/build/libs 하위에 .jar 형식의 파일이 생성된다.

하지만 이 방법에도 여전히 문제가 있다.

문제점

  1. 어떤 라이브러리를 사용하는지 정확히 알 수 없는 문제
    jar 파일 안의 풀어진 클래스가 어떤 라이브러리로부터 왔는지 알 수 없음
  2. 여러 라이브러리 간에 동명의 클래스가 포함되는 경우
    동명의 클래스가 존재하면, 그 중 하나의 클래스만 저장된다. 의도한 대로 동작하지 않을 우려가 있다.

Executable JAR

Executable JAR는 실행가능한 jar 라는 의미이다. 자바 표준에서 정의된 것은 아니고, `스프링 부트`에서 새롭게 정의한 것이다.

특징은 JAR 파일 내부에 다른 JAR 파일들을 포함시킬 수 있다는 것이다.

JAR 안에 JAR?

자바 표준에서 JAR 내부에 다른 JAR를 넣는 것은 불가능하지만, 실행가능한 jar는 다음과 같은 방식으로 이를 가능케 한다.

META-INF/MANIFEST.MF 파일에서 Main-Class에 실제 main()이 있는 클래스가 아니라 Jar Launcher가 지정된다.
Jar Launcher 에서는 JAR 안에 JAR를 넣고 읽을 수 있도록 하는 기능을 정의해두었다. 그래서 JAR 파일 실행 시에 먼저 Jar Launcher가 실행되어 내부에 JAR를 넣을 수 있도록 처리한 후, 실제 main()을 실행한다.

Main-Class에는 Jar Launcher 지정,
Start-Classmain()이 있는 클래스 지정

Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: hello.ExternalApplication

빌드

프로젝트 폴더에서
./gradlew clean buildFatJar
명령어로 빌드한다.

{프로젝트 폴더 경로}/build/libs 하위에 .jar 형식의 파일이 생성된다.

스프링 부트 내장 톰캣 사용

start.spring.io 사용

https://start.spring.io/ 에서 빠른 설정으로 프로젝트를 생성할 수 있다.

Spring Boot는 괄호가 붙지 않는 가장 최신 버전을 선택하고, 패키징을 Jar로 설정한다.
Dependencies에서 Spring Web을 추가하면 내장 톰캣을 사용할 수 있다.

수동으로 추가하기

혹은 이미 만들어진 프로젝트에서 내장 톰캣을 사용하고 싶다면, build.gradle에서 다음과 같이 설정한다.

plugins {
  id 'java'
  id 'org.springframework.boot' version '3.2.3'
  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()
}

내장 톰캣을 사용하기 위해서 dependencies에
implementation 'org.springframework.boot:spring-boot-starter-web'를 추가하면 된다.

이제 WAS를 따로 설치할 필요없이 라이브러리처럼 내장 톰캣을 사용할 수 있게 되었다!
이는 모두 스프링 부트의 Executable Jar 덕분이다.

728x90