Skip to content

Commit

Permalink
feat: Add chapter 15~25
Browse files Browse the repository at this point in the history
  • Loading branch information
yumin-kim committed Apr 28, 2024
1 parent cf5e3d0 commit c76e615
Show file tree
Hide file tree
Showing 22 changed files with 824 additions and 0 deletions.
1 change: 1 addition & 0 deletions chapter15/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# 15장. 아키텍처란?

- [zhoon](./zhoon/)
- [yumin](./yumin/)
97 changes: 97 additions & 0 deletions chapter15/yumin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# 15. 아키텍처란?

아키텍처라는 단어는 권력과 신비로움을 연상케 한다.

소프트웨어 아키텍트는 프로그래머이며, 앞으로도 계속 프로그래머로 남는는다.

소프트웨어 아키텍트라면 코드에서 탈피하여 고수준의 문제에 집중해야 한다는 거짓말에 절대로 속아 넘어가서는 안 된다.

소프트웨어 시스템의 아키테처란 시스템을 구축했던 사람들이 만들어낸 시스템의 형태다.

이러한 일을 용이하게 만들기 위해서는 가능한 한 많은 선택지를, 가능한 한 오래 남겨두는 전략을 따라야 한다.

아키텍처의 주된 목적은 시스템의 생명주기를 지원하는 것이다.

---

### 개발

---

개발하기 힘든 시스템이라면 수명이 길지도 않고 건강하지도 않을 것이다.

팀별 단일 컴포넌트 아키텍처가 시스템을 배포, 운영, 유지보수하는 데 최적일 가능성은 거의 없다. 그럼에도 여러 팀이 순전히 일정에만 쫓겨서 일한다면, 결국 이 아키텍처로 귀착될 것이다.

### 배포

---

소프트웨어 시스템이 사용될 수 있으려면 반드시 배포할 수 있어야 한다.

배포 비용이 높을수록 시스템의 유용성은 떨어진다.

따라서 소프트웨어 아키텍처는 시스템을 단 한 번에 쉽게 배포할 수 있도록 만드는 데 그 목표를 두어야 한다.

만약 아키텍트가 배포 문제를 초기에 고려했다면 이와는 다른 결정을 내렸을 것이다.

더 적은 서비스를 사용하고, 서비스 컴포넌트와 프로세스 수준의 컴포넌트를 하이브리드 형태로 융합하며, 좀 더 통합된 도구를 사용하여 상호 연결을 관리했을 것이다.

### 운영

---

아키텍처가 시스템 운영에 미치는 영향은 개발, 배포, 유지보수에 미치는 영향보다는 덜 극적이다.

운영에서 겪는 대다수의 어려움은 소프트웨어 아키텍처에는 극적인 영향을 주지 않고도 단순히 하드웨어를 더 투입해서 해결할 수 있다.

아키텍처가 개발자에게 시스템의 운영 방식을 자라 드러내 준다고 할 수 있다.

시스템 아키텍처는 유스케이스, 기능, 시스템의 필수 행위를 일급 엔티티로 격상시키고, 이들 요소가 개발자에게 주요 목표로 인식되도록 해야 한다.

이를 통해 시스템을 이해하기 쉬워지며, 따라서 개발과 유지보수에 큰 도움이 된다.

### 유지보수

---

유지보수는 모든 측면에서 봤을 때 소프트웨어 시스템에서 비용이 가장 많이 든다.

주의를 기울여 신중하게 아키텍처를 만들면 이 비용을 크게 줄일 수 있다.

### 선택사항 열어 두기

---

구조적 가치는 중요한 가치인데, 소프트웨어를 부드럽게 만드는 것이기 때문이다.

소프트웨어를 만든 이유는 기계의 행위를 빠르고 쉽게 변경하는 방법이 필요했기 때문이다.

이러한 유연성은 시스템의 형태, 컴포넌트의 배치 방식, 컴포넌트가 상호 연결되는 방식에 상당히 크게 의존한다.

아키텍트의 목표는 시스템에서 정책을 가장 핵심적인 요소로 식별하고, 동시에 세부사항은 정책에 무관하게 만들 수 있는 형태의 시스템을 구축하는 데 있다. 이를 통해 세부사항을 결정하는 일은 미루거나 연기할 수 있게 된다.

