
p.26
컨테이너 오케스트레이션은 컨테이너의 라이프사이클을 관리하는 일종의 체계라고 할 수 있습니다.
클라우드 네이티브 애플리케이션을 운영할 때 각각의 컨테이너를 수동으로 직접 관리하는 것은 사실 불가능하다고 할 수 있습니다. 그렇기 때문에 컨테이너 오케스트레이션이 클라우드 아키텍처에서 필수불가결한 요소인 것이죠.
핵심 기능
- 자동화된 프로비저닝
- 고가용성
- 크기 조절 (스케일링)
- 자원 관리
- 부하 분산
- 서비스 검색
- 어피니티
- 상태 모니터링
- 롤링 업그레이드
p.58
단일 수신자 패턴
단일 수신자 패턴에서는 하나의 마이크로서비스가 메시징 인프라스트럭처를 통해 단 하나의 마이크로서비스 혹은 시스템에만 메시지를 전달합니다.
예를 들어 온라인 쇼핑몰 시스템에서 주문을 처리하는 경우, 비동기 메시지를 메시지 브로커 큐에 집어넣으면 주문 처리 서비스가 이 메시지를 전달 받아서 필요한 작업을 처리할 것입니다. 단일 생산자와 단일 소비자 사이에 정보 교환이 이루어지는 것이기 때문에 이런 패턴을 점대점 비동기 메시징이라고도 부릅니다.

p.61
다중 수신자 패턴

일반적으로 여러 소비자에게 동일한 메시지를 전달할 때 최소 한 번 전달과 같은 요구사항을 만족시키고자 할 경우 다중 소비자 패턴을 사용하기보다는 단일 수신자 패턴처럼 각 소비자별로 큐를 따로 두고 이 큐에 메시지를 전달하는 방식을 더 많이 사용합니다.
다중 수신자 패턴은 전통적인 Pub/Sub 구조를 의미한다.
Redis의 Pub/Sub 구조를 생각하면 된다. 메시지를 영구 저장하지 않고, 구독 서버가 다운되어 있으면 메시지를 수신 받지 못하는 경우도 발생하다. 오로지 실시간성과 속도에 집중
대안으로 Kafka를 사용하면 다중 수신자 패턴 구현 시 메시지의 유실 없이 컨슈머 서버가 처리 할 수 있다.
p.78
웹 소켓 프로토콜로도 동기 요청-응답 패턴을 구현할 수 있습니다. 웹소켓은 간단히 말해 웹에서 TCP를 구현한 것이라 할 수 있습니다. 웹 소켓은 양방향 통신을 지원하며 한 번 연결하면 비동기 메시징이 가능하기 때문에 단순한 요청-응답 프로토콜에 비해 훨씬 강력합니다.

