모듈(Module)은 Java 9에서 도입된 핵심 개념으로, 코드를 모듈이라 불리는 작은 단위의 컴포넌트로 분리할 수 있게 해줍니다. 각 모듈은 자신만의 명확한 책임을 가지며, 정상적으로 동작하기 위해 필요한 다른 모듈에 대한 의존성을 선언합니다. 모듈을 선언하려면 소스 코드의 루트 디렉터리에 "module-info.java" 파일을 포함시켜야 합니다.
module-info 파일에는 여러 유형의 "requires" 절이 존재하며, 각각의 용도와 특징이 다릅니다.
1) requires <module>
기본적으로 모듈은 모듈 경로(module-path)에 존재하는 다른 모듈들을 인식하지 못합니다. 따라서 다른 모듈에 접근하고자 할 때마다 module-info.java 파일에 "requires" 구문을 반드시 추가해야 합니다.
module com.tutorialspoint.gui {
requires com.tutorialspoint.model;
requires java.desktop;
}2) requires transitive <module>
예를 들어, 우리 모듈인 "com.tutorialspoint.model"이 "com.core" 모듈에서 내보낸(exported) 인터페이스 타입을 반환한다고 가정해 보겠습니다. 이 경우 해당 모듈을 사용하려는 다른 모듈 역시 컴파일 오류 없이 두 번째 모듈의 클래스에 접근하기 위해 "com.core"에 대한 의존성이 필요합니다.
Java 9는 이러한 전이적 의존성을 표현하기 위해 "transitive" 키워드를 제공합니다. 덕분에 "com.tutorialspoint.model"을 사용하는 모듈은 별도의 선언 없이도 "com.core"에 접근할 수 있으며, 구현 변경이 훨씬 수월해집니다.
module com.tutorialspoint.model {
requires transitive com.core;
}3) requires static <module>
"requires static" 키워드는 선택적 의존성(optional dependency) 개념을 나타냅니다. 이 방식으로 선언된 모듈은 다음과 같은 특징을 가집니다.
- 컴파일 시 필수: 컴파일 시점에 해당 모듈이 모듈 경로에 존재하지 않으면 컴파일 오류가 발생할 수 있습니다.
- 런타임 시 선택: 애플리케이션이 시작될 때 수행되는 무결성 검사 단계에서 해당 모듈이 고려되지 않습니다. 즉, 모듈이 없더라도 애플리케이션은 정상적으로 실행됩니다.
예를 들어, 애플리케이션의 데이터를 Oracle 데이터베이스 또는 H2 데이터베이스 중 어느 곳에든 저장할 수 있는 기능을 제공하고 싶다면 다음과 같이 선언할 수 있습니다.
module com.tutorialspoint.model {
requires static ojdbc;
requires static h2database.h2;
}이처럼 requires 절의 세 가지 변형을 적절히 활용하면, 모듈 간 의존성을 명확하게 관리하고 더욱 견고하고 유연한 Java 애플리케이션을 설계할 수 있습니다.