이커머스 도메인 서비스를 MSA 아키텍처로 학습하면서 카프카라는 기술을 도입해야 하는 시점이 왔다.
현재 주문 서비스에 많은 트래픽이 발생하면서 하나의 인스턴스로만 구성되어있던 주문 서비스를 한 개 이상의 서비스로 구성을 해줘야 한다.
여기서 스케일 아웃 방식을 택하여 인스턴스를 늘리는 방법을 택하였고,
FeingClient 기술을 사용하고 있어서 라운드 로빈방식으로 부하를 분산 시켜주었다.
문제점은 스케일 아웃으로 인스턴스를 늘리고 나면 DB를 어떻게 설계할 것인지 1차 고민이 왔었다.
두 가지 방법을 떠올렸는데 첫 번째로 DB는 한 개만 쓰는 것으로 모든 주문 서비스들이 하나의 DB만 바라보는 것이었다.
이 방법은 결과물의 정합성은 쉽지만, 성능이나 확장성이랑 안정성에서 문제가 발생한다. 특히 지금 같이 트래픽이 많은 주문 서비스에서는 병목이 심각해진다.
두 번째 방법은 각 인스턴스마다 개별 DB를 두는 것이다. 이 방법은 확장성과 성능에서 강력하다 대신 데이터 동기화와 일관성 확보가 어렵다 라는 것이다.
아래는 두 가지 방법을 표로 정리해두었다.
| 여러 인스턴스가 하나의 DB 공유 | 인스턴스마다 개별 DB 할당 | |
| 확장성(Scalability) | DB가 단일 병목. 애플리케이션을 늘려도 성능 증가가 제한됨. | 각 DB가 분산되어 확장성 매우 높음. 인스턴스 수만큼 병렬 처리 가능. |
| DB 부하 분산 | 모든 읽기/쓰기 부하가 한 DB로 집중. | 부하가 인스턴스별 DB로 분산됨. |
| 락 경합(Lock Contention) | 동일 테이블/행 락 충돌 증가 → 지연·데드락 발생 가능. | 충돌 없음. 각 인스턴스는 자기 DB만 접근. |
| Connection Pool 문제 | 인스턴스 증가 → 커넥션 수 폭증 → DB connection limit 초과 위험. | 각 DB가 분산되므로 connection pressure가 대폭 감소. |
| 서비스 장애 영향도 | DB 장애 = 주문 서비스 전체 중단. | 특정 인스턴스/DB 문제는 해당 인스턴스만 영향. 전체 서비스는 정상. |
| 데이터 정합성(Consistency) | 하나의 DB라 정합성 관리가 쉬움. | 인스턴스 간 DB Sync 필요. 강한 정합성 보장 어려움. |
| 트랜잭션 관리 | 단일 DB라 글로벌 트랜잭션 필요 없음. | 인스턴스끼리 데이터 누적·조회가 달라질 수 있음. |
| 데이터 일관성(Read Consistency) | 단일 DB 기준으로 정확한 최신 데이터 조회 가능. | 인스턴스마다 데이터 시점이 다름. 분산 데이터 문제 발생. |
| 운영 비용 | DB 하나만 관리하면 됨. 비교적 저렴. | DB가 여러 개라 비용·운영 복잡성 증가. |
| 백업/복구 | 한 번에 백업·복구 가능. | 각각의 DB를 따로 백업/복구해야 함. 관리 부담 증가. |
| MSA 원칙 적합성 | 서비스간 DB 공유로 사실상 모놀리식 형태. | "각 서비스는 독립적"이라는 MSA 원칙에 더 가까움. |
| 성능 | DB 병목으로 고성능 유지 어려움. | 고성능. 인스턴스 수만큼 병렬로 처리량 증가. |
| 적용 추천 상황 | 데이터 일관성이 최우선이고 트래픽이 적을 때. | 트래픽 폭증, 독립성·확장성이 매우 중요할 때. |
그래서 DB를 개별적으로 두려고 하는데 문제점이 데이터 동기화가 되지 않는 다는 점이다.
예를 들어서 유저가 주문을 생성하게 되면 라운드 로빈 알고리즘을 통하여 주문 서비스의 각각 인스턴스에 주문이 생성되게 된다.
이때 유저의 주문 정보를 조회하게 되면 여러 개의 모든 주문 리스트가 나오지 않는다.
각각의 주문 서비스 인스턴스에 번갈아가면서 저장이 되기 때문에 데이터의 정합성에서 문제가 발생했다.
예시로 사진을 보여주자면
첫 번째로 조회한 데이터의 orders 내용에 하나의 정보만 들어가있고,

