본문 바로가기
디자인패턴/Clean Architecture

클린 아키텍처(Clean Architecture) 개념 및 원칙

by 굿햄 2023. 2. 1.
개발이란 마치 여러 개의 기반이 되는 블록을 만들어 설계 원칙에 따라 조립하여 완성해 나아가는 과정이라고 생각합니다.
여기서 설계 원칙은 수 많은 디자인 패턴들을 의미하며, 이번 주는 아키텍처 패턴에 속하는 클린 아키텍처에 대해 알아보는 것으로 이번 주 스터디 그룹의 주제를 선정하였습니다.

화자는 실무에서 MVVM 패턴과 클린 아키텍처를 함께 사용 중이나, 이 둘에 대한 설명에 명확한 답변을 할 수 있을지 의문을 가져 이번 기회에 명확히 알아두고자 합니다.

 

클린 아키텍처를 사용해야 하는 이유


기원

엉클 밥(Robert C. Martin)은 애자일 방법론의 지지자이자, SOLID (객체지향 설계 원칙)의 창시자로 알려져 있습니다.

그는 'Clean code'와 'Clean architecture'등의 책의 저자이며 소프트웨어 개발에 있어 원칙, 패러다임, 설계 등을 알리는데 크게 기여하였습니다.

클린 아키텍처란?

오늘날까지 수많은 아키텍처(마이크로 서비스, 서버리스 등)들은 전반적으로 '관심의 분리'(Seperation of concerns) '테스트 가능성'(Testability)을 보편적인 요구를 갖고 있습니다.

 

클린 아키텍처는 이러한 요건을 만족하는, 간단히 말하면 '추상화 개념'(Abstraction principle)으로써 관심사를 분리시키고 의존도를 낮추는 것에 목적을 둔 아키텍처입니다.

의존도를 낮추고 서로에게 주는 영향을 감소함으로써 유지보수의 용이성이 향상됩니다.
덕분에 낮은 비용으로 새로운 기능을 추가할 수 있습니다.

 

기본적인 원리는 종속성 규칙(Dependency Rule)을 지키는 것입니다. 각 코드의 종속성은 외부에서 내부로 안쪽으로만 가리킬 수 있고, 고수준 정책(High level policy)이 저수준 정책(Low level policy)의 변경에 영향을 받지 않도록 하는 것입니다.

여기서 의미하는 수준(Level)은 입/출력과의 거리를 의미하는 것으로,
고수준 정책은 보통 UI 또는 인터페이스와 거리가 먼 비즈니스 영역 Business Rules, Entities 등을 의미하며,
저수준 정책은 위와 반대로 거리가 가까운 인프라 영역이나 UI 영역 Presentation, Controllers 등을 의미한다.

 

아래의 예시 레이어는 4개의 계층으로 나누어져 있지만, 그 이상의 계층을 사용하는 것은 흔한 일이며 종속성 규칙을 준수하며 캡슐화를 진행하게 됩니다.

클린 아키텍처의 레이어 구조


출처: https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

안쪽에 위치할수록 고수준 정책이며, 바깥쪽에 위치할 수록 저수준 정책을 의미합니다.

엔티티 (Entities)

의도에 따라 도메인 계층으로도 불리며, 엔티티 계층은 하나 이상의 프로그램 간에 공유될 수 있다는 가정하에 만드는 수명이 긴 객체입니다. (즉, 재사용의 가능성이 높다는 것을 인지하고 외부에 의해 변경될 가능성을 낮추어야 합니다.)

이곳에는 Enterprise 규모의 비즈니스 데이터를 포함하거나 핵심이 되는 비즈니스 규칙을 캡슐화합니다.

유즈케이스 (Use cases)

애플리케이션 계층으로도 불리며, 어플리케이션 규모의 비즈니스 규칙을 포함합니다.

이 레이어의 변경사항은 엔티티에 영향을 미쳐서는 안 되며,

인프라 단의 DB나 UI, 라이브러리와 같은 외부요소에 의해 영향을 받지 않는다는 것을 원칙으로 합니다.

이는 즉 해당 계층의 수정은 응용 프로그램의 동작에 영향을 미친다는 의미입니다.

인터페이스 어뎁터 (Interface Adapter)

어뎁터 계층은 DB나 Web, UI와 같은 바깥 계층에서 사용하기 편리하도록, 유즈케이스 또는 엔티티 계층에서 데이터를 변환하는 어뎁터의 집합입니다.

