Skip to content

Commit

Permalink
[wynter] ch09, 10 완료
Browse files Browse the repository at this point in the history
  • Loading branch information
MOBUMIN committed Oct 28, 2024
1 parent 6dce750 commit aa128cf
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 0 deletions.
90 changes: 90 additions & 0 deletions ch09_기초/ch09_wynter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# CHAPTER 9 기초

아키텍처 스타일은 종종 아키텍처 패턴이라고도 부르며, 다양한 아키텍처 특성을 다루는 컴포넌트의 명명된 관계를 기술합니다.

## 9.1 기초 패턴

레이어는 기능에 따라 서로 다른 관심사를 분리하는 개념으로, 소프트웨어 자체만큼이나 오래됐습니다.

### 9.1.1 진흙잡탕

뭐 하나 뚜렷한 아키텍처 구조가 전무한 상태를 진흙잡탕이라고 표현합니다.
진흙잡탕은 요즘에는 보통 실제 내부 구조라 할 만한 것은 하나도 없는, 데이터베이스를 직접 호출하는 이벤트 핸들러를 가진 단순한 스크립팅 애플리케이션을 가리킵니다.

### 9.1.2 유니터리 아키텍처

이제 유니터리(단일, 통일) 시스템은 임베디드 시스템과 그 밖에 매우 제약이 많은 극소수 환경을 제외하면 거의 쓰이지 않습니다. 성능, 확장 등의 운영 아키텍처 특성을 유지하려면 관심사를 분리할 필요가 있습니다.

### 9.1.3 클라이언트/서버

프런트엔드와 백엔드로 기술적으로 기능을 분리한 two-tier 또는 클라이언트/서버 아키텍처는 대표적인 기본 아키텍처 스타일입니다.

**데스크톱 + 데이터베이스 서버**

프레젠테이션 로직은 데스크톱에 두고 계산량이 많은 액션은 사양이 탄탄한 데이터베이스 서버에서 실행했습니다.

**브라우저 + 웹 서버**

**3티어**

고성능 데이터베이스 서버를 사용하는 데이터베이스 티어, 애플리케이션 서버가 관리하는 애플리케이션 티어, 프런트엔드 티어, 이렇게 세 티어가 완성됐습니다.

### 9.2 모놀리식 대 분산 아키텍처

아키텍처 스타일은 크게 전체 코드를 단일 단위로 배포하는 모놀리식과 원격 액세스 프로토콜을 통해 여러 단위로 배포하는 분산형, 두 종류입니다.

**모놀리식**
* 레이어드 아키텍처(10장)
* 파이프라인 아키텍처(11장)
* 마이크로커널 아키텍처(12장)

**분산형**
* 서비스 기반 아키텍처(13장)
* 이벤트 기반 아키텍처(14장)
* 공간 기반 아키텍처(15장)
* 서비스 지향 아키텍처(16장)
* 마이크로서비스 아키텍처(17장)

분산 아키텍처 스타일은 모놀리식 아키텍처 스타일에 비해 성능, 확장성, 가용성 측면에서 훨씬 강력하지만, 이런 파워에도 결코 무시할 수 없는 트레이드오프가 수반됩니다.

### 9.2.1 오류#1 네트워크는 믿을 수 있다

개발자, 아키텍트 모두 네트워크는 믿을 수 있다고 전제하지만 실제로는 전혀 그렇지 않습니다. 그래서 타임아웃 같은 장치를 마련하거나, 서비스 사이에 회로 차단기(서킷 브레이커)를 두는 것입니다. 시스템이 네트워크에 더 의존할수록 시스템의 신뢰도는 잠재적으로 떨어질 가능성이 있습니다.

### 9.2.2 오류#2 레이턴시는 0이다

메서드나 함수를 이용해 다른 컴포넌트를 로컬 호출하면 그 소요 시간(t_local)은 대개 나노 초 내지 밀리초 단위로 측정되지만, 동일한 호출을 원격 액세스 프로토콜을 통해서 수행하면 서비스 액세스 시간(t_remote)이 밀리초 단위로 측정됩니다. 여러분은 운영 환경에서 평균 왕복 레이턴시가 얼마나 되는지 알고 있습니까? 레이턴시 값은 대부분 네트워크 관리자에게 물어보면 쉽게 알아낼 수 있습니다.

### 9.2.3 오류#3 대역폭은 무한하다

스탬프 커플링은 다음과 같은 방법으로 해결할 수 있습니다.

* 프라이빗 REST API 엔드포인트를 둔다.
* 계약에 필드 셀렉터를 사용한다.
* GraphQL로 계약을 분리한다.
* 컨슈머 주도 계약과 값주도 계약을 병용한다.
* 내부 메시징 엔드포인트를 사용한다.ㄱ

### 9.2.4 오류#4 네트워크는 안전하다

### 9.2.5 오류#5 토폴로지는 절대 안 바뀐다

네트워크를 구성하는 모든 라우터, 허브, 스위치, 방화벽, 네트워크 ,어플라이언스 등 전체 네트워크 토폴로지가 불변일 거란 가정은 섣부른 오해입니다.

(wynter)SRE에서 한 업데이트로 뭔가 오류가 있었던 적이 예전에 왕왕 있었는데, 그런게 떠오르네요..

### 9.2.6 오류#6 관리자는 한 사람뿐이다

### 9.2.7 오류#7 운송비는 0이다

### 9.2.8 오류#8 네트워크는 균일하다

### 9.2.9 다른 분산 아키텍처 고려 사항

**분산 로깅**

(wynter) 오 완전 인정.

**분산 트랜잭션**


85 changes: 85 additions & 0 deletions ch10_레이어드_아키텍처_스타일/ch10_wynter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# CHAPTER10 레이어드 아키텍처 스타일

레이어드 아키텍처는 단순하고 대중적이면서 비용도 적게 들어 모든 애플리케이션의 사실상 표준 아키텍처입니다. 그러나 레이어드 아키텍처 스타일은 묵시적 아키텍처 안티패턴, 우발적 아키텍처 안티패턴 등의 몇몇 아키텍처 안티패턴의 범주에 속합니다.

> 묵시적 아키텍처 안티패턴: 개발자들이 머릿속에 암묵적으로 이해하고 있는 아키텍처가 실제 구현에서 드러나지 않아, 새로운 팀원이 이해하거나 유지보수하기가 어려워지는 문제입니다.
> 우발적 아키텍처 안티패턴: 의도치 않게 시스템이 특정한 구조를 형성하게 된 경우
(wynter) https://github.com/goorm-dev/goorm-slack-workflows/blob/main/cs-manager-app/utils/notion.ts 이거 안티패턴이 된 거 아닌가 싶긴 하네요

## 10.1 토폴로지

레이어드 아키텍처에서 내부 컴포넌트는 논리적으로 수평한 레이어들로 구성되며, 각 레이어는 애플리케이션에서 주어진 역할을 수행합니다. 일반적으로 프레젠테이션(라우터), 비즈니스(컨트롤러), 퍼시스턴스(서비스), 데이터베이스(레파지토리)의 4개 표준 레이어로 구성합니다.

10.2 그림
* (프레젠테이션 레이어 / 비즈니스 레이어 / 퍼시스턴스 레이어) => (데이터베이스 레이어)
* (프레젠테이션 레이어) => (비즈니스 레이어 / 퍼시스턴스 레이어) => (데이터베이스 레이어)
* (다 함께)

레이어드 아키텍처 스타일의 각 레이어는 아키텍처 내부에서 특정한 역할과 임무를 수행합니다. 프레젠테이션 레이어는 모든 유저 인터페이스와 브라우저 통신 로직을, 비즈니스 레이어는 요청을 받아 알맞은 비즈니스 규칙을 실행하는 일을 합니다. 각 레이어는 주어진 비즈니스 요청을 충족하는 데 필요한 업무 위주로 추상화되어 있습니다. 예를 들어 프레젠테이션 레이어는 데이터를 조회하는 방법 은 알 필요 없고, 받아온 정보를 잘 보여주기만 하면 됩니다.

이러한 관심사의 분리 개념덕분에 레이어드 아키텍처 스타일은 아키텍처 내부의 역할 및 책임 모델을 효과적으로 구성할 수 있습니다. 개발자 본인의 기술 역량을 도메인의 기술적인 부분에 집중시킬 수 있지만, 그런 장점을 대가로 전체적인 민첩성이 떨어지는 트레이드오프가 있습니다.

레이어드 아키텍처는 (도메인 분할 아키텍처의 반대인) 기술 분할된 아키텍처입니다. 즉, 컴포넌트를 (고객 같은) 도메인 단위로 묶는 게 아니라, 아키텍처의 (프레젠테이션 또는 비즈니스 같은) 기술 역할에 따라 묶기 때문에 비즈니스 도메인이 각각 모든 아키텍처 레이어에 분산됩니다.

## 10.2 레이어 격리

레이어드 아키텍처의 각 레이어는 폐쇄 또는 개방 상태입니다.

<img src="wynter/10-3.png" />

단순 조회 요청이라면 불필요한 레이어를 건너뛰고 프레젠테이션 레이어가 데이터베이스를 직접 액세스하는 편이 더 빠르고 간편할 것입니다(2000년대 초반에는 이를 추월 차선 리더 패턴이라고 했습니다). 이렇게 요청이 다른 레이어를 건너뛸 수 있으려면 사이 레이어는 개방 되어 있어야 하는데, 폐쇄와 개방 중 어느 쪽이 나을까요? 이 질문에 답하려면 레이어 격리라는 핵심 개념을 알고 있어야 합니다.

(wynter, 뒷장 읽기전 답) 당연히 폐쇄가 낫다. 간단한 로직 처리를 위해 개방시켜버리면 결국 의도치 않은 레이어 간의 의존성이 생겨버려서 망해버리고 말 것임

레이어 격리는 어느 아키텍처 레이어에서 변경이 일어나도 다른 레이어에 있는 컴포넌트에 아무런 영향을 끼치지 않기에 레이어 간 계약은 불변임을 의미합니다. 프레젠테이션 레이어가 퍼시스턴스 레이어에 직접 액세스할 수 있으면 퍼시스턴스 레이어에서 변경이 발생할 경우, 비즈니스 레이어, 프레젠테이션 레이어 둘 다 영향을 받게 되고, 결국 컴포넌트 간의 레이어 상호 의존도가 높아져서 단단히 커플링된 애플리케이션이 될 것입니다. 이런 아키텍처는 매우 취약하고 변경하기 힘들고 비용도 많이 듭니다.

## 10.3 레이어 추가

어떤 레이어는 개방하는 것이 더 합리적인 경우도 있습니다.

<img src="wynter/10-4.png" />

* 공통 비즈니스 로직을 비즈니스 레이어에 구현했으나, 프레젠테이션 레이어는 이 공유 로직을 사용하지 않는다는 결정을 했음
* 그러나 비즈니스 레이어에 구현되면 결국 프레젠테이션 레이어가 접근할 수 있음.

<img src="wynter/10-5.png" />

* 이렇게 하면 프레젠테이션 레이어는 액세스하지 못함
* 비즈니스 레이어는 그냥 퍼시스턴스로 갈 수도 있고, 공통 로직을 사용하고 갈 수도 있음!

## 10.4 기타 고려 사항

아직 아키텍처 스타일을 완전히 결정하지 못했다면 대부분의 애플리케이션에서 레이어드 아키텍처는 좋은 출발점이 될 것입니다.

레이어드 아키텍처에서는 아키텍처 싱크홀 안티패턴을 조심해야 합니다. 요청이 한 레이어에서 다른 레이어로 이동할 때 각 레이어가 아무 비즈니스 로직도 처리하지 않고 그냥 통과시키는 안티패턴을 말합니다. 이런 흐름은 불필요한 객체 초기화 및 처리를 빈번하게 유발하고 쓸데없이 메모리를 소비하여 성능에도 부정적인 영향을 끼칩니다.

아키텍처 싱크홀 안티패턴에 해당하는 시나리오가 전무한 레이어드 아키텍처는 아마 하나도 없을 것입니다. 80 대 20(파레토 법칙)을 적용해서 전체 요청의 20%가 싱크홀인 정도면 그런대로 괜찮은 수준입니다. 아니라면 이 문제 도메인에 레이어드 아키텍처는 적합한 아키텍처 스타일이 아니라는 증거입니다.

## 10.5 왜 이 아키텍처 스타일을 사용하는가

레이어드는 모두에게 익숙하고 복잡하지 않고 비용도 가장 저렴한 아키텍처 스타일이므로 꽤괜한 선택입니다.

레이어드 아키텍처 기반의 애플리케이션은 규모가 커질수록 유지 보수성, 민첩성, 시험성, 배포성 같은 아키텍처 특성이 점점 나빠집니다. 대규모 앱이나 시스템은 다른 더 모듈러한 아키텍처 스타일이 더 잘 맞습니다.

점점 나빠지는 이유
* 레이어 간 의존성이 복잡해져 전체 시스템 이해가 어려워집니다.
* 특정 기능 변경 시 여러 레이어에 걸쳐 수정이 필요할 수 있습니다.
* 특정 레이어의 병목 현상이 전체 성능에 영향을 줍니다.
* 전체 시스템을 한 번에 배포해야 하는 경우가 많습니다.
* 부분적 업데이트나 롤백이 어려울 수 있습니다.
* 특정 레이어의 기술 변경이 다른 레이어에 영향을 줄 수 있습니다.

## 10.6 아키텍처 특성 등급

<img src="wynter/10-6.png" />

배포성과 시험성 별점이 매우 낮습니다. 클래스 파일 3줄 정도의 간단한 변경을 해도 전체적으로 다시 배포해야 하고, 데이터베이스 변경, 구성 변경도 수반될 수 있으며, 다른 코드도 함께 고쳐야할 수도 있습니다. 이 3줄짜리 단순한 변경은 다른 수십 가지 변경들 속에 파묻혀 배포 리스크가 한층 더 가중됩니다. 같은 이유로 시험성도 낮습니다. 코드 3줄만 고치면 되는데 굳이 전체 회귀 테스트 스위트를 작성해서 실행하지 않거나, 수십 가지 다른 변경을 모놀리식 애플리케이션에 동시 반영하는 경우 문제가 커집니다. 그래도 2개인 이유는 컴포넌트 또는 전체 레이어를 모킹 또는 스터빙할 수 있어 전체 테스트 공수는 덜 들기 때문입니다.

배포와 아키텍처 측면에서 모듈성이 부족하므로 탄력성과 확장성은 낮습니다. 상대적으로 확장이 가능한 것들도 더러 있지만, 그렇게 확장하려면 대부분 멀티스레딩, 내부 메시징을 비롯해 이 아키텍처와는 안 어울리는 갖가지 병렬 처리 프랙티스와 기법이 동원됩니다. 이것도 단일 퀀텀을 기반으로 한 특정 지점까지만 확장이 가능합니다.

이 아키텍처는 병렬 처리가 거의 안 되고, 폐쇄적인 레이어 구조와 싱크홀 아키텍처 안티패턴 때문에 고성능 시스템에 어울리지 않기 때문에 성능도 낮습니다.

모놀리식 배포 탓에, 부족한 모듈성 때문에 내고장성도 별로입니다. 어느 한 작은 파트에 OOM이 발생하면 애플리케이션 전체적으로 영향을 받고 충돌이 발생합니다.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit aa128cf

Please sign in to comment.