WebFlux와 헥사고날 아키텍처

김기현·2025년 8월 19일

Spring WebFlux

목록 보기
28/28

WebFlux와 헥사고날 아키텍처가 잘 어울리는 이유

WebFlux와 헥사고날 아키텍처가 잘 어울리는 이유는 두 기술의 핵심 철학이 유사하기 때문이다. 둘 다 분리와 유연성을 추구하기 때문이다.


1. 비즈니스 로직과 외부 의존성 분리

헥사고날 아키텍처는 비즈니스 로직을 데이터베이스, 메시징 시스템, 외부 API 등과 같은 외부 기술로부터 분리한다.
이 분리 작업은 포트(Port)어댑터(Adapter)라는 개념을 통해 이루어진다.

  • 포트: 비즈니스 로직이 외부와 통신하기 위한 추상적인 인터페이스.
  • 어댑터: 이 인터페이스를 구현하는 구체적인 기술 구현체.

WebFlux의 논블로킹 특성 역시 외부 I/O 작업을 블로킹하지 않고 처리하는 것을 목표로 한다. 헥사고날 아키텍처는 이러한 외부 I/O 로직을 어댑터에 격리시켜, 비즈니스 로직이 WebFlux의 핵심인
MonoFlux를 활용하는 데 집중할 수 있게 해준다.


2. 논블로킹 패터다임의 확장

WebFlux는 논블로킹 I/O를 통해 시스템의 동시성을 높인다. 헥사고날 아키텍처를 적용하면 이 논블로킹 패러다임을 애플리케이션의 모든 계층으로 확장할 수 있다.

예를 들어, 데이터베이스를 R2DBC 어댑터로, 외부 API를 WebClient 어댑터로 구현하면, 비즈니스 로직은 MonoFlux같이 반응형 타입에만 의존하게 된다. 이렇게 되면 비즈니스 로직 자체는
특정 I/O 기술에 묶이기 않고 순수하게 논리적인 흐름에만 집중할 수 있다.


3. 테스트 용이성

헥사고날 아키텍처는 테스트 용이성을 크게 향상시킨다. 외부 의존성(데이터베이스, 외부 API)을 어댑터로 분리했기 때문에, 비즈니스 로직을 테스트할 때는 실제 어댑터 대신 목(Mock) 객체를 사용해
포트를 구현하면 된다.
이는 WebFlux 프로젝트에서도 마찬가지이다. 비즈니스 로직을 테스트할 때 실제 WebClient 호출 없이 XXXApiPort같은 가짜 구현체를 사용하면 된다. 이는 테스트를 빠르고 안정적으로 만들어준다.


4. 코드 변경의 파급 효과 최소화

헥사고날 아키텍처는 변경에 대한 영향을 최소화한다. 예를 들어, 현재 사용 중인 외부 AI API 제공업체를 다른 곳으로 변경해야 할 때, 수정이 필요한 XXXApiAdapter만 수정하면 된다.
비즈니스 로직이나 WebFlux 컨트롤러는 전혀 변경할 필요가 없다. 이는 WebFlux의 논블로킹 스트림을 유지하면서도 기술적 변화에 유연하게 대응할 수 있게 해준다.


헥사고날 아키텍처의 구성 요소

헥사고날 아키텍처는 포트와 어댑터라는 두 가지 주요 구성 요소로 이루어져있다. 이들을 통해 내부 비즈니스 로직과 외부 기술을 분리한다.


포트(Port)

포트는 애플리케이션의 내부와 외부를 연결하는 인터페이스 역할을 한다. 비즈니스 로직이 외부 세계와 통신하기 위한 규칙과 계약을 정의한다. 포트는 두 가지 유형으로 나뉜다.

  • 인바운드 포트(Inbound Port): 외부에서 내부로 요청이 들어오는 입구이다. 사용자의 요청을 받는 REST API 컨트롤러나 메시지 큐 리스너 등이 여기에 해당한다.
  • 아웃바운드 포트(Outbound Port): 내부에서 외부로 요청이 나가는 출구이다. 데이터베이스에 데이터를 저장하거나, 외부 API를 호출하는 등의 행위가 여기에 속한다.

