개발 학습 실습실

이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.

기초개념 › 구조와 운영

메시지 큐

메시지 큐는 무엇이고, 직접 부르는 것과 무엇이 다른가

메시지 큐는 일을 시키는 서버와 일을 하는 서버 사이에 놓인, 일을 적어 두는 줄입니다. 코드 칸은 세 줄입니다. 첫 줄의 주문 서버는 알림 서버에 시킬 일을 "주문 N 알림"이라는 짧은 글(메시지)로 적어 큐에 넣고, 결과를 받으면 바로 다음 일로 넘어갑니다(메시지를 보내는 쪽). 둘째 줄의 큐는 넣은 메시지를 넣은 차례대로 보관합니다. 셋째 줄의 알림 서버는 큐에서 메시지를 하나씩 꺼내 처리합니다(메시지를 받는 쪽). 이 실습은 큐 줄을 지우고 주문 서버가 알림 서버에 요청을 직접 보내게 하거나, 알림 서버가 주문 3건을 보내는 동안 꺼져 있다가 나중에 켜지게 해 가며, 주문 서버가 받는 결과와 알림이 처리되는 모습을 봅니다. 코드 칸은 실행한 프로그램의 소스가 아니라 무엇이 무엇을 하는지 적은 글이고, 실행에 쓴 큐는 이 실습을 위해 쓴 아주 작은 큐 서버입니다(Kafka나 RabbitMQ 같은 실제 제품이 아닙니다).

실행으로 대조 · Node.js 24.18.0의 http 서버 둘(이 실습을 위해 쓴 작은 큐 서버와 직접 요청을 받는 알림 서버)과 주문 서버 역할의 프로그램으로 네 가지 조합을 실행해 대조. Kafka, RabbitMQ 같은 실제 제품은 실행하지 않음

이 실습에 나오는 개념

메시지 큐
보내는 쪽(생산자)과 받는 쪽(소비자) 사이에서 메시지를 보관했다가 전달하는 중간 저장소다. 생산자는 메시지를 넣고 바로 다음 일을 하며, 소비자는 자신의 속도로 꺼내 처리한다. 두 시스템이 서로를 직접 호출하지 않아 결합이 낮아지고, 순간적으로 몰린 요청을 쌓아 두었다가 처리할 수 있다.
생산자와 소비자
생산자(producer)는 메시지를 만들어 보내는 프로그램이고, 소비자(consumer)는 그 메시지를 받아 처리하는 프로그램이다. RabbitMQ 튜토리얼은 메시지를 보내는 프로그램을 producer, 메시지가 오기를 주로 기다리는 프로그램을 consumer라고 부르고, Kafka 문서는 이벤트를 쓰는 클라이언트 애플리케이션을 producer, 이벤트를 읽고 처리하는 쪽을 consumer라고 부른다. 큐를 사이에 두면 생산자는 큐에 메시지를 넣기만 하고, 소비자는 준비됐을 때 큐에서 꺼낸다. Kafka 문서는 둘이 서로를 모르는 채로 완전히 분리되어 있다고 설명한다.
메시지 브로커
메시지 브로커는 생산자가 보낸 메시지를 받아 소비자에게 전달해 주는 중간 프로그램이다. RabbitMQ 튜토리얼은 RabbitMQ를 메시지를 받아서 전달하는 메시지 브로커라고 소개한다. 브로커 안에는 메시지를 보관하는 큐가 있고, 같은 튜토리얼은 큐를 호스트의 메모리와 디스크가 허락하는 만큼 메시지를 담는 큰 완충 공간으로 설명한다. 생산자와 소비자는 서로 직접 연결하지 않고 브로커를 통해 메시지를 주고받는다.

이렇게 바꿔 보세요

