1. 들어가며
우리는 평소 스프링 부트 프로젝트를 만들 때 Controller, Service, Repository 크게 이 3가지 계층으로 나누어 아키텍처를 구성한다.
하지만 프로젝트를 하다 보면 문득 이런 생각이 들어보았을 것이다.
다른 방법으로 프로젝트 아키텍처를 구성해볼 수는 없을까?
이번 포스트에서는 우리가 가장 흔하게 사용하는 계층형 아키텍처부터 시작해서 클린 아키텍처와 헥사고날 아키텍처에 대해 훑어보며 다양한 아키텍처에 대해 살펴보자.
2. 아키텍처란 무엇일까?
IEEE 국제 표준(ANSI/IEEE Std 1471-2000)에서는 소프트웨어 아키텍처의 정의를 다음과 같이 설명한다.
구성요소들간의 관계, 환경, 설계와 발전을 관리하는 원칙으로 이루어진 시스템의 근본적인 구조
클래스, 파일, 컴포넌트, 모듈 등 코드들이 서로 어떻게 연관되었고, 상호작용하는지 설계하는 것이다.
그렇다면 소프트웨어 아키텍처는 왜 중요할까? 아래 비유를 통해 알아보자.
위 이미지 속 문구 더미를 소프트웨어고, 가운데에 있는 포스트잇을 소프트웨어의 핵심 기능이라고 가정해보자.
소프트웨어는 주기적으로 변경이 된다. 만약 여기서 가위를 칼로 바꾸려면 어떻게 해야 할까?
주변 물체들과 연결되어 있던 끈을 풀고 칼을 다시 묶어주어야 한다.
만약 연필이 "내게 필요했던건 가위야!"라고 한다면? 또 다시 끈을 풀고 다시 가위를 묶어야 한다.
이러한 변경은 다른 객체들에게 연쇄적으로 영향을 주게 된다. 만약 아래와 같이 소프트웨어가 이루어져 있으면 어떨까?
이번에 가위를 칼로 바꿀 때는 가위에 연결된 선을 풀고 칼을 새로 묶으면 된다. 이러한 변경은 다른 객체들에게 영향을 주지 않기 때문에 신경 쓸 필요가 없어졌다.
🍥 소프트웨어에서는 이러한 선들을 의존성이라고 한다.
이처럼 소프트웨어의 아키텍처의 목표는 소프트웨어의 제작과 유지보수를 보다 쉽게 하는 것, 즉 필요한 시스템을 만들고 유지보수하는 데 투입되는 인력을 최소화하는 것이다.
3. 아키텍처의 구성
소프트웨어는 크게 도메인과 인프라스트럭처로 나누어져 있다.
도메인이란 소프트웨어로 해결하고자 하는 문제 영역을 의미한다.
번역 앱이라면 번역이, 온라인 쇼핑몰이라면 물품 판매 프로세스가 도메인이 될 것이다.
도메인은 서비스의 본질로, 자주 바뀌지 않는다.
인프라스트럭처는 도메인을 소프트웨어로 제공하기 위해 필요한 요소들로 구성된다.
UI, DB, API 등이 인프라스트럭처에 해당된다.
도메인에 비해 인프라스트럭처는 자주 변경될 가능성이 높다.
4. 계층형 아키텍처 (Layered Architecture)
계층형 아키텍처는 소프트웨어 개발에서 가장 일반적으로 널리 사용되는 아키텍처이다. 구성되는 계층의 숫자에 따라 N-계층 아키텍처(N-tier Architecture)라고도 한다.
구조가 단순하고, 보편적으로 사용하기 때문에 처음 프로젝트를 시작할 때 적합하다는 장점이 있다.
각 계층은 특정 역할과 관심사별로 구분되어 특정 계층의 구성요소는 해당 계층에 관련된 기능만 수행한다.
Presentation Layer
사용자에게 데이터를 전달하기 위해 화면에 정보를 표시하는 것을 주 관심사로 두며, 대표적으로 View와 Controller로 구성되어 있다.
Presentation Layer에서는 비즈니스 로직이 어떻게 수행되는지 알 필요가 없다.
Business Layer
비즈니스 로직의 수행을 주 관심사로 두며, 대표적으로 Service와 Domain Model로 구성되어 있다.
마찬가지로 화면에 데이터를 출력하는 방법이나 데이터를 어디서, 어떻게 가져오는지에 대해서는 알고 있지 않다.
Persistence Layer
어플리케이션의 영속성을 구현하기 위해 데이터의 출처와 데이터를 가져와서 다루는 것을 주 관심사를 둔다. 대표적으로 Repository와 DAO로 구성되어 있다.
정리하면 Presentation Layer는 Business Layer에게 데이터 처리를 요청하고, Business Layer는 Persistence Layer에게 데이터 조회를 요청한다.
그 결과는 Persistence Layer에서 Business Layer로, Business Layer에서 Presentation Layer로 전달되어 사용자에게 출력된다.
결과적으로 Presentation Layer가 Persistence Layer를 직접적으로 참조하는 것이 되기 때문에 의존성이 생기게 되고, Persistence Layer의 변경이 Presentation Layer에 영향을 주게 된다.
그렇기 때문에 영속성에 대한 의존성이 강해지고, 데이터베이스 주도 설계로 이어질 수 있다. 또한 소프트웨어가 커지고 복잡해질수록 한 패키지에 여러 클래스가 담기기 때문에 조직화가 어려워지고, 비즈니스 로직이 여기저기 퍼져있어 도메인 파악이 힘들어진다.
인프라스트럭처의 변경이 적고 규모가 작은 프로젝트 초기에는 계층형 아키텍처가 좋지만, 프로젝트의 규모가 커지고 도메인이 복잡해질수록 관리가 어려워질 것이다.
5. 클린 아키텍처
클린 아키텍처는 로버트 마틴이 정립한 아키텍처로, 추상화를 통해 관심사를 분리하고 의존도를 낮추는 것에 중점을 둔 아키텍처이다.
사진에서 화살표는 의존성을 의미한다. 표현 계층(Presentation Layer)에서 영속성 계층(Persistence Layer)로 의존성의 방향이 향했던 계층형 아키텍처와 달리 클린 아키텍처는 의존성의 방향이 웹 계층과 영속성 계층인 바깥 쪽(저수준)에서 도메인 계층인 안 쪽 방향(고수준)으로 향한다.
의존성의 방향이 안쪽으로 향한 것은 아키텍처가 도메인을 보호하고 집중한다는 것을 강조한다.
🍥 용어 정리
Entity(엔티티) - 도메인 계층이라고도 불리며, Enterprise 규모의 비즈니스 데이터를 포함하거나 핵심이 되는 비즈니스 규칙을 캡슐화 한다.
Use Case(유즈 케이스) - 애플리케이션 계층이라고도 불리며, 어플리케이션 규모의 비즈니스 규칙을 포함한다.
6. 헥사고날 아키텍처
헥사고날 아키텍처(육각형 아키텍처)는 알리 스테어 콕번이 만든 용어로, 클린 아키텍처의 원칙을 보다 구체화한 아키텍처라고 생각하면 된다.
헥사고날 아키텍처에서는 도메인을 각 포트(인터페이스)가 감싸고 외부로부터 보호하고 있는 것이 핵심이다.
왼쪽에 있는 Input Port는 Web Adapter로부터 입력을 받고, 오른쪽에 있는 Output Port는 처리 결과를 저장한다.
🍥 용어 정리
Port - 도메인 모델과 외부의 통신을 정의한 인터페이스, 외부에 있는 Adapter는 Port를 통해 내부 도메인에 접근해야 한다.
Adapter - 인터페이스를 따르면서 내부 구현은 인터페이스에 위임하는 것, 특정 포트에 연결되어 외부와 도메인 간 통신을 가능하게 한다.
헥사고날 아키텍처의 핵심은 어댑터와 포트이다. 이들은 육각형 안팎의 관심사를 분리하여 의존성이 내부로만 향하고 도메인을 보호할 수 있도록 한다.
그렇기 때문에 도메인은 인프라스트럭처에 의존하지 않아도 되며, 외부 기술을 변경하여도 영향을 받지 않는다.
7. 그렇다면 클린 아키텍처가 최고인가?
계층형 아키텍처에서 대규모 프로젝트로 나아갈 때의 불편함을 클린 아키텍처를 적용하며 해소할 수 있음을 배웠다.
그렇다면 무조건 헥사고날 아키텍처가 계층형 아키텍처보다 좋은걸까?
항상 그런 것은 아니다. 어떤 아키텍처도 완벽하지 않다.
헥사고날 아키텍처를 적용하면 패키지와 클래스의 수가 계층형 아키텍처를 사용했을 때보다 더 증가하게 된다. NHN FORWARD 22 발표에 따르면 헥사고날 아키텍처를 적용했을 때가 계층형 아키텍처에 비해 라인은 1.15배, 파일은 1.5배, 패키지 수는 4.28배 증가했다고 한다.
대규모의 프로젝트를 진행하고, 프로젝트의 인원이 모두 클린 아키텍처에 대해 이해하고, 외부 요소의 변화가 잦을 때는 헥사고날 아키텍처의 도입을 고려하는 것이 좋다.
하지만 의존 관계가 복잡하지 않고 외부 요소의 변화가 적은 소규모의 프로젝트에서는 계층형 아키텍처로 간단하게 프로젝트를 구현하는 것이 효율적이다.
Reference
https://pusher.com/tutorials/clean-architecture-introduction/#exercise-make-a-dependency-graph
https://youtu.be/saxHxoUeeSw?si=N20WFWEmXUHTI7KO
https://youtu.be/g6Tg6_qpIVc?si=gnyvp6hbgIyXEK6-