CodeGym /행동 /JAVA 25 SELF /module-info.java: 문법, 모듈 생성

module-info.java: 문법, 모듈 생성

JAVA 25 SELF
레벨 60 , 레슨 1
사용 가능

1. module-info.java란 무엇이며 어디에 있나요?

Java에서 모듈은 항상 파일 module-info.java로 시작합니다. 이는 모듈의 이름, 내보낼 패키지, 다른 모듈에 대한 의존성을 선언하는 일종의 “여권”입니다.

어디에서 찾을까요?
파일 module-info.java는 모듈 소스 폴더의 루트에 있어야 합니다. 예를 들어, 프로젝트 구조가 다음과 같다면:

project-root/
└── src/
    └── my.module.name/
        ├── module-info.java
        └── com/
            └── example/
                └── api/
                    └── MyClass.java

여기서 my.module.name은 모듈 이름입니다(명명 규칙은 뒤에서 다룹니다).

이 파일이 없으면 모듈로 인정되지 않습니다!
이 파일이 없다면, 폴더와 클래스가 아무리 많아도 그냥 “구식” Java 프로젝트일 뿐입니다.

2. module-info.java 문법: 핵심 요소

가장 단순한 파일 예시부터 바로 살펴보죠 — 전혀 어렵지 않습니다, 정말로!

module my.module.name {
    exports com.example.api;
    requires java.sql;
}

부분별로 살펴보기:

module my.module.name { ... }
이는 my.module.name이라는 이름의 모듈 선언입니다. 모듈 이름은 고유 식별자이며, 보통 루트 패키지와 일치합니다(예: com.example.app).
재미있는 사실: 모듈을 java.base라고 이름 붙이면 컴파일러가 좋아하지 않습니다. 표준 모듈을 바꾸려는 시도는 하지 마세요.

exports com.example.api;
이 줄의 의미는 “나는 com.example.api 패키지에 있는 내용을 공개하겠다”입니다. 해당 패키지 내부에서 public로 표시된 것은 다른 모듈에서도 볼 수 있습니다. 그 외는 철저히 내부 전용입니다.

requires java.sql;
여기서는 컴파일러에게 솔직히 말합니다: “작동하려면 표준 모듈 java.sql이 필요합니다.” 이것을 선언하지 않으면 그 모듈의 클래스를 사용할 수 없습니다.

추가 키워드(참고용)

  • opens <package>; — 리플렉션을 위해 패키지를 개방합니다(예: Jackson 같은 직렬화 라이브러리).
  • uses <service-interface>; — 모듈이 어떤 서비스(인터페이스)를 사용함을 나타냅니다.
  • provides <service-interface> with <implementation-class>; — 모듈이 해당 서비스의 구현을 제공함을 알립니다.

이번 강의에서는 exportsrequires에 집중합니다 — 학습용과 실무 프로젝트의 대다수에서 이 둘이면 충분합니다.

3. 예시 module-info.java

예제 1. 최소 모듈

module com.example.hello {
    exports com.example.hello.api;
}
  • 이 모듈은 com.example.hello.api 패키지만 내보냅니다.
  • 예를 들어 com.example.hello.internal에 있는 것들은, public 클래스가 있더라도 다른 모듈에서 보이지 않습니다.

예제 2. 의존성이 있는 모듈

module com.example.dbclient {
    exports com.example.db.api;
    requires java.sql;
}

우리는 JDBC를 사용할 수 있습니다. 단, java.sql에 대한 의존성을 정직하게 선언했기 때문입니다.

예제 3. 여러 패키지 내보내기

module com.example.library {
    exports com.example.library.api;
    exports com.example.library.utils;
}

원한다면 여러 패키지를 내보낼 수 있습니다(하지만 무분별하게 전부 내보내지는 마세요 — 그것이 바로 모듈의 존재 이유입니다!).

4. 제약과 규칙