처음 설정으로 실행하면

  1. 주문 서버가 주문 3건을 차례로 받습니다. 코드 칸의 첫 줄대로, 주문 서버는 주문을 받을 때마다 알림 서버에 시킬 일을 "주문 N 알림"이라는 짧은 글로 적어 큐에 넣습니다. 이 짧은 글을 메시지라고 합니다. 큐는 메시지를 넣은 차례대로 보관하는 줄이고, 알림 서버는 큐에서 메시지를 꺼내 처리합니다. 알림 서버는 지금 켜져 있고, 켜질 때 큐는 비어 있었습니다. 알림 서버는 0.1초마다 큐를 들여다보고 메시지가 있으면 꺼냅니다. 알림 하나를 처리하는 데는 이 실행에서 0.3초가 걸리게 했습니다.
  2. 주문 서버가 "주문 1 알림" 메시지를 큐에 넣는 요청을 보냅니다. 큐가 메시지를 맨 뒤에 보관하고 "넣었다"고 답합니다. 주문 서버는 이 답을 받는 즉시 이 주문의 결과(성공)를 받고 다음 주문으로 넘어갑니다. 알림 서버가 이 메시지를 처리했는지는 이 답에 들어 있지 않습니다. 큐에 쌓인 메시지는 1건입니다.
  3. 주문 서버가 "주문 2 알림" 메시지를 큐에 넣고, 큐가 "넣었다"고 답하자 결과(성공)를 바로 받습니다. 큐에 쌓인 메시지는 2건입니다.
  4. 주문 서버가 "주문 3 알림" 메시지를 큐에 넣고, 큐가 "넣었다"고 답하자 결과(성공)를 바로 받습니다. 큐에 쌓인 메시지는 3건입니다.
  5. 주문 서버는 주문 3건의 결과를 모두 받았습니다. 알림 서버는 켜져 있지만 0.1초마다 큐를 들여다보고, 알림 하나를 처리하는 데 0.3초가 걸립니다. 그래서 주문 서버가 결과를 모두 받은 때 알림은 아직 하나도 처리되지 않았고, 메시지 3건이 큐에서 차례를 기다립니다. 주문 서버는 이 알림들이 언제 끝나는지 알 수 없습니다.
  6. 알림 서버가 큐에서 맨 앞 메시지 "주문 1 알림"을 꺼내 처리합니다(0.3초). 큐에는 2건이 남습니다. 메시지는 넣은 차례대로 꺼내집니다.
  7. 알림 서버가 큐에서 다음 메시지 "주문 2 알림"을 꺼내 처리합니다. 큐에는 1건이 남습니다.
  8. 알림 서버가 큐에서 다음 메시지 "주문 3 알림"을 꺼내 처리합니다. 큐에는 0건이 남습니다. 알림 3건이 모두 처리되었습니다. 처리된 차례는 주문 1, 주문 2, 주문 3으로 넣은 차례와 같습니다.

실행 결과

코드

# 주문 서버: 주문 3건마다 "주문 N 알림" 메시지를 큐에 넣는다
# 큐: 메시지를 넣은 차례대로 보관한다
# 알림 서버: 큐에서 하나씩 꺼내 처리한다

이 모형의 전제