두 번째로 실행한 데이터에는 보면 orders 내용에 두 개의 정보가 들어가있다.

개별 DB가 되어있기에 조회할 때마다 모든 데이터를 가지고 올 수 없어서 문제가 발생한 장면이다.
따라서 여러 인스턴스를 만들었을 때 생기는 문제점으로 데이터 정합성 + 트래픽 처리 + DB 부하인데, 이것들을 해결 하기 위해 Kafka를 사용하기로 검색해보았고, 이 문제를 Event Driven Architecture 방식으로 해결하기로 했습니다.
Kafka를 도입해야 하는 이유
1. 데이터 정합성 문제 해결 (DB 여러 개 사용 시 필수)
인스턴스마다 DB가 따로 있으면 주문 생성, 상태 변경 이벤트를 공유해야 한다.
카프카를 사용하게 되면 모든 주문 생성/변경을 event로 브로드캐스크한다.
각 인스턴스가 필요 시 자신의 DB에 반영한다.
2. 트래픽을 DB가 직접 받지 않게 한다. (DB 부하 감소)
카프카로 주문 이벤트를 먼저 적재하고, DB 저장은 Consumer가 순서대로 처리하므로
DB connection 폭주를 방지하고, Lock 충돌 최소화를해주며 순간 폭증 트래픽을 완충해준다.
3. 주문 서비스 인스턴스 증가 시 자연스럽게 확장된다.
4. 비동기 처리로 전체 서비스 반응성을 향상시킨다.
주문을 받고 -> 이벤트로 publish -> 응답 반환을 해준다.
실제로 무거운 작업(결제, 메시지 발송, 배달 생성 등)은 비동기 처리로 가능하다.
RabbitMQ VS Kafka
RabbitMQ 또한 메시지큐 기반의 처리로 이벤트 전달을 할 수 있다.
설치 구성이 쉽고 실시간 작업 큐 처리에 적합 하다
하지만, 실시간 전달 중심이고 메시지를 오래 저장하거나 리플레이하는 구조는 아니다.
대용량 트래픽 이벤트 스트리밍에서는 카프카보다 약하다.
DB 동기화, 이벤트 로그 유지 부분에서 RabbitMQ는 한계가 있다.
Docker 환경에서 주키퍼와 카프카환경 세팅
다운로드해서 사용할 수 있는 데 저는 도커컴포즈 파일을 사용하여 환경을 구성했습니다.
services:
zookeeper:
image: confluentinc/cp-zookeeper:7.5.0
container_name: zookeeper
ports:
- "2181:2181"
environment:
ZOOKEEPER_CLIENT_PORT: 2181
kafka:
image: confluentinc/cp-kafka:7.5.0
container_name: kafka
ports:
- "9092:9092"
depends_on:
- zookeeper
environment:
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
도커 컴포즈로 실행
docker compose up -d
도커 환경에서 주키퍼 & 카프카 접속
docker exec -it zookeeper bash
docker exec -it kafka bash
Kafka Topic Create Test
토픽 생성
kafka-topics --create --topic quickstart-events --bootstrap-server localhost:9092 --partitions 1
토픽 리스트 조회
kafka-topics --list --bootstrap-server localhost:9092
'TIL & STUDY' 카테고리의 다른 글
| TIL 20 : Kafka INTERNAL/EXTERNAL Listener (1) | 2025.11.22 |
|---|---|
| TIL 19 : docker zookeeper & kafka & kafka connect setting (0) | 2025.11.21 |
| TIL 17 : 블로킹과 논블로킹 (0) | 2025.11.08 |
| TIL 16 : 데이터 삭제 요청 처리 방법과 자바 코테 연습등 기타 공부! (0) | 2025.10.09 |
| TIL 15 : URI 버전관리와 다국어 언어 처리 (0) | 2025.10.02 |