증권거래소- Matching Engine 구조 정리

zion·2026년 3월 15일

가상 면접 사례로 배우는 대규모 시스템 설계 기초 2에서는 마지막 장에서 거래소의 핵심 구조인 Matching Engine을 중심으로 시스템 아키텍처를 설명한다.

책을 읽고 거래소 시스템 구조를 정리하고, 핵심 개념인 Order Book과 매칭 로직을 Rust로 간단히 정리해 보았다.


거래소 시스템 전체 구조

일반적인 증권거래소 시스템은 다음과 같은 구조를 가진다.

Client
   ↓
Order Gateway
   ↓
Matching Engine
   ↓
Market Data
   ↓
Persistence

각 구성 요소의 역할은 다음과 같다.

Order Gateway

외부 클라이언트의 진입점이다.

  • 주문 검증
  • 인증
  • Rate limit
  • 내부 서비스로 라우팅

Matching Engine

거래소의 핵심 컴포넌트다.

  • 주문을 오더북에 저장
  • 매수/매도 주문 매칭
  • 거래 체결

Market Data

체결 결과와 호가 정보를 외부에 전달한다.

예를 들어

  • 실시간 호가
  • 체결 가격
  • 거래량

같은 데이터가 여기서 생성된다.

Persistence

거래 기록을 저장하는 계층이다.

  • 거래 로그
  • 주문 상태
  • 감사 로그

실제 거래소에서는 이벤트 로그 기반 저장을 많이 사용한다.

대표적인 거래소 예로는

  • NASDAQ
  • 한국거래소

같은 시스템이 있다.


거래소의 핵심 — Order Book

거래소의 핵심 데이터 구조는 Order Book이다.

오더북은 체결되지 않고 대기 중인 주문을 관리하는 자료구조다.

주문은 두 가지로 나뉜다.

  • Bid (매수 주문) — 높은 가격 우선
  • Ask (매도 주문) — 낮은 가격 우선
매수(Bid) — 높은 가격 우선        매도(Ask) — 낮은 가격 우선
─────────────────────              ─────────────────────
100,500원  x 10주                  101,000원  x 5주
100,000원  x 20주                  101,500원  x 15주
 99,500원  x 5주                   102,000원  x 8주

체결은 다음 조건이 만족되면 발생한다.

매수 최고가 ≥ 매도 최저가

Price-Time Priority

대부분의 거래소는 Price-Time Priority 규칙을 사용한다.

우선순위는 다음과 같다.

1️⃣ 가격 우선
2️⃣ 시간 우선

예를 들어 같은 가격에 두 주문이 있다면

10:01 주문
10:03 주문

먼저 들어온 주문이 먼저 체결된다.


오더북 자료구조 설계

오더북에서 가장 중요한 연산은 두 가지다.

1️⃣ 최우선 호가 조회
2️⃣ 같은 가격 내 FIFO 유지

이 두 조건을 만족하기 위해 다음 자료구조를 사용했다.

pub struct OrderBook {
    bids: BTreeMap<Reverse<u64>, VecDeque<Order>>,
    asks: BTreeMap<u64, VecDeque<Order>>,
}

구조 선택 이유는 다음과 같다.

BTreeMap

  • 키가 항상 정렬된 상태 유지
  • 최우선 호가 접근이 쉬움
keys().next()

Reverse

Rust의 기본 정렬은 오름차순이다.

매수 주문은 높은 가격 우선이므로 Reverse로 뒤집는다.


VecDeque

같은 가격의 주문은 FIFO를 유지해야 한다.

push_back()
pop_front()

이 구조로 Price-Time Priority 규칙을 자연스럽게 구현할 수 있다.


주문 매칭 로직

매수 주문이 들어왔을 때 매칭 흐름은 다음과 같다.

1️⃣ 매도 측 최저가 확인
2️⃣ 가격 조건 확인
3️⃣ 체결 수량 계산
4️⃣ 주문 잔량 업데이트
5️⃣ 완전 체결 주문 제거

핵심 로직은 다음과 같다.

fn match_buy(&mut self, mut taker: Order) -> Vec<Trade> {

체결 가격은 항상 maker 주문 기준이다.

먼저 들어온 주문의 가격으로 거래가 체결된다.

실제 거래소도 동일한 규칙을 사용한다.


서비스 통신 — gRPC 선택

서비스 간 통신 방식으로 REST 대신 gRPC를 사용했다.

RESTgRPC
인터페이스OpenAPIproto
직렬화JSONProtobuf
성능텍스트바이너리
스트리밍별도 구현기본 지원

매칭엔진처럼 대량의 주문을 빠르게 처리해야 하는 서비스에서는 JSON 직렬화 비용이 부담이 될 수 있다.

Protobuf는

  • 더 작은 메시지 크기
  • 빠른 파싱

이라는 장점이 있다.


proto — 서비스 계약

gRPC에서는 proto 파일이 서비스 계약(contract) 역할을 한다.

service MatchingEngine {
  rpc SubmitOrder(OrderRequest) returns (OrderResponse);
  rpc CancelOrder(CancelRequest) returns (CancelResponse);
  rpc GetOrderBook(Symbol) returns (OrderBookSnapshot);
}

이 파일 하나로

  • 서버 코드
  • 클라이언트 코드

가 자동 생성된다.

Rust에서는 tonic을 사용했다.

fn main() -> Result<(), Box<dyn std::error::Error>> {
    tonic_build::compile_protos("../proto/matching.proto")?;
    Ok(())
}

실제 거래소와의 차이

이번 구현은 구조 이해를 위한 간단한 실험이다.

실제 거래소 시스템은 훨씬 복잡하다.

예를 들어

  • lock-free 자료구조
  • 멀티 스레드 매칭
  • shard된 order book
  • event sourcing 기반 로그
  • 초저지연 네트워크

같은 기술들이 사용된다.


마치며

증권거래소 시스템에서 가장 중요한 컴포넌트는 Matching Engine이다.

핵심은 복잡한 알고리즘보다 자료구조 설계에 있다.

이번 글에서는

BTreeMap + VecDeque

조합을 사용해 Price-Time Priority 기반 오더북 구조와 매칭 로직을 Rust로 정리해 보았다.

profile
be_zion

0개의 댓글