네 가지 조합을 모두 이 PC의 127.0.0.1 안에서만 실제로 실행해 대조했습니다. 외부 네트워크에는 연결하지 않았고 새로 내려받은 것도 없습니다. 실행에 쓴 큐는 이 실습을 위해 Node.js 24.18.0의 http 서버로 쓴 아주 작은 큐 서버입니다. 메모리의 배열 하나에 메시지를 넣는 요청과 맨 앞 메시지를 꺼내는 요청, 두 가지만 받고, Kafka나 RabbitMQ 같은 실제 메시지 큐 제품이 아닙니다(이 PC에 그런 제품이 없고 설치하지 않았습니다). 그래서 이 실습이 보이는 것은 "큐가 하는 일"의 가장 단순한 모양이고, 실제 제품의 동작을 확인한 것이 아닙니다. 주문 서버 역할의 프로그램은 주문 3건을 앞 주문의 결과를 받은 뒤에 다음 주문을 보내는 식으로 차례로 보냅니다. 큐에 넣는 조합에서는 큐 서버에 넣는 요청을 보내고 그 답을 결과로 삼으며, 직접 부르는 조합에서는 알림 서버에 요청을 보내 처리가 끝난 뒤의 답을 결과로 삼습니다. 알림 서버는 직접 요청을 받는 http 서버이거나(직접 부르는 조합), 0.1초마다 큐 서버에서 메시지 하나를 꺼내 처리하는 반복(큐에 넣는 조합)이고, 어느 쪽이든 알림 하나를 처리하는 데 0.3초가 걸리게 했으며 한 번에 하나씩 처리합니다. 0.1초와 0.3초는 이 실습에서 정한 값입니다. 알림 서버가 꺼져 있다가 켜지는 조합은 주문 3건의 결과를 모두 받은 뒤에 알림 서버를 켰고, 꺼져 있는 동안 그 번호에는 기다리는 프로그램이 없었습니다. 실행 프로그램은 화면의 코드와 같은 글을 만들어 읽고(몇 건인지, 큐 줄이 있는지, 알림 서버가 어떻게 받는지, 꺼져 있다가 켜지는지) 그 내용대로 띄웠습니다. 대조한 것은 주문 서버가 받은 결과(성공이나 실패, 그리고 알림 처리가 끝난 뒤에 결과를 받았는지 끝나기 전에 받았는지를 사건이 일어난 순서 기록으로 판단), 처리된 알림과 그 차례, 알림 서버가 켜질 때 큐에 있던 메시지 수이고, 큐에서 꺼낸 차례가 넣은 차례와 같았다는 것, 꺼져 있는 알림 서버에 직접 보낸 요청은 연결 거부 오류(ECONNREFUSED)로 모두 실패하고 알림 서버가 켜진 뒤 1초를 지켜봐도 처리된 알림이 없었다는 것도 확인했습니다. 큐에 넣는 조합에서 주문 서버가 결과를 모두 받은 때 큐에는 메시지 3건이 있었는데, 이것은 알림 서버가 켜져 있어도 0.1초마다 큐를 들여다보게 만든 이 실행의 구조 때문입니다. 알림 서버가 켜질 때 큐가 비어 있었다는 것은 이 실행에서 알림 서버를 주문보다 먼저 켰기 때문입니다. 문서로 확인한 것은 RabbitMQ 튜토리얼 1(Hello World)의 Introduction(보내는 프로그램은 producer, 메시지가 오기를 주로 기다리는 프로그램은 consumer, 큐는 메시지를 담는 우체통 같은 것이고 사실상 큰 메시지 완충 공간이며, RabbitMQ는 메시지를 받아서 전달하는 메시지 브로커라는 정의)과 Apache Kafka 문서 Introduction 쪽(https://kafka.apache.org/intro의 Main Concepts and Terminology: producer는 이벤트를 쓰는 클라이언트 애플리케이션, consumer는 이벤트를 읽고 처리하는 쪽이며 둘은 서로를 모르는 채로 완전히 분리되어 있다는 설명)입니다. 문서는 WebFetch가 돌려준 인용문으로 확인했고(페이지 전체를 직접 읽지는 않았습니다) 절 번호는 따로 없습니다. 화면에서 줄인 것이 많습니다. 주문 서버가 메시지를 큐에 넣는 일과 큐가 받아 두는 일은 하나의 요청과 답으로 줄였고, 그 답이 "받아 두었다"는 것만 알려 줄 뿐 알림이 처리됐는지는 알려 주지 않는 것은 이 작은 큐 서버를 그렇게 만들어서 나온 동작입니다. 실제 제품은 메시지를 처리한 쪽이 처리했다고 알리는 확인 응답, 큐를 디스크에 보관하는 것, 큐 자체가 꺼지는 경우, 메시지가 사라지거나 두 번 전달되는 경우, 소비자를 여럿 두었을 때의 순서와 분배, 실패한 처리를 다시 하는 것을 다루는데, 이 실습은 그중 어느 것도 보지 않았습니다. 이런 세부는 "세부 동작 실습: 메시지 큐 기본", "세부 동작 실습: Kafka 파티션과 컨슈머", "세부 동작 실습: Saga와 Outbox"에서 다룹니다. 큐가 어울리는 일과 직접 부르는 것이 어울리는 일을 가르는 기준(결과가 바로 필요한가)도 이 실습은 판단하지 않았습니다. 브라우저는 쓰지 않았고, 알림 서버 하나와 주문 서버 하나만 두었습니다.

앞 실습: 캐시 · 다음 실습: 배포

이 실습에 나오는 개념

이 실습에서 보는 것

설정

코드


      
    

그림

실행 비교

이렇게 바꿔 보세요