본문 바로가기
source-code/software

클린 아키텍처

by mattew4483 2023. 8. 20.
728x90
반응형

1장 - 설계와 아키텍처란?

소프트웨어 아키텍처의 목표는 필요한 시스탬을 만들고 유지보수하는 데 투입되는 인력을 최소화하는 데 있다.

비용은 최소화하고 생산성은 초대화할 수 있는 설계와 아키텍처를 가진 시스템을 만드려면, 이러한 결과로 이끌어 줄 시스템 아키텍처가 지닌 속성을 알고 있어야 한다.

2장 - 두 가지 가치에 대한 이야기

모든 소프트웨어 시스템은 이해관계자에게 '행위(behavior)'와 '구조(structure)' 란 두 가지 가치를 제공한다.
...
소프트웨어를 만든 이유는 기계의 행위를 쉽게 변경할 수 있도록 하기 위해서다.
...
이해관계자가 기능에 대한 생각을 바꾸면, 이러한 변경사항을 간단하고 쉽게 적용할 수 있어야 한다. 이러한 변경사항을 적용하는 데 드는 어려움은 변경되는 범위(scope)에 비례해야하며, 변경사항의 형태(shape)와는 관련이 없어야 한다.
...
아키텍처가 특정 형태를 다른 형태보다 선호하면 할수록, 새로운 기능을 이 구조에 맞추는 게 더 힘들어진다. 따라서 아키텍처는 형태에 독립적이어야 하고, 그럴수록 더 실용적이다.

소프트웨어의 첫 번째 가치인 행위 는 긴급하지만 매번 높은 중요도를 가지는 것은 아니다. 소프트웨어의 두 번째 가치인 아키텍처는 중요하지만 즉각적인 긴급성을 필요로 하는 경우는 절대 없다.
...
기능의 긴급성이 아닌 아키텍처의 중요성을 설득하는 일은 소포트웨어 개발팀이 마땅히 책임져야 한다.

3장 - 패러다임 개요

구조적 프로그래밍, 객체 지향 프로그래밍, 함수형 프로그래밍

각 패러다임은 부정적인 의도를 가지는 일종의 추가적인 규칙을 부과한다. 즉, 패러다임은 무엇을 해야 할지를 말하기보다는 무엇을 해서는 안 되는지를 말해준다.
...
세 가지 패러다임 각각은 우리에게서 goto문, 함수 포인터, 할당문을 앗아간다.

우리는 아키텍처 경계를 넘나들기 위한 메커니즘으로 다형성을 이용한다. 우리는 함수형 프로그래밍을 이용하여 데이터의 위치와 접근 방법에 대해 규칙을 부과한다. 우리는 모듈의 기반 알고리즘으로 구조적 프로그래밍을 사용한다.
세 가지 패러다임과 아키텍처의 세 가지 큰 관심사(함수, 컴포넌트 분리, 데이터 관리)가 어떻게 서로 연관되는지에 주목하자.

4장 - 구조적 프로그래밍

즉, 모듈을 증명 가능하게 하는 바로 그 제어 구조(순차, 분기, 반복)가 모든 프로그램을 만들 수 있는 제어 구조의 최소 집합과 동일하다는 사실이었다.

오히려 소프트웨어는 과학과 같다. 최선을 다하더라도 올바르지 않음을 증명하는 데 실패함으로써 올바름을 보여주기 때문이다.
...
구조적 프로그래밍은 프로그램을 증명 가능한 세부 기능 집합으로 재귀적으로 분해할 것을 강요한다. 그러고 나서 테스트를 통해 증명 가능한 세부 기능들이 거짓인지를 증명하려고 시도한다. 이처럼 거짓임을 증명하려는 테스트가 실패한다면, 이 기능들은 목표에 부합할 만큼은 충분히 참이라고 여기게 된다.

소프트웨어 아키텍트는 모듈, 컴포넌트, 서비스가 쉽게 반증 가능하도록(테스트하기 쉽도록) 만들기 위해 분주히 노력해야 한다.

