← 모든 글
Spring

3. Named Interfaces

기본적으로 Spring Modulith에서는 애플리케이션 모듈의 베이스 패키지(예: example.order)만 외부 모듈에 노출되는 Provided Interface 역할을 해요.

by rati·

기본적으로 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")를 선언하면 두 가지 효과가 발생해요:

  1. 다른 애플리케이션 모듈의 코드가 SomeSpiInterface를 참조하여 사용할 수 있게 되요.
  2. 다른 모듈에서 의존성을 설정할 때, 이 명명된 인터페이스(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 문자열에 오타가 날 경우, 모듈 검증 테스트가 깨질 때까지 파악하기 어려울 수 있다.
끝까지 읽어주셔서 감사합니다.
DISCUSSION

이 글에 대해 이야기해요

질문이나 다른 관점을 남겨 주세요.