본문 바로가기
Dev

코빗은 왜 체결 엔진에 Rust를 선택했을까? (feat. 추리)

by Day-T 2026. 4. 22.

 

💡 코빗 기술 블로그를 보고 개인적으로 추리한 내용입니다.

💡 코빗과는 무관합니다.


두 편의 블로그

 

2025년 9월, 코빗 CTO는 블로그에 「Go와 Rust, 코빗은 이렇게 나눠 씁니다」를 공개했다. 글 중간에 짧은 아키텍처 도식이 있었다.


눈에 띄는 점이 있었다. 같은 시스템의 내부를 두 언어로 나눠 놓은 것이다. 더 구체적으로는 체결 엔진과 시세 서비스만 Rust였다.

한 달 뒤인 10월 14일, 같은 저자의 후속 글이 올라왔다. 「완전히 새롭게 구축한 코빗의 가상자산 거래 시스템」. 1편이 언어 선택에 초점을 맞췄다면, 2편은 시스템 전체의 설계를 서술했다. Event-Driven Architecture, Kafka 기반 이벤트 버스, Transactional Outbox, Active/Standby 이중화된 체결 엔진.

두 글을 나란히 놓고 읽으면 질문이 구체화된다.

> "Korbit은 왜 체결 엔진에 Rust를 선택했을까?"

단순히 "빠른 언어가 필요했다"로 답할 수 있는 질문은 아니다. 체결 엔진이 Active/Standby 이중화 속에서 Kafka로 상태를 동기화하고 인메모리 오더북을 주기적으로 스냅샷하며 이벤트를 재생 가능하게 보존하는 시스템이라면, 거기에 맞는 언어는 어떤 성질을 가져야 하는가.

 

이 글은 그 질문을 따라가는 추리다.


Chapter 1 · 선택의 발단

 

 

⚙️ 2013년의 유산

코빗은 2013년 설립된 국내 최초의 가상자산 거래소다. 10년이 지난 시점에 코빗의 거래 시스템은 2018년부터 운영되어 온 1세대 아키텍처였다. 2편 블로그는 당시 상태를 짧게 요약한다.

> "2018년부터 2024년 상반기까지 쓰이던 거래 시스템은 가상자산 시장이 더욱 성장하면서 그 한계가 분명해졌고, 2024년 초까지 거래량 급증 시 장애가 간헐적으로 반복되었습니다."

거래소에서 "장애가 간헐적으로 반복"되었다는 서술은, 사용자의 주문이 지연되거나 막힌 순간이 여러 번 있었다는 뜻이다.

 


⚙️ 레거시의 구조적 문제

블로그는 원인을 명확히 지목한다.

> "거대한 모놀리식 구조와 동기 처리 방식이 결합되어 있었기에 특정 기능이 지연되면 주문, 체결, 잔고 갱신 등이 연쇄적으로 지연되며 결국 전체 시스템이 다 흔들리는 식이었습니다. 데이터가 단일 데이터베이스에 집중되어 병목도 상당했고…"


한 군데가 느려지면 모두가 같이 느려진다. 도미노 구조다. 365일 24시간 운영해야 하는 거래소가 가장 피해야 할 구조다.

 


⚙️ 전면 재구축 결정

2023년 초, 코빗 CTO가 선임된다. 그는 곧 전면 재구축을 결정한다.

> "거래 시스템을 처음부터 다시 만들자."

거래소는 영업을 멈출 수 없는 시스템이다. 사용자의 자산이 움직이는 와중에 기반을 바꾸는 작업은 쉽지 않다. 그럼에도 부분 수선으로는 이미 한계였다. 공사는 약 1년 진행되었고, 2024년 6월 새 시스템이 운영 환경에 배포된다.

 


⚙️ 새 설계의 세 축

새 시스템의 원칙은 블로그에 세 단어로 정리되어 있다.

> 성능, 안정성, 복원력.

그리고 이를 구현하는 세 가지 축.

1. 이벤트 기반(Event-Driven) 설계 — 서비스 간 직접 호출 최소화
2. 마이크로서비스 아키텍처(MSA) — 도메인별 독립 확장
3. Kafka 기반 비동기 이벤트 버스 — 처리 순서 보장 + 재생 가능성


