Language/Java

intelliJ, Gradle 환경에서 JavaFX 사용하기

KAispread 2023. 1. 1. 00:24
728x90
반응형

최근 크롤링에 대해 공부하며 여러 시도를 해보다 자바 프로그램을 만들게 되었다. 기존에는 웹 애플리케이션을 구상했지만 운영상의 이유로 자바 기반 프로그램으로 계획을 변경했고, jar 파일을 콘솔에서 실행시키는 것보다 GUI 를 만들어야겠다고 생각했다. 그렇게 관련 자료를 찾아보던 중 JavaFX 라는 것을 알게되었다. 국내에서는 JavaFX 관련 자료가 그리 많지 않아, 초기 세팅에 애를 먹었기 때문에 이번 포스팅을 통해 JavaFX 사용법을 공유하려한다.


JavaFX?

JavaFX는 Java 기반의 GUI(Graphic User Interface) 를 만들 수 있는 라이브러리이다. 자바 기반이기 때문에 윈도우나 맥, 리눅스에서 사용할 수 있는 것이 가장 큰 장점이다. 기존 GUI 라이브러리인 Swing을 대체하기 위해 만들어졌으며 Swing과의 연동도 가능하다. 

웹 애플리케이션에서는 HTML을 사용하는 것 처럼 JavaFX에서는 FXML이라는 XML 파일을 사용하여 화면을 구성한다.

Scene Builder 라는 도구를 이용하여 코드뿐만 아니라 Drag & Drop으로도 화면을 구성할 수 있다. 또한 CSS 를 사용하여 화면 스타일을 설정할 수 있다. 사용 방법도 웹 애플리케이션에 사용되는 CSS와 비슷하다.

 

실행 환경 

구분 버전
Java 11
JavaFX 19
Gradle 7.4
OS window x64

 

1. Scene Builder 설치

Scene Builder 는 JavaFX 의 화면을 쉽게 개발할 수 있는 UI를 제공한다. 다음의 링크에서 설치 가능하며 Oracle 아이디가 필요하다.

https://www.oracle.com/java/technologies/javafxscenebuilder-1x-archive-downloads.html

 

JavaFX Scene Builder 1.x Archive

We’re sorry. We could not find a match for your search. We suggest you try the following to help find what you’re looking for: Check the spelling of your keyword search. Use synonyms for the keyword you typed, for example, try "application" instead of

www.oracle.com

 

위 URL에서 자신의 운영체제에 맞는 파일을 다운받아 설치하면 된다.

 

2. JavaFX SDK 설치

https://gluonhq.com/products/javafx/

 

JavaFX - Gluon

Roadmap Release GA Date Latest version Long Term Support Extended or custom support Details 20 March 2023 (planned) early access no 19 September 2022 19 (September 2022) no upon request details 18 March 2022 18.0.2 (July 2022) no upon request details 17 Se

gluonhq.com

다음은 JavaFX를 실행할 SDK를 다운받아야 한다. 위 URL에서 자신의 운영체제에 맞는 SDK를 다운받자.
(필자의 경우 window 64비트 환경이기때문에 첫 번째 SDK를 다운받았다.)

 

 

다운받은 SDK는 압축을 풀고 찾기 쉬운 위치에 옮겨 놓자.

필자의 경우, 이 라이브러리를 C:\javafx-sdk-19 에 위치시켰고 이 경로는 이후 IntelliJ의 설정에 필요하니 메모장에 붙여넣자.

 

3. 프로젝트 패키징

전체 패키지는 다음과 같이 구성했다.

 

4. build.gradle 에 다음 코드 추가

plugins {
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.0.9'
    id 'org.beryx.jlink' version '2.24.4'
}

javafx {
    version = "19"
    modules = [ 'javafx.controls', 'javafx.fxml']
}

mainClassName = "org.example.HelloFX"

jlink {
    options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
    launcher {
        name = 'demono'
    }
}
  • mainClassName : JavaFX 를 실행시킬 Main 클래스명을 패키지 경로와 함께 입력한다
  • jlink.launcher.name : 프로젝트 명을 입력한다

 

위 코드 입력후 우측 상단의 파란색 코끼리 아이콘을 클릭하거나, 우측 Gradle 탭에서 build 해주도록 하자

 

5. VM Options 설정

프로젝트 우측 상단에 Main클래스명이 적힌 아이콘을 클릭하고 Edit Configurations.. 설정으로 들어간다.

 

다음화면에서 Modify options를 클릭하고 Add VM options 를 클릭하여 VM 설정을 추가한다.

 

새로 생긴 입력란에 다음 명령어를 입력한다.

  • --module-path "JavaFX SDK 경로/lib" --add-modules javafx.controls,javafx.fxml

이전 단계에서 SDK 압축을 푼 경로 + /lib 만 바꿔주고 로 설정하고 Apply 를 클릭하여 설정을 완료한다.

 

 


적용

Main 클래스 생성

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.util.Objects;

