# 클린 아키텍처

클린 아키텍처

## 1부 소개

***

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

대충 짜면 단기적이어도 빠른게 아니라 느리게 개발하게 된다. 제대로 개발하는게 무조건 옳다.

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

행위 : 프로구램에 대한 요구사항 아키텍처 : 얼마나 변화에 부드러운지

아이젠하워 매트릭스 기준으로 아키텍처는 중요하기 때문에 행위보다 우선순위가 높은데, 이를 거꾸로 생각하면 안된다.

## 2부 벽돌부터 시작하기 : 프로그래밍 패러다임

***

### 3장 패러다임 개요

구조적 프로그래밍은 goto문을 앗아가고, 객체 지향 프로그래밍은 함수 포인터를 앗아가고, 함수형 프로그래밍은 할당문을 앗아간다

### 4장 구조적 프로그래밍

프로그램은 반증은 가능하지만 증명은 불가능하다 goto문 없앤 이유 : 작게 쪼개서 디버깅(반증) 쉽게 하려고

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

객체 지향은 무엇인가? 데이터와 함수의 조합? 아님 현실 세계를 모델링? 아님 캡슐화, 상속, 다형성?

* 캡슐화는 c가 더 완벽하게 함
* 상속도 c로도 가능하긴 함
* 다형성은 완전 편해짐. 고로 객체 지향은 "제어 흐름의 간접적 전환을 가능하게 한다"

다형성 편하게 못 썼을 땐 제어 흐름과 의존성 방향이 동일 다형성 쓰면 인터페이스로 의존성 역전 가능.

소스 코드 의존성을 역전하여, 저수준의 UI와 데이터베이스가 고수준의 업무 규칙에 의존하도록 만들 수 있다.

고로 객체 지향은 모든 소스 코드 의존성에 대한 제어 권한을 획득하는 능력이다.

### 6장 함수형 프로그래밍

함수형 언어에서 변수는 변경되지 않는다.

가변 변수가 유발하는 것들

* 경합(race) 조건
* 교착상태(deadlock) 조건
* 동시 업데이트(concurrent update) 문제

즉, 동시성 응용 프로그램에서 마주치는 모든 문제는 가변 변수가 없으면 절대로 안 생긴다. 불변성과 관련된 가장 주요한 타협 중 하나는 서비스를 가변 컴포넌트와 불변 컴포넌트로 분리하는 일. 최대한 많은 처리를 불변 컴포넌트로 옮겨야 하고, 가변 컴포넌트에선 최대한 많은 코드를 빼내는게 좋다.

이벤트 소싱 : 가변 변수에 상태 저장하지 말고 트랜잭션을 죄다 저장한다. 상태가 필요해지면 상태 시작점부터 모든 트랜잭션 처리한다. 이러면 응용 프로그램을 완전한 함수형으로 만들 수 있다.

**결론**

* 구조적 프로그래밍 : 제어흐름의 직접적인 전환에 부과되는 규율
* 객체 지향 프로그래밍 : 제어흐름의 간접적인 전환에 부과되는 규율
* 함수형 프로그래밍 : 변수 할당에 부과되는 규율

## 3부 설계 원칙

***

### 7장 SRP: 단일 책임 원칙

단일 책임 원칙은 모든 모듈이 단 하나의 일만 해야 한다는게 아니다. 단일 모듈은 변경의 이유가 오직 하나뿐이어야 한다는 것이다. 다시 말하면, 하나의 모듈은 오직 한 집단에 대해서만 책임져야 한다는 것이다.

**예시** Employee 클래스가 각각 회계팀, 인사팀, 개발팀에서 사용하는 급여계산(), 시간보고(), 저장() 기능을 갖고 있다면 회계팀에서 결정한 조치가 인사팀이 의존하는 무언가에 영향을 줄 수 있다. 변경사항이 충돌하여 병합이 더 자주 일어난다. 그리고 병합은 언제나 위험하다.

**해결책** 다양한 해결책이 있겠지만, 모두 메서드를 각기 다른 클래스로 이동시키는 방식이다. 가장 확실한 해결책은 데이터와 메서드를 분리한다. 개발자가 세 가지 클래스를 인스턴스화하고 추적해야 한다는 단점이 있다. 퍼사드(Facade) 패턴을 사용해 이를 보완할 수 있다. (EmployeeFacade에 코드는 거의 없고, 세 클래스의 객체를 생성하고 요청된 메서드를 가진 객체로 위임한다)

어떤 개발자는 가장 중요한 업무 규칙을 데이터와 가깝게 배치하는 방식을 선호한다. 이 경우라면 가장 중요한 메서드는 기존의 Employee 클래스에 그대로 유지하고, Employee 클래스를 나머지 덜 중요한 메서드들에 대한 퍼사드로 사용할 수도 있다.

