Clean Architecture - 구조

luneah·2022년 2월 21일
3
post-thumbnail


클린 아키텍처는 크게 세개의 레이어로 나눠져있으며 각각 도메인, 어댑터, 인프라스트럭쳐 레이어로 불린다. 도메인 레이어는 또 엔티티와 유즈케이스로 나뉘어진다.

✓ 엔티티

엔티티는 비즈니스 로직들의 집합으로 메서드를 갖는 객체일 수도 있지만 데이터 구조와 함수의 집합일 수도 있다. 엔티티는 가장 일반적이고 고수준의 규칙을 캡슐화하며 독립적이어야하고, 특정 어플리케이션의 무언가가 변경되더라도 엔티티 계층에는 절대 영향을 주어선 안 된다. 예를 들어, 대출에 10% 이자를 부과하는 것은 은행이 가진 규칙이다. 이것을 컴퓨터로 처리하느냐 서면으로 처리하느냐의 차이만 있을 뿐이다.

✓ 비즈니스 로직

홈페이지 회원가입을 할 때를 예로 들 수 있는데 유저는 아이디 중복 검사, 본인 인증, 비밀번호 재검사 등을 해야한다. 회원가입 기능을 수행할 때 크게 두 가지의 부분으로 나눌 수 있는데, 하나는 중복 아이디가 있는지 없는지 검사하는 서버통신의 과정들이고 다른 하나는 유저에게 단순히 텍스트나 alert창으로 알려주는 것이다. 후자를 흔히 Presentation 영역 또는 View 영역으로 불리고, 데이터를 단순히 보여주기만 하는 역할이다. 그리고 전자를 Model, 즉 데이터를 가공하고 로직을 통과시키는 부분이다. 우리는 전자의 Model을 비즈니스 로직이라고 한다. 이처럼 비즈니스 로직은 가장 핵심이 되는 요소를 말한다.

✓ 유즈케이스

Use case란 내가 만들고자하는 시스템을 사용하는 클라이언트가 그 시스템을 통해 하고자 하는 것을 말한다.

예를 들어, '영화관' 이라는 서비스가 있다고 가정해보면 영화관에서 손님(클라이언트)은 '영화 예매'를 할 수도 있고, '예매 취소'를 할 수도 있고, '환불', '팝콘 사기'를 할 수도 있다. 이 때, 이런 '영화 예매', '예매 취소', '환불', '팝콘 사기' 등이, '영화관'이라는 시스템에 사용자가 요청할 수 있는, '영화관'의 Use case라고 한다. use case는 entities에 의존하는 동시에 상호작용한다. 앞서 말했듯 영화관에서 영화, 손님, 비용 등은 엔티티가 되고 손님이 영화를 예매하는 것은 유즈케이스가 된다.

해당 레이어의 소프트웨어는 시스템의 모든 유즈케이스를 캡슐화하고 구현하며 유즈케이스는 엔티티와의 데이터 흐름을 조정하고, 엔티티가 유스케이스의 목표를 달성하도록 지시한다. 또 의존성 규칙에 의해 유스케이스의 변경이 엔티티에 영향을 주어서는 안 되며, 데이터베이스, UI, 프레임워크의 변경으로부터도 유스케이스가 영향을 받지 않는다. 하지만, 애플리케이션의 조작에 대한 변경이 일어나면 유스케이스에 영향을 미칠 수도 있다.
그리고 이들 또한 엔티티와 마찬가지로 바깥 계층에 대해 아무 것도 알지 못한다. 다만, 이 계층에서는 바깥 계층에서 사용할 수 있는 interface를 정의한다.

✓ 도메인 레이어

유즈케이스와 엔티티를 합쳐 도메인 레이어라고 부르는데 도메인 레이어는 누구와도 의존성을 이루지 않는 독립적인 레이어이다. 이 레이어에서는 비즈니스와 관련된 로직을 담당하며 앱에서 사용할 Model과 각각의 비즈니스 로직 단위를 나타내는 UseCase, UseCase의 실질적인 구현을 담당하게 할 Repository 인터페이스로 구성이 되어있다.

✓ 인터페이스 어댑터

인터페이스 어댑터는 domain 과 infrastructure 사이의 번역기 역할을 수행한다. 즉, 바깥 또는 안쪽으로 전달되는 모든 데이터를 데이터를 전달 받는 레이어에 용이한 형식으로 변환시켜주는 역할을 한다. View 레이어로 전달되는 데이터를 문자열로 변경해 전달하는 것을 예로 들 수 있다. 프리젠터, 뷰, 컨트롤러는 모두 해당 레이어에 속한다.

✓ 프레임워크와 드라이버

인프라스트럭쳐는 가장 바깥쪽 레이어로 데이터베이스나 웹 프레임워크 등 일반적으로 프레임워크나 도구로 구성된다. 대개, 이 레이어에는 안쪽의 원과 통신할 연결 코드 이외에는 별다른 코드를 작성하지 않는다. 네트워크, UI, 데이터베이스, 라이브러리와 프레임워크, 인아웃풋 장치 등이 이에 포함되고 이 레이어에 있는 것들은 빈번하게 변경되는 것이므로 추상화와 제어의 역전을 통해 안쪽 레이어들을 변경으로부터 안전하게 만들어야 한다.

✓ 어댑터 / 인프라스트럭쳐

앞에 보여드렸던 프레임워크와 드라이버가 인프라스트럭쳐 레이어가 되고 도메인 레이어와 인프라스트럭쳐 사이의 번역기 역할을 수행해주는 인터페이스 어댑터 부분이 어댑터 레이어로 분류된다.

✓ 교차 경계

Adapter 에 해당하는 Presenter 와 Controller 가 상위 계층인 Use cases 와 어떻게 소통하는지 나타내고 있다. 하지만 여기서 이상한 점이 하나 보인다. 제어의 흐름 (Flow of Control) 을 보면 Controller → Use cases → Presenter 로 흘러감을 확인할 수가 있다. 고수준의 Use cases 가 저수준의 Presenter 를 참조하고 있는 상황이다.

이 문제를 해결하기 위해서는 다음 두 상황을 고려해야 한다. 첫 번째로 의존성 규칙을 지키기 위해서는 단순하고, 고립된 형태의 데이터 구조를 사용해야 한다. DB의 형식의 데이터 구조 또는 Framework에 종속적인 데이터 구조가 사용되게 된다면, 이러한 저수준의 데이터 형식을 고수준에서 알아야 하기 때문에 의존성 규칙을 위반하게 된다.


두 번째로는 crossing boundaries 이다. 이는 제어의 흐름은 원의 내부에서 외부로 향할 수 있는데, 이는 의존성 규칙에 위배되므로 의존성 역전의 원칙(DIP)을 이용하여 해결해야 한다는 이야기이다. 즉, 고수준에서 저수준에 직접 참조하게 될때 interface를 하나 둠으로써, 저수준의 세부사항을 알 필요 없고 변동사항에도 영향을 받지 않게 된다.


그렇다면 4개의 원이 아니면 안 되는가? 그렇지 않다. 이 원은 단순히 컨셉을 전달하기 위한 수단이며 항상 4개일 이유는 없고 필요에 따라 더 많아질 수도 있다. 하지만 무슨 일이 있어도 소스 코드 의존성은 항상 안쪽을 향하며 안쪽으로 갈수록 추상화와 정책의 수준은 높아지는 의존성 규칙이 적용된다.

profile
하늘이의 개발 일기

0개의 댓글