๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Kafka

6. RabbitMQ

by ์ ผ์ ผ39 2026. 3. 23.

6.1 RabbitMQ๋ž€?

  • ์ •์˜: ๋ฉ”์‹œ์ง€๋ฅผ ์†ก์‹ ์ž(ํ”„๋กœ๋“€์„œ)๋กœ๋ถ€ํ„ฐ ์ˆ˜์‹ ์ž(์ปจ์Šˆ๋จธ)์—๊ฒŒ ์ „๋‹ฌํ•˜๋Š” ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค(Message Broker)
  • ์ž‘๋™ ์›๋ฆฌ: ๋ฐ์ดํ„ฐ๋ฅผ ์ฆ‰์‹œ ์ „๋‹ฌํ•˜์ง€ ์•Š๊ณ  ํ(Queue)์— ์•ˆ์ „ํ•˜๊ฒŒ ์ €์žฅํ–ˆ๋‹ค๊ฐ€, ์ ์ ˆํ•œ ์‹œ์ ์— ์ปจ์Šˆ๋จธ์—๊ฒŒ ์ „๋‹ฌํ•จ.
  • ํ•ต์‹ฌ ์—ญํ• :
    1. ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ: ์ž‘์—… ์™„๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ๋ฉ”์‹œ์ง€๋งŒ ๋˜์ง€๋ฏ€๋กœ ์‹œ์Šคํ…œ ์‘๋‹ต์„ฑ์ด ๋†’์•„์ง.
    2. ๋ถ€ํ•˜ ๋ถ„์‚ฐ: ์—ฌ๋Ÿฌ ์ปจ์Šˆ๋จธ๊ฐ€ ํ๋ฅผ ๋‚˜๋ˆ„์–ด ์ฒ˜๋ฆฌํ•˜์—ฌ ์‹œ์Šคํ…œ ๋ถ€ํ•˜๋ฅผ ๊ท ํ˜• ์žˆ๊ฒŒ ์กฐ์ ˆํ•จ.
    3. ๋‚ด๊ฒฐํ•จ์„ฑ: ๋ฉ”์‹œ์ง€ ์ง€์†์„ฑ์„ ํ†ตํ•ด ์žฅ์•  ๋ฐœ์ƒ ์‹œ์—๋„ ๋ฐ์ดํ„ฐ ์†์‹ค์„ ๋ฐฉ์ง€ํ•จ.

6.1.2 RabbitMQ์˜ ์žฅ๋‹จ์ 

๊ตฌ๋ถ„ ์ฃผ์š” ๋‚ด์šฉ
์žฅ์  ์‹ ๋ขฐ์„ฑ: ๋””์Šคํฌ ์ €์žฅ ๋ฐ ACK(ํ™•์ธ ์‘๋‹ต) ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ œ๊ณต.

์œ ์—ฐ์„ฑ: ๋‹ค์–‘ํ•œ ๋ฉ”์‹œ์ง€ ํŒจํ„ด๊ณผ AMQP, MQTT ๋“ฑ ๋‹ค์ค‘ ํ”„๋กœํ† ์ฝœ์„ ์ง€์›ํ•จ.

ํ™•์žฅ์„ฑ: ํด๋Ÿฌ์Šคํ„ฐ๋ง ๋ฐ ํŽ˜๋”๋ ˆ์ด์…˜์„ ํ†ตํ•œ ๊ณ ๊ฐ€์šฉ์„ฑ ๊ตฌํ˜„ ๊ฐ€๋Šฅํ•จ.

๊ด€๋ฆฌ ์šฉ์ด: ์ง๊ด€์ ์ธ ์›น UI ๋Œ€์‹œ๋ณด๋“œ์™€ ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹œ์Šคํ…œ์„ ์ œ๊ณตํ•จ.
๋‹จ์  ์šด์˜ ๋ณต์žก์„ฑ: ์ดˆ๊ธฐ ์„ค์ • ๋ฐ ํด๋Ÿฌ์Šคํ„ฐ ๊ด€๋ฆฌ์— ๋…ธ๋ ฅ์ด ํ•„์š”ํ•จ.

์˜ค๋ฒ„ํ—ค๋“œ: ์ค‘์•™ ๋ธŒ๋กœ์ปค ๋ฐฉ์‹์ด๋ฏ€๋กœ ํŠธ๋ž˜ํ”ฝ ํญ์ฃผ ์‹œ ๋ธŒ๋กœ์ปค ์ž์ฒด๊ฐ€ ๋ณ‘๋ชฉ์ด ๋  ์ˆ˜ ์žˆ์Œ.

