[Gradle] runnable jar (fat jar) 생성 방법

해니·2023년 1월 31일
post-thumbnail

1. build.gradle 설정



1) jar {}

  • from

    • dependeny에서 사용하는 library들을 jar로 묶어준다.
  • manifest

    • manifest 파일은 JAR 파일에 패키징된 파일들에 대한 정보를 가지고 있는 특별한 파일이다.
    • 항상 META-INF/MANIFEST.MF 경로에 하나의 manifest 파일이 존재한다.
    • jar { manifest{...} }에 메인 클래스를 지정해준다.

jar {
	
	from {
	
		configurations.compileClasspath.files.collect {
			it.isDirectory() ? it : zipTree(it)
		}
		
		//configurations.runtimeClasspath.collect {
		//	 it.isDirectory() ? it : zipTree(it) 
		// }
		
	}
	
	
	
	manifest {
		attributes 'Main-Class': 'com.pacakage.Launcher'
	}
	

}






manifest만 작성한 경우 라이브러리들이 묶여지지 않는다.



from을 함께 작성한 경우 jar에 라이브러리들이 묶여진다.





2) sourceSets {}

  • .java 파일 외 xml 등의 resource파일은 src/main/resources 내부에 두는 것이 권장 설정이다.
  • .class 파일만 빌드 대상이므로 , src/main/java 내부에 *.mapper.xml 등의 파일이 있는 경우 빌드 대상에서 제외되어 추가적인 설정이 필요하다.




sourceSets {
    main {
        resources {
            srcDirs 'src/main/java'
        }
    }
}





src/main/java에 포함된 *.xml 파일들이 jar에 묶여지지 않았다.



sourceSets 설정 후 *.xml 파일들이 jar에 묶여진 것을 볼 수 있다.





2. Gradle Task

1) build - clean

  • buildDir 폴더를 제거한다.
  • clean 작업을 하지 않고 build 진행 시, 정리 되지 않은 빌드 폴더가 남아있는 경우 오류가 발생할 수 있다.

2) build - jar

  • main 클래스를 포함하여 jar를 구성한다.



위 작업을 수행하면 build/libs 디렉토리에 jar 파일이 생성된 것을 볼 수 있다. 😉


그런데...

리눅스 환경에서 쉘 파일로 실행해도 로그에 아무 것도 뜨지 않아
명령어를 직접 실행해 보니 아래와 같은 오류가 발생했다.



MANIFEST.MF 파일을 확인해 보니 메인 클래스 설정은 잘 돼있고 ..
jar 파일 안에도 메인 클래스 파일이 정상적으로 있는데 뭘까.........


구글링 결과 .. 운영 환경에서 jar 파일 실행 시 ,
dependency에 걸려있는 라이브러리 파일을 모두 복사해야 하고, 그 라이브러리가 사용하고 있는 라이브러리 까지 복사해서 클래스패스에 넣어줘야 하는데 빌드 시 이런 과정을 거치지 않아 생긴 오류로 추정 중이다. 😢
(Maven을 사용하고 있다면 기본적으로 포함되어 있는 dependency:copy-dependencies 플러그인으로 쉽게 되지만, Gradle은 이러한 기능이 없다고 함. )






3. shadowJar

  • 기본적으로 gradle로 빌드를 하면 개발자가 작성한 코드만 컴파일 돼서 build/libs 경로에 jar 파일로 패키징된다.
  • 개발이 끝나고 IDE를 벗어나 커맨드로 동작시키려면 dependency로 걸어서 사용하던 라이브러리 파일들은 내가 손수 찾아서 클래스패스에 넣어줘야 한다.
  • shadowJar 플러그인을 사용하면 jar 파일 내에 라이브러리를 쉽게 넣어줄 수 있다.


plugins {
    ...
    
    id 'com.github.johnrengelman.shadow' version '6.1.0'
    
    ...
}




jar task가 실행될 때 마다 shadowJar가 실행되게 하려면, 아래처럼 jar에 finalizedBy를 달아주면 된다.



jar {
	
	finalizedBy shadowJar
	
	from {
	
		configurations.compileClasspath.files.collect {
			it.isDirectory() ? it : zipTree(it)
		}
		
		//configurations.runtimeClasspath.collect {
		//	 it.isDirectory() ? it : zipTree(it) 
		// }
		
	}
	
	
	
	manifest {
		attributes 'Main-Class': 'com.pacakage.Launcher'
	}
	

}


  





Gradle Tasks 탭에서 빌드를 하게 되면 build/libs 경로에 2개의 파일이 생성된다.

  • xxx.jar : 기존 프로젝트
  • xxx-all.jar : 라이브러리가 포함된 프로젝트

shadowJar로 생성한 jar로 실행하니 잘 동작했다. 🥰
jvm 동작 방식과 gradle 사용법에 대한 학습을 더 해야할듯....





출처
https://gyrfalcon.tistory.com/entry/Gradle-Executable-jar
http://daplus.net/gradle-gradle%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-%EC%A2%85%EC%86%8D%EC%84%B1%EC%9D%B4%EC%9E%88%EB%8A%94-jar-%EB%B9%8C%EB%93%9C/
https://tlo-developer.tistory.com/275
https://da-nyee.github.io/posts/java-gradle-dependency-configurations/
https://jjeong.tistory.com/1565
https://sup2is.tistory.com/73
https://kkang-joo.tistory.com/3
https://pasudo123.tistory.com/416
https://blog.leocat.kr/notes/2017/10/11/gradle-shadowjar-make-fat-jar
https://blog.leocat.kr/notes/2017/10/10/gradle-copy-dependencies

profile
💻 ⚾️ 🐻 이전했어요..! ➡️ https://dev-haeni.tistory.com/

0개의 댓글