5장 - 객체 지향 프로그래밍

객체 지향 언어가 다형성을 안전하고 편리하게 제공한다는 사실은 소스 코드 의존성을 어디에서든 역전시킬 수 있다는 뜻이기도 하다.
이러한 접근법을 사용한다면, 객체 지향 언어로 개발된 시스템을 다루는 소프트웨어 아키텍트는 시스템의 소스 코드 의존성 전부에 대해 방향을 결정할 수 있는 절대적인 권한을 갖는다. 즉, 소스 코드 의존성이 제어흐름의 방향과 일치되도록 제한되지 않는다.

(소프트웨어 아키텍트 관점에서) 객체 지향이란 다형성을 이용하여 전체 시스템의 모든 소스 코드 의존성에 대한 절대적인 제어 권한을 획득할 수 있는 능력이다.
...
저수준의 세부사항은 중요도가 낮은 플러그인 모듈로 만들 수 있고, 고수준의 정책을 포함하는 모듈과는 독립적으로 개발하고 배포(배포 독립성, 개발 독립성)할 수 있다.

6장 - 함수형 프로그래밍

우리가 동시성 애플리케이션에서 마주치는 모든 문제, 즉 다수의 스레드와 프로세스를 사용하는 애플리케이션에서 마주치는 모든 문제는 가변 변수가 없다면 절대로 생기지 않는다.
(함수형 언어의 불변성이 아키텍쳐 레벨에서 중요한 이유)

불변성과 관련하여 가장 주요한 타협 중 하나는 애플리케이션, 또는 애플리케이션 내부의 서비스를 가변 컴포넌트와 불변 컴포넌트로 분리하는 일이다. 불변 컴포넌트에서는 순수하게 함수형 방식으로만 작업이 처리되며, 어떤 가변 변수도 사용되지 않는다. 불변 컴포넌트는 변수의 상태를 변경할 수 있는, 즉 순수 함수형 컴포넌트가 아닌 하나 이상의 다른 컴포넌트와 서로 통신한다.

... 변수를 변경하는 컴포넌트와 그렇지 않은 컴포넌트를 분리해야 한다는 것이다. 그리고 이렇게 분리하려면 가변 변수들을 보호하는 적절한 수단을 동원해 뒷받침해야 한다.

현명한 아키텍트라면 가능한 한 많은 처리를 불변 컴포넌트로 옮겨야 하고, 가변 컴포넌트에서는 가능한 한 많은 코드를 빼내야 한다.

요약하면

  • 구조적 프로그래밍은 제어흐름의 직접적인 전환에 부과되는 규율이다.
  • 객체 지향 프로그래밍은 제어흐름의 간접적인 전환에 부과되는 규율이다.
  • 함수형 프로그래밍은 변수 할당에 부과되는 규율이다.
    ...
    소프트웨어, 즉 컴퓨터 프로그램은(소프트웨어의 핵심은) '순차(sequence)', '분기(selection)', '반복(iteraction)', '참조(indirection)'로 구성된다.

3부 - 설계 원칙 (SOLID)

  • SRP : 단일 책임 원칙
    소프트웨어 시스템이 가질 수 있는 최적의 구조는 시스템을 만드는 조직의 사회적 구조에 커다란 영향을 받는다. 따라서 각 소프트웨어 모듈은 변경의 이유가 하나, 단 하나여야만 한다.
  • OCP : 개방-폐쇠 원칙
    기존 코드를 수정하기보다는 반드시 새로운 코드를 추가하는 방식으로 시스템의 행위를 변경할 수 있도록 설계해야만, 소프트웨어 시스템을 쉽게 변경할 수 있다.
  • LSP : 리스코프 치환 원칙
    상호 대체 가능한 구성요소를 이용해 소프트웨어 시스템을 만들 수 있으려면, 이들 구성요소는 반드시 서로 치환 가능해야하다는 계약을 반드시 지켜야 한다.
  • ISP : 인터페이스 분리 원칙
    소프트웨어 설계자는 사용하지 않는 것에 의존하지 않아야 한다.
  • DIP : 의존성 역전 원칙
    고수준 정책을 구현하는 코드는 저수준 세부사항을 구현하는 코드에 절대로 의존해서는 안 된다. 대신 세부사항이 정책에 의존해야 한다.

