1. 모듈식 프로젝트 빌드: 기본 원칙
컴파일러와 모듈 경로(module-path)
모듈이 등장하기 전에는 전체 애플리케이션을 이른바 classpath—Java가 클래스를 찾는 디렉터리와 JAR 파일 목록—로 컴파일하고 실행했습니다. 모듈 시스템에서는 새로운 개념인 module-path가 도입되었습니다. 이제 컴파일러와 JVM은 클래스가 어디에 있는지만이 아니라, 그 클래스가 어떤 모듈에 속하는지, 어떤 의존성이 있는지, 어떤 패키지를 export하는지도 알아야 합니다.
핵심 포인트:
- 모듈식 프로젝트에서는 --module-path를 -classpath 대신 사용합니다.
- 컴파일러와 JVM은 애플리케이션에 필요한 모든 모듈(및 그 의존성)을 볼 수 있어야 합니다.
예시: 모듈 수동 컴파일
두 개의 모듈 core와 app이 있고, app이 core에 의존한다고 가정해 봅시다.
디렉터리 구조:
project-root/
core/
src/
main/
java/
module-info.java
com/example/core/...
app/
src/
main/
java/
module-info.java
com/example/app/...
수동 컴파일(데모):
# 먼저 core를 컴파일합니다
javac -d out/core core/src/main/java/module-info.java core/src/main/java/com/example/core/*.java
# 그런 다음 app을 컴파일합니다(core를 module-path로 지정)
javac --module-path out/core -d out/app app/src/main/java/module-info.java app/src/main/java/com/example/app/*.java
실행:
java --module-path out/core:out/app -m app/com.example.app.Main
실제 프로젝트에서는 수동으로 할 필요가 없습니다 — Maven/Gradle 또는 IDE를 사용하세요.
2. Maven: 다중 모듈 프로젝트 빌드
Maven에서의 다중 모듈 프로젝트 구조
Maven은 오래전부터 여러 모듈(submodule) 구조를 지원합니다. Java 모듈 시스템이 도입된 이후에는 이러한 Maven의 ‘모듈’이 종종 Java 모듈과 일치하지만, 항상 그런 것은 아닙니다. Maven 모듈은 단지 하위 프로젝트이고, Java 모듈은 module-info.java에 기술된 단위를 의미합니다. 보통은 Maven 모듈 1개 = Java 모듈 1개입니다.
일반적인 구조:
myproject/
pom.xml # Parent POM
core/
pom.xml # core 모듈
src/main/java/
module-info.java
com/example/core/...
app/
pom.xml # app 모듈
src/main/java/
module-info.java
com/example/app/...
parent POM 예시 (myproject/pom.xml):
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>myproject</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>core</module>
<module>app</module>
</modules>
</project>
core 모듈의 POM 예시 (core/pom.xml):
<project>
<parent>
<groupId>com.example</groupId>
<artifactId>myproject</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>core</artifactId>
<dependencies>
<!-- 여기에는 다른 모듈이나 서드파티 라이브러리에 대한 의존성을 추가할 수 있습니다 -->
</dependencies>
</project>
app 모듈의 POM 예시 (app/pom.xml):
<project>
<parent>
<groupId>com.example</groupId>
<artifactId>myproject</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>app</artifactId>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
중요:
- app에서 core가 필요하다면, app 모듈의 module-info.java에 requires core;가 있어야 합니다.
- 적절한 <dependency>와 <module>을 선언하면 Maven이 의존성을 자동으로 처리합니다.
Maven이 module-info.java를 처리하는 방식
- Maven은 module-info.java를 다른 클래스와 함께 자동으로 컴파일합니다.
- 모듈 의존성이 있으면, Maven은 컴파일 및 실행 시 이를 module-path에 추가합니다.
- 오래된 플러그인이나 Java 9 미만을 사용하면 모듈 시스템이 작동하지 않습니다.
Maven으로 모듈식 애플리케이션 실행
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<release>21</release> <!-- 또는 사용하는 Java 버전 -->
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<mainClass>com.example.app.Main</mainClass>
<commandlineArgs></commandlineArgs>
</configuration>
</plugin>
</plugins>
</build>
mvn clean install
mvn -pl app exec:java
Maven과 모듈 사용 시 흔한 오류
오류: "module not found". 올바른 module-path를 지정하지 않았거나, 의존 아티팩트에 대한 <dependency>가 누락되었습니다.
모듈 이름 중복. 프로젝트에는 동일한 이름의 모듈이 두 개 있으면 안 됩니다(예: 두 개의 module core).
exports/requires 누락. 패키지를 exports 하거나 의존성을 requires로 선언하는 것을 잊으면 컴파일 오류가 발생합니다.
classpath와 module-path 혼용. 모듈을 지원하지 않는 구식 플러그인/설정을 사용하지 마세요. 예측 불가능한 오류로 이어집니다.
3. Gradle: 다중 모듈 프로젝트 빌드
Gradle 프로젝트 구조
myproject/
settings.gradle
build.gradle
core/
build.gradle
src/main/java/module-info.java
src/main/java/com/example/core/...
app/
build.gradle
src/main/java/module-info.java
src/main/java/com/example/app/...
settings.gradle
rootProject.name = 'myproject'
include 'core', 'app'
build.gradle(루트)
subprojects {
apply plugin: 'java'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21) // 또는 사용하는 Java 버전
}
}
repositories {
mavenCentral()
}
}
core용 build.gradle
plugins {
id 'java'
}
group = 'com.example'
version = '1.0-SNAPSHOT'
app용 build.gradle
plugins {
id 'java'
id 'application'
}
group = 'com.example'
version = '1.0-SNAPSHOT'
dependencies {
implementation project(':core')
}
application {
mainModule = 'app' // Java 모듈 이름
mainClass = 'com.example.app.Main'
}
빌드 및 실행
./gradlew build
./gradlew :app:run
Gradle은 module-info.java를 찾으면 module-path를 자동으로 구성합니다. 문제가 있다면 최신 Gradle과 플러그인을 사용 중인지 확인하세요.
4. IntelliJ IDEA: 모듈식 프로젝트 생성 및 설정
모듈식 프로젝트 생성
- File → New → Project → Java → “Create module-info.java”를 체크하세요.
- 다중 모듈 프로젝트의 경우: File → New → Module — 프로젝트에 새 모듈을 추가하세요.
- Maven/Gradle을 사용하는 경우 pom.xml 또는 build.gradle로 프로젝트를 가져오면 됩니다.
IDEA에서의 구조
- Project View에서 각 모듈은 개별 브랜치로 표시됩니다.
- 각 모듈에는 자체 module-info.java와 소스가 있습니다.
- IDEA는 어떤 모듈이 어떤 패키지를 export/require하는지 힌트를 제공합니다(exports/requires).
- 패키지 export나 의존성 선언을 빠뜨리면 IDEA가 오류를 하이라이트합니다.
module-path 확인, main 클래스 실행
- Run/Debug Configurations에서 다음을 지정하세요:
- Main class(예: com.example.app.Main)
- Module(예: app)
- IDEA가 module-path는 자동으로 처리합니다.
- Maven/Gradle로 실행하는 경우 해당 실행 구성(예: app [run])을 사용하세요.
외부 모듈과 라이브러리 가져오기
- 외부 라이브러리를 사용하려면(예: requires java.sql; 또는 서드파티 JAR) — Maven/Gradle을 통해 의존성을 추가하세요.
- 라이브러리가 모듈식이 아니라면(module-info.java 없음) 자동으로 “unnamed module”에 포함됩니다(대부분의 라이브러리에서 정상입니다).
5. 실습: 간단한 다중 모듈 프로젝트 만들기
1단계. 프로젝트 구조 만들기
myproject/
core/
src/main/java/module-info.java
src/main/java/com/example/core/HelloService.java
app/
src/main/java/module-info.java
src/main/java/com/example/app/Main.java
HelloService.java (core):
package com.example.core;
public class HelloService {
public String getHello() {
return "core 모듈에서 인사합니다!";
}
}
module-info.java (core):
module core {
exports com.example.core;
}
Main.java (app):
package com.example.app;
import com.example.core.HelloService;
public class Main {
public static void main(String[] args) {
HelloService service = new HelloService();
System.out.println(service.getHello());
}
}
module-info.java (app):
module app {
requires core;
}
2단계. Maven으로 빌드하고 실행하기
프로젝트 루트에 parent pom.xml을 만들고 두 개의 하위 프로젝트(core/pom.xml, app/pom.xml)를 위와 같이 생성합니다. app/pom.xml에 core에 대한 의존성을 추가합니다.
mvn clean install
mvn -pl app exec:java
3단계. Gradle로 빌드하고 실행하기
settings.gradle, 루트 build.gradle, 그리고 각 모듈의 파일을 생성합니다. app/build.gradle에 core 의존성과 mainClass를 지정합니다.
./gradlew build
./gradlew :app:run
4단계. IntelliJ IDEA로 가져오고 실행하기
- File → Open으로 프로젝트 루트를 엽니다.
- IDEA가 Maven/Gradle 구조를 인식하고 모듈을 생성합니다.
- 각 모듈에서 module-info.java를 열어 모듈 간 관계를 확인할 수 있습니다.
- Main.java를 컨텍스트 메뉴로 실행(“Run Main.main()”)하면 IDEA가 module-path를 자동으로 설정합니다.
6. 모듈식 프로젝트 빌드 시 흔한 오류
오류 №1: 잘못된 module-path. 실행 또는 컴파일 시 “module not found” 또는 “package is not visible” 같은 메시지가 나타나면, 대개 올바른 module-path를 지정하지 않았거나 pom.xml/build.gradle에 의존성을 추가하지 않은 것입니다.
오류 №2: 모듈 이름 중복. 프로젝트에는 동일한 이름의 모듈이 두 개 있으면 안 됩니다(예: module core가 두 개). 이는 컴파일 오류로 이어집니다.
오류 №3: export되지 않은 패키지. 클래스가 module-info.java의 exports로 내보내지지 않은 패키지에 있으면, public이어도 다른 모듈에서 사용할 수 없습니다. 해당 클래스를 사용하려 할 때 컴파일 오류가 발생합니다.
오류 №4: requires 미선언. 다른 모듈의 클래스를 사용하면서 module-info.java에 requires를 선언하지 않으면, 컴파일러가 오류를 냅니다.
오류 №5: 외부 라이브러리와의 호환성 문제. module-info.java가 없는 라이브러리(“unnamed module”로 포함됨)는 보통 문제 없지만, 일부 구성에서는 추가 설정이 필요할 수 있습니다.
오류 №6: 잘못된 모듈에서 실행. 실행 구성의 IDEA 또는 Maven/Gradle에서 main 클래스나 모듈을 잘못 지정하면 애플리케이션이 시작되지 않습니다. Main, mainModule/module, 사용 중인 프로파일을 확인하세요.
GO TO FULL VERSION