๋ฆฌ์†Œ์Šค ์†Œ๋น„: ๋ฉ”๋ชจ๋ฆฌ์™€ CPU ์‚ฌ์šฉ๋Ÿ‰์ด ๋†’์€ ํŽธ์ž„.

๋ฉ”์‹œ์ง€ ํฌ๊ธฐ: ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ์ „์†ก์—๋Š” ์ ํ•ฉํ•˜์ง€ ์•Š์Œ.

6.2 RabbitMQ์˜ ๊ธฐ๋ณธ ๊ตฌ์„ฑ ์š”์†Œ

  • ๋ฉ”์‹œ์ง€(Message): ์ „๋‹ฌํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฐ์ดํ„ฐ์˜ ์ตœ์†Œ ๋‹จ์œ„ (์˜ˆ: ์ฃผ๋ฌธ ๋‚ด์—ญ)
  • ํ”„๋กœ๋“€์„œ(Producer): ๋ฉ”์‹œ์ง€๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋ฐœ์†กํ•˜๋Š” ์ฃผ์ฒด
  • ํ(Queue): ๋ฉ”์‹œ์ง€๊ฐ€ ๋จธ๋ฌด๋ฅด๋Š” ์ €์žฅ์†Œ๋กœ, FIFO(์„ ์ž…์„ ์ถœ) ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•จ.
  • ์ปจ์Šˆ๋จธ(Consumer): ํ์—์„œ ๋ฉ”์‹œ์ง€๋ฅผ ๊ฐ€์ ธ์™€ ์‹ค์ œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง(์˜ˆ: ์ด๋ฉ”์ผ ๋ฐœ์†ก)์„ ์ฒ˜๋ฆฌํ•จ.
  • ์ต์Šค์ฒด์ธ์ง€(Exchange): ํ”„๋กœ๋“€์„œ๋กœ๋ถ€ํ„ฐ ๋ฐ›์€ ๋ฉ”์‹œ์ง€๋ฅผ ์–ด๋–ค ํ๋กœ ๋ณด๋‚ผ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๋ผ์šฐํ„ฐ ์—ญํ• 