좋은 아키텍트는 결정되지 않는 사항의 수를 최대화한다.

### 장치 독립성

---

오늘날의 운영체제는 입출력 장치를 소프트웨어 함수로 추상화했고, 해당 함수는 천공카드와 같은 단위 레코드를 처리한다.

프로그램은 운영체제의 서비스를 호출하고, 해당 서비스가 추상화된 단위 레코드 장치를 처리한다.

이제는 동일한 프로그램을 아무런 변경 없이도 카드에서 읽고 쓰거나 테이프에서 읽고 쓸 수 있게 되었다. 개방 폐쇄 원칙이 탄생한 순간이다.

장치 독립성이 지닌 가치는 굉장하다.

어떤 장치를 사용할지 전혀 모른 채, 그리고 고려하지 않고도 프로그램을 작성할 수 있었다.

그런 후 운영체제에게는 자기 테이프에 인쇄하도록 지시할 수 있었고, 이를 통해 수십만 장에 달하는 편지 양식을 인쇄할 수 있었다.

### 결론

---

이 장에 포함된 두 가지 이야기는 소규모 사례이지만, 그 원칙은 아키텍트가 대규모 시스템에 적용할 수 있는 예이기도 하다.

좋은 아키텍트는 세부사항을 정책으로부터 신중하게 가려내고, 정책이 세부사항과 결합되지 않도록 엄격하게 분리한다. 이를 통해 정책은 세부사항에 관한 어떠한 지식도 갖지 못하게 되며, 어떤 경우에도 세부사항에 의존하지 않게 된다. 좋은 아키텍트는 세부사항에 대한 결정을 가능한 한 오랫동안 머물 수 있는 방향으로 정책을 설계한다.
1 change: 1 addition & 0 deletions chapter16/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# 16장. 독립성

- [zhoon](./zhoon/)
- [yumin](./yumin/)
133 changes: 133 additions & 0 deletions chapter16/yumin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# 16. 독립성

좋은 아키텍처에는 다음을 지원해야 한다.

- 시스템의 유스케이스
- 시스템의 운영
- 시스템의 개발
- 시스템의 배포

### 유스케이스

---

시스템의 아키텍처는 시스템의 의도를 지원해야 한다는 뜻이다.

아키텍처는 반드시 유스케이스를 지원해야 한다.

좋은 아키텍처가 행위를 지원하기 위해 할 수 있는 일 중에서 가장 중요한 사항은 행위를 명확히 하고 외부로 드러내며, 이를 통해 시스템이 지닌 의도를 아키텍처 수준에서 알아볼 수 있게 만드는 것이다.

### 운영

---

아키텍처는 더 실질적이며 덜 피상적인 역할을 맡는다.

만약 시스템이 단일체(monolith)로 작성되어 모노리틱 구조로 갖는다면, 다중 프로세스, 다중 스레드, 또는 마이크로서비스 형태가 필요해질 때 개선하기가 어렵다.

### 개발

---

콘웨이의 법칙

- 시스템을 설계하는 조직이라면 어디든지 그 조직의 의사소통 구조와 동일한 구조의 설계를 만들어 낼 것이다.

### 배포

---

아키텍처는 배포 용이성을 결정하는 데 중요한 역할을 한다.

이때 목표 : 즉각적인 배포

좋은 아키텍처라면 시스템이 빌드된 후 즉각 배포할 수 있도록 지원해야 한다.

### 선택사항 열어놓기

---

좋은 아키텍처는 컴포넌트 구조와 관련된 이 관심사들 사이에서 균형을 맞추고, 각 관심사 모두를 만족시킨다.

현실에서는 이러한 균형을 잡기가 매우 어렵다. 대부분의 경우 우리는 모든 유스케이스를 알 수는 없으며, 운영하는 데 따르는 제약사항, 팀 구조, 배포 요구사항도 알지 못하기 때문이다.

몇몇 아키텍처 원칙은 구현하는 비용이 비교적 비싸지 않으며, 관심사들 사이에서 균형을 잡는데 도움이 된다.