### 8장 OCP: 개방 폐쇄 원칙

소프트웨어 개체는 확장엔 열려있고, 변경엔 닫혀 있어야 한다 그러려면 시스템을 컴포넌트 단위로 분리하고, 저수준 컴포넌트 변경에서 고수준 컴포넌트를 보호할 수 있는 의존성 계층 구조를 만들어야 한다

### 9장 LSP: 리스코프 치환 원칙

자식 클래스들은 서로 바꿔 끼워도 부모의 행위가 바뀌지 않는다. 예를 들어, 정사각형의 부모에 직사각형을 넣지 말라는 뜻. 직사각형은 setH, setW로 높이 너비 따로 설정하는데 정사각형은 setSide로 함께 변경하면 사용할 때 혼동 생긴다.

어쩔 수 없이 예외가 들어가면 if문으로 처리하지 말고 그거 공통적으로 처리하는 로직을 따로 만들어라.

### 10장 ISP: 인터페이스 분리 원칙

서로 다른 사용자가 같은 클래스의 서로 다른 메서드에 의존한다고 할 때, 메서드 하나를 변경하면 그에 의존하지 않는 사용자들도 다시 컴파일하게 됨. 이때 의존하는 메서드들을 인터페이스로 나눠버리면 의존하고 있는 메서드를 바꿨을 때만 다시 컴파일되고 재배포됨.

정적 타입 언어는 소스 코드에 포함된 `included` 선언문으로 인해 의존성이 발생한다. 루비나 파이썬 같은 동적 타입 언어에서는 소스 코드에 이러한 선언문이 존재하지 않는다. 대신 런타임에 추론이 발생하기 때문에, 의존성도 없고, 재컴파일과 재배포도 필요없다.

자바, C# 등은 late binding으로 private이 아닌 객체는 시그니처를 변경하면 의존하는 코드만, 구현 코드만 변경됐다면 그것도 재컴파일 필요 없어짐. 즉, ISP는 언어 종류에 따라 영향받는 정도가 다름.

아키텍처 수준에서도 ISP는 중요하다. 필요 이상으로 많은 걸 포함하는 모듈에 의존하는 건 해로운 일이다. 의존하던 프레임워크의 불필요한 기능 때문에 문제가 발생하거나 재배포를 해야 할 수도 있기 때문.

### 11장 DIP: 의존성 역전 원칙

유연성이 극대화된 시스템이란, 소스 코드 의존성이 추상에 의존하며 구체에는 의존하지 않는 시스템.

규칙이라고 보기는 현실적이지 않다. 하지만 변동성이 큰 구체적 요소는 의존하지 않도록 해야 한다. 그것은 열심히 개발 중이라 자주 변경되는 모듈들을 의미한다.

인터페이스는 변동성이 낮아야 한다. 가급적 인터페이스를 변경하지 않고도 구현체에 기능을 추가하도록 노력해야 한다.

구체적 실천법

* 변동성이 큰 구체 클래스를 참조하지 말고, 추상 인터페이스를 참조하라.
* 변동성이 큰 구체 클래스로부터 파생하지 마라. 상속은 아주 신중하게 사용해라.
* 구체 함수를 오버라이드 하지 마라. 의존성도 상속받게 되기 때문.
* 구체적이며 변동성이 크다면 절대로 그 이름을 언급하지 말라.

구체적 의존성을 완전히 없애는 건 불가능하다. 팩토리를 쓴다거나 해서 줄일 순 있다.

## 4부 컴포넌트 원칙

***

### 12장 컴포넌트

컴포넌트는 시스템의 구성 요소로 배포할 수 있는 가장 작은 단위 .war, .jar, .dll, .exe 등을 컴포넌트라 할 수 있다.

### 13장 컴포넌트 응집도

어떤 클래스를 어느 컴포넌트에 포함시켜야 할까? 컴포넌트 응집도와 관련된 세 가지 원칙이 있다.

**REP: 재사용/릴리스 등가 원칙**

재사용 단위는 릴리스 단위와 같다. 컴포넌트 버전 관리도 제대로 하고, 구성 요소도 목적이 같은 것들만 넣으라는 뜻.

**CCP: 공통 폐쇄 원칙**

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

SRP에서 단일 클래스에 변경의 이유가 여러 개 있어서는 안 된다고 하듯이, CCP에서도 단일 컴포넌트는 변경의 이유가 여러 개 있어서는 안된다고 하는 것.

**CRP: 공통 재사용 원칙**

컴포넌트 사용자들을 필요하지 않은 것에 의존하게 강요하지 말라. 강하게 결합되지 않은 클래스들을 동일한 컴포넌트에 위치시키지 말라는 뜻.