6.3 RabbitMQ์™€ AMQP ํ”„๋กœํ† ์ฝœ

  • AMQP (Advanced Message Queuing Protocol): ๋ฉ”์‹œ์ง€ ์ง€ํ–ฅ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์œ„ํ•œ ํ‘œ์ค€ ํ”„๋กœํ† ์ฝœ
  • ์ฃผ์š” ๊ฐœ๋…:
    • ๋ฐ”์ธ๋”ฉ(Binding): ์ต์Šค์ฒด์ธ์ง€์™€ ํ ์‚ฌ์ด์˜ ์—ฐ๊ฒฐ ๊ณ ๋ฆฌ
    • ์ต์Šค์ฒด์ธ์ง€ ์œ ํ˜•:
      1. Direct: ๋ผ์šฐํŒ… ํ‚ค๊ฐ€ ์ •ํ™•ํžˆ ์ผ์น˜ํ•˜๋Š” ํ๋กœ ์ „๋‹ฌํ•จ
        * ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ผ์šฐํŒ… ํ‚ค๊ฐ€ error์ธ ๋ฉ”์‹œ์ง€๋Š” error๋ผ๋Š” ๋ฐ”์ธ๋”ฉ ํ‚ค๋ฅผ ๊ฐ€์ง„ ํ๋กœ ์ „๋‹ฌ๋จ
      2. Topic: ํŒจํ„ด(*, #)์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ผ์šฐํŒ… ํ‚ค์™€ ๋งค์นญ๋˜๋Š” ํ๋“ค๋กœ ์ „๋‹ฌํ•จ.
      3. Fanout: ๋ผ์šฐํŒ… ํ‚ค๋ฅผ ๋ฌด์‹œํ•˜๊ณ  ์—ฐ๊ฒฐ๋œ ๋ชจ๋“  ํ์— ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธํ•จ.
      4. Headers: ๋ผ์šฐํŒ… ํ‚ค ๋Œ€์‹  ๋ฉ”์‹œ์ง€ ํ—ค๋” ๊ฐ’์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ผ์šฐํŒ…ํ•จ.

๋”๋ณด๊ธฐ

(๊ทธ๋ƒฅ ์‹ค์Šตํ•  ๋•Œ ์ ์–ด๋‘”๊ฑฐ๋ผ ์•ˆ ๋ณด์…”๋„ ๋ฉ๋‹ˆ๋‹ค)

market.product ์ต์Šค์ฒด์ธ์ง€์— ๋˜‘๊ฐ™์€(๊ตฌ์„ฑํŽธ์ง‘์—์„œ ๋ณต์ œํ•œ) product๊ฐ€ 2๊ฐœ ๋‹ฌ๋ ค์žˆ์Œ.. ๋ฉ”์‹œ์ง€๊ฐ€ ์™”์„ ๋•Œ ๋ˆ„๊ตฌ์—๊ฒŒ ๊ฐˆ ๊ฒƒ์ธ๊ฐ€ ํ™•์ธ! (๋ผ์šด๋“œ ๋กœ๋นˆ)

๋„์ปค ์‹คํ–‰ ํ›„
http://localhost:15672/
์ดˆ๊ธฐ ๋กœ๊ทธ์ธ์—์„œ ๋”ฑํžˆ ์„ค์ •ํ•œ ๊ฒŒ ์—†์œผ๋ฉด guest guest

 

6.4 ์‹ค์Šต 1 : ์ธํ”„๋ผ ๊ตฌ์ถ• (Docker)

๋„์ปค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ด€๋ฆฌ ๊ธฐ๋Šฅ์ด ํฌํ•จ๋œ RabbitMQ๋ฅผ ์‹คํ–‰ํ•จ.

# -d: ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์‹คํ–‰
# -p: 5672(ํ†ต์‹ ์šฉ), 15672(๊ด€๋ฆฌ UI์šฉ) ํฌํŠธ ํฌ์›Œ๋”ฉ
# rabbitmq:management: ๊ด€๋ฆฌ ๋„๊ตฌ๊ฐ€ ํฌํ•จ๋œ ์ด๋ฏธ์ง€ ์‚ฌ์šฉ
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 --restart=unless-stopped rabbitmq:management
  • ์ ‘์†: localhost:15672
    • ์ดˆ๊ธฐ ๋กœ๊ทธ์ธ์—์„œ ๋”ฑํžˆ ์„ค์ •ํ•œ ๊ฒŒ ์—†์œผ๋ฉด (ID/PW: guest / guest)

6.5 ์‹ค์Šต 2 : Order Application ๊ตฌํ˜„

1) build.gradle ์„ค์ •

dependencies {
    // RabbitMQ ์—ฐ๋™์„ ์œ„ํ•œ ์Šคํƒ€ํ„ฐ
    implementation 'org.springframework.boot:spring-boot-starter-amqp'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    // ํ…Œ์ŠคํŠธ์šฉ RabbitMQ ์œ ํ‹ธ๋ฆฌํ‹ฐ
    testImplementation 'org.springframework.amqp:spring-rabbit-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

2) application.properties ์„ค์ •

spring.application.name=order

# ์ปค์Šคํ…€ ์„ค์ •: ์ต์Šค์ฒด์ธ์ง€ ๋ฐ ํ ์ด๋ฆ„ ์ •์˜
message.exchange=market
message.queue.product=market.product
message.queue.payment=market.payment

# RabbitMQ ์—ฐ๊ฒฐ ์ •๋ณด
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

3) OrderApplicationQueueConfig.java (์„ค์ • ์ •๋ณด)

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class OrderApplicationQueueConfig {

    @Value("${message.exchange}")
    private String exchange;

    @Value("${message.queue.product}")
    private String queueProduct;

    @Value("${message.queue.payment}")
    private String queuePayment;

    // 1. ์ต์Šค์ฒด์ธ์ง€ ๋นˆ ๋“ฑ๋ก (Topic ๋ฐฉ์‹)
    @Bean public TopicExchange exchange() { return new TopicExchange(exchange); }

    // 2. ๋ฉ”์‹œ์ง€๋ฅผ ๋‹ด์„ ํ ๋นˆ ๋“ฑ๋ก
    @Bean public Queue queueProduct() { return new Queue(queueProduct); }
    @Bean public Queue queuePayment() { return new Queue(queuePayment); }

    // 3. ๋ฐ”์ธ๋”ฉ: ์ต์Šค์ฒด์ธ์ง€์™€ ํ๋ฅผ ์—ฐ๊ฒฐ (๋ผ์šฐํŒ… ํ‚ค ์„ค์ •)
    @Bean public Binding bindingProduct() { 
        return BindingBuilder.bind(queueProduct()).to(exchange()).with(queueProduct); 
    }
    @Bean public Binding bindingPayment() { 
        return BindingBuilder.bind(queuePayment()).to(exchange()).with(queuePayment); 
    }
}