구조의 중심에는 체결 엔진이 있다. 이벤트를 소비해 매칭을 수행하고 결과를 다시 이벤트로 발행한다. 여기서 원래 질문으로 돌아간다. 왜 이 위치만 Rust였을까. 답을 찾으려면 이 위치가 어떤 조건을 요구하는지부터 봐야 한다.


Chapter 2 · 현장의 조건 — 가상자산 거래소라는 공간

 

 

⚙️ 전통 금융과의 차이

가상자산 거래소는 전통 금융과 성격이 다르다. 2편 블로그가 이 차이를 정리한다.


주식 시장은 증권사·거래소·예탁결제원이 역할을 나누고, 결제 주기는 T+2다. 자금 정산에 이틀의 여유가 있다. 가상자산 거래소는 이 과정을 한 조직이 전부 처리하며 결제는 즉시 이루어진다. 폐장도 없다.

코빗은 2025년 10월 기준 일일 천만 건 단위의 주문 트랜잭션을 처리한다. 급등락이 오면 이 부하는 몇 배로 뛴다. 블로그가 이 특성을 한 줄로 정리한다.

> "가상자산 거래소는 단순한 웹, 앱 서비스가 아니라 고도의 설계를 요구하는 미션 크리티컬 시스템이라고 보는 것이 정확합니다."

 


⚙️ 체결 엔진의 두 요구 조건


체결 엔진은 두 조건을 동시에 만족해야 한다.

조건 의미
초저지연 초당 수천 건 이상, 급등장에는 수만 건의 주문을 마이크로초 단위로 매칭
결정론적 성능 평균이 아니라 모든 주문이 예측 가능한 지연 안에 처리되어야 한다


특히 두 번째 조건이 까다롭다. "대체로 빠르다"로는 부족하고 "언제 어느 순간에도 일정해야" 한다. 이 지점에서 GC(가비지 컬렉터)를 가진 언어들이 구조적 약점을 드러낸다. GC는 근본적으로 "언제 얼마나 멈출지 보장을 주지 못하는" 메커니즘이기 때문이다.

여기까지가 1편 블로그의 단서 범위였다. 2편 블로그의 추가 단서들은 조금 뒤에 본다. 그전에 후보 언어인 Rust부터 확인한다.


Chapter 3 · Rust의 배경

 

 

⚙️ Rust의 탄생 배경

 

2000년대 중반, Graydon Hoare는 개인 프로젝트로 새 언어 설계를 시작했다. 2009년부터 Mozilla가 후원하며 차세대 브라우저 엔진(Servo) 개발에 힘을 실었다. 당시 C++은 성능을 주는 대신 메모리 관리 문제를 개발자 규율에 걸어 두는 언어였다. 댕글링 포인터, 이중 해제, 데이터 경합이 반복해서 보안 취약점으로 이어졌다.

Microsoft 보안 응답 센터(MSRC)가 공개한 자료에 따르면, 자사 제품 CVE의 약 70%가 메모리 안전성 문제였다. Google도 Chromium에서 비슷한 진단을 했다. "고성능을 위해 지불해 온 대가"가 이 비율이었다.

Hoare의 질문은 단순했다.

> "성능을 포기하지 않으면서 메모리 버그를 컴파일 시점에 차단할 수는 없을까?"

Rust는 이 질문에서 출발했다. 즉 처음부터 "극한의 성능이 필요하지만 안정성도 포기할 수 없는 시스템"을 겨냥했다. 이 설계 의도는 체결 엔진의 요구 사항과 정확히 겹친다.

 


⚙️ 업계 타임라인


참고로 Microsoft의 Galen Hunt가 올린 "2030년까지 C/C++ 대체"라는 채용 공고는 일부 매체에 "MS가 윈도를 Rust로 재작성한다"로 확대 해석되었고 본인이 해명했다. 실상은 "대형 벤더들이 Rust를 C/C++ 후임 후보로 다루기 시작했다" 정도가 정확하다.

공통점은 분명하다. 시스템 안정성이 사업 생존과 직결되는 조직이 먼저 Rust를 도입하고 있다. 코빗이 이 리스트에 있다는 점이 출발점이다.


Chapter 4 · 소유권 — GC 없는 메모리 관리

 

 

⚙️ 두 진영 사이의 선택지