p.89
마이크로서비스와 시스템이 늘어날수록 클라우드 네이티브 애플리케이션의 복잡도도 증가합니다.
따라서 이 패턴을 적용하기 전에 서비스를 어느 정도 단위로 세분화해서 나누고 있는지를 잘 파악해야 합니다. 만약 마이크로서비스간의 연결과 상호작용이 너무 많다면, 서비스를 너무 잘게 나눈 것입니다. 이런 경우에는 마이크로서비스 설계 단계로 돌아가서 서비스를 개별 기능이나 도구 단위가 아닌 비즈니스 기능 단위로 다시 정의하는 것이 좋습니다. ^a0f4d0
도메인 경계를 나누는 것은 정말로 어려운 문제이다.
DDD의 Event Storming, Bounded Context를 진행한다.
Event Storming을 통해 도메인 전체를 이해하고 비즈니스 흐름을 파악한다. 이후에 Event Storming 결과를 바탕으로 경계를 설정한다.
초기에는 큰 컨텍스트로 시작한다.
운영 지표를 통해서 병목/변경 결함이 보이면 점진적으로 분할한다.
p.98
타임아웃은 서비스가 다른 서비스를 호출하고 응답을 기다리는 경우 사용합니다. 다른 서비스 또는 시스템 호출 시 타임아웃을 사용하지 않으면, 어떤 경우에는 해당 서비스를 영원히 기다리는 상황이 발생할 수도 있습니다. 이러면 응답성이 떨어집니다.
타임 아웃을 사용하면 동작하지 않거나 이상 동작하는 다른 서비스들을 격리할 수 있으며 이들 서비스가 전체 서비스에 문제를 일으키는 것을 방지할 수 있습니다. ^a4460a
분산 시스템에서 필수적인 방어 메커니즘
타임아웃이 없으면 스레드 고갈로 전체 시스템이 다운될 수 있다.
마이크로서비스 환경에서는 타임아웃을 적용해서 한 서비스의 장애가 다른 서비스로 번지는 것을 막는게 가장 중요하다.
p.99
네트워크를 통한 서비스 간 통신은 간헐적인 오류가 생기기도 합니다. 재시도 로직의 핵심은 네트워크의 오류 등과 같은 상황에서 동일한 서비스를 한 번 이상 호출하여 원하는 응답을 얻는 것입니다.
최근에 Spring Retry 기능이 추가되었다.
멱등이 보장하는 연동 구간일 때 사용 (GET, PUT, DELETE는 보통 안전)
p.107
사이드카 패턴은 클라우드 네이티브 애플리케이션 개발에서 인기 있는 패턴 중 하나입니다. 주 컨테이너의 기능을 몰라보게 향상시키고 확장할 수 있기 때문이죠. 하지만 이 패턴을 적용할 때는 그에 걸맞는 대가도 따라온다는 사실을 명심하세요.
- 사이트카와 함께 사용한다는 것은 그만큼 관리하고 실행해야 할 인스턴스 수도 증가함을 의미
- 비즈니스 로직과 관련된 기능을 절대 사이드카에 구현하지 마세요. 사이드카의 핵심을 벗어나는 일입니다. 비즈니스 로직을 여러 계층에 노출시킬 뿐 아니라 소유권 등에서 끔찍한 문제가 발생할 수도 있습니다.
사이드카 패턴은 언제 사용하면 좋을까? 로깅, 모니터링, 메트릭 수집 할 때 사용하면 좋다. 메인 컨테이너는 비즈니스 로직에만 집중하는 서버가 되어야 하고, 사이드카 컨테이너는 인프라에만 집중해야 한다.
p.122
조합 로직에서 호출하는 서비스를 되도록 적게 유지하는 것이 좋습니다. 조합 로직에서 네다섯 개의 서비스를 호출해야 한다는 것은 서비스를 잘못 나누었거나, 또는 서비스들을 너무 작게 나누었다는 뜻일수도 있습니다.
서비스 코레오그래피 패턴은 여러 마이크로서비스와 시스템 간 상호작용을 통해 새로운 비즈니스 기능을 만들 때 메시지 브로커나 이벤트 허브를 통한 비동기 이벤트 주도 통신 방법을 사용합니다.
서비스를 너무 작게 나누었다는 신호
- 조합 로직에서 4~5개 이상 호출해야 응답을 받을 수 있음 (경계가 너무 잘게 쪼개짐)
- 어떤 기능을 추가할 때 항상 여러 서비스 코드를 동시에 수정해야 함 (독립 배포성이 깨짐)
- 데이터 조합만 하는 API가 많아짐 (도메인 로직은 없고, 조합만 남발되는 경우)
- 주문의 상태 변경이 발생했을 때 결제, 배송, 고객 서비스까지 호출해서 데이터를 변경해야 하는 경우
p.125
서비스 간 전달이 보장되고 신뢰할 수 있는 메시지 브로커가 필요한 경우 AMQP 기반 브로커를 써야 하고 메시지 전송에 특별한 제약은 없으나 높은 확장성을 가지는 발행자-구독자 패턴 적용이 필요한 경우 카프카를 사용하면 됩니다.
사가 패턴
한 서비스에서 동작이 실패하면, 다른 서비스 상호작용을 전부 취소한다던가 말이죠. 이런 경우 트랜잭션의 경계가 여러 마이크로서비스와 서비스에 걸쳐 있기 때문에, 분산 트랜잭션이라고 부르기도 합니다.
카프카도 메시지 전달 보장을 신뢰할 수 있다.
다만 RabbitMQ는 메시지 안정성을 Kafka는 높은 확장성을 더 강조하는 구조이다.
Kafka는 토픽을 파티션 단위로 분산 저장 할 수 있다. 즉, 파티션을 브로커 노드 여러 개에 분산 저장할 수 있기 때문에 데이터 분산측면에서 확장성이 좋다고 이야기 한다.
p.128
클라우드 네이티브 애플리케이션의 사가 패턴은 여러 마이크로서비스에 분산해서 구현해야 하며 마이크로서비스들이 추가되거나 제거될 수 있기 때문에 각 분산 트랜잭션의 보관 기간이 짧습니다.
p.129
사가 패턴을 적용하면 각 마이크로서비스에 로컬 트랜잭션과 더불어 연관 보상 트랜잭션도 함께 구현해야 합니다.
항공권 취소, 호텔 예약 취소, 렌트카 취소와 같은 작업이 이에 해당한다.
사가 패턴을 적용해서 분산 트랜잭션을 통해 서비스를 조합하고 비즈니스 기능을 만드는 것은 꼭 필요한 경우에만 사용해야 합니다. 대부분의 경우에는 여러 서비스에서 실행하는 분산 트랜잭션을 필요로 하지 않습니다.
분산 트랜잭션이 요구되는 도메인을 한번 찾아보자.
항공권 + 호텔 + 렌트카 한 번에 예약
항공권 예약은 성공했는데 호텔 예약이 실패하면 전체 예약을 취소해야 한다.
결제 프로세스
주문 생성 -> 결제 승인 -> 재고 차감 -> 배송 요청
결제는 성공했는데 재고 차감이 실패했다면 결제 취소(환불) 보상 트랜잭션 필요
특히 결제 환불은 강한 일관성을 요구하기 때문에 분산 트랜잭션 필요성이 크다.
금융 서비스
A 계좌에서 돈을 출금하고 B 계좌에 입금
출금만 되고 입금이 안 되면 출금 취소 처리 필요
금전적 손실이나 비즈니스에 치명적인 문제는 꼭 분산 트랜잭션을 구현해서 제품 품질을 보장해야 한다.
p.130
정형 데이터 : 미리 정의한 스키마에 잘 맞는 데이터
반정형 데이터 : 데이터가 일부 구조적 형태를 띄고 있음. 모든 데이터의 해당 키가 같은 갯수의 필드나 하위 데이터를 가지고 있다고 보장할 수는 없음. 이런 형태의 데이터는 JSON이나 XML, YAML로 쉽게 표현할 수 있다.
비정형 데이터 : 참조 등을 위한 의미 있는 필드를 전혀 가지고 있지 않습니다. 이미지나 비디오, 저수준 텍스트 콘텐츠가 이에 해당합니다. 이런 콘텐츠는 대개 콘텐츠를 이해할 필요 없이 그냥 저장합니다.
정형 데이터
미리 정의된 스키마에 완벽히 맞는 데이터
RDBMS 관계형 데이터베이스에 적합
+----+----------+-------------------+-------+------------+
| ID | 이름 | 이메일 | 나이 | 가입일 |
+----+----------+-------------------+-------+------------+
| 1 | 김철수 | kim@example.com | 25 | 2024-01-15 |
| 2 | 이영희 | lee@example.com | 30 | 2024-01-16 |
+----+----------+-------------------+-------+------------+
반정형 데이터
일부 구조를 가지지만 스키마가 유연한 데이터
기본 구조는 있지만 필드가 일관되지 않음
어떤 레코드는 특정 필드가 없을 수 있음
중첩된 구조나 배열을 포함할 수 있음
MongoDB, Amazon DynamoDB에 적합
[
{
"id": 1,
"name": "김철수",
"email": "kim@example.com",
"age": 25,
"address": {
"city": "서울",
"district": "강남구"
},
"hobbies": ["독서", "영화감상"]
},
{
"id": 2,
"name": "이영희",
"email": "lee@example.com",
"phone": "010-1234-5678",
"address": {
"city": "부산"
},
"skills": ["Python", "Java", "JavaScript"]
},
{
"id": 3,
"name": "박민수",
"age": 28,
"social": {
"twitter": "@parkms",
"linkedin": "park-minsu"
}
}
]
비정형 데이터
구조가 없거나 매우 자유로운 형태의 데이터
텍스트 문서, 이미지 파일, 동영상 파일
저장할 때 대부분 원본 그대로 보관
객체 저장소 (Object Storage), S3, Google Cloud Storage에 저장
p.138
관계형 데이터베이스는 그 설계부터 확장성이 떨어집니다. 확장성이라는 관점에서 관계형 데이터베이스는 primary, secondary 구조밖에 지원하지 않으며, 여러 노드 중 단 하나의 노드에만 쓰기가 가능하고 나머지 워커 노드는 읽기만 지원합니다.
따라서 클라우드 네이티브 애플리케이션에서 관계형 데이터베이스는 저장해야 하는 레코드 수가 데이터베이스가 효과적으로 처리할 수 있는 수를 넘지 않을 때만 사용하는 것이 좋습니다.
RDBMS에서 쓰기 작업을 Master DB에만 가능하도록 설계한 이유는 데이터의 일관성과 무결성을 보장하기 위함이다.
데이터 일관성 보장
- 동시 쓰기로 인한 충돌 문제 방지
- Master 노드에서 단일 쓰기의 장점은 트랜잭션을 순차적으로 처리 할 수 있기 때문이다.
트랜잭션 순서 보장
- 여러 쓰기 노드에서의 문제점 발생 가능성 방지 (네트워크 지연으로 인한 순서 뒤바뀜)
여기서 생각해 볼 문제는 master -> slave로의 복제 지연 발생이다.
복제 지연이 발생하면 데이터 일관성이 깨진다.
비즈니스 로직에서 slave DB의 데이터를 읽었을 때 복제 지연으로 인하여 최신 데이터를 읽지 못하는 상황이 발생할 수 있다.
이에 대한 솔루션으로는 중요한 로직 처리시 master DB 바라보게 하기
또는 replication lag 상황을 모니터링하여 지연이 발생한다면 master DB 바라보게 하기
proxySQL 도입을 통해서 복제 지연 해결하기 등이 있다.
여러가지 해답이 있지만 최선의 방법을 전략적으로 선택해 나가야 한다.
p.141
NoSQL 데이터베이스는 기본적으로 분산 동작하기 때문에 CAP 이론을 적용할 수 있습니다.
CAP은 일관성, 가용성, 분할 내성을 나타내며 CAP 이론은 어떤 분산 시스템도 일관성과 가용성, 불할 내성 이 세가지를 모두 만족할 수 없다는 것을 뜻합니다.
가용성 : 분산 시스템의 일부 노드가 문제가 발생해도 전체 시스템이 문제없이 동작하는 것
일관성 : 특정 노드에 있는 데이터에 대한 수정 또는 변경이 시스템의 다른 모든 노드에 전파되는 것
분할 내성 : 일부 노드가 다른 노드와 연결될 수 없는 상황, 즉 분할이 발생한 경우에도 시스템에 이상이 생기지 않는 것을 뜻합니다.
세 가지 모두를 만족할 수 없기 때문에, 어떤 NoSQL 데이터베이스는 가용성보다 일관성을 더 중시하고, 또 다른 데이터베이스는 일관성보다 가용성을 더 중시하는 등의 설계를 보입니다.
p.145
분산 데이터 관리 방식의 단점 중 하나로는 각 서비스별로 별도의 데이터 스토어를 사용하는 데 따른 비용 부담입니다. 그래서 크지 않은 조직의 경우 하이브리드 데이터 관리 방식을 사용하기도 합니다.
이 방식에서는 여러 마이크로서비스가 같은 데이터베이스를 공유합니다.
하이브리드 데이터 관리 방식을 사용할 때는 다른 서비스가 사용하는 데이터베이스 테이블에 직접 접근하지 않도록 주의해야 합니다. 어떤 서비스가 다른 서비스의 테이블에 직접 접근하게 되면 시스템의 복잡도가 아주 크게 증가하며 나중에 데이터베이스를 여러 데이터베이스로 나누고 테이블을 분리할 때 문제가 생길 수 있습니다.
조직 규모가 작거나 MSA 전환 초기에 자주 선택되는 방식이다.
이와 같은 전략은 비용 절감, 운영의 단순화를 위한 선택이다.
서비스 성장이 급격하게 늘어났을 때 각 서비스별로 DB를 분리해도 된다.
단, API를 거치지 않고 다른 서비스 도메인의 테이블에 접근하는 건 금지해야 한다.
나중에 DB를 분리하고 싶어도 분리하지 못하는 상황이 발생할 것이다.
다른 테이블과 조인하여 서비스 경계를 무너뜨리는 경우가 종종 있는데 이러다가는 운영 유지보수 비용이 폭발적으로 증가하게 된다.
p.150
여러 마이크로서비스가 데이터에 접근하는 경우 이 패턴을 사용할 수 있습니다. 이 경우 특정 단일 마이크로서비스가 해당 데이터에 대한 소유권을 가지지 않으며 여러 마이크로서비스가 작업에 이 데이터를 사용하게 됩니다. 공통으로 사용하는 데이터는 독립적인 데이터 서비스로 제공하여 데이터를 사용하는 모든 애플리케이션이 API를 통해 데이터에 접근하도록 만듭니다.
클라우드 네이티브 애플리케이션에서 여러 마이크로서비스가 같은 데이터에 접근하는 것은 피해야 하는 패턴입니다. 같은 데이터에 접근하는 마이크로서비스 간 강한 연관성이 생기며 마이크로서비스들이 스스로 크기를 조절하거나 발전하기 어렵게 만들기 때문입니다.
p.153
상품 재고를 확인하려면 물품을 제공하는 서로 다른 다양한 업체의 데이터 서비스를 통해 데이터를 제공받아야 합니다.
이 경우 모든 상품 제공 업체로부터 데이터를 받아서 상품 목록을 제공하는 공통 서비스를 만들어서 사용하면 마이크로서비스들이 데이터를 모아서 가공하는 중복 작업을 할 필요 없이 해당 서비스에서 데이터를 받으면 되고, 클라이언트의 복잡도가 낮아지며 클라이언트에 영향을 미치지 않고 데이터 서비스를 변경할 수도 있습니다.
중앙에서 한 번 래핑해주고 그 결과를 내려주는 구조이기에 중복 제거, 단순화, 변경 격리 효과가 있다.
p.154
웹사이트의 모든 콘텐츠를 전부 읽어올 때까지 사용자를 기다리게 만드는 것보다는 여러 번의 비동기 호출을 통해 웹 사이트 콘텐츠를 여러 조각으로 가져오고 가져온 부분을 그때그때 화면에 표시하는 것이 사용자 경험 측면에서 더 낫습니다. 이런 형태의 애플리케이션을 RIA 라고도 부릅니다.
p.163
샤드 키를 만들 때는 자동으로 그 값이 증가하는 필드는 사용하지 않는 것이 좋습니다. 샤드끼리는 서로 통신하지 않기 때문에 자동으로 증가하는 필드 값을 샤드 키로 사용할 경우 여러 샤드가 동일한 샤드 키를 생성할 수 있으며 서로 다른 데이터를 로컬에서 같은 키로 참조하는 일이 일어날 수 있습니다. 이는 특히 데이터를 재배치하는 과정에서 데이터를 재분배할 때 문제가 될 수 있습니다.
아울러 샤드 키는 데이터를 가급적 균등하게 배분할 수 있는 값을 선택하는 것이 좋습니다. 샤드 크기가 균등하지 않으면 기대했던 만큼의 확장성이 나오지 않습니다. 가장 큰 샤드가 항상 가장 좋지 않은 성능을 보일 것이며 결국 병목 지점이 될 것입니다.
auto-increment 값을 해싱해서 샤드 키로 사용하면 데이터를 균등하게 분산 할 수 있다.
hash(id) / N 같은 방식으로 분배하면 샤드 전체에 골고루 퍼짐
p.165
온라인 쇼핑몰 애플리케이션의 경우 일반적으로 데이터 갱신과 같은 명령보다는 상품 조회와 같은 질의 서비스를 더 많이 사용하며, 마찬가지로 상품 구매보다는 상품 정보 조회가 더 빈번하게 일어납니다. 따라서 쇼핑몰 애플리케이션에서 명령 서비스보다 더 많은 질의 서비스를 사용함으로써 성능을 개선할 수 있습니다.
온라인 쇼핑몰은 고객이 여러 상품을 조회하면서 비교한다. 실제 구매로 이어지는 건 소수이다.
이커머스는 전체 데이터베이스 작업의 80~90%가 조회이다.
읽기 집약적 서비스
- 이커머스
- 뉴스/미디어 사이트
- 검색 엔진
- SNS 피드
쓰기 집약적 서비스
- IoT 센서 데이터
- 로깅 시스템
- 채팅
- 금융 거래
p.166
CQRS는 명령과 질의 작업 간에 높은 일관성이 필요한 경우 사용해서는 안 됩니다. 데이터를 변경하면 갱신된 내용은 이벤트 소싱과 같은 패턴을 통해 이벤트 형태로 비동기적으로 질의 스토어에 전달됩니다. CQRS는 약한 일관성을 사용해도 무방한 경우에만 적용해야 합니다. ^4bacc2
쓰기 전용 DB -> 읽기 전용 DB로의 동기화 지연이 발생할 수 있기 때문에 강한 일관성이 깨질 수 있다.
강한 일관성이 필요한 로직에서는 쓰기 전용 DB를 바라보게 하고, 그 외에는 읽기 전용 DB를 바라보게 처리하는 방법을 사용
하지만 이와 같은 룰을 정해도 시간이 지나면 사람들은 잊는다.
여러가지 해결 방안들이 있지만 적용하면 시스템의 복잡도가 상승한다.
비용이 가장 적게 드는 방법을 채택하는 게 좋겠지?
핵심 도메인(금융, 결제, 주문 등)은 무조건 쓰기 전용 DB를 조회하고, 비핵심 도메인(상품 리스트, 로그, 통계 등)은 읽기 전용 DB를 조회하게 하는 방법이 좋겠다.
아래와 같이 애노테이션으로 쓰기, 읽기 DB를 구분하는 처리 방법이 효과적인 듯 하다.
@ReadOnlyRepository
@StrongConsistencyRepository
p.177
캐시를 공유하지 않는 경우 사용 가능한 메모리를 전부 소진할 수 있기 때문에 결국 데이터를 캐시에 추가하지 못하는 상황이 발생합니다.
이런 경우 새로운 데이터를 캐시에 저장하기 위해서 예전 캐시 데이터를 삭제하는 정책을 사용합니다. 가장 자주 사용하는 삭제 정책은 LRU(Least Recently Used)이며, 가장 오랫동안 사용하지 않은 캐시 데이터를 삭제하고 그 자리에 새로운 데이터를 저장합니다.
p.181
캐시 데이터를 로컬에 저장할 때 가장 큰 문제점은 서비스를 확장할 때 각 서비스가 자신만의 로컬 캐시를 가지게 되며 서로 다른 시점에 데이터 스토어와 캐시 데이터를 동기화한다는 것입니다.
캐시 데이터가 동기화된 이후에 데이터 변경이 이루어지면, 각 마이크로서비스들의 캐시 데이터가 일치하지 않는 상태가 됩니다. 이로 인해 두 개의 서비스에 동일한 질의를 요청하는 경우 서로 다른 값을 반환할 수 있습니다.
발행자/구독자 패턴을 통해 데이터에 대한 변경이 일어나는 경우 모든 캐시에 무효화 정보를 전달함으로써 해결할 수 있습니다.
p.184
CDN 내부 동작 원리 이해
- CDN 핵심 목표는 사용자가 요청한 컨텐츠를 가장 가까운 캐시 서버에서 빠르게 제공하는 것이다. 이를 위해서는 사용자에게 어떤 서버 IP를 반환할지를 결정하는 글로벌 트래픽 분산 기술이 필요하다. 여기서 사용되는 게 바로 GSLB 이다.
- Local DNS : 사용자가 속한 네트워크(회사, 가정, ISP)에서 사용하는 Resolver 서버
- 도메인 이름 -> IP 주소로 변환해 주는 역할
p.198
몽고DB는 가용성보다 데이터 일관성을 더 중요시합니다.
몽고DB의 가용성은 주 스토어에서만 가능한 읽기 및 쓰기, 그리고 여러 개의 복제를 통해 이루어집니다. 주 스토어가 사용이 불가능한 경우 읽기 및 쓰기 작업은 10~40초 가량 일시적으로 중단되며 그동안 복제 스토어 중 하나를 주 스토어로 선출합니다.
다른 NoSQL 데이터베이스가 그러하듯 데이터 트랜잭션에는 적합하지 않습니다.
몽고DB는 높은 처리량과 빠른 읽기/쓰기 성능에 최적화되어 설계되었다.
몽고 4.0 이후부터 멀티 도규먼트 트랜잭션 처리를 통해서 여러 도큐먼트를 하나의 트랜잭션으로 묶을 수 있다.
하지만 트랜잭션은 성능 비용이 크기 때문에 몽고DB의 장점과 상충한다.
아직까지는 중요한 비즈니스 트랜잭션에서는 전통적인 RDBMS가 더 적합하다.
p.201
데이터 스토어별 사용하면 좋은 경우, 사용해서는 안 되는 경우
| 데이터 스토어 유형 | 사용하면 좋은 경우 | 사용하면 안 되는 경우 |
|---|---|---|
| 관계형 DB (MySQL, PostgreSQL, Oracle) | - 트랜잭션(ACID) 보장이 중요한 경우 - 스키마가 명확하고 안정적인 경우 - 관계(Join) 기반 질의가 필요한 경우- 금융, 결제, 주문/회원 관리 |
- 대규모 수평 확장이 꼭 필요한 경우 - 스키마 변경이 매우 잦은 경우 - 단순 키-값 조회만 필요한 경우 |
| Key-Value Store (Redis, Memcached, DynamoDB KV) | - 초고속 읽기/쓰기 캐시 - 세션, 토큰, 카운터, 랭킹 등 단순 데이터 - TTL(만료 시간) 필요 데이터 |
- 범위 검색, 조건 질의 필요 - 관계형 조인 필요 - 장애 시 데이터 영속성이 중요한 경우 |
| Document Store (MongoDB, Couchbase) | - 유연한 스키마가 필요한 경우 - JSON/BSON 기반 복잡한 엔티티 저장 - 사용자 프로필, 상품 카탈로그- 빠른 개발/프로토타이핑 |
- 강한 일관성/트랜잭션이 중요한 경우 - 관계형 조인이 많은 경우 |
| Wide-Column Store (Cassandra, HBase, ScyllaDB) | - 초대규모 데이터(페타바이트 이상) - 쓰기 성능과 수평 확장이 중요한 경우 - 로그, IoT 센서, 타임라인 데이터 |
- 복잡한 Join/트랜잭션이 필요한 경우 - 데이터 모델링이 자주 바뀌는 경우 |
| 검색 엔진 (Elasticsearch, Solr, OpenSearch) | - 전문 검색(Full-text Search) - 로그 분석, 모니터링 (ELK 스택) - 필터링 + 정렬 + 검색이 복합적으로 필요한 경우 |
- 강한 일관성이 필요한 경우 - 트랜잭션 처리 도메인 (결제, 금융) - 저장소로만 사용하는 경우 |
| 시계열 DB (InfluxDB, TimescaleDB, Prometheus) | - 시계열(time-series) 데이터 (모니터링, IoT, 금융 시세) - 시계열 집계/쿼리가 많은 경우 - 효율적인 TTL 기반 데이터 관리 |
- 일반 CRUD 트랜잭션 처리 - 관계형 조인이 필요한 경우 |
| 오브젝트 스토리지 (S3, GCS, MinIO) | - 이미지, 동영상, 로그, 백업 등 대용량 파일 저장 - 정적 콘텐츠 제공 (CDN 연동) - 저비용 장기 보관 |
- 초저지연 트랜잭션 필요 - 범위/조건 기반 질의 필요 - 소규모 데이터 자주 갱신 |
p.211최대 한 번 전달
이벤트는 소비자에게 한 번만 전달되거나 전달되지 않습니다. 이벤트가 전달되는 동안 소비자가 온라인 상태가 아니거나 네트워크 전송이 실패하면 소비자는 이벤트를 받지 못합니다.
더 중요한 점은 메시지 브로커가 같은 이벤트를 다시 전달하려고 시도하지 않는다는 것입니다.최소 한 번 전달
이벤트가 소비자에게 전달된다는 것을 보장합니다.
하지만 소비자는 같은 이벤트를 한 번 이상 소비하는 경우도 발생하는데, 소비자 측에서 메시지 브로커 측에 이벤트를 전달받았다는 응답을 보내지 않으면 메시지 브로커는 이벤트가 전달되지 않았다고 생각하고 같은 이벤트를 계속 다시 보내기 때문입니다. 소비자는 이렇게 중복 발송되는 이벤트를 잘 처리할 수 있어야 합니다.
불행히도 이벤트가 소비자에게 딱 한 번만 전달되는 딱 한 번 전달 과 같은 보장은 네트워크와 시스템의 불확실성때문에 구현되지 않았습니다. ^4217e5
p.212
사용자 트랜잭션 정보와 같은 중요한 비즈니스 정보는 이벤트를 최소 한 번 전달하고 딱 한 번만 처리하도록 합니다.
간단한 알림 정보나 주기적인 업데이트와 같이 손실되어도 크게 지장이 없는 이벤트는 최대 한 번 전달로도 충분합니다. 더 높은 수준의 전달 보장은 더 높은 성능이나 복잡도를 요구하기 때문에 반드시 보장해야 하는 최저 수준을 선택하는 것이 좋습니다.
p.214
생산자/소비자 패턴
생산자 애플리케이션과 소비자 애플리케이션이 이벤트 큐를 통해 서로 비동기로 통신합니다. 이벤트 큐는 어떤 소비자가 어떤 이벤트를 처리하게 할지, 그리고 이벤트 처리 동안 소비자가 제대로 도작하지 않은 경우 어떤 행동을 취할지 결정합니다.
ex : 스타벅스(생산자)에서 커피를 만들어 선반(큐)에 올려놓으면 손님(소비자)가 가져감
1:1 관계에 가깝다.
생산자가 넣은 메시지를 소비자가 하나씩 가져가는 방식
p.218
발행자/구독자 패턴
발행자 구독자 패턴을 통해 특정 토픽에 대한 비동기 통신을 구현할 수 있습니다. 토픽을 통해 모든 이벤트를 모든 구독자에게 전달할 수 있습니다.
ex : 운영자(생산자)가 뉴스레터를 발생하면, 구독 신청한 사용자들(구독자)의 이메일에 전송됨
1:N 관계에 가깝다.
발행자가 보낸 이벤트가 여러 구독자에게 동시에 전달되는 방식
p.223
스토어 앤 포워드 패턴
이 패턴은 최소 한 번 이벤트를 전달하는 것을 보장하기 때문에 클라이언트 구조가 복잡합니다.
이벤트를 서비스(서버)에 전달하기 전 데이터베이스에 이벤트를 우선 저장합니다.
이벤트를 성공적으로 전달하면 클라이언트는 스토어에서 이벤트를 지웁니다.
이벤트 전달에 실패하면 이벤트 전달을 재시도합니다.
그동안 클라이언트는 보내야 할 이벤트를 더 받을 수 있으며, 이 이벤트들은 스토어에 저장합니다.
서비스에 다시 연결이 가능해지면 대기 중이던 모든 이벤트를 전달하며 서비스가 이벤트를 받았다는 응답을 보내면 스토어에서 이벤트들을 지웁니다. ^14d203
수신자가 메시지를 DB에 저장하고 송신자가 DB에 저장되어 있는 메시지를 하나씩 가져가서 처리하는 방식이다. 처리가 완료되면 DB에 저장되어 있는 메시지를 삭제한다.
이와 같은 방식은 최소 한 번 이벤트 전달 방식을 보장하고, 수신자와 송신자가 서로 비동기 처리를 하기 때문에 처리량이 향상되고 확장성과 유연성이 높아진다.
p.232
이벤트 전달 패턴 정리
- 생산자/소비자
- 생산자가 데이터를 생성하고 큐에 저장, 소비자가 큐에서 데이터를 가져와 처리
- 장점 : 생산자와 소비자 간 결합도 낮음, 처리 속도 차이 해결
- 단점 : 메시지 순서 보장이 어려울 수 있음
- 발행자/구독자
- 발행자가 토픽/채널에 메시지 발행, 여러 구독자가 동일한 메시지 수신
- 장점 : 확장성, 느슨한 결합
- 단점 : 메시지 전달 보장이 어려움, 구독자 관리 복잡
- 파이어 앤 포겟
- 송신자가 메시지를 보낸 후 응답을 기다리지 않음
- 장점 : 높은 처리량, 낮은 지연 시간
- 단점 : 전달 보장 없음, 에러 처리 어려움
- 스토어 앤 포워드
- 중간 노드가 메시지를 임시 저장, 적절한 시점에 목적지로 전달
- 장점 : 신뢰성 있는 메시지 전달, 네트워크 장애 대응
- 단점 : 지연 발생, 추가 저장 공간 필요
- 폴링
- 클라이언트가 주기적으로 서버에 요청
- 장점 : 클라이언트가 처리량 제어 가능
- 단점 : 불필요한 네트워크 트래픽, 지연 발생
- 요청 콜백
- 비동기 요청 후 완료 시 콜백 함수 실행
- 장점 : 실시간 응답, 효율적인 리소스 사용
- 단점 : 콜백 지옥, 에러 처리 복잡
p.251
RabbitMQ는 푸시 방식과 풀 방식 모두 지원하기 때문에 구독 큐를 통해 브로커가 소비자에게 메시지를 전달할 수도 있으며 소비자가 필요한 메시지를 가져갈 수도 있습니다.
RabbitMQ에서는 푸시 방식만 있는 줄 알았다.
풀 방식도 지원했었구나.

'독서' 카테고리의 다른 글
| 연희동 러너 (0) | 2025.10.19 |
|---|---|
| DDD START! (0) | 2025.10.11 |
| 코드 너머 (1) | 2025.09.19 |
| 역설계 (2) | 2025.09.10 |
| TCP/IP 쉽게, 더 쉽게 (7) | 2025.08.25 |
| 쿠버네티스 개발 전략 (7) | 2025.08.11 |
| 조대협의 서버 사이드 대용량 아키텍처와 성능 튜닝 (11) | 2025.08.09 |
| 퓨처셀프 (17) | 2025.08.05 |