4) OrderController.java & OrderService.java

// Controller: ์‚ฌ์šฉ์ž ์š”์ฒญ์„ ๋ฐ›๋Š” ์ง„์ž…์ 
@RestController
@RequiredArgsConstructor
public class OrderController {
    private final OrderService orderService;

    @GetMapping("/order/{id}")
    public String order(@PathVariable String id) {
        orderService.createOrder(id); // ์ฃผ๋ฌธ ์ƒ์„ฑ ๋กœ์ง ํ˜ธ์ถœ
        return "Order complete";
    }
}

// Service: ๋ฉ”์‹œ์ง€ ๋ฐœํ–‰ ๋กœ์ง ๋‹ด๋‹น
@Service
@RequiredArgsConstructor
public class OrderService {
    @Value("${message.queue.product}")
    private String productQueue;

    @Value("${message.queue.payment}")
    private String paymentQueue;

    private final RabbitTemplate rabbitTemplate; // ์Šคํ”„๋ง์ด ์ œ๊ณตํ•˜๋Š” RabbitMQ ์†ก์‹  ํ…œํ”Œ๋ฆฟ

    public void createOrder(String orderId) {
        // convertAndSend(๋ผ์šฐํŒ…ํ‚ค, ๋ฉ”์‹œ์ง€๊ฐ์ฒด) ํ˜•ํƒœ๋กœ ์ „์†กํ•จ
        // ์—ฌ๊ธฐ์„œ๋Š” ๊ฐ๊ฐ ์ƒํ’ˆ ์ •๋ณด ํ์™€ ๊ฒฐ์ œ ์ •๋ณด ํ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋ƒ„
        rabbitTemplate.convertAndSend(productQueue, orderId);
        rabbitTemplate.convertAndSend(paymentQueue, orderId);
    }
}

6.6 ์‹คํ–‰ ๋ฐ ํ™•์ธ ๊ฒฐ๊ณผ

  1. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰: /order/1 ๊ฒฝ๋กœ๋กœ GET ์š”์ฒญ์„ ๋ณด๋ƒ„.
  2. ๋Œ€์‹œ๋ณด๋“œ ํ™•์ธ: localhost:15672 ์ ‘์† ์‹œ market ์ต์Šค์ฒด์ธ์ง€์™€ 2๊ฐœ์˜ ํ(market.product, market.payment)๊ฐ€ ์ž๋™ ์ƒ์„ฑ๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Œ.
  3. ๋ฉ”์‹œ์ง€ ํ™•์ธ:
  • ํ ์ƒ์„ธ ํŽ˜์ด์ง€์˜ 'Get Messages' ์„น์…˜์„ ํ†ตํ•ด ์‹ค์ œ ์ „๋‹ฌ๋œ 1 ์ด๋ผ๋Š” ์ฃผ๋ฌธ ID ๊ฐ’์„ ์ง์ ‘ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Œ.
  • ํ˜„์žฌ ์ปจ์Šˆ๋จธ(์ˆ˜์‹ ์ž)๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”์‹œ์ง€๋Š” ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š๊ณ  ํ์˜ Total ํ•ญ๋ชฉ์— ์Œ“์—ฌ ์žˆ๊ฒŒ ๋จ.

๐Ÿ’ก ์ถ”๊ฐ€ ์„ค๋ช…: ์‹ค๋ฌด์—์„œ๋Š” ๋ฉ”์‹œ์ง€ ์œ ์‹ค์„ ๋ง‰๊ธฐ ์œ„ํ•ด ํ ์ƒ์„ฑ ์‹œ durable=true ์˜ต์…˜์„ ์ฃผ๊ฑฐ๋‚˜, ์ปจ์Šˆ๋จธ์—์„œ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ์ค‘ ์—๋Ÿฌ๊ฐ€ ๋‚˜๋ฉด ํ๋กœ ๋‹ค์‹œ ๋Œ๋ ค๋ณด๋‚ด๋Š”(Redeliver) ์ „๋žต์„ ์„ธ๋ฐ€ํ•˜๊ฒŒ ๊ตฌ์„ฑํ•ด์•ผ ํ•จ.