Post

JavaFX 어플리케이션 배포

JavaFX 어플리케이션의 배포는 생각보다 단순하지 않을 수 있습니다. 특히 Java9+ 에서 JavaFX은 JDK로부터 분리되어 빌드방법도 조금 달라졌습니다. 이번 포스팅에서는 JavaFX 애플리케이션을 배포하는 방법을 알아보겠습니다.

포스팅에서의 최종 배포 형태는 다음과 같습니다.

  • Desktop용 프로그램이 대상입니다.
  • Windows용 exe 설치파일이 결과물입니다.
  • 자체 JRE를 포함하여 추가적인 JRE 설치가 필요없습니다.

타 OS들은 기반 내용 설명과 References 들을 참고하여 충분히 응용할 수 있습니다.

커스텀 JRE를 생성하기 위해 jlink를 사용합니다.
jlink는 빌드과정에서 프로젝트에서 사용하는 모듈만 포함하는 커스텀 JRE를 만들어 포함시킵니다. 이를 통해 JRE의 용량을 획기적으로 줄일 수 있습니다. 마인크래프트 게임 또한 JLink를 사용하여 배포되고 있습니다.
JavaFX 외에도 Docker 이미지의 용량을 획기적으로 줄이는 등 다양한 활용이 가능합니다.

jlink를 사용하려면 프로젝트가 모듈형식이어야 합니다.
java9+ 부터 도입된 모듈시스템은 쉽게말해 패키지 단위의 접근제한설정입니다. 프로젝트에 module-info.java가 정의된 경우 따로 할 일은 없습니다. 해당 파일을 사용하지 않으려면 jdeps를 통해 종속성을 지정해주어야 합니다.
제작과정에서 조심할 부분은 사용할 모든 외부라이브러리가 모듈시스템을 지원해야합니다. 오래된 라이브러리나 리플렉션 관련 라이브러리를 사용하는 경우 조심하세요. 모듈 시스템이 업데이트되지 않아 jlink를 적용할 수 없게될 수 있습니다.

Non-Modular의 경우 빌드방법이 다릅니다.
비모듈식 라이브러리를 사용하는 경우 javafx-maven-plugin을 사용할 수 없습니다. Spring Boot와 같은 다수의 유명한 라이브러리들 또한 모듈시스템을 제공하지 않습니다. 이 경우, 먼저 Fat-jar을 생성하고, jdeps를 사용해 종속성을 추립니다. 이후 jlink를 이용하여 별도로 런타임을 생성하는 과정이 필요합니다. 시간이 된다면 추후 포스팅에서 다루겠습니다.

🤔 1. JavaFX 배포 고려사항


Java로 작성한 프로그램은 빌드 후 JAR 형태로 패키징 되곤 합니다. 이렇게 패키징된 JARjava 혹은 javaw 커맨드를 통해 실행됩니다.

1
javaw -jar myapp.jar

하지만 JAR 파일은 최종적인 형태가 아닙니다.

  • 기존 Java 개발자가 아니라면 실행을 위해 JRE를 설치해야 합니다.
  • JRE가 설치되어 있더라도 하위버전이면 업그레이드 해야 합니다.
  • 최종 사용자는 실행파일을 더블클릭해서 실행하기를 원합니다.

사용자를 늘리기 위해서는 유입에 장애물이 적어야 합니다. 이를 위해 JRE를 배포에 포함시키고 exe 형태로 패키징하는 것이 합리적입니다.

📦 2. 배포파일에 JRE를 포함시키기


도입부에서 소개한 jlink 툴을 사용하면 사용자 정의 JRE가 포함된 배포가 가능합니다.

jlink를 사용하기 위해서는 module-info.java 혹은 jdeps를 통해 모듈을 정의해야 합니다. JavaFX는 해당 과정을 포함한 JavaFX-Maven-Plugin이 제공됩니다. 이번 포스팅에서는 플러그인 방식만 살펴보겠습니다. jdeps와 커맨드를 사용하는 방법은 References를 참고하시길 바랍니다.

2-1. Maven Plugin을 사용하여 빌드


JavaFX-Maven-Plugin을 사용합니다. 문서가 잘 되있기에 상세한 설명은 생략합니다. 해당 플러그인을 사용하기 위해서는 module-info.java가 정의되어있어야 합니다. 위치는 다음과 같습니다.