좋은 아키텍처는 선택사항을 열어 둠으로써, 향후 시스템에 변경이 필요할 때 어떤 방향으로든 쉽게 변경할 수 있도록 한다.

### 유스케이스 결합 분리

---

우리는 시스템을 수평적 계층으로 분할하면서 동시에 해당 계층을 가로지르는, 얇은 수직적인 유스케이스로 시스템을 분할할 수 있다.

이와 같이 결합을 분리하려면 주문 추가 유스케이스의 UI와 주문 삭제 유스케이스의 UI를 분리해야 한다.

시스템에서 서로 다른 이유로 변경되는 요소들의 결합을 분리하면 기존 요소에 지장을 주지 않고도 새로운 유스케이스를 계속해서 추가할 수 있게 된다.

### 결합 분리 모드

---

유스케이스를 위해 수행하는 그 작업들은 운영에도 도움이 된다.

운영 측면에서 이점을 살리기 위해선 결합을 분리할 때 적절한 모드를 선택해야 한다.

많은 아키텍트가 마이크로서비스라고 하는 구분 기준은 모호한 면이 있다.

실제로 서비스에 기반한 아키텍처를 흔히들 서비스 지향 아키텍처라고 부른다.(Service-Oriented Architecture)

기억해야 할 점은 좋은 아키텍처는 선택권을 열어 둔다는 사실이다.

결합 분리 모드는 이러한 선택지 중 하나다.

### 개발 독립성 / 배포 독립성

---

컴포넌트가 완전히 분리되면 팀 사이의 간섭은 줄어든다.

유스케이스와 계층의 결합이 분리되면 배포 측면에서도 고도의 유연성이 생긴다.

### 중복

---

아키텍츠는 종종 함정에 빠지곤 한다. 전적으로 중복에 대한 공포로부터 발생하는 함정이다.

진짜 중복

- 한 인스턴스가 변경되면, 동일한 변경을 그 인스턴스의 모든 복사본에 반드시 적용해야 한다.

우발적인 중복

- 중복으로 보이는 두 코드 영역이 각자의 경로로 발전한다면, 즉 서로 다른 속도와 다른 이유로 변경된다면 두 코드는 진짜 중복이 아니다.

유스케이스를 수직으로 분리할 때 이러한 문제와 마주칠 테고, 이들 유스케이스를 통합하고 싶다는 유혹을 받게 될 것이다. 왜냐하면 이들 유스케이스가 서로 비슷한 화면 구조, 비슷한 알고리즘, 그리고 비슷한 데이터베이스 쿼리와 스키마를 가지기 때문이다.

### 결합 분리 모드 (다시)

---

- 소스 수준 분리 모드
- 소스 코드 모듈 사이의 의존성을 제어할 수 있다.
- 배포 수준 분리 모드
- jar, DLL, 공유 라이브러리와 같이 배포 가능한 단위들 사이의 의존성을 제어할 수 있다.
- 서비스 수준 분리 모드
- 의존하는 수준을 데이터 구조 단위까지 낮출 수 있고, 순전히 네트워크 패킷을 통해서만 통신하도록 만들 수 있다.

단순히 서비스 수준에서의 분리를 기본 정책으로 삼는 게 해결책이 될 수 있음.

이 방식은 비용이 많이 들고, 결합이 큰 단위에서 분리된다는 문제가 있다.

서비스 수준의 결합 분리가 지닌 또 다른 문제점은 개발 시간 측면뿐 아니라 시스템 자원 측면에서도 비용이 많이 든다는 사실이다.

컴포넌트가 서비스화될 가능성이 있다면 나는 컴포넌트 결합을 분리하되 서비스가 되기 직전에 멈추는 방식을 선호한다.

좋은 아키텍처는 시스템에 모노리틱 구조로 태어나서 단일 파일로 배포되더라도, 이후에는 독립적으로 배포 가능한 단위들의 집합으로 성장하고, 또 독립적인 서비스나 마이크로서비스 수준까지 성장할 수 있도록 만들어져야한다.

### 결론

---