흔히 MVC, MVVM과 같은 아키텍처를 포함하는 것이 이 영역으로 컨트롤러, 프레젠터, 게이트웨이 등이 속합니다.

프레임워크와 드라이버 (Frameworks & Drivers)

인프라 계층이라고도 불리며, 가장 외부에 있는 레이어로 DB, 웹 프레임워크와 같은 세부 정보(Details)를 나타내는 계층입니다.

이곳은 보통 글루 코드(Glue code)만 작성하며, 시간이 지남에 따라 구성이 변경될 수 있으므로 엔티티 계층(또는 도메인 계층)에 추상화(abstracted)하여 도메인 계층에 영향을 주지 않고 인터페이스를 수정하고 업데이트할 수 있습니다.

 

주요 원칙


Dependency rule (종속성 규칙)

종속성의 흐름을 제어하는 것이 주목적입니다. 캡슐화를 통해 내부 계층을 외부 계층과 분리시키고 내부는 외부를 모르게 하며 영향을 받지 않도록 종속성의 흐름을 제어할 수 있습니다.

Abstraction principle (추상화 원리)

가장 안쪽에 있는 계층이 제일 추상화된 영역으로 바깥 영역으로 향할수록 내부 계층을 활용하여 세부 사항을 구현합니다.

SOLID (객체 지향 설계 원칙)

확장가능하며 유지보수 가능한 유연한 소프트웨어 아키텍처를 만들기 위한 5가지의 필수 원칙입니다.

출처: https://www.techtarget.com/searchapparchitecture/tip/A-primer-on-the-clean-architecture-pattern-and-its-principles

  1. 단일 책임 원칙(SRP) - 클래스(함수와 데이터를 결합한 집합)는 하나의 이유(액터)만으로 수정되어야 하며 하나의 책임/기능/역할을 가져야 한다.
  2. 개방-폐쇄 원칙(OCP) - 객체는 확장할 수 있어야 하고, 변경에는 닫혀있어야 한다.
  3. 리스코프 치환 원칙(LSP) - 상위 타입의 객체를 하위 타입의 객체로 치환해도 정상 동작해야 한다.
  4. 인터페이스 분리 원칙(ISP) - 클라이언트는 자신이 사용하는 함수에만 의존해야 하며, 인터페이스는 더 작게 분리되어야 한다.
  5. 의존 역전 원칙(DIP) - 고수준 모듈은 저수준 모듈(세부사항)을 의존해서는 안되며, 추상화에 의존해야 한다.

위의 원칙 중 DIP는 가장 중요한 요소로 Presenter이 경계를 넘어(Crossing Boundaries) Controller로 부터 데이터 흐름을 받는 것을 예시로 들 수 있습니다.

Actor(액터)란 소프트웨어를 둘러싼 사용자와 이해관계자를 뜻하는 것으로, 액터가 소프트웨어의 동작이 변경되긴 원하면 그것이 클래스를 변경하는 이유인 것입니다.
예를 들어 학생과 교수가 사용하는 대학 사이트에서 성적 산정방식이 변경된다면, 교수는 성적 계산 시스템이 바뀌어야 한다고 주장할 것입니다. 이때 액터는 교수이며, 계산 클래스의 변경 이유를 제공하고, 다른 시스템에 대해서는 이해관계를 갖지 않습니다.

Reuse/release equivalence principle (REP: 재사용/릴리즈 등가 원칙)

재사용 단위는 릴리즈 단위와 같다

릴리즈에는 관련 없는 클래스 모음을 포함하면 안 됩니다. 릴리즈는 재사용 가능하게 만들어야 하며, 독립적으로 배포할 수 있도록 응집력 높은 클래스와 모듈로 구성되어야 합니다.

만약 소프트웨어 컴포넌트가 릴리즈를 통해 추적 관리되지 않는다면 문제가 발생할 것입니다.

예시로는 배포 버전(릴리즈 번호)을 통해 컴포넌트를 재사용하고 변경사항을 추적하는 것입니다.

Common closure principle (CCP: 공통 폐쇄 원칙)

동일한 이유로 동일한 시점에 변경되는 클래스는 같은 컴포넌트로 묶어라
서로 다른 시점에 다른 이유로 변경되는 클래스는 다른 컴포넌트로 분리하라

