클린 아키텍쳐, Clean Architecture (인사이트)
08 Feb 2020 | book
클린 아키텍쳐, 소프트웨어 구조와 설계의 원칙
로버트 C. 마틴 지음
SA(소프트웨어 아키텍트)가 되기 위하여 알아야 할 기본 원칙들이 책에 나와있다. 다양한 시스템 종류와 변하는 최신 패러다임에도 불구하고 아키텍쳐의 규칙이란 일관적이고 보편적이다.
패러다임
객체지향 프로그래밍
의존성 역전
전형적인 호출 트리의 경우 main 함수가 고수준 함수를 호출하고, 고수준 함수는 중간 수죽 함수를, 중간 수준은 저수준 함수를 호출한다. 이러한 호출 트리에서의 소스 코드 의존성의 방향은 반드시 제어흐름을 따르게 된다.
하지만 다형성이 끼어들면 특별한 일이 일어난다.
소스 코드 사이에 인터페이스를 추가함으로써 방향을 역전시키는 것이다. 이런 접근법을 통해 OO 언어로 개발된 시스템을 다루는 아키텍트는 소스 코드 의존성 전부에 대해 방향을 결정할 수 있는 절대적인 권한을 갖는다.
이 힘으로, 업구 규칙이 데이터베이스와 UI에 의존하는 대신에, 소스 코드 의존성을 반대로 배치하여 UI가 업무 규칙에 의존하도록 만들 수 있다.
함수형 프로그래밍
아키텍트라면 동시성 문제에 대해 지대한 관심을 가져야 한다. 우리는 스레드와 프로세스가 여러 개인 상황에서도 설계한 시스템이 강건하기를 바라기 때문이다.
이 때 불변성을 고려하여 설계한다면 (가변 변수가 갱신되지 않는다면) 이런 문제를 해결할 수 있다.
애플리케이션을 제대로 구조화하려면 변수를 변경하는 컴포넌트와 변경하지 않는 컴포넌트를 분리해야 한다.
설계 원칙
- SRP : 단일 책임 원칙
- 소프트웨어 모듈은 변경의 이유가 단 하나여야 한다.
- OCP : 개방-폐쇄 원칙
- 기존 코드를 수정하기보다는 새로운 코드를 추가하는 방식으로 시스템의 행위를 변경할수 있도록 설계해야 한다.
- LSP : 리스코프 치환 원칙
- 상호 대체 가능한 구성요소를 이용해 소프트웨어 시스템을 만들 수 있도록 해야한다. 구성요소들 끼리 치환 가능해야 한다.
- ISP : 인터페이스 분리 원칙
- 소프트웨어 설계자는 사용하지 않은 것에 의존하지 않아야 한다.
- DIP : 의존성 역전 원칙
- 고수준 정책을 구현하는 코드는 저수준 세부사항을 구현하는 코드에 절대로 의존해서는 안되며, 세부사항이 정책에 의존해야 한다.
컴포넌트
컴포넌트는 배포 단위다. 런타임에 플러그인 형태로 결합할 수 있는 동적 링크 파일이 이 책에서 말하는 컴포넌트다.
어떤 클래스를 어느 컴포넌트에 포함시켜야 할까?
컴포넌트 응집도 원칙
- REP : 재사용/릴리스 등가 원칙
- CCP : 공통 폐쇄 원칙
- CRP : 공통 재사용 원칙
어느 클래스들을 묶어서 컴포넌트로 만들지를 결정할 때, 재사용성과 개발가능성이라는 요구에 맞게 균형을 잡는 일은 중요하다.
컴포넌트 결합 원칙
- ADP : 의존성 비순환 원칙
- 순환이 생기면 컴포넌트를 분리하긱 상당히 어려워진다. 단위 테스트를 하고 릴리스를 하는 일도 굉장히 어려워지고 에러도 쉽게 발생한다.
뿐만 아니라 의존성 그래프에 순환이 생기면 컴포넌트를 어떤 순서로 빌드해야 올바를지 파악하기가 상당히 힘들어진다. 순환이 생기면
사실 올바른 순서라는 것 자체가 없을 수 있다.
이런 순환을 끊고 다시 DAG로 원상복구 하는 일은 언제라도 가능하다.
- DIP (의존성 역전 원칙) 을 적용한다.
- 순환이 생기면 컴포넌트를 분리하긱 상당히 어려워진다. 단위 테스트를 하고 릴리스를 하는 일도 굉장히 어려워지고 에러도 쉽게 발생한다.
아키텍처
아키텍처란 무엇인가? 소프트웨어 시스템의 아키텍처란 시스템을 구축했던 사람들이 만들어낸 시스템의 형태다. 시스템을 컴포넌트로 분할하는 방법,
분할된 컴포넌트를 배치하는 방법, 컴포넌트가 서로 의사소통하는 방식에 따라 정해진다. 그리고 그 형태는 아키텍처 안에 담긴 소프트웨어 시스템이
쉽게 개발, 배포, 운영, 유지보수되도록 만들어진다.
좋은 아키텍처는 다음을 지원해야 한다.
- 시스템의 유스케이스
- 시스템의 운영
- 시스템의 개발
- 시스템의 배포
아키텍쳐 경계 긋기
소프트웨어 아키텍처는 선을 긋는 기술이다. 일찍 내려진 결정에 따른 결합(coupling)을 줄여야 하는데, 이는 유스케이스와 아무런 관련이 없는 결정들이다.
프레임워크, 데이터베이스, 웹 서버, 유틸리티 라이브러리, 의존성 주입에 대한 결정 등이 여기 포함된다. 좋은 시스템 아키텍처란 이러한 결정이 부수적이고,
결정을 연기할 수 있는 아키텍쳐다.
소프트웨어 아키텍처에서 경계선을 그리러면 먼저 시스템을 컴포넌트 단위로 분할하고, 일부 컴포넌트는 업무 규칙에 해당하며 나머지 컴포넌트는 플러그인에 대항한다.
컴포넌트 사이의 화살표는 핵심 업무를 향하도록 컴포넌트의 소스를 배치해야 한다.
클린 아키텍쳐
- 의존성 규칙 : 소스 코드 의존성은 반드시 안쪽으로, 고수준의 정책을 향해야 한다.
- 엔티티 : 엔티티는 전사적인 핵심 업무 규칙을 캡슐화한다.
- 유스케이스 : 엔티티로 들어오고 나가는 데이터 흐름을 조정하고, 엔티티가 자신의 핵심 업무 규칙을 사용해서 유스케이스의 목적을 달성하도록 이끈다.
- 인터페이스 어댑터 : 데이터를 유스케이스와 엔티티에게 가장 편리한 형식에서 데이터베이스나 웹 같은 외부 에이전시에게 가장 편리한 형식으로 변환한다.
- 프레임워크와 드라이버
험블 객체 패턴이란
험블 객체 패턴은 디자인 패턴으로, 테스트하기 어려운 행위와 테스트하기 쉬운 행위를 단위 테스트 작성자가 분리하기 쉽게 하는 방법으로 고안되었다.
기본적인 본질은 남기고, 테스트하기 어려운 행위를 모두 험블 객체로 옮기는 것이다.
예를 들어 GUI의 경우 단위 테스트가 어려운데, 화면을 보면서 각 요소가 필요한 위치에 적절히 표시되었는지 검사하는 테스트는 작성하기가 매우
어렵기 때문에 험블 객체 패턴을 사용하여 두 부류의 행위를 분리하여 프레젠터와 뷰라는 서로 다른 클래스로 만드는 것이다.