11 KiB
11 KiB
category, tags, title, description, last_updated
| category | tags | title | description | last_updated | ||
|---|---|---|---|---|---|---|
| Unified |
|
Hexagonal Architecture | 헥사고날 아키텍처(포트와 어댑터 아키텍처)는 소프트웨어의 핵심인 비즈니스 로직(도메인)을 외부 요소(데이터베이스, 사용자 인터페이스, 프레임워크 등)로부터 완전히 격리하는 설계 패턴이다. | 2026-05-02 |
Hexagonal Architecture
📌 Brief 비즈니스 Summary
헥사고날 아키텍처(포트와 어댑터 아키텍처)는 소프트웨어의 핵심인 비즈니스 로직(도메인)을 외부 요소(데이터베이스, 사용자 인터페이스, 프레임워크 등)로부터 완전히 격리하는 설계 패턴이다. 시스템을 내부(도메인)와 외부(인프라스트럭처)로 명확히 분리하며, 이들 간의 모든 상호작용은 정의된 인터페이스인 '포트'와 이를 구현하는 '어댑터'를 통해서만 이루어지도록 강제한다. 이를 통해 특정 기술이나 프레임워크의 변경이 비즈니스 로직에 영향을 미치지 않도록 보호하며, 높은 유지보수성과 독립적인 테스트 환경을 제공한다 [1-4].
📖 Core Content
헥사고날 아키텍처는 시스템을 견고하고 유연하게 만들기 위해 다음과 같은 핵심 개념과 구조로 설계된다.
- 아키텍처의 핵심 원리 (의존성 역전) 전통적인 계층형 아키텍처(Layered Architecture)에서는 상위 계층이 하위 계층(DB 등)에 직접 의존하여 기술적 변경에 취약했다. 헥사고날 아키텍처는 의존성 역전 원칙(DIP)을 활용하여 도메인과 인프라 모두가 추상화(인터페이스)에 의존하게 만들어, 외부 기술의 종속성을 제거한다 [5-7].
- 주요 구성 요소
- 도메인 (Domain/Inside): 애플리케이션의 핵심 비즈니스 로직과 규칙을 포함하며, 시스템의 중심에 위치한다. 외부 프레임워크나 외부 기술의 세부 사항을 전혀 알지 못하는 순수한 코드로 구성된다 [8-10].
- 포트 (Ports): 외부 세계와 도메인 간의 통신 규칙을 정의하는 계약(인터페이스)이다.
- 입력 포트(Inbound/Driving Ports): UI나 외부 서비스가 도메인의 기능을 호출할 수 있도록 제공하는 인터페이스이다 [4, 11].
- 출력 포트(Outbound/Driven Ports): 도메인이 외부 자원(데이터베이스, 메시징 큐 등)에 접근해야 할 때 사용하는 인터페이스이다 [4, 11].
- 어댑터 (Adapters/Outside): 외부의 데이터를 도메인이 이해할 수 있도록 번역하거나 그 반대의 역할을 수행하는 인프라스트럭처 계층이다. REST API 컨트롤러(입력 어댑터)나 데이터베이스 리포지토리(출력 어댑터) 등이 이에 해당한다 [4, 9, 12, 13].
- 프레임워크별 실전 설계 패턴
- Spring Boot (Java): 도메인 모델을 영속성 계층의
@Entity어노테이션으로부터 철저히 분리하여 순수 자바 객체(POJO)로 구성한다.@Configuration클래스를 활용하여 프레임워크 레벨에서 순수 도메인 객체와 외부 어댑터 간의 의존성을 조립(주입)한다 [13-15]. - NestJS (TypeScript): 모듈 시스템과 의존성 주입(DI)을 활용해 헥사고날 구조를 강제한다. 실무에서는 Domain, Application, Infrastructure, Presentation의 4계층으로 나누어 외부 요청 처리(컨트롤러)부터 비즈니스 오케스트레이션(서비스), 데이터 매핑까지 엄격히 분리한다 [16].
- Spring Boot (Java): 도메인 모델을 영속성 계층의
⚖️ Trade-offs & Caveats
헥사고날 아키텍처는 유연성과 테스트 용이성을 극대화하지만, 도입 시 다음과 같은 제약 및 반대 급부가 발생할 수 있다.
- 초기 복잡성 및 오버헤드 증가: 단순한 CRUD 기능이나 마감일이 촉박한 소규모 프로젝트에 적용할 경우, 포트와 어댑터 등 다수의 인터페이스와 계층을 만들어야 하므로 작성해야 할 보일러플레이트 코드와 초기 복잡성이 불필요하게 증가한다 [17, 18].
- 과도한 분리 (Destructive Decoupling): 아키텍처의 규칙에 얽매여 불필요한 부분까지 인터페이스와 추상화를 강제하게 되면, 코드를 추적하기 어려워지는 '숨겨진 미로(The Hidden Maze)' 현상이 발생할 수 있다 [18].
- 학습 곡선과 팀의 저항: 기존의 데이터베이스 주도 설계나 전통적 계층형 구조에 익숙한 팀에게는 '포트와 어댑터', '의존성 역전'이라는 새로운 패러다임 전환이 필요하다. 이는 초기 개발 속도를 늦추고 개발자들의 문화적 저항을 유발할 수 있다 [18].
- 다중 어댑터 관리의 어려움: 시스템 규모가 커지고 외부 API, 새로운 데이터베이스, 메시징 시스템 등이 추가됨에 따라 관리해야 할 어댑터의 수가 급증하여 시스템 유지보수와 조정이 까다로워질 수 있다 [18].
🔗 Knowledge Connections
Related Concepts
[관계 유형 A (아키텍처/기반 원칙)]
-
Dependency Inversion Principle
- 연결 이유: 헥사고날 아키텍처가 도메인과 외부 인프라를 성공적으로 격리할 수 있게 하는 객체 지향 프로그래밍의 핵심 원칙(SOLID의 D)이다 [7, 19].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 고수준 모듈(도메인)이 저수준 모듈(DB, UI)에 의존하지 않고, 두 계층 모두가 '추상화(포트/인터페이스)'에 의존하여 변경의 영향을 최소화하는 메커니즘을 이해할 수 있다 [7, 20].
-
- 연결 이유: 헥사고날 아키텍처와 동일하게 비즈니스 로직을 중심에 두고 외부 프레임워크를 분리한다는 근본적인 목표를 공유하는 대체/유사 아키텍처 패턴이다 [21, 22].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 포트와 어댑터 대신 동심원 형태의 계층(Entities, Use Cases, Adapters, Frameworks)으로 시스템을 시각화하고, 의존성 방향을 항상 내부로 향하게 하는 보편적 설계 사상을 폭넓게 이해할 수 있다 [22, 23].
-
- 연결 이유: 헥사고날 아키텍처가 보호하고자 하는 '시스템의 핵심'을 모델링하는 사상과 구조를 제공한다 [24, 25].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 유비쿼터스 언어(Ubiquitous Language), 엔티티(Entity), 값 객체(Value Object), 집재(Aggregate) 등의 개념을 통해 비즈니스 핵심을 어떻게 소프트웨어로 매핑하고, 헥사고날 아키텍처 내부를 채우는지 구체적인 설계 방법을 파악할 수 있다 [23, 25].
[관계 유형 B (구현/활용 도구)]
- Dependency Injection
- 연결 이유: 헥사고날 아키텍처의 의존성 역전을 실무 수준의 프레임워크 코드(Spring Boot, NestJS 등)에서 실제로 구현해 주는 기술적 도구이다 [13, 15, 16, 26].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 도메인 로직 내에 프레임워크 기술이 침투하지 않도록, 애플리케이션 외곽(Bootstrap/Configuration)에서 인터페이스(포트)에 어댑터 구현체를 조립하고 주입하는 연결 방식을 명확히 이해할 수 있다 [13, 26, 27].
Deeper Research Questions
- 단순한 CRUD 애플리케이션에서 헥사고날 아키텍처의 도입으로 인한 초기 코드 오버헤드와 복잡성을 정당화할 수 있는 '도메인 복잡도'나 '시스템 확장성'의 임계점은 무엇인가? [17, 18]
- Spring Boot 환경에서 영속성 계층(DB)의 Entity와 도메인 객체를 완전 분리할 때 발생하는 데이터 매핑 객체 간 오버헤드와 성능 저하 문제를 어떻게 최적화할 수 있는가? [13-15]
- 실제 인프라 환경(DB, API 등)을 완벽히 배제한 채 도메인을 격리 테스트(Unit Test)할 때, 모킹(Mocking) 도구와 포트 인터페이스를 어떻게 결합하여 사용하는 것이 효과적인가? [20, 28]
- NestJS 환경에서 헥사고날 아키텍처를 구현할 때, 읽기 성능과 쓰기 일관성을 위해 CQRS(명령/조회 책임 분리) 패턴을 어떻게 하이브리드 방식으로 결합할 수 있는가? [16]
- 마이크로서비스(Microservices) 아키텍처 내의 각 서비스에 헥사고날 설계를 적용할 경우, 서비스 간의 통신과 이벤트 메시징 처리를 위한 포트와 어댑터는 어떻게 설계되어야 하는가? [23, 29]
Practical Application Contexts
- Implementation: Spring Boot 환경에서는
@Configuration을 활용해 도메인 서비스에 출력 어댑터(JPA Repository 등)를 빈(Bean)으로 연결해주고 도메인 클래스에는@Service와 같은 프레임워크 어노테이션을 피한다. NestJS 환경에서는 Module 시스템을 통해 Domain, Application, Infrastructure, Presentation 계층을 분리 구현한다 [13, 15, 16, 30]. - System Design: 소프트웨어 설계 시 데이터베이스 스키마나 UI 화면을 먼저 설계하는 방식에서 탈피하여, 비즈니스 규칙과 프로세스를 먼저 설계한 뒤 이들이 외부와 소통할 인터페이스(포트)를 정의하는 방식으로 접근한다 [18].
- Operation / Maintenance: 비즈니스 요구사항이나 핵심 로직을 전혀 수정하지 않고도, 인프라단의 데이터베이스를 MongoDB에서 Cassandra로 교체하거나 기존 REST API를 CLI 기반으로 전환하는 등 유연한 시스템 운영이 가능하다 [14, 31].
- Learning Path: 도메인 엔티티 및 비즈니스 유스케이스 구현 → 필요한 입력/출력 포트(인터페이스) 정의 → 각 프레임워크(Spring Boot, Node.js 등)에 맞는 어댑터 구현 → 의존성 주입(Bootstrap)을 통한 조립 순서로 아키텍처를 점진적으로 학습하고 실습한다 [27].
- My Project Relevance: 소스에 관련 정보가 부족합니다.
Adjacent Topics
- Test-Driven Development
- 확장 방향: 헥사고날 아키텍처가 제공하는 강한 격리성과 낮은 결합도를 활용하여, 데이터베이스나 외부 API 어댑터를 구현하기 전 도메인 로직부터 테스트 코드(Mock/Stub 활용)로 철저히 검증하며 시스템을 구축해 나가는 개발 방법론으로 지식을 확장할 수 있다 [15, 20, 27, 28].
- CQRS (Command Query Responsibility Segregation)
- 확장 방향: 헥사고날 아키텍처를 적용한 시스템이 대규모 트래픽을 처리해야 할 때, 데이터의 읽기/쓰기 성능을 극대화하기 위해 상태를 변경하는 명령(Command) 포트/어댑터와 상태를 반환하는 조회(Query) 포트/어댑터를 분리하는 전략으로 확장할 수 있다 [16, 32, 33].
Last updated: 2026-05-02