기본적으로 Spring Modulith에서는 애플리케이션 모듈의 베이스 패키지(예: example.order)만 외부 모듈에 노출되는 Provided Interface 역할을 해요.
하지만 베이스 패키지가 아닌, 하위의 다른 패키지(예: example.order.spi)를 외부 모듈에 추가로 노출하고 싶을 때 사용하는 기능이 바로 명명된 인터페이스(Named Interfaces) 에요!
노출하고 싶은 하위 패키지의 package-info.java 파일에 @NamedInterface 어노테이션을 붙여서 이름을 지정해주면 되요.
Example
╰─ src/main/java
├─ example
│ ╰─ Application.java
├─ …
├─ example.order
│ ╰─ OrderManagement.java
├─ example.order.spi
│ ├─ package-info.java // @NamedInterface("spi")를 설정해요!
│ ╰─ SomeSpiInterface.java
╰─ example.order.internal
╰─ SomethingOrderInternal.java
example.order.spi 패키지의 package-info.java
// example/order/spi/package-info.java
@NamedInterface("spi")
package example.order.spi;
import org.springframework.modulith.NamedInterface;
이렇게 @NamedInterface("spi")를 선언하면 두 가지 효과가 발생해요:
- 다른 애플리케이션 모듈의 코드가
SomeSpiInterface를 참조하여 사용할 수 있게 되요. - 다른 모듈에서 의존성을 설정할 때, 이 명명된 인터페이스(
spi)를 콕 집어서 참조할 수 있어요.
IMPORTANT · > 이름은 자유롭게 지어도 되요
"spi"는 공식 문서의 예시일 뿐이에요. 이 어노테이션의 목적은 “이 하위 폴더(패키지)에 내가 원하는 이름표를 붙이는 것”이에요.** 예시**
- 외부 노출용 API 폴더로 이름 붙이기:
@org.springframework.modulith.NamedInterface("open-api") package example.order.api;- 결제 모듈에서 이벤트 리스너들만 모아둔 폴더로 이름 붙이기:
@org.springframework.modulith.NamedInterface("payment-events") package example.payment.listener;
Named Interface 호출
Named Interface는 다른 외부 모듈(inventory)에서 호출할 때 다음과 같은 방법으로 설정할 수 있어요.
{모듈명} :: {Name}
예를 들어, inventory 모듈이 order 모듈의 open-api 라는 인터페이스만 사용할 수 있도록 제한하는 방법은 다음과 같아요.
// example/inventory/package-info.java
@org.springframework.modulith.ApplicationModule(
allowedDependencies = "order :: open-api"
)
package example.inventory;
이렇게 설정하면, inventory 모듈 내부에서 order 모듈에 대한 접근은 다음과 같이 변경되요.
order-api라는Named Interface: 접근 가능.- 그 외 : 접근 불가
- 기본적인
Provided Interface에 만약@NamedInterface("open-api")가 존재하지 않는다면, 접근할 수 없어요.
- 기본적인
NOTE · > 명시적 의존성(
allowedDependencies)을 지정하지 않은 모듈들의 경우, 기본적으로order모듈의 루트 패키지와 명명된 인터페이스(예:order.api) 모두에 자유롭게 접근할 수 있어요.
모든 명명된 인터페이스 허용하기 (*)
만약 특정 모듈(order)에 선언된 여러 개의 명명된 인터페이스 모두를 허용하고 싶다면 와일드카드 별표(*)를 사용할 수도 있어요.
// example/inventory/package-info.java
@org.springframework.modulith.ApplicationModule(
allowedDependencies = "order :: *"
)
package example.inventory;
이외에도 명명된 인터페이스에 대해 더 복잡하고 일반적인 제어가 필요하다면 커스텀 네임드 인터페이스 탐색 옵션 설정을 활용할 수 있답니다.
장점
- 베이스 패키지가 아니더라도 특정 하위 패키지만 안전하게 외부로 노출(API화)할 수 있다.
- 이중 콜론(
::)을 사용하여 모듈의 세부 패키지 단위까지 정교하게 의존성 범위를 통제할 수 있다. - 이름표를 도메인이나 역할의 성격(예:
open-api,payment-events)에 맞추어 유연하게 지을 수 있어 가독성이 향상된다.
단점
- 노출해야 하는 하위 패키지마다
package-info.java와@NamedInterface설정을 관리해주어야 하는 번거로움이 있다. - 의존 관계를 적는
allowedDependencies문자열에 오타가 날 경우, 모듈 검증 테스트가 깨질 때까지 파악하기 어려울 수 있다.
이 글에 대해 이야기해요
질문이나 다른 관점을 남겨 주세요.