메모리 관리 전략은 전통적으로 두 갈래였다.


한쪽은 개발자 규율에 안전을 걸고 다른 쪽은 런타임 GC에 위임한다. 양쪽 모두 어딘가를 포기한다.

Rust는 중간에 다른 길을 제시한다. GC도 없고 `free`도 없다. 컴파일러가 소스 코드만 보고 "이 메모리는 어느 지점에서 해제될 것인가"를 정적으로 추론한다.

 


⚙️ 세 가지 규칙

Rust 소유권의 규칙은 셋이다.

1. 모든 값은 단 하나의 소유자(owner)를 가진다.
2. 소유자가 스코프를 벗어나면 값은 자동으로 해제된다.
3. 값을 다른 변수에 대입하거나 함수에 넘기면 소유권이 이동(move)한다. (단, `i32`·`bool` 등 `Copy` 트레이트를 구현한 타입은 복사된다.)

 


소유권이 이동한 변수는 다시 쓸 수 없다. 컴파일러가 거부한다. 이중 해제, use-after-free 같은 C/C++의 전형적 버그가 언어 차원에서 차단된다.

 


⚙️ 언어별 대입 비교

 

Java/Python은 참조를 공유한다. 안전하지만 회수 시점은 GC가 정한다. C++은 기본적으로 값을 복제한다. `std::string` 같은 RAII 타입에선 안전하지만 raw pointer에서는 얕은 복사로 double-free 위험이 남는다. Rust는 `s1`을 무효화하고 `s2`로 소유권을 넘긴다.

 


⚙️ 체결 엔진과의 접점

체결 엔진은 초당 수천~수만 건의 주문을 처리하면서 매 주문마다 객체를 할당·해제한다. GC 언어에서는 이 할당이 쌓이다가 Stop-the-world가 트리거 되어 매칭 루프가 멈춘다.

Rust에서는 해제 시점이 컴파일 타임에 결정되어 스코프 단위로 분산된다. "언제 GC가 찾아올지 모른다"는 불안이 없다. 체결 엔진이 요구하는 꼬리 지연의 예측 가능성은 이 구조 위에서 가능하다.

첫 번째 접점을 확보했다.


Chapter 5 · 빌림과 Fearless Concurrency

 

 

⚙️ 빌림 규칙

소유권만 있으면 코드가 불편하다. 값을 읽을 때마다 소유권을 옮길 수는 없다. Rust는 빌림(borrowing)으로 이를 해결한다. 소유권은 두고 값을 참조만 한다. 단, 철칙이 붙는다.

참조 종류 허용 개수 다른 참조와의 공존
불변 참조 `&T` 여러 개 불변끼리는 가능
가변 참조 `&mut T` 오직 1개 어떤 참조와도 불가


이 두 줄은 멀티스레드 환경의 데이터 경합(Data Race) 정의 그 자체다. "누군가 쓰고 있을 때 다른 누구도 읽거나 쓸 수 없다"는 동시성 조건을 Rust는 컴파일 타임에 검증한다.

 


⚙️ Send와 Sync

소유권·빌림·수명이 합쳐지면 멀티스레드 환경의 데이터 경합이 컴파일 타임에 사라진다. Rust 커뮤니티는 이를 Fearless Concurrency라고 부른다.

타입 시스템에는 두 마커 트레이트가 있다.


- `Send` — 이 타입은 다른 스레드로 소유권을 넘겨도 안전하다.
- `Sync` — 이 타입은 여러 스레드에서 동시에 참조해도 안전하다.


`Arc` 대신 `Rc`(단일 스레드용)를 넣으면 컴파일러가 거부한다.

 


⚙️ 체결 엔진과의 접점

실제 체결 엔진은 단일 스레드가 아니다.


매칭 스레드가 오더북을 수정하는 사이 시세 스레드가 읽으면 중간 상태가 노출된다. 잔량이 순간적으로 음수가 되거나 가격이 역전된다. 재현이 어려운 버그다.

Rust는 이런 코드를 컴파일 단계에서 거부한다. 개발자는 설계를 바로잡아야 한다. 가장 흔한 해법은 액터 모델 — 오더북의 소유권을 단 하나의 스레드에 귀속시키고 외부는 채널로만 접근하는 방식이다.


다른 언어에서는 권장 패턴으로 배우는 구조를 Rust에서는 타입 시스템이 강제한다. 두 번째 접점.


Chapter 6 · 제로 비용 추상화와 생태계

 

 

⚙️ 추상화의 비용

Rust는 이터레이터, 제네릭, 트레이트, 클로저 같은 고수준 추상화를 제공하면서도 런타임 비용이 0이다. 이 개념은 Bjarne Stroustrup의 정의가 유명하다.

> "쓰지 않는 것에 대해서는 비용을 지불하지 않는다.  
>  쓰는 것에 대해서는, 그보다 더 잘 손으로 짤 수도 없다."


LLVM 백엔드는 이 체인을 단일 for 루프로 펼친다(monomorphization + inlining). 결과는 손으로 짠 C 코드와 거의 같다.


체결 엔진의 핫 패스에서 이 특성은 유용하다. 선언적 코드로 가독성을 지키면서 성능은 저수준으로 얻는다.

 


⚙️ Cargo와 주요 크레이트


Gradle/Maven/sbt, pip/poetry/ruff 같은 도구를 조립해 본 개발자라면 Cargo의 통합성이 편리하다고 느낀다. `Cargo.toml`과 `Cargo.lock`으로 빌드의 재현 가능성이 보장된다.

체결 엔진에 직결되는 크레이트는 다음과 같다.

크레이트 역할 체결 엔진에서의 쓰임
tokio 비동기 런타임 Kafka 컨슈머/프로듀서 파이프라인
rdkafka Kafka 클라이언트 이벤트 송수신
crossbeam Lock-Free 큐 Lock-Free Ring Buffer · 시세 전파
prost/serde 직렬화 Protocol Buffers / JSON
parking_lot 고속 Mutex 공유 상태 보호
metrics/prometheus 관측성 지연 히스토그램


특히 `crossbeam`을 눈여겨볼 필요가 있다. 1편 블로그에 등장하는 "Lock-Free Ring Buffer"는 이 크레이트 생태계 없이는 현실적이지 않다.

세 번째 접점까지 확보했다. 이제 실제 코빗 시스템으로 들어간다.


Chapter 7 · 코빗 시스템의 구성

 

지금까지 Rust의 설계 특성을 살폈다. 다음은 그 특성이 코빗의 실제 시스템에서 어떻게 사용되는지 확인할 차례다. 2편 블로그의 정보가 여기서 집중적으로 쓰인다.

 


⚙️ 단서 ① — Event-Driven 흐름과 체결 엔진의 위치

2편 블로그는 이벤트 흐름을 명확히 그려 둔다.

> 1. 주문이 접수되면 주문 서비스가 이벤트를 발행합니다.
> 2. 체결 엔진이 이를 소비해 매칭을 수행합니다.
> 3. 매칭 결과에 따른 이벤트가 발행됩니다.
> 4. 잔고 서비스와 시세 서비스가 이를 소비해 각자 상태를 갱신합니다.


주목할 점은 체결 엔진의 위치다. 주변이 전부 비동기로 흘러가는 구조에서, 체결 엔진만 순서와 상태를 엄격히 관리해야 하는 동기 로직이다. "151,000원에 0.3 BTC 매수"라는 주문은 그 순간의 오더북 상태에 정확히 반응해야 한다. 여기서 결정론적 실행이 중요해진다.

 


⚙️ 단서 ② — 400만 건 vs 42,000건

1편 블로그에는 두 숫자가 등장한다.

- 1초당 400만 건 — In-memory 체결 성능 벤치마크
- 1초당 42,000건 — Kafka 입력에서 Kafka 출력까지의 실제 거래 체결

100배 가까운 갭의 원인은 경계에 있다. 400만 건은 엔진 내부 순수 매칭 속도, 42,000건은 네트워크·직렬화·청산결제 동기화까지 더한 end-to-end 수치다.


엔진 한계 성능이 400만 건이라는 것은 두 가지를 의미한다. 병목은 엔진이 아니라 주변이고, 엔진에는 충분한 헤드룸이 있다. 평소 42,000건이 처리되는 와중에 10배 폭증이 와도 엔진은 문제가 되지 않는다. 이 여유가 거래소 운영에 필요하다.

 


⚙️ 단서 ③ — Active/Standby 이중화

2편 블로그는 체결 엔진의 이중화 구조를 이렇게 설명한다.

> "인메모리 방식으로 동작하는 체결 엔진은 Active/Standby 이중화 구조로 운영해 두 매칭 엔진이 Kafka를 통해 상태를 지속적으로 동기화하면서 Active에 장애가 발생하면 즉시 Standby로 전환이 이루어지게 했습니다."


이 한 줄에 설계 부담이 숨어 있다. 두 엔진이 같은 입력을 받아 같은 상태에 도달하려면 체결 엔진이 결정론적(deterministic)이어야 한다. 같은 이벤트 순서를 주면 반드시 같은 오더북과 같은 체결 이벤트가 나와야 한다.

먼저 짚을 점은, 함수적 결정론(같은 이벤트 → 같은 상태) 자체는 언어 선택과 직접 관련이 없다는 것이다. LMAX Exchange처럼 JVM 위에서도 구현된 선례가 있다. 진짜 문제는 타이밍 결정론이다. 가비지 컬렉션은 "언제 얼마나 멈출지"에 대한 하드 보장을 주지 않으며, JIT 컴파일러의 워밍업 시점도 런타임마다 다르다. Active/Standby 전환은 장애 순간과 트래픽 피크가 겹치는 지점에서 일어나고, 그 순간 Standby가 GC Pause에 들어가 있으면 장애 복구 자체가 꼬리 지연으로 다시 번진다.

Rust는 GC가 없다. 메모리가 컴파일 타임에 결정된 스케줄로 해제된다. JIT도 없다. 타이밍 편차를 만드는 런타임 변수가 구조적으로 줄어든다. Active/Standby의 즉시 전환이 안전하려면 GC 없는 언어가 가장 자연스러운 선택지다. 지금까지 확보한 접점 중 가장 직접적인 이유다.

 


⚙️ 단서 ④ — Transactional Outbox

분산 시스템에서 피해야 할 상황은 부분 실패다. DB에는 기록되었는데 이벤트가 발행되지 않은 경우, 또는 그 반대. 코빗은 Transactional Outbox 패턴으로 해결했다.


Outbox는 DB 트랜잭션 안에 이벤트 기록을 함께 포함시키는 기법이다. 커밋이 성공하면 DB 변경과 outbox 기록이 같이 반영된다. 릴레이 프로세스가 outbox를 폴링해 Kafka로 전송한다. 데이터베이스와 메시지 브로커의 불일치가 구조적으로 발생하지 않는다.

블로그는 보존 정책도 명시한다.

> "Outbox 전송 내역을 데이터베이스와 S3에 보존하고 있어 만일의 경우에도 이벤트를 재생해 상태를 재구성할 수 있기 때문입니다."

모든 상태 변경이 불변 이벤트로 남는다. 인프라 문제가 생겨도 이벤트만 있으면 오더북을 처음부터 다시 재생해 복구할 수 있다. Event Sourcing의 본질적 구현이다.

 


⚙️ 단서 ⑤ — 멱등성

블로그의 또 한 줄.

> "각 이벤트는 고유 ID를 부여하고 멱등성을 확보해 같은 이벤트가 중복 수신되어도 결과는 동일하게 유지됩니다."


멱등성이 보장되면 Kafka의 at-least-once 보장과 결합해 실질적으로 강한 일관성에 근접한 효과가 난다. Rust의 타입 시스템과 표준 컬렉션은 이 패턴을 안전하고 빠르게 구현하기에 적합한 도구다.

 


⚙️ 단서 ⑥ — 인메모리 오더북 스냅샷

블로그의 또 한 줄.

> "주기적으로 오더북(Orderbook) 스냅샷을 저장하고 … 만일의 경우에도 이벤트를 재생해 상태를 재구성할 수 있기 때문입니다."


스냅샷 작업에는 조건이 붙는다. 매칭 루프를 멈추지 않고 스냅샷을 떠야 한다. 멈추면 지연이 튄다. 오더북은 초당 수만 건 바뀌는 활성 구조이므로 스냅샷 작업을 얹으려면 동시성 안전성이 필요하다.

Rust는 `Arc<RwLock<OrderBook>>` 같은 공유 상태, `parking_lot`의 고속 락, `crossbeam`의 epoch-based GC 같은 도구를 제공한다. 타입 시스템이 스냅샷 스레드가 매칭 스레드의 상태를 안전하게 읽는 것을 검증한다. GC 언어에서도 스냅샷 콘텐츠의 일관성 자체는 락/COW로 보장할 수 있지만, 스냅샷과 매칭 루프가 GC pause를 함께 떠안아 튈 위험이 남는다. Rust에서는 해제 시점이 스코프 단위로 분산되어 그 변수가 없다.

 


⚙️ 단서 ⑦ — Redis → Lock-Free Ring Buffer

1편 블로그의 다음 문장은 전/후 시스템의 본질적 차이를 담고 있다.

> "시세 정보 서비스는 Lock-Free Ring Buffer를 사용함으로써 Redis에 캐시 된 값을 사용하던 이전 시스템보다 더욱 빠르게 시세 정보를 이용자에게 제공할 수 있게 되었습니다."


Redis는 네트워크를 타야 한다. 단위가 ms다. Lock-Free Ring Buffer는 같은 프로세스 메모리 공간에 올라간다. 단위가 μs 이하다.

Lock-Free 자료구조는 구현 난도가 높은 영역이다. C++로는 memory ordering 한 줄만 틀려도 몇 달 뒤 버그가 재현된다. Java로는 GC 때문에 "lock-free"라는 전제가 무색해진다.


Rust는 데이터 경합(data race)을 `Send`/`Sync` 트레이트로 컴파일 타임에 차단하고, GC가 없어 lock-free 성능이 온전히 나온다. (memory ordering 선택은 여전히 개발자 책임이지만, `unsafe` 경계를 명시적으로 좁힐 수 있다.)

 

 

⚙️ 단서 ⑧ — Go의 80% 비용 절감

Rust가 그렇게 적합하다면 주문 API는 왜 Go였을까. 1편 블로그가 이유를 적는다.

> "레거시 웹소켓 서버와 동일한 사양의 서버를 Go로 새롭게 구현했는데, 월간 AWS 비용 지출이 크게는 약 80% 감소했습니다."

Go가 이 영역에서 강한 이유는 다음과 같다.

  • M:N 스레드 모델. 수천 개 고루틴이 소수의 OS 스레드(기본값: CPU 코어 수) 위에 멀티플렉싱 된다. 컨테이너 footprint가 작다.
  • 가벼운 메모리 사용량. JVM보다 훨씬 작다. AWS EKS 컨테이너 환경에 적합하다.
  • 빠른 컴파일과 단순한 문법. 비즈니스 로직을 빠르게 반복·배포할 수 있다.

 

"자주 변하는 비즈니스 로직 + 부하 높은 I/O" 영역에서는 Go가 더 합리적이다. Rust로 만들어도 동작하지만, 개발 속도와 인프라 비용의 트레이드오프에서 Go가 우위였다.

CTO의 한 줄이 이 판단을 요약한다.

> "Rust로 견고한 엔진을 다지고, Go로 유연한 비즈니스 로직을 구현한다."

 


⚙️ 단서 ⑨ — Kafka + gRPC + Protobuf

두 언어 사이의 경계면도 설계된다.

> "이들 시스템 간 통신에 gRPC를 사용하거나 Kafka에 Protocol Buffers 메시지를 실어 사용하고 있습니다."

Protocol Buffers는 Go와 Rust 양쪽에서 비슷한 성능으로 직렬화된다. gRPC는 HTTP/2 기반의 언어 중립 RPC다. Kafka는 두 언어 모두 안정된 클라이언트를 가진다. 경계 비용이 최소화되도록 설계되어 있다.

미래의 재조정도 고려한 선택이다. 체결 엔진이 훗날 다른 언어로 이전되더라도 경계가 언어 중립적이면 교체가 가능하다.


Chapter 8 · Rust 채택의 비용

 

Rust가 지불한 대가도 짚을 필요가 있다.

 


⚙️ 학습 곡선

1편 블로그도 이 점을 숨기지 않는다.

> "저희도 처음부터 Rust가 익숙한 것은 아니었습니다. 러스트는 분명히 진입 장벽이 있고 학습 곡선도 만만하지는 않습니다. 저희도 처음 Rust를 시작할 때에는 unsafe 코드를 작성하기도 했고요."

소유권·빌림·수명은 익숙해지기까지 수개월이 걸린다. GC 언어 배경의 개발자는 초반에 컴파일러와 많이 부딪힌다. 그 과정에서 unsafe 블록에 의존하게 될 수도 있다.

 


⚙️ 컴파일 시간과 에러 처리

Rust는 컴파일이 느리다. 프로젝트가 커지면 빌드 시간이 C++ 수준에 근접한다. `Result`/`Option` 기반 에러 처리는 안전하지만 코드량이 늘어난다.

 


⚙️ 코빗의 판단

이 비용은 모두 초기 1회성이다. 체결 엔진처럼 한 번 잘 만들어 수년간 쓰는 시스템에서는 비용 대비 효용이 좋다. 블로그의 정리.

> "저희는 Rust로 한 번 잘 만든 강건한 엔진은 오랫동안 신뢰를 가지고 운영할 수 있다는 결론을 내릴 수 있었습니다."

추가로 블로그는 도구 환경 변화도 언급한다.

> "생성형 AI 덕분에 Rust의 진입 장벽이 낮춰졌을 뿐만 아니라 개발의 편의성도 크게 높아졌다고 생각합니다."

소유권/수명 관련 컴파일 에러는 코딩 에이전트가 대응하기 쉬운 영역이다. 학습 곡선 부담은 앞으로 더 줄어들 가능성이 크다.


Chapter 9 · 업계의 흐름

 

코빗의 선택이 고립된 실험인지 확인할 필요도 있다. 유사한 패턴이 업계에 반복된다.

  • 리눅스 커널: 2022년 6.1부터 Rust 코드가 실험적으로 도입됐다. 1991년 탄생 이래 30여 년 만의 C 외 언어 진입이다.
  • Microsoft: MSRC의 메모리 안전성 통계 공개 이후 Rust를 공식 연구 대상으로 삼았다.
  • Discord: Read States 서비스를 Go에서 Rust로 전환해 꼬리 지연 개선 사례를 공개했다. 코빗이 명시적으로 참조한 사례다.


공통점은 "낮은 지연과 높은 처리량을 동시에 요구하는 조직이 먼저 Rust를 택한다"는 것이다. 거래소 체결 엔진만큼 이 조건에 부합하는 워크로드는 드물다. 코빗의 선택은 흐름 위에 있다.


정리

 

 

⚙️ 조건과 언어

체결 엔진의 요구 조건을 언어별로 대응시키면 다음과 같다.

요구 조건 C/C++  Java/Go Rust
초저지연 (Average Latency) ⭕ (최상) 🔺 (우수) (최상)
결정론적 성능 (Tail Latency) ⭕ (GC 없음) (GC Pause)  ⭕ (GC 없음)
컴파일 타임 메모리 안전성 ❌ (수동 관리) ⭕ (런타임 보호) ⭕ (소유권 시스템)
컴파일 타임 동시성 안전성 ❌ (개발자 책임) 🔺 (도구 의존) ⭕ (Send/Sync)
장기 운영 신뢰성 🔺 (휴먼 에러 위험) ⭕ (생태계 성숙) ⭕ (언어적 강제)


C/C++은 성능을 주는 대신 안전성을 개발자 규율에 건다. Java/Go는 생산성은 좋지만 결정론적 지연에서 한계가 있다. Rust는 다섯 조건을 모두 만족한다.

 


⚙️ 단서의 정렬

"Korbit은 왜 체결 엔진에 Rust를 선택했을까?"라는 질문에 대해 지금까지의 단서를 정리하면 다음과 같다.

  1. Rust의 설계 의도 자체가 "성능과 안전의 동시 달성"으로, 체결 엔진의 요구와 겹친다.
  2. 소유권이 만드는 GC 부재와 결정론적 해제는, 꼬리 지연의 예측 가능성에 직접 대응한다.
  3. 빌림과 Fearless Concurrency는 멀티스레드 체결 엔진의 데이터 경합 버그를 컴파일 시점에 차단한다.
  4. 제로 비용 추상화로 선언적 코드와 저수준 성능을 함께 얻는다.
  5. Cargo/Crates.io 생태계가 Lock-Free Ring Buffer 같은 고난이도 부품을 제공한다.
  6. Event-Driven MSA + Kafka 구조에서 체결 엔진은 결정론적 소비-발행 루프가 되어야 한다.
  7. Active/Standby 이중화의 즉시 전환에서 꼬리 지연을 지키려면 GC 없는 언어가 구조적으로 유리하다. 함수적 결정론 자체는 언어 독립적이지만, 전환 순간의 타이밍 결정론은 런타임 특성에 직접 좌우된다.
  8. Outbox + Event Sourcing + 멱등성이 Rust의 타입 시스템과 직렬화 성능 위에서 구현된다.
  9. 인메모리 오더북 스냅샷 + 이벤트 재생은 Fearless Concurrency가 대응하는 복원력 설계다.