12장 - 컴포넌트

컴포넌트는 배포 단위다.
...
컴포넌트가 마지막에 어떤 형태로 배포되든, 잘 설계된 컴포넌트라면 반드시 독립적으로 배포 가능한, 따라서 독립적으로 개발 가능한 능력을 갖춰야 한다.

13장 - 컴포넌트 응집도

REP : 재사용/릴리스 등가 원칙
재사용 단위는 릴리스 단위와 같다

컴포넌트를 구성하는 모든 모듈은 서로 공유하는 중요한 테마나 목적이 있어야 한다.

현재 앱에서 날짜 표기하는 UI를 구현하기 위해 date_fns 라이브러리를 import 한 후 각 컴포넌트 별로 직접 날짜 표기를 변경해주고 있다. 해당 컴포넌트의 목적은 date_fns를 사용하는 것이 아니라, 날짜 표기를 변경해주는 것이라 봤을 때, 이를 외부 함수로 분리한 후 분리된 함수를 가져와 사용하는 것이 좋았겠다.

CCP : 공통 폐쇠 원칙
동일한 이유로 동일한 시점에 변경되는 클래스를 같은 컴포넌트로 묶어라. 서로 다른 시점에 다른 이유로 변경되는 클래스는 다른 컴포넌트로 분리하라.

물리적 또는 개념적으로 강하게 결합되어 항상 함께 변경되는 클래스들은 하나의 컴포넌트에 속해야 한다.

CRP : 공통 재사용 원칙
컴포넌트 사용자들을 필요하지 않는 것에 의존하게 강요하지 말라.

어떤 컴포넌트가 다른 컴포넌트를 사용하면, 두 컴포넌트 사이에는 의존성이 생겨난다. 어쩌면 사용하는 컴포넌트가 사용되는 컴포넌트에서 단 하나의 클래스만 사용할 수도 있다. 그렇다고 해서 의존성은 조금도 약해지지 않는다.
...
따라서 의존하는 컴포넌트가 있다면 해당 컴포넌트의 모든 클래스에 대해 의존함을 확실히 인지해야 한다. 바꿔 말하면, 한 컴포넌트에 속한 클래스들은 더 작게 그룹지을 수 없다.

오로지 REP와 CRP에만 중점을 두면('포함'의 원칙), 사소한 변경이 생겼을 때 너무 많은 컴포넌트에 영향을 미친다. 반대로 CCP('배제'의 원칙)와 REP에만 과도하게 집중하면 불필요한 릴리스가 너무 빈번해진다.
...
뛰어난 아키텍트라면 이 군형 삼각형에서 개발팀이 현재 관심을 기울이는 부분을 충족시키는 위치를 찾아야 하며, 또한 시간이 흐르면서 개발팀이 주의를 기울이는 부분 역시 변한다는 사실도 이해하고 있어야 한다.

14장 - 컴포넌트 결합

ADP : 의존성 비순환 원칙
컴포넌트 의존성 그래프에 순환이 있어서는 안 된다.

SDP : 안정된 의존성 원칙
안정성의 방향으로(더 안정된 쪽에) 의존하라.

SAP : 안정된 추상화 원칙
컴포넌트는 안정된 정도만큼만 추상회되어야 한다.

728x90
반응형

'source-code > software' 카테고리의 다른 글

첫 실무 test code 작성 및 고민  (0) 2023.08.20
클린 코드  (0) 2023.08.20
Jest를 통한 unit test  (0) 2023.08.18
SEO  (1) 2021.07.28
API 활용하기  (0) 2021.05.23