모듈 이름

  • 보통 루트 패키지와 일치합니다(예: com.example.app).
  • 표준 모듈 이름(java.base, java.sql 등)과 같아서는 안 됩니다.
  • 공백, 특수문자, 숫자로 시작하는 이름 등을 포함해서는 안 됩니다.
  • 권장: 충돌을 피하려면 조직 또는 프로젝트의 역도메인 이름을 사용하세요.

모듈 하나당 module-info.java 는 하나
하나의 모듈에는 이러한 파일이 딱 하나만 있을 수 있습니다. 두 개 이상이면 컴파일러가 “모듈 스캔들”을 일으킬 겁니다.

하나의 패키지는 하나의 모듈에서만 export
동일한 패키지를 서로 다른 두 모듈에서 동시에 내보낼 수는 없습니다. 같은 이름의 여권을 두 개 갖는 것과 같아서, 국가는 용납하지 않습니다.

모듈 안의 패키지
실제로 이 모듈의 소스 구조에 존재하는 패키지만 내보낼 수 있습니다. 존재하지 않는 패키지를 내보내려 하면 컴파일 오류가 발생합니다.

5. 실습: 프로젝트에 module-info.java 만들기

다음과 같은 간단한 프로젝트가 있다고 가정해봅시다:

project-root/
└── src/
    └── com.example.greetings/
        ├── module-info.java
        └── com/
            └── example/
                └── greetings/
                    ├── api/
                    │   └── Greeter.java
                    └── internal/
                        └── SecretSauce.java

1단계. module-info.java 생성

module com.example.greetings {
    exports com.example.greetings.api;
}

2단계. 다른 모듈에서 internal 패키지의 클래스를 사용해 보기

두 번째 모듈 com.example.app이 있고, 이 모듈이 SecretSauce에 접근하려 한다고 해봅시다:

module com.example.app {
    requires com.example.greetings;
}

import com.example.greetings.internal.SecretSauce; // 오류!

결과:
컴파일러는 “패키지 com.example.greetings.internal은 모듈 com.example.greetings에서 내보내지 않았다”고 말할 것입니다. 클래스 SecretSaucepublic이라 해도 다른 모듈에서는 접근할 수 없습니다.
이것이 바로 모듈 수준의 진정한 캡슐화입니다!

3단계. requires 를 선언하지 않으면?

com.example.app에서 requires com.example.greetings;를 작성하지 않고 com.example.greetings.api의 클래스를 사용하려 하면, 컴파일러는 다음과 같은 오류를 냅니다:

package com.example.greetings.api is not visible

6. module-info.java 작업 시 흔한 실수

오류 №1: 모듈 이름과 프로젝트 구조 불일치.
모듈을 com.example.app이라고 이름 붙였는데 폴더 구조가 src/main/java/app라면, 컴파일러는 무엇을 원하는지 이해하지 못합니다. 모듈 이름은 보통 루트 패키지와 일치하며, 폴더 구조도 이를 반영해야 합니다.

오류 №2: 무분별한 전체 export.
정말로 다른 모듈에서 보여야 하는 것만 export해야 합니다. 단지 “간단해서”라는 이유로 exports com.example;처럼 통째로 내보내지 마세요. 이는 캡슐화를 해칩니다.

오류 №3: requires 를 빼먹음.
다른 모듈 또는 표준 라이브러리(예: java.sql)의 클래스를 사용하면서 의존성을 선언하지 않으면 컴파일 오류가 발생합니다.

오류 №4: exports 에 존재하지 않는 패키지.
exports com.example.foo;라고 썼는데 그런 패키지가 없다면 — 컴파일러가 오류를 보고합니다.

오류 №5: export되지 않은 패키지의 public 클래스.
클래스가 public으로 선언되었더라도, 그 클래스가 export되지 않은 패키지에 있다면 해당 클래스는 모듈 내부에서만 보입니다. 오류는 아니지만 초보자에게는 종종 놀라운 지점입니다.

오류 №6: 하나의 모듈에 여러 module-info.java.
하나의 모듈에는 module-info.java 파일이 정확히 하나만 있어야 합니다. 두 개 이상이면 컴파일러가 프로젝트를 빌드하지 못합니다.

코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION