-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
216 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
--- | ||
|
||
title: 유연한 시스템을 위해 알아야할 내용들 | ||
date: 2023-12-29 | ||
categories: [MicroService] | ||
tags: [MicroService] | ||
layout: post | ||
toc: true | ||
math: true | ||
mermaid: true | ||
|
||
--- | ||
|
||
# 참고 자료 | ||
|
||
[참고 자료](https://www.youtube.com/watch?v=3pybGLmTREw&list=PLwouWTPuIjUgr29uSrSkVo8PRmem6HRDE&index=12) | ||
|
||
--- | ||
|
||
# 유연한 시스템이 왜 필요한 것인가? | ||
|
||
사용자가 많아지게 되면 서비스를 제공할 수 있는 가용 서버를 늘려 트래픽에 대응할 수 있어야한다. | ||
|
||
하지만 어떤 조직이던간에 우리가 사용할 수 있는 자원은 필연적으로 한정되어있다. 따라서 자원을 효율적으로 사용하여 가용할 수 있는 서버를 늘리는 것이 우아하게 사용자의 요구를 처리할 수 있는 방법이다. | ||
|
||
그래서 등장한 개념이 `마이크로서비스 아키텍처`이다. 기존 모놀리틱 서버는 배정받은 역할이 너무나도 크기 때문에 기동시키기에는 사용하는 자원에 부담이 크다. | ||
|
||
또한 확장을 고려할 필요없는 내용을 포함해서도 확장되기 때문에 자원을 효율적으로 사용하지 못하게 된다. | ||
|
||
이 내용을 그림으로 표현할 수 있는데 아래와 같다. | ||
|
||
![img.png](https://github.com/K-Diger/K-Diger.github.io/blob/main/images/flexible-architecture/mono.png?raw=true) | ||
|
||
현재 어떤 이벤트로 인해서 결제에 대한 API 요청이 폭증했다고 가정해보자 | ||
|
||
모놀리틱의 구성이라면 한 대의 서버로 감당이 안되기 때문에 기존 서버와 똑같은 내용을 가진 서버를 가져와 기동시켜야한다. 저 많은 API를 처리하기 위한 모놀리틱 서버의 스펙 비용은 약 1000만원이라고 해보자 | ||
|
||
마이크로서비스의 개념을 적용하면 어떻게 될까? 결제에 대한 API 요청이 폭증했으니 결제를 담당하는 마이크로서버만 확장하면 되지 않을까? | ||
|
||
그림으로 표현하면 아래와 같다. | ||
|
||
![img_1.png](https://github.com/K-Diger/K-Diger.github.io/blob/main/images/flexible-architecture/msa1.png?raw=true) | ||
|
||
마이크로 서버의 스펙 비용은 각각 약 250만원이라고 하자 | ||
|
||
마이크로서비스로 시스템을 초기부터 운영하려면 초기 비용은 많이 들 수 있다. 또한 이를 유지보수하는 인적/기술적인 비용도 만만치않긴하다. | ||
|
||
하지만 지금 상황에서 결제 API에 대한 서버를 늘릴 때는 2대를 추가로 늘려도 모놀리틱의 절반 가격인 500만원으로 요청을 더 원활하게 받아낼 수 있게 된다. | ||
|
||
비용이 구체적이고 현실적인 예시는 아닐 수 있지만 이렇게 자원을 늘리고 줄이는 아키텍처가 한정된 자원을 효율적으로 쓰이는 것에 장점이 있다고 판단하여 많은 서비스들이 적용하고 있다. | ||
|
||
--- | ||
|
||
# 마이크로서비스 핵심 개념 6가지 | ||
|
||
## 1. 독립적으로 배포할 수 있어야한다. | ||
|
||
약 6가지 핵심 개념 중 가장 중요한 개념이다. 이 외의 개념이 이 개념을 충족시키기위해서 존재한다고 볼 수 있을 정도이다. | ||
|
||
**다른 마이크로서비스를 배포하지 않고 마이크로서비스를 배포/변경할 수 있어야한다.** | ||
|
||
이 개념을 충족시키기 위해 마이크로서비스간 결합도/의존도를 낮춰야한다. 서비스간에 잘 정의되고 안정된 명세가 필요하다. | ||
|
||
--- | ||
|
||
## 2. 비즈니스 도메인을 중심으로 모델링 해야한다. | ||
|
||
**도메인을 기준으로하여 서비스 경계를 정의해야한다.** | ||
|
||
- 즉, 한 마이크로서비스가 기능에 필요한 전체를 구현해야한다는 것이다. | ||
- 이렇게 하면 새 기능 출시가 쉬워지고 다른 방식으로 마이크로서비스를 재조합하기 유용해진다. | ||
|
||
한 기능의 구현이 여러 마이크로서비스에 걸쳐있으면 기능 출시 시 비용이 올라간다. | ||
|
||
- 예를 들어 글쓰기 기능을 새로 만든다고 했을 때, 약 10개의 마이크로서비스와 상호작용을 해야한다고 가정해보자 | ||
- 그렇다면 10개의 마이크로서비스와 통신하는 내용을 작성하게 된다. | ||
- 각 마이크로서비스를 담당하는 팀과의 커뮤니케이션, 서비스 통신 순서 관리, 예외처리에 대한 내용 등 구현 비용이 크게 늘어나게 된다. | ||
|
||
따라서 여러 서비스에 걸친 변경을 최소화해야한다. | ||
|
||
--- | ||
|
||
## 3. 자신의 상태를 가져야한다. | ||
|
||
**마이크로서비스는 DB 혹은 데이터를 공유하지 않아야한다.** | ||
|
||
- 다른 마이크로서비스가 갖고 있는 데이터에 접근해야 하면 DB에 직접 접근하지 않고 API등을 통해서 접근해야한다. | ||
- 데이터를 공유하지 않는다 --> 구현을 숨긴다 --> 결합도를 낮춘다. | ||
|
||
--- | ||
|
||
## 4. 마이크로서비스를 다룰 수 있는 역량을 고려해야한다. | ||
|
||
마이크로서비스를 설계할 때 주의할 점으로 아래와 같은 말이 있다고한다. | ||
|
||
```text | ||
내 머리가 이해할 수 있을 정도의 크기를 가져야한다. (James Lewis) | ||
``` | ||
|
||
하지만 위의 주의점은 상황에 따라 다를 수 밖에 없다. | ||
|
||
--> 오랫동안 해당 서비스에 몸을 담은 사람 vs 신규 입시자 | ||
|
||
--> 스타트업 vs 대기업 | ||
|
||
이렇게 마이크로서비스를 다루는 대상마다 적용되는 범위가 달라질 수 밖에 없다. | ||
|
||
따라서 **마이크로서비스의 크기라는 용어보다는** 다음 두 가지의 구체화에 대한 내용에 집중하는 것이 좋다. | ||
|
||
- 얼마나 많은 마이크로서비스를 다룰 수 있는가? | ||
- 마이크로서비스가 증가하면 복잡도가 증가하며 새 기술을 습득해야하는 가능성이 크다. | ||
|
||
- 마이크로서비스 경계를 정의해야한다. | ||
- 실타래가 복잡하게 얽히지 않도록 해야한다. | ||
|
||
--- | ||
|
||
## 5. 마이크로서비스는 유연해야한다. | ||
|
||
지금 더 비용을 들여 미래에 유연해질 수 있는 옵션을 구매해야한다. (여기서 옵션이란 하드웨어 스펙이 아닌 기술과 확장 가능성인 인적/기술적 비용을 가리킨다.) | ||
|
||
아직 벌어지지 않은 미래에 너무 많은 옵션을 구매하는 것은 오버 엔지니어링이 될 가능성이 크다. | ||
|
||
**따라서 점진적으로 마이크로서비스로 전환해야하며 이 과정에서도 과하지 않게 설계해야한다.** | ||
|
||
--- | ||
|
||
## 6. 아키텍처와 조직을 맞춰야한다. | ||
|
||
**조직 간 이관이나 사일로(부서 이기주의 현상)를 줄여야한다.** | ||
|
||
아키텍처의 구조는 조직의 구조를 따르게 되어있다. (조직도 생태계를 반영할 수 밖에 없음) | ||
|
||
기존 `프론트 팀, 백엔드 팀, DB 팀`과 같이 조직을 큰 단위로 구성했다면 `고객 팀, 구매 팀`등으로 세분화하여 여러 포지션을 한 조직에 모아 줄이는 것이 적절하다. | ||
|
||
이렇게하면 각 담당한 도메인에 대한 응집도를 높일 수 있어 비즈니스 도메인에 집중할 수 있는 장점이 있다. | ||
|
||
--- | ||
|
||
# 비즈니스 도메인을 중심으로 모델링 하기 | ||
|
||
[참고자료](https://www.youtube.com/watch?v=xf0kXMTFJm8&list=PLwouWTPuIjUgr29uSrSkVo8PRmem6HRDE&index=5) | ||
|
||
## 왜 도메인을 중심으로 모델링을 해야하는가? | ||
|
||
위에서 살펴본 마이크로서비스 핵심 개념 2번을 다시 떠올려보자 | ||
|
||
```text | ||
**도메인을 기준으로하여 서비스 경계를 정의해야한다.** | ||
- 즉, 한 마이크로서비스가 기능에 필요한 전체를 구현해야한다는 것이다. | ||
- 이렇게 하면 새 기능 출시가 쉬워지고 다른 방식으로 마이크로서비스를 재조합하기 유용해진다. | ||
한 기능의 구현이 여러 마이크로서비스에 걸쳐있으면 기능 출시 시 비용이 올라간다. | ||
- 예를 들어 글쓰기 기능을 새로 만든다고 했을 때, 약 10개의 마이크로서비스와 상호작용을 해야한다고 가정해보자 | ||
- 그렇다면 10개의 마이크로서비스와 통신하는 내용을 작성하게 된다. | ||
- 각 마이크로서비스를 담당하는 팀과의 커뮤니케이션, 서비스 통신 순서 관리, 예외처리에 대한 내용 등 구현 비용이 크게 늘어나게 된다. | ||
따라서 여러 서비스에 걸친 변경을 최소화해야한다. | ||
``` | ||
|
||
도메인을 기준으로 설계를 한다면 유연한 시스템을 구축할 수 있다는 점이 있다고 했다. | ||
|
||
그렇다면 도메인을 중심으로 설계하는 방법을 알아보는게 좋겠다. | ||
|
||
## 1. CQRS 패턴 | ||
|
||
Command(Create, Update, Delete)와 Query(Read)를 분리하는 것이 도메인 중심 설계의 기초가 될 수도 있다. | ||
|
||
- Command는 시스템의 데이터를 변경하는 행위를 가리킨다. | ||
- 주문 취소, 배송 완료 등 | ||
- Query는 시스템의 데이터를 조회하는 행위를 가리킨다. | ||
- 주문 목록 조회 등 | ||
|
||
CQRS는 `Responsibility Segregation (책임 분리)`라는 중요 개념에 의해 탄생했다. | ||
|
||
- **책임은 구성 요소의 역할을 의미한다.** | ||
- 구성 요소(모델)는 아래 요소들을 가리킨다. | ||
- 클래스, 함수, 모듈, 패키지, 웹서버, DB 등 | ||
|
||
이 요소들을 적절하게 분리하는 것이 권장된다. | ||
|
||
- Command 역할을 수행하는 구성요소 | ||
- Query 역할을 수행하는 구성 요소를 나누는 것이 CQRS이다. | ||
|
||
코드만 나누는 것이 아니라 구현 방법, DB, 프로세스를 나누기도 한다. 중요한 점은 변경하는 구성요소, 조회하는 구성요소를 나누는 것이다. | ||
|
||
![img.png](https://github.com/K-Diger/K-Diger.github.io/blob/main/images/flexible-architecture/CQRS1.png?raw=true) | ||
|
||
위와 같이 표현할 수 있겠다. 그런데 이 방식이 굳이 필요한 이유가 무엇일까? | ||
|
||
`Command`에서 사용되는 `Member`와 `Query`에 사용되는 `MemberData`는 무슨 차이가 있을까? | ||
|
||
### 단일 책임 원칙 위반 | ||
|
||
![img.png](https://github.com/K-Diger/K-Diger.github.io/blob/main/images/flexible-architecture/CQRS2.png?raw=true) | ||
|
||
위 그림처럼 `마지막 로그인에 대한 기록`, `최근 주문 일자에 대한 기록`, `회원 이름 변경 기능 추가`가 추가되었다고 했을 때 유저 테이블에서 이 모든 내용을 담도록 했다. | ||
|
||
적절하게 속성을 부여한 것처럼 볼 수도 있지만 유저에 부여된 역할/책임이 모호해졌다. 즉, 유저 객체가 하는 일이 너무 많다는 것이다. | ||
|
||
이렇게 하나의 객체에 너무 많은 역할과 책임을 부여하면 유지보수성이 상당히 떨어지게 된다. | ||
|
||
기능에 따라 사용되는 필드가 달라진다. | ||
|
||
이름 변경 기능은 Member 테이블에 해당하는 내용만 참조하면 되지만 | ||
|
||
주문 목록 조회 기능을 구현하기 위해서 Member, LoginHistory, Order 총 세 가지의 테이블을 참조해야한다. | ||
|
||
|
||
|
||
--- | ||
|
||
## 2. 도메인 중심 설계 (DDD) |
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.