코빗의 선택은 취향이나 유행의 문제가 아니라, 시스템의 요구 조건에 기술을 정확히 매칭한 결과로 보는 편이 정확하다.

 


⚙️ 인사이트

기술 선택의 출발점은 "왜 Rust인가"가 아니라 "내 시스템이 요구하는 조건은 무엇인가"다. 그 질문에 답을 적어 내려가다 보면 후보 언어는 좁혀진다. 코빗의 경우에는 다섯 조건이 Rust를 가리켰다.

각자의 시스템은 각자의 조건을 가진다. 그 조건에 맞는 답이 반드시 Rust일 필요는 없다. 다만, 어떤 조건에 대해서는 Rust가 매우 유력한 후보가 된다는 점은 분명해졌다.


출처

https://tech.korbit.co.kr/posts/go-and-rust-how-korbit-uses-them/

 

Go와 Rust, 코빗은 이렇게 나눠 씁니다

코빗의 새로운 거래 시스템은 Go와 Rust를 적극적으로 사용했습니다. 구체적으로는 주문 서비스와 Open API 서비스는 Go, 특히 거래소의 핵심이라고 할 수 있는 체결 엔진과 시세 정보 서비스는 Rust

tech.korbit.co.kr

https://tech.korbit.co.kr/posts/system-architecture-design-of-cryptocurrency-exchange/

 

완전히 새롭게 구축한 코빗의 가상자산 거래 시스템

가상자산 시장은 쉼 없이 운영되며, 시장 급등락 상황에서는 평소 대비 몇 배의 트래픽 증가에도 안정성을 유지해야 하는 기술적 도전 과제를 안고 있습니다. 이 글에서는 코빗이 거래 시스템을

tech.korbit.co.kr

https://www.elancer.co.kr/blog/detail/808

 

왜 많은 개발자들이 Rust(러스트)로 이동할까? I 이랜서 블로그

등장하자마자 개발자들의 관심을 불러온 언어가 있습니다. 바로 ‘Rust(러스트)’인데요, Rust에 어떤 특징이 있길래 개발자들은 Rust에 환호할까요? 함께 알아보겠습니다.

www.elancer.co.kr

https://medium.com/happyprogrammer-in-jeju/rust%EB%A5%BC-%EC%97%85%EB%AC%B4%EC%9A%A9-%EC%96%B8%EC%96%B4%EB%A1%9C-%EC%93%B0%EB%8B%A4-7723cd2c0a59

 

Rust를 업무용 언어로 쓰다

업무용 프로그래밍 언어 활용 경험

medium.com

https://blog.insightbook.co.kr/2026/02/03/%EB%9F%AC%EC%8A%A4%ED%8A%B8-%EC%83%9D%ED%83%9C[…]0%EB%A5%BC-%EC%9C%84%ED%95%9C%E3%80%8A%EA%B8%B0%EB%B3%B8/

 

러스트 생태계 탐험을 시작한 개발자를 위한《기본기부터 다시 새기는 러스트 특강》

약 한 달 전 마이크로소프트 최고(distinguished) 엔지니어 게일런 헌트가 링크드인에 채용 공고를 올리면서 다음과 같은 내용을 덧붙여 화제가 됐습니다. 제 목표는 2030년까지 마이크로소프트에서

blog.insightbook.co.kr

https://medium.com/curg/rust%EC%9D%98-%EC%86%8C%EC%9C%A0%EA%B6%8C-%EC%9D%B4%EC%95%BC%EA%B8%B0-a4c19c1b2c10