1
2
3
4
5
src/main/java
├── com.example.myModule
│   ├── Application.java
│   └── HelloFXController.java
└── module-info.java

module-info.java의 간단한 사용법은 다음과 같습니다.

1
2
3
4
5
6
7
module com.example.myModule {
    requires javafx.controls;
    requires javafx.fxml;

    opens com.example.myModule to javafx.fxml;
    exports com.example.myModule;
}

개발과정에서 module is not visible과 같은 메시지를 띄워주기 때문에, 그떄그때 requiresopens & exports를 추가해가며 가시성을 조정하면 됩니다.


pom.xml에 플러그인을 설정해주겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<plugin>
  <groupId>org.openjfx</groupId>
  <artifactId>javafx-maven-plugin</artifactId>
  <version>0.0.8</version>
  <executions>
    <execution>
      <!-- Default configuration for running with: mvn clean javafx:run -->
      <id>default-cli</id>
      <configuration>
        <mainClass>
            com.example.myModule/com.example.myModule.HelloApplication
        </mainClass>
        <launcher>app</launcher>
        <jlinkZipName>app</jlinkZipName>
        <jlinkImageName>app</jlinkImageName>
        <noManPages>true</noManPages>
        <stripDebug>true</stripDebug>
        <noHeaderFiles>true</noHeaderFiles>
      </configuration>
    </execution>
  </executions>
</plugin>

IntelliJ 기본 생성 파일입니다. 각 인자들은 런타임 이미지를 만드는데 사용됩니다. 간략히 설명하겠습니다.

  • mainClass : 모듈명/메인클래스명
  • launcher : 생성될 런처파일 이름
  • jlinkZipName : 생성될 zip 파일 이름
  • jlinkImageName : 생성될 이미지폴더 이름
  • noManPages : Java Docs 관련 내용 제외
  • stripDebug : 디버그정보 제외
  • noHeaderFiles : 링크에 의하면, C 네이티브 코딩 관련 라이브리리 폴더입니다. 사용하지 않는다면 필요없습니다.

추가적인 설정들은 JavaFX-Maven-Plugin에 정리되어있습니다. 하지만 위 설정만 사용해도 충분합니다.


이제 Maven의 javafx:jlink 생명주기를 실행하여 jlink할 수 있습니다.

1
.\mvnw clean javafx:jlink

추가적으로 이 플러그인이 있으면 javafx:run 생명주기 또한 제공되어, 빌드 후 바로 실행해볼 수 있습니다.

1
.\mvnw clean javafx:run

먼저 빌드(jlink)의 최종결과를 살펴보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
target
├── app
│   ├── bin
│   │   ├── app
│   │   ├── app.bat
│   │   ├── ...
│   ├── conf
│   ├── legal
│   ├── lib
│   └── release
├── classes
│   ├── ...
├── generated-sources
│   ├── ...
├── maven-status
│   ├── ...
└── app.zip

최종 결과물은 app.zip입니다. 결과물 폴더들을 압축한 파일입니다. 26.6MB네요. 결과물 내용을 보면 앞서 플러그인에서 설정한 인자들이 어떤 것과 매칭되는지 쉽게 알 수 있습니다.

app/bin 폴더 내에 app과 Window용 app.bat 파일이 포함된 것을 볼 수 있습니다. 두 파일이 launcher 이며, 클릭해서 실행 가능합니다. 파일 내용은 동일합니다.

1
2
3
4
#!/bin/sh
JLINK_VM_OPTIONS=
DIR=`dirname $0`
$DIR/java $JLINK_VM_OPTIONS -m com.example.javafxdeployexample/com.example.javafxdeployexample.HelloApplication "$@"

단순한 bash shell 이네요. 좋습니다.

app/bin폴더를 추가적으로 둘러보면 JRE가 포함된 것을 알 수 있습니다. 즉, 현재 상태로도 배포가 가능하긴 합니다. 최종사용자는 압축을 풀고 app 혹은 app.bat을 실행하면 됩니다.

💻 3. 실행파일 형태로 패키징


드디어 마지막입니다. 플랫폼에 종속적인 실행파일로 최종 패키징합시다. 여기에는 jpackage 툴이 사용되는데, 역시 JPackage-Maven-Plugin이 제공됩니다. 역시 커맨드 사용법은 생략하고 플러그인 사용법만 소개하겠습니다.

