자바 모듈
시작
모듈은 자바 9부터 추가된 기능이다. 모듈의 도입은 애플리케이션 아키텍처에 깊은 영향을 미친다. 그래서 모듈을 이해하고 사용하는 것은 중요하다고 생각하여 이 글을 작성하게 되었다.
본문
배경
모듈은 런타임에 의미를 가지는 응용 프로그램 배포 및 의존성의 단위다.
이는 다음과 같은 자바 개념과 다르다.
JAR 파일은 런타임에는 보이지 않으며 단순히 클래스 파일들을 포함하고 있는 압축된 디렉터리다.
패키지는 실제로 접근 제어를 위해 클래스를 그룹화하기 위한 네임스페이스다.
의존성은 클래스 레벨에서만 정의한다
접근 제어와 리플렉션이 결합돼 명확한 배포 단위 경계 없이 최소한의 시행으로 개방적인 시스템을 생성한다.
반면에 모듈은 다음과 같은 특징을 가진다.
모듈은 모듈 간의 의존성 정보를 정의하므로 컴파일 또는 애플리케이션 시작 시점에서 모든 종류의 해결(resolution)과 연결(linkage) 문제를 감지할 수 있다.
적잘한 캡슐화를 제공해서 내부 패키지와 클래스를 조작하려는 사용자로부터 안전하게 보호할 수 있다.
최신 자바 런타임에서 이해하고 사용할 수 있는 메타데이터가 포함된 적절한 배포 단위이며 자바 타입 시스템에서 표현한다.
프로젝트 직소(Project Jigsaw)
이 프로젝트는 다음과 같은 목표를 가지고 있다.
JDK 플랫폼 소스 모듈화하기
프로세스 풋프린트 줄이기
애플리케이션 시작 시간 개선하기
JDK와 애플리케이션 코드에서 모듈 사용할 수 있게 하기
자바에서 처음으로 진정한 의미의 엄격한 캡슐화 허용
이전에는 불가능했던 새로운 접근 제어 모드를 자바 언어에 추가하기
이러한 목표는 다시 JDK와 자바 런타임에 더욱 밀접하게 초점을 맞춘 후 다은과 같은 2차 목표로 추진됐다.
단일 모놀리식 런타임 JAR(rt.jar) 끝내기
JDK 내부를 적절히 캡슐화해서 보호하기
외부에 영향 없이 주요 내부의 변경 가능하게 하기(승인되지 않은 비JDK 사용을 막는 변경 포함)
모듈을 슈퍼 패키지로 도입하기
아래 명령어로 모듈을 확인할 수 있다.
모놀리식은 아닌 모듈식 자바 런타임
모듈은 프로그램의 생명 주기에서 서로 다른 시점(각각 컴파일/링크 타임과 런타임)에 사용되는 두가지 새로운 형식(JMOD 및 JIMAGE)을 재공한다
JMOD 형식은 기존 JAR 파일과 유사하지만 자바 8에서처럼 별도의 공유 객체 파일을 제공하지 않고 네이티브 코드를 단일 파일의 일부로 포함한다.
JIMAGE 형식은 자바 런타임 이미지의 내부 구조를 설명하는 새로운 형식이다. 이 형식은 모듈화된 JMOD 파일을 사용하여 자바 런타임 이미지를 생성한다.
jimage
도구를 사용하면 자바 런타임 이미지의 내부 구조를 살펴볼 수 있다.
또는
rt.jar
에서 벗어남으로써 자바 런타임 이미지는 더 작아지고 더 빨라지며 더 캡슐화된다.
내부의 캡슐화
자바는 공개(public), 비공개(private), 보호된(protected) 및 패키지(package) 접근 제어를 제공한다. 이러한 접근 제어는 클래스와 인터페이스의 멤버에 적용된다.
하지만 리플렉션이나 다른 수단을 사용하면 이러한 접근 제어를 우회할 수 있다. 이는 자바의 캡슐화를 약화시키고 런타임에 더 많은 문제를 발생시킨다.
모듈은 이러한 문제를 해결하기 위해 새로운 접근 제어 모드를 도입한다. 이러한 모드는 다음과 같다.
open
모듈은 모든 패키지를 공개한다.opens
패키지는 모든 클래스를 공개한다.exports
패키지는 모든 클래스를 공개한다.exports ... to
패키지는 특정 패키지에만 클래스를 공개한다.uses
서비스를 사용한다.provides ... with
서비스를 제공한다.
각 모듈의 module-info.class
파일에 이러한 접근 제어 모드를 정의할 수 있다. 모듈에서 module-info.class
을 확인할수 있다.
예시로 java.base
모듈의 module-info.class
파일을 확인 할 수 있다.

모듈식 JVM
HelloWorld.java
를 생성하고 다음과 같이 작성한다.
그리고 다음과 같이 실행한다.
결과는 다음과 같다.
스택 프레임은 이제 패키지 이름, 클래스 이름, 라인 번호뿐만 아니라 모듈 이름(java.base)도 포함한다.
Java 8의 결과는 다음과 같다.
모뮬 생성 및 사용
hello
모듈을 생성한다.hello
모듈에 module-info.java 파일을 생성한다.module hello { // util 모듈을 사용한다. requires util; }hello
모듈에 HelloWorld.java 파일을 생성한다.package me.service.hello; import me.service.util.Utils; public class HelloWorld { public static void main(String[] args) { System.out.println(Utils.getHelloWorld()); } }util
모듈을 생성한다.util
모듈에 module-info.java 파일을 생성한다.module util { exports me.service.util; }util
모듈에 Utils.java 파일을 생성한다.package me.service.util; public class Utils { public static String getHelloWorld() { return "Hello world!"; } }디렉터리 구조 확인
project/ ├── hello/ │ └── src/ │ ├── module-info.java │ └── me/service/hello/HelloWorld.java ├── util/ │ └── src/ │ ├── module-info.java │ └── me/service/util/Utils.java └── out/util
모듈을 컴파일한다.$ javac -d out/util util/src/module-info.java util/src/me/service/util/Utils.javahello
모듈을 컴파일한다.$ javac --module-path out/util -d out/hello hello/src/module-info.java hello/src/me/service/hello/HelloWorld.javahello
모듈을 실행한다.$ java --module-path out/util:out/hello --module hello/me.service.hello.HelloWorld결과는 다음과 같다.
Hello world!
jdeps
도구
jdeps
도구는 모듈 간의 의존성을 분석하는데 사용된다.
마무리
이로 인해 자바 모듈에 대한 이해를 높일 수 있었다. 모듈은 자바의 새로운 기능이며 애플리케이션 아키텍처에 깊은 영향을 미친다. 그래서 모듈을 이해하는 것은 중요하다. 이 글에서 간단하게 모듈을 생성하고 사용하는 방법을 정리했다.