시스템의 결합 분리 모드는 시간이 지나면서 바뀌기 쉬우며, 뛰어난 아키텍트라면 이러한 변경을 예측하여 큰 무리 없이 반영할 수 있도록 만들어야 한다는 점이다.
1 change: 1 addition & 0 deletions chapter17/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# 17장. 경계: 선 긋기

- [zhoon](./zhoon/)
- [yumin](./yumin/)
73 changes: 73 additions & 0 deletions chapter17/yumin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# 17. 경계 : 선 긋기

소프트웨어 아키텍처는 선을 긋는 기술이며, 나는 이러한 선을 경계라고 부른다.

경계는 소프트웨어 요소를 서로 분리하고, 경계 한편에 있는 요소가 반대편에 있는 요소를 알지 못하도록 만든다.

아키텍트의 목표는 필요한 시스템을 만들고 유지하는 데 드는 인적 자원을 최소화하는 것이라는 사실을 상기하자.

결합은 너무 일찍 내려진 결정에 따른 결합이다.

좋은 시스템 아키텍처란 이러한 결정이 부수적이며, 결정을 연기할 수 있는 아키텍처다. 좋은 시스템 아키텍처는 이런 결정에 의존하지 않는다.

### 어떻게 선을 그을까? 그리고 언제 그을까?

---

관련이 있는 것과 없는 것 사이에 선을 긋는다.

데이터베이스는 GUI와는 관련이 없으므로, 이 둘 사이에도 반드시 선이 있어야 한다.

데이터베이스는 업무 규칙과 관련이 없으므로 이 둘 사이에도 선이 있어야 한다.

데이터베이스는 업무 규칙이 간접적으로 사용할 수 있는 도구다.

선은 Database / Interface바로 아래에 그어진다.

DatabaseAccess에서 출발하는 두 화살표에 주목해야 한다.

이들 두 화살표는 DatabaseAccess클래스로부터 바깥쪽으로 향한다.

이 도표에서 DatabaseAccess가 존재한다는 사실을 알고 있는 클래스는 없다.

데이터베이스에 대한 결정은 연기할 수 있으며, 데이터베이스를 결정하기에 앞서 업무 규칙을 먼저 작성하고 테스트하는 데 집중할 수 있음을 의미한다.

### 입력과 출력은?

---

모델은 인터페이스를 전혀 필요로 하지 않는다.

관련성이 낮은 컴포넌트가 관련성이 높은 컴포넌트에 의존한다는 사실을 다시 한번 볼 수 있다.

### 플러그인 아키텍처

---

데이터베이스와 GUI에 대해 내린 두 가지 결정을 하나로 합쳐서 보면 컴포넌트 추가와 관련한 일종의 패턴이 만들어진다.

사실 소프트웨어 개발 기술의 역사는 플러그인을 손쉽게 생성하여, 확장 가능하며 유지보수가 쉬운 시스템 아키텍처를 확립할 수 있게 만드는 방법에 대한 이야기다.

### 플러그인에 대한 논의

---

경계는 변경의 축이 있는 지점에 그어진다.

경계의 한쪽에 위치한 컴포넌트는 경계 반대편의 컴포넌트와는 다른 속도로, 그리고 다른 이유로 변경된다.

GUI는 업무 규칙과는 다른 시점에 다른 속도로 변경되므로, 둘 사이에는 반드시 경계가 필요하다.

단일 책임 원칙은 어디에 경계를 그어야 할지를 알려준다.

### 결론

---

소프트웨어 아키텍처에서 경계선을 그리려면 먼저 시스템을 컴포넌트 단위로 분할해야 한다.

일부 컴포넌트는 핵심 업무 규칙에 해당한다.

이는 의존성 역전 원칙과 안정된 추상화 원칙을 응용한 것임을 눈치챌 수 있어야 한다.

의존성 화살표는 저수준 세부사항에서 고수준의 추상화를 향하도록 배치된다.
1 change: 1 addition & 0 deletions chapter18/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# 18장. 경계 해부학

- [zhoon](./zhoon/)
- [yumin](./yumin/)
Loading

0 comments on commit c76e615

Please sign in to comment.