먼저 플러그인 설정에 앞서 Wix를 설치해주겠습니다. Windows용 Installer를 구성하는데 필요한 프로그램이며, jpackage 내부에서 사용합니다. 링크에서 wix311.exe를 다운받아 설치해주세요.

pom.xml에 플러그인을 설정해주겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<plugin>
  <groupId>org.panteleyev</groupId>
  <artifactId>jpackage-maven-plugin</artifactId>
  <version>1.6.0</version>
  <configuration>
    <name>HelloFX</name>
    <appVersion>0.0.1</appVersion>
    <vendor>com.example</vendor>
    <destination>target/dist</destination>
    <module>com.example.javafxdeployexample/com.example.javafxdeployexample.HelloApplication</module>
    <runtimeImage>target/app</runtimeImage>

    <winShortcut>true</winShortcut>
    <icon>appIcon.ico</icon>
    <javaOptions>
      <option>-Dfile.encoding=UTF-8</option>
    </javaOptions>
</configuration>
</plugin>
  • name, appVersion, vendor : 프로그램에 대한 설명을 지정합니다.
  • destination : 결과물이 저장될 폴더를 지정합니다.
  • module : 모듈/메인클래스명
  • runtimeImage : 위 jlink단계에서 생성된 이미지폴더를 지정합니다.

바로가기 생성, 아이콘 설정 등 다양한 옵션을 사용할 수 있으며, VM 옵션, Parameter 등도 추가할 수 있습니다. 자세한 내용은 JPackage 가이드를 참고하세요.

설정값이 OS 종속적인 것을 알 수 있습니다. 여러 OS용 설정값을 분리해서 작성할 수 있습니다. 플러그인 가이드를 참고하세요. Examples 섹션을 보면 됩니다.

jpackage 가이드는 커맨드를 기준으로 설명하고 있습니다.
플러그인에서는 자동완성이 제공됩니다. 예를들어 windows 관련 설정은 <win 까지만 입력 후 Ctrl + Space로 확인할 수 있습니다. linuxmac도 동일하게 사용할 수 있습니다. 인자가 없다면 단순 flag 이므로 true로 설정하면 됩니다.

VM옵션을 사용하여 최종적인 튜닝을 고려하세요.
조금 맥락에서 벗어난 이야기지만 VM 옵션을 사용하여 최종적인 튜닝을 할 수 있습니다. 힙사이즈, GC전략, 네이티브 메서드 캐싱 관련하여 고려해볼 수 있습니다. 추후 포스팅에서 다루겠습니다.

최종 결과를 보겠습니다. Window Installer 형태로 배포가 되었으며 용량은 28MB 입니다. 적절하네요. 아이콘도 제대로 뜨고 바탕화면 바로가기도 제대로 생성됩니다.

위치 옵션을 주지 않으면 C:\Program Files 폴더에 설치됩니다. 프로그램 추가/제거에서 삭제할 수 있습니다. 실행이 잘 되는군요. 이로써 모든 배포과정이 마무리되었습니다.

📝 4. 정리


우리는 JavaFX 빌드를 하기 위해서 다음과 같은 과정을 거쳤습니다.

  • module-info.java 혹은 jdeps 툴을 이용해서 종속성 묶어주기
  • jlink 툴을 사용하여 런타임 이미지 생성
  • jpackage 툴을 사용하여 Installer로 패키징


플러그인 설정이 완료되었다면

1
.\mvnw clean javafx:jlink jpackage:jpackage

커맨드를 통해 target/dist 폴더에서 결과물을 얻을 수 있습니다.

더 나아가 javafx:jlinkjpackage:jpackage 두 Maven 생명주기를 사용자 정의로 묶을 수 있습니다. 하지만 생략하겠습니다.

최종예제는 Github에서 확인할 수 있습니다. 모두 고생하셨습니다.

References

  • https://dev.to/cherrychain/javafx-jlink-and-jpackage-h9
  • https://learn.microsoft.com/ko-kr/java/openjdk/java-jlink-runtimes
  • https://docs.oracle.com/en/java/javase/18/jpackage/packaging-overview.html#GUID-C1027043-587D-418D-8188-EF8F44A4C06A
  • https://docs.oracle.com/en/java/javase/15/docs/specs/man/jpackage.html
This post is licensed under CC BY 4.0 by the author.

Trending Tags