이 글은 Medium에 있는 Bharath의 The Clean Architecture — Beginner’s Guide를 허락받고 개인적인 학습 목적으로 번역한 내용입니다.
번역에 문제가 있을 경우 알려주시면 감사하겠습니다.
클린 아키텍처는 로버트 C. 버틴 이 제안한 시스템 아키텍처로 헥사고날 아키텍처, 어니언 아키텍처 같은 수많은 다른 아키텍처 가이드라인에서 파생된 아키텍처입니다.
클린 아키텍처는 개발자가 확장, 테스트, 유지보수를 더 용이하게 할 수 있는 수많은 가이드라인 중 한 개입니다.
우리는 왜 아키텍트가 필요할까?
"소프트웨어 아키텍처의 목적은 시스템을 만들고 유지하는데 필요한 인적자원을 최소화하는 것이다." - 로버트 C. 버틴, 클린 아키텍처
올바른 아키텍처의 장점
- 테스트에 용이함
- 유지보수 가능
- 변경에 용이함
- 쉽게 개발
- 쉽게 배포
- 독립적
클린 아키텍처
이미지를 보면 총 4개의 레이어가 있는 것을 볼 수 있습니다. 파란색, 초록색, 빨간색 그리고 노란색 레이어.
각 동그라미는 서로 다른 소프트웨어 영역을 나타냅니다. 가장 밖에 있는 레이어는 소프트웨어에서 가장 낮은 레벨이고 안으로 들어갈수록 레벨이 점점 높아지고 변경이 더 잘 안 일어나게 됩니다.
종속성 규칙 (Dependency Rule)
종속성 규칙에 따르면 소스 코드는 오로지 안쪽 레이어에만 의존할 수 있습니다.
이 말은 가장 안쪽에 있는 레이어는 다른 레이어에 대해서 아무것도 알면 안 됩니다. 즉 안쪽에 있는 레이어는 밖에 있는 다른 레이어에 의존하면 안 됩니다. 그림에서 검은색 화살표가 종속성 규칙이 어떻게 적용되어 있는 보여줍니다.
종속성 규칙은 이 아키텍처가 동작할 수 있도록 해주는 중요한 규칙입니다. 그리고, 이해하는데 어려움을 느낄 수 있습니다. 그래서 이 규칙을 하나씩 살펴보면서 어떠한 문제를 야기하는지 이해한 다음 이 규칙을 지키는 방법에 대해서 살펴보겠습니다.
가장 첫 번째로 위에 있는 원형 그림이 많은 사람한테 오히려 이해하는데 어려움을 줄 수 있기 때문에 일자형으로 변경해 보겠습니다
첫 번째 그림과 이 그림의 색상은 같은 레이어를 보여줍니다.
기억해야 될 점은, 화살표는 A가 B에게 의존하고 있다는 식으로 읽어야 합니다. 예를 들어서 프레임워크와 드라이버는 인터페이스 어답터에 의존하고 어답터는 애플리케이션 비즈니스 룰에 의존하고 있으며 마지막으로 애플리케이션 비즈니스 룰은 엔터프라이즈 비즈니스 룰에 의존하고 있다.
가장 밑에 있는 레이어는 위에 있는 레이어에 의존하면 안 된다.
프레임워크와 드라이버
이 레이어에 포함되는 소프트웨어 영역은
- 유저 인터페이스
- 데이터베이스
- 외부 인터페이스 (예: 네이티브 플랫폼 API)
- 웹 (예: 네트워크 요청)
- 장치들 (예: 프린터, 스캐너)
인터페이스 어답터
여기에 포함되는 것은
- 프레젠터 (UI 로직, State)
- 컨트롤러 (UI 로직, State) (애플리케이션에서 필요한 메서드들을 가지고 있는 인터페이스로 웹, 장치, 외부 인터페이스로 구현됨)
- 게이트웨이 (데이터베이스로 구현된 인터페이스로 애플리케이션이 실행한 모든 CRUD 동작들을 가지고 있는다)
애플리케이션 비즈니스 룰
코어 비즈니스 룰은 아니지만 애플리케이션에 필수적인 룰이 여기에 포함된다. 이 레이어는 유즈 케이스를 가지고 있다. 이름에서 알 수 있듯이, 이 레이어는 애플리케이션에 모든 유즈 케이스를 제공해 줍니다. 즉 애플리케이션이 제공하는 모든 기능을 가지고 있는다.
그리고, 이 레이어는 어떤 컨트롤러, 게이트웨이가 불릴지 결정을 합니다. 가끔씩 다른 모듈에 있는 컨트롤러가 필요할 때도 있습니다.
여기에서 서로 다른 모듈들이 조정되고 있습니다. 예를 들어서, 이번달동안 x 만큼 구매한 사용자들에 대해서 할인을 진행하고 싶다고 해보겠습니다.
그럼 먼저 구매 모듈로부터 이 사용자가 이번달에 얼마를 사용했는지 가져오고, 체크아웃 모듈에 사용자를 위한 할인을 적용해야 됩니다. 여기서 구매 모듈에 있는 applyDiscountUseCase가 불리게 되고, 할인을 체크아웃 모듈에 적용하게 됩니다.
엔터프라이즈 비즈니스 룰
이 레이어는 코어 비즈니스 룰이나 도메인 고유의 비즈니스 룰을 가지고 있는다. 그리고 변화가 가장 적게 일어납니다.
밖에 있는 레이어에 변화가 일어나도 이 레이어에는 영향을 주지 못합니다. 비즈니스 룰이 변하는 일은 자주 없기 때문에, 이 레이어에 변화를 주는 일 자체가 매우 적습니다. 이 레이어는 엔티티를 가지고 있습니다.
엔티티는 비즈니스 룰에 필요한 코어 데이터 구조가 될 수도 있고 아니면 비즈니스 로직을 담고 있는 메서드를 가진 객체가 될 수 있습니다.
예를 들어서, 은행 애플리케이션에서 이자를 계산하는 모듈은 코어 비즈니스 로직이 되며 이 레이어에 포함이 되어 있어야 합니다.
그럼 이제부터 간단한 예제를 통해 더 이해를 해봅시다.
이 예시는 네트워크 요청이 한 개밖에 없는 매우 간단한 애플리케이션을 보여줍니다.
어떻게 번역 API를 사용해 사용자가 준 문장을 번역하는 앱을 만들까요? 한번 만들어봅시다.
각 레이어는 특정한 일을 합니다. 문제가 없어 보이죠? 그럼 한번 의존성 흐름을 확인해서 위에 있는 아키텍처가 문제가 있는지 확인해 봅시다.
종속성 규칙을 아직 기억하고 있죠? "종속성 규칙에 따르면 소스 코드는 오로지 안쪽 레이어에만 의존할 수 있습니다."
UI -> 프레젠터 ( ✅ 문제없고 )
프레젠터 -> Translate 유즈 케이스 ( ✅ 문제없고 )
Translate 유즈 케이스 -> Translate 컨트롤러 ( ❌ 규칙 위반 )
Translate 컨트롤러 -> 웹 ( ❌ 규칙 위반 )
볼 때는 문제가 없었는데 말이죠.
데이터 요청 흐름을 본다면 이렇게 돼야 한다. UI -> 프레젠터 -> 유즈 케이스 -> 컨트롤러 -> 웹
생각해 보면, 어떻게 컨트롤러가 웹에 의존하지 않으면서 웹으로부터 데이터를 받을 수 있을까요? 그리고 유즈 케이스가 컨트롤러를 의존하지 않고 컨트롤러로부터 데이터를 받을 수 있을까요?
하지만 종속성 규칙에 의하면 의존성은 무조건 안쪽으로만 향해야 한다고 되어 있습니다. 그리고 이 규칙이 있어야지만 이 아키텍처가 동작한다고도 말했고요.
이 규칙을 통과하기 위해서는, 화살을 다른 방향으로 뒤집어야 합니다. 이게 가능하냐고요? 이때 다형성이 적용됩니다. 이 예제에 다형성을 적용한 순간, 놀라운 일이 벌어집니다.
두 개 레이어 사이에 인터페이스를 한 개 배치하는 것으로 의존성을 역전할 수 있습니다. 이 원칙은 의존성 역전 원칙이라고 불립니다.
그럼 종속성 규칙을 위반하고 있는 예제에 의존성 역전 원칙을 적용해 봅시다.
흐름은 이렇게 변한다:
의존성 흐름도 같이 살펴보면:
이제 안쪽 레이어가 밖에 있는 레이어를 의존하는 경우가 없어진 것을 볼 수가 있습니다. 오히려 밖에 있는 레이어가 안에 있는 레이어에 의존하고 있습니다.
그래서 왜 밖에 있는 레이어가 안에 있는 레이어를 의존해야 될까?
한번 호텔에 있다고 상상해 봅시다. 호텔이 우리가 원하는 것을 제공해 주길 바라지, 호텔이 제공하고 싶은 걸 원하지는 않죠? 여기서도 같은 일이 일어납니다, 우리는 데이터베이스가 애플리케이션이 요청하는 데이터를 주고 싶지 아무거나 주는 건 원하지 않습니다.
애플리케이션이 원하는 데이터를 요청하지만 데이터베이스나 API가 어떻게 데이터를 준비할지는 전혀 신경 쓰지 않습니다. 이렇게 해야 애플리케이션이 데이터베이스나 API에 의존하지 않게 됩니다. 나중에 데이터베이스나 API를 수정하면 애플리케이션이 요청하는 데이터를 줄 수만 있으면 그냥 변경하면 됩니다. 애플리케이션은 데이터베이스나 API가 수정되었다는 사실조차 모르게 됩니다.
그리고 한 방향 종속성 규칙은 애플리케이션이 데드락 상태에 빠지는 것을 방지합니다. 예를 들어 서로 의존하는 2개의 레이어가 있을 때, 첫 번째 레이어를 수정하면 두 번째 레이어도 수정해야 되고, 두 번째 레이어를 수정하면 첫 번째도 같이 수정해야 된다.
이게 로버트 C. 마틴이 얘기한 클린 아키텍처입니다.
긴 글을 읽어주셔서 감사합니다.
'게시글 번역' 카테고리의 다른 글
(번역) 비인기 의견: 좋은 엔지니어가 되는건 그 어느때보다 어렵다 (1) | 2024.05.09 |
---|---|
(번역) 좋은 코드/나쁜 코드란? 프로그래머가 아닌 사람들을 위한 예시 (0) | 2024.04.12 |