백엔드 개발을 하다 보면 이런 말을 자주 듣는다.
- “이건 비동기로 처리해야 해요”
- “Kafka로 이벤트 흘리면 됩니다”
- “MQ로 분리하세요”
그런데 막상 물어보면
👉 왜 비동기가 필요한지,
👉 동기/비동기가 실제로 뭐가 다른지,
👉 MQ와 Kafka가 왜 등장했는지
명확하게 설명하기는 쉽지 않다.
이 글에서는 아무 배경지식 없는 상태에서도 이해할 수 있도록
비동기 처리의 등장 배경부터 차근차근 설명해본다.
1. 우리가 처음 만드는 시스템은 전부 “동기 처리”다
대부분의 웹 서비스는 이렇게 시작한다.
클라이언트 요청 → 서버 처리 → 응답
이를 동기(Synchronous) 처리라고 한다.
예시: 주문 API
주문 요청
→ 결제 처리
→ 재고 차감
→ 주문 저장
→ 알림 발송
→ 응답
이 구조는 이해하기 쉽고, 구현도 단순하다.
그래서 대부분의 초보 단계 서비스는 이 방식으로 시작한다.
2. 동기 처리의 진짜 문제는 “속도”가 아니다
많은 사람들이 이렇게 생각한다.
“동기 처리는 느리고, 비동기는 빠르다”
❌ 반은 맞고, 핵심은 아니다
동기 처리의 진짜 문제는 결합도와 장애 전파다.
2-1. 하나라도 느리면 전체가 느려진다
결제: 50ms
재고: 20ms
알림: 300ms (외부 API)
→ 전체 응답: 370ms
알림 하나 때문에 주문 API 전체가 느려짐
2-2. 하나라도 실패하면 전체가 실패한다
결제 성공
재고 차감
성공 알림
서버 장애 ❌
👉 결과
- 주문 API 실패
- 이미 결제는 됐는데 사용자에겐 “실패” 응답
이런 상황, 실제 서비스에서 엄청 자주 발생한다.
2-3. 트래픽이 늘수록 서버는 버틴다
동기 구조에서는 요청 하나가 처리될 때까지
서버의 스레드/리소스를 계속 점유한다.
요청 증가
→ 처리 대기 증가
→ 응답 지연
→ 타임아웃
👉 결국 전체 서비스가 느려지거나 죽는다
3. 그래서 등장한 개념이 “비동기 처리”
비동기 처리의 핵심 아이디어는 간단하다.
“지금 당장 응답할 것”과
“나중에 처리해도 되는 것”을 분리하자
3-1. 비동기 처리의 기본 구조
요청
→ 핵심 처리
→ 이벤트/메시지 발행
→ 응답
(뒤에서)
→ 부가 처리
주문 예시로 다시 보면
주문 요청
→ 결제
→ 주문 저장
→ 응답
(비동기)
→ 알림 발송
→ 통계 집계
→ 로그 저장
👉 사용자 입장:
- 주문은 빨리 완료됨
👉 서버 입장:
- 무거운 작업을 뒤로 미룸
4. “그럼 그냥 Thread로 빼면 되지 않나?”
처음엔 이런 생각이 든다.
new Thread(() -> sendNotification()).start();
❌ 이 방식의 문제
- 서버 재시작 → 작업 유실
- 장애 발생 시 재처리 불가
- 분산 환경에서 관리 불가능
👉 “비동기”는 Thread 문제가 아니라 “신뢰성” 문제
5. 여기서 등장하는 게 MQ(Message Queue)
RabbitMQ, ActiveMQ 같은 MQ는
“비동기 처리를 안전하게 하기 위한 중간 저장소”다.
6. MQ는 어떤 문제를 해결하나?
MQ가 없을 때
서버 → 알림 서버
(직접 호출)
- 알림 서버 장애 → 요청 실패
- 알림 서버 느림 → 전체 지연
MQ가 있을 때
서버 → MQ → 알림 서버


MQ의 역할
- 메시지를 큐에 저장
- Consumer가 자기 속도로 처리
👉 서버는 “던지고 끝”
7. MQ 동작을 아주 쉽게 비유하면
요청자 = 주문 직원
MQ = 접수함
Consumer = 처리 담당자
- 직원은 접수함에 서류만 넣고 다른 일 함
- 담당자는 하나씩 꺼내 처리
👉 업무 분리 + 안정성 확보
8. MQ 예시 코드 (감만 잡기)
Producer (메시지 발행)
rabbitTemplate.convertAndSend(
"order.queue",
new OrderCreatedEvent(orderId)
);
Consumer (메시지 처리)
@RabbitListener(queues = "order.queue")
public void handle(OrderCreatedEvent event) {
sendNotification(event);
}
중요한 점:
- Producer는 Consumer가 누구인지 모름
- 느리든, 잠시 멈추든 영향 없음
9. 그런데 MQ로는 부족한 순간이 온다
서비스가 커지면 이런 요구가 생긴다.
- 같은 이벤트를 여러 시스템이 사용하고 싶다
- 과거 이벤트를 다시 처리하고 싶다
- 초당 수십만 건 이상 처리하고 싶다
👉 이때 MQ만으로는 한계가 온다.
10. 그래서 Kafka가 등장한다
Apache Kafka는
단순 큐가 아니라 이벤트 로그 시스템 이다.
메시지를 “전달”하는 게 아니라
이벤트를 “기록”한다


11. Kafka를 한 문장으로 말하면
Kafka는
시스템에서 발생한 모든 일을 시간순으로 저장하는 로그 다.
12. Kafka가 MQ와 다른 핵심 포인트
| 구분 | MQ | Kafka |
| 메시지 구조 | Queue | Log(Stream) |
| 소비 방식 | 1번 소비 | 여러 번 소비 가능 |
| 재처리 | 어려움 | 매우 쉬움 |
| 확장성 | 중 | 매우 높음 |
| 용도 | 작업 분리 | 이벤트 중심 아키텍처 |
13. Kafka 예시 (이벤트 중심 사고)
주문 생성 이벤트
- 결제 서비스
- 알림 서비스
- 통계 서비스
👉 모두 같은 이벤트를 각자 소비
Producer
kafkaTemplate.send(
"order.created",
orderId.toString(),
new OrderCreatedEvent(orderId)
);
Consumer (결제)
@KafkaListener(topics = "order.created", groupId = "payment")
public void pay(OrderCreatedEvent e) { }
Consumer (알림)
@KafkaListener(topics = "order.created", groupId = "notification")
public void notify(OrderCreatedEvent e) { }
14. 정리: 왜 비동기가 필수가 되었는가
- 동기 구조는 단순하지만 잘 깨진다
- 비동기는 속도가 아니라 안정성을 위한 선택
- MQ는 작업 분리
- Kafka는 이벤트 흐름 관리
15. 요약
비동기 처리는
“지금 당장 끝낼 일”과
“나중에 해도 되는 일”을 분리하는 설계다.MQ는 작업을 안전하게 미루는 도구이고,
Kafka는 시스템의 모든 이벤트를 기록하는 스트림 엔진이다.
'개발지식 > Backend Engineering' 카테고리의 다른 글
| 전략 패턴 + AOP로 정책 로직을 분리한 설계 (0) | 2025.12.28 |
|---|---|
| 🌿 Spring Data JPA – 반환 타입 정리 (0) | 2025.11.13 |
| 📚 Spring Data JPA Repository 제대로 써보기 (0) | 2025.11.12 |
| Kotlin 기본 구조(예시 코드) (8) | 2025.08.14 |
| Kotlin 기본 함수(예시 코드) (7) | 2025.08.12 |