어댑터(Adapter)

어댑터는 포트가 정의한 인터페이스를 구현하는 구현체이다. 외부 시스템과 실제 통신하는 역할을 맡는다. 어댑터도 포트처럼 두 가지 유형으로 나뉜다.

  • 인바운드 어댑터(Inbound Adapter): 사용자의 요청을 받아 인바운트 포트를 호출한다. 예를 들어, Spring WebFlux 컨트롤러가 사용자 요청을 받아 도메인 서비스(인바운드 포트)를 호출하는
    역할을 수행한다.
  • 아웃바운드 어댑터(Outbound Adapter): 아웃바운드 포트를 구현하여 실제 외부 시스템과 통신한다. 예를 들어 R2DBC 어댑터가 아웃바운드 포트 인터페이스를 구현하여 데이터베이스 I/O 작업을 하거나,
    WebClient 어댑터가 외부 API를 호출하는 역할을 한다.

내부 도메인

핵사고날의 핵심 영역으로 비즈니스 로직이 위치한다. 이 영역은 외부의 어떤 기술에도 의존하지 않는 순수한 비즈니스 규칙과 도메인 모델로 구성된다. 포트를 통해 외부와 통신하며, 어댑터의 구현 기술과는 완전히
분리되어있다.


헥사고날 아키텍처를 사용할 때 디렉토리 구조

헥사고날 아키텍처의 구성 요소인 도메인, 어댑터, 포트를 중심으로 프로젝트의 최상위 패키지를 구성하는 것이 좋다.

1. 도메인

핵심 비즈니스 로직을 포함하는 패키지. 외부 의존성 없이 순수한 비즈니스 규칙과 엔티티를 담는다.

  • domain/model: 비즈니스 도메인의 핵심 엔티티나 값 객체(VO)가 위치한다.
  • domain/service: 핵심 비즈니스 로직을 구현하는 서비스 클래스가 위치한다. 이 서비스들은 외부 기술에 의존하지 않고, 포트 인터페이스를 통해 외부와 통신한다.

2. 어댑터

외부 세계외 통신하는 구체적인 기술 구현체들을 모아두는 패키지이다.

  • adapter/in: 외부에서 들어오는 요청을 처리하는 인바운드 어댑터가 위치한다. Spring WebFlux에서는 보통 Controller가 해당한다.
  • adapter/out: 내부에서 외부로 나가는 요청을 처리하는 아웃바운드 어댑터가 위치한다. 데이터베이스 접근(ex: R2DBC 레포지토리), 외부 API 호출(ex: WebClient) 등이 여기에
    속한다.

3. 포트

도메인과 어댑터 사이의 계약을 정의하는 인터페이스들을 모아두는 패키지이다.

  • port/in: 외부에서 들어오는 요청을 처리하기 위한 인바운드 포트가 위치한다. 이는 도메인 서비스가 구현해야 하는 인터페이스를 의미한다.
  • port/out: 내부에서 외부로 나가는 요청을 처리하기 위한 아웃바운드 포트가 위치한다. 이는 아웃바운드 어댑터가 구현해야 하는 인터페이스이다.

최상위 패키지 구조 예시

src/main/java/com/your-project-name
├── domain                 // 핵심 비즈니스 로직
│   ├── model              // 엔티티, 값 객체 등
│   └── service            // 핵심 비즈니스 서비스
│
├── adapter                // 외부와의 통신 기술 구현체
│   ├── in                 // 인바운드 어댑터 (ex: controllers)
│   └── out                // 아웃바운드 어댑터 (ex: repositories, api clients)
│
├── port                   // 내부와 외부를 연결하는 인터페이스
│   ├── in                 // 인바운드 포트
│   └── out                // 아웃바운드 포트
│
└── Application.java       // 메인 애플리케이션 클래스

이러한 구조를 따르면 각 패키지의 역할이 명확해져 유지보수와 확장이 용이해진다.

profile
백엔드 개발자를 목표로 공부하는 대학생

0개의 댓글