public class HelloFX extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
    	// fxml/testFX.fxml을 불러온다.
        Parent root = FXMLLoader.load(Objects.requireNonNull(
        	getClass().getClassLoader().getResource("fxml/testFX.fxml")
        ));
        
        // GUI에 보일 프로그램 제목을 설정한다.
        primaryStage.setTitle("Instagram Downloader");
        
        // 화면을 설정한다. 두번째 파라미터는 프로그램의 Width, 세번째는 Height를 설정한다
        primaryStage.setScene(new Scene(root, 800, 600));
        
        // 화면을 보여주는 작업을 실행한다
        primaryStage.show();
    }
}

JavaFX 를 실행시키기 위해선 Main 메서드를 생성하고 Application 을 상속받아 start() 메서드를 Override 시켜주면 된다.

  •  FXMLLoader 에서 getClass().getClassLoader().getResource("fxml/testFX.fxml")로 fxml 파일을 불러와야 오류가 생기지 않음

 

FXML

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Font?>

<GridPane alignment="center" hgap="10" vgap="10"
          xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8"
          fx:controller="org.example.FXController">
    <padding>
        <Insets bottom="40" left="40" right="40" top="40"/>
    </padding>
    <columnConstraints>
        <ColumnConstraints halignment="RIGHT" maxWidth="Infinity" minWidth="150" prefWidth="150">
        </ColumnConstraints>
        <ColumnConstraints hgrow="ALWAYS" maxWidth="Infinity" minWidth="200" prefWidth="200">
        </ColumnConstraints>
    </columnConstraints>
    <children>

        <Label fx:id="title" text="Instagram Image Downloader" GridPane.columnIndex="0" GridPane.columnSpan="2"
               GridPane.halignment="CENTER" GridPane.rowIndex="0" GridPane.rowSpan="1">
            <font>
                <Font name="Raleway-VariableFont_wght" size="24"/>
            </font>
            <GridPane.margin>
                <Insets bottom="20" left="0" right="0" top="20"/>
            </GridPane.margin>
        </Label>

        <Label text="닉네임 : " GridPane.columnIndex="0" GridPane.rowIndex="1">
        </Label>
        <TextField fx:id="nickname" prefHeight="40" GridPane.columnIndex="1" GridPane.rowIndex="1"/>

        <Label text="다운받을 폴더 경로 : " GridPane.columnIndex="0" GridPane.rowIndex="2">
        </Label>
        <TextField fx:id="filePath" prefHeight="40" GridPane.columnIndex="1"
                   GridPane.rowIndex="2"/>

        <Label text="이미지 개수(생략 가능):" GridPane.columnIndex="0" GridPane.rowIndex="3">
        </Label>

        <TextField fx:id="imgCount" prefHeight="40" GridPane.columnIndex="1" GridPane.rowIndex="3"/>

        <Button fx:id="downButton" defaultButton="true" onAction="#downloadButton" prefHeight="40" prefWidth="100"
                text="DOWNLOAD" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="CENTER"
                GridPane.rowIndex="4" GridPane.rowSpan="1">
            <GridPane.margin>
                <Insets bottom="20" left="0" right="0" top="20"/>
            </GridPane.margin>
        </Button>

        <TextArea fx:id="process" prefHeight="60" text="Result Field" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="5"
                  minHeight="150"/>
    </children>
    <rowConstraints>
        <RowConstraints/>
        <RowConstraints/>
        <RowConstraints/>
        <RowConstraints/>
        <RowConstraints/>
        <RowConstraints/>
    </rowConstraints>
</GridPane>

최근에 개발했던 fxml 파일이다. Main 클래스인 HelloFX를 실행시키면 FXMLLoader 에서 이 파일을 불러와서 화면에 띄운다.

xml 형식으로 되어있으며, StackPanefx:controller 에 화면을 제어하는 클래스 명을 입력해주면 해당 클래스에서 정의한 메서드들이 화면을 제어하게 된다.

 

Controller 생성 

public class FXController implements Initializable {
    @FXML
    TextField nickname, filePath, imgCount;

    @FXML
    TextArea process;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
    }

    @FXML
    private void downloadButton(ActionEvent event) {
        process.setText("Hello");
    }
}

JavaFX를 제어하기 위한 Controller 클래스이다. fxml 파일에서 fx:controller 에 본 클래스를 등록하면 된다.

Initializable 인터페이스를 implements하고 initialize() 메서드를 Override 하면 된다. 

@FXML 태그를 fxml 요소인 TextField나 Label 같은 객체에 붙여주고 fxml 에서 정의한 id변수명을 일치시키면 자동으로 값을 매핑시켜준다. 

@FXML 태그를 메서드에 붙여주면 버튼 클릭시 이벤트 같은 것들을 정의해줄 수 있다.

 

 

결과

위 fxml 파일을 열면 다음과 같은 UI를 볼 수 있다.

 


 

개인적으로 JavaFX가 많이 사용되는 것도 아니고 사용하기에 그리 친절한 라이브러리도 아니라고 생각한다. 따라서, 모든 UI를 직접 개발하는 것 보다는 여러 템플릿에서 필요한 부분만 적절하게 변경하여 사용하는 것이 좋을 듯 하다. 참고로 자료를 찾을 땐 해외 사이트에서 찾는것이 더 빠르다. 

 

 

참고

728x90
반응형