SOLID 원칙 중 SRP(단일 책임 원칙)을 컴포넌트 단위로 확장한 것으로, 

SRP에서는 단일 클래스의 변경 이유가 하나여야 하며, 다른 액터에 의해 변경되는 메서드, 함수 집합을 클래스로 추출, 분리하라고 합니다. 마찬가지로 CCP에서는 어떤 요구사항이나 수정에 의해 변경되는 것을 컴포넌트 단위로 분리하는 것입니다.

Component(컴포넌트)는 재사용이 가능한 독립된 모듈을 의미, 런타임에 플러그인 형태로 결합할 수 있는 동적 링크파일 또는 배포 단위

Common reuse principle (CRP: 공통 재사용 원칙)

컴포넌트 사용자들을 필요하지 않은 것에 의존하게 하지 말라
반대로 재사용되는 경향이 있는 클래스와 모듈들은 같은 컴포넌트에 포함해야 한다

SOLID 원칙 중 ISP(인터페이스 분리 원칙)을 컴포넌트 단위로 확장한 것으로,

ISP에서는 사용하지 않는 메서드가 있는 클래스에 의존하지 말라고 합니다. 마찬가지로 CRP에서는 강하게 결합되지 않은 클래스들을 동일한 컴포넌트에 넣지 말라고 합니다.

 

반대로 이야기하면, 강하게 결합한 클래스는 동일한 컴포넌트에 위치해야 합니다.

 

 

아래는 각 두 원칙을 우선시할 시 나타나는, 반대편 꼭짓점 원칙을 포기하는 비용을 나타내는 Tension Diagram입니다. 이는 만들고 있는 구조가 수용 가능한지 판가름하는 기준으로 삼을 수 있습니다.

1. 앱을 적절히 분할하지 않으면, 간단한 기능을 배포하기 위해 많은 릴리즈가 필요합니다.

2. 컴포넌트의 관심사를 올바르게 잡지 않으면, 재사용 가능성이 줄어듭니다.

3. 앱이 필요로 하는 것보다 더 많은 기능을 가진 컴포넌트를 만들면, 잦은 수정이 필요합니다.

 

Acyclic dependency principle (ADP: 의존성 비순환 원칙)

컴포넌트 의존성 그래프에 순환이 있어서는 안 된다

종속성에 순환이 있을 경우 하나의 컴포넌트 수정으로 다른 컴포넌트까지의 호환을 함께 확인해야 하는 문제와, 어떤 순서로 빌드를 해야 하는지 파악하는데 문제가 발생합니다.

이를 해결하기 위한 방법으로 DIP(의존성 역전 원칙)을 주로 사용합니다.

Port and adapter(헥사고날 아키텍처)

헥사고날 아키텍처는 클린 아키텍쳐 이전에 나온 '관심사의 분리'에 초점을 둔 아키텍처입니다.

주요 원칙은 기술에 구애받지 않는 코드의 가장자리에 입력 및 출력을 갖는 것입니다.

포트는 노출되는 유즈케이스 인터페이스인 반면, 어뎁터는 이 인터페이스의 구현한 변환 계층입니다.

출처:https://reflectoring.io/spring-hexagonal/

결론


아키텍처는 상황에 따라 적절히 수정하여 적용할 수 있습니다.

그러나 위의 규칙은 공통적인 기본 원칙이며, 이를 준수할 시 유지보수의 용이성과, 테스트 가능한 시스템을 설계할 수 있습니다. 또한 외부의 영향을 최소화하여 라이브러리 등의 교체 시 이로 인한 이슈를 최소화할 수 있는 강력한 이점을 가지고 있습니다.

 

부록


MVVM과 Clean Architecture의 차이

종종 MVVM과 Clean Architecture를 같이 묶어 설명하는 글이 많다 보니 헷갈릴 때가 있었습니다.

클린 아키텍처는 Layer(계층) 분리에 중점을 둔 아키텍처로, 최고 수준의 추상화에서 소프트웨어를 설계하기 위한 규칙이지만,

MVVM은 전체 레이어를 Presentation/Data/Domain 레이어로 나뉘어 Presentation Layer에서 구현됩니다. 

출처:https://tech.olx.com/clean-architecture-and-mvvm-on-ios-c9d167d9f5b3


ⓒ 굿햄 2022. daryeou@gmail.com all rights reserved.

 

댓글