ISP에서 사용하지 않은 메서드가 있는 클래스에 의존하지 말라고 하듯이, CRP에서도 사용하지 않은 클래스를 가진 컴포넌트에 의존하지 말라고 하는 것.

**원칙들 사이의 균형**

REP와 CCP는 컴포넌트를 크게 만들고, CRP는 작게 만든다. 크게 만들수록 사소한 변경에 너무 많은 컴포넌트가 영향을 받는다. 작게 만들수록 불필요한 릴리스가 너무 빈번해진다.

프로젝트 초기에는 CCP가 REP보다 훨씬 중요하다. 개발 가능성이 재사용성보다 더욱 중요하기 때문.

프로젝트가 성숙하고, 그 프로젝트로부터 파생된 또 다른 프로젝트가 시작되면, REP가 점점 더 중요해진다.

### 14장 컴포넌트 결합

**ADP: 의존성 비순환 원칙**

컴포넌트 의존성 그래프에 순환이 있어서는 안된다. (컴포넌트는 유니티로 치면 asmdef로 어셈블리 나누는 거)

의존하던 무언가가 고장나면 그거 고치느라 개발이 지연되는 숙취 증후군이 발생할 수 있다. 프로젝트 규모가 커지면 숙취 증후군 때문에 stable 빌드를 몇 주씩이나 못 뽑게 된다. 해결책은 '주 단위 빌드'와 '의존성 비순환 원칙'이다.

주 단위 빌드

* 중간 규모 프로젝트에서 흔하게 사용된다
* 일주일의 첫 4일 동안은 서로를 신경쓰지 않는다
* 금요일이 되면 변경된 코드를 모두 통합하여 시스템을 빌드한다
* 프로젝트가 커지면 금요일 하루 만에 통합을 끝마치지 못하게 된다

점진적 빌드

* 개발 환경을 릴리스 가능한 컴포넌트 단위로 분리한다
* 개발자는 해당 컴포넌트가 동작하도록 만든 후에 배포한다
* 다른 팀은 새 릴리스를 적용할지 말지 결정한다
* 통합은 작고 점진적으로 이루어진다

의존성 구조에 순환이 생기면 숙취 증후군을 피해갈 수 없다. 서로서로 의존해버리면 그것들이 하나의 거대한 컴포넌트가 돼버리기 때문이다. 모두 항상 정확하게 동일한 릴리스를 사용해야 하고, A를 고치면 B와 C도 반드시 빌드하고 통합해야 한다.

컴포넌트 사이의 순환 끊기

* 의존성 역전을 적용한다
* A와 B가 모두 의존하는 클래스를 새로운 컴포넌트로 이동시킨다

컴포넌트 구조는 시스템에서 가장 먼저 탑다운으로 설계할 수 있는 방식이 아니다. 시스템이 성장하고 변경될 때 함께 진화하는 것이다. 컴포넌트 의존성은 애플리케이션의 기능을 기술하는 일과는 거의 관련이 없기 때문이다. 컴포넌트 의존성 다이어그램은 프로그램의 빌드 가능성과 유지보수성을 보여주는 지도이다.

**SDP: 안정된 의존성 원칙**

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

모듈을 만들 때 변경하기 쉽게 설계했어도, 의존성을 매달아 버리면 변경하기 어려워진다. 반대로 말해, 해당 모듈에 의존하는게 없으면 변경하기 쉽고 안정적이라는 뜻.

불안정한 컴포넌트도 있고 안정된 컴포넌트도 존재하는게 정상이다.

안정한 쪽이 불안정한 쪽에 의존하게 된다면 의존성 역전을 적용하라.

**SAP: 안정된 추상화 원칙**

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

안정됐는데(의존하는 모듈들이 많은데) 구체적이라면 관리도 어렵고 변경 시 고통스럽다. 데이터베이스 스키마 같이 변동성이 높으면 곤란하지만, 유틸리티 라이브러리 같이 변동성이 낮으면 해롭지 않다.

반대로 아무도 의존하고 있지 않은데 추상화돼있으면 의미가 없다.

## 5부 아키텍처

***

### 15장 아키텍처란?

소프트웨어를 부드럽게 유지하는 방법 : 선택사항을 가능한 한 많이, 오랫동안 열어둔다 열어 둬야 할 세부사항 : 입출력 장치, 데이터베이스, 웹 시스템, 서버, 프레임 워크, 통신 프로토콜 등

(이후 내용들은 경험해보지도 못했는데 추상적인 이야기들이 이어져서 일단 여기까지)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lazyartisan.gitbook.io/note/main-page/books/undefined-2.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
