Zenith (manifast-platform) 아키텍처

갱신 2026-08-12

동접 ~3000 기준. UI·REST·SSE·Solana 구독을 분리하고, 부하가 큰 서비스만 Railway replica로 늘린다.

레포 manifast-platform · Railway revolution-platform

4+1
앱 서비스 · 원샷 load-test
Mongo+Redis
데이터 계층
마켓당 1
Solana 구독 (ingest)
replica
realtime · trade-api · web
브라우저는 SSE를 realtime에, 주문·잔고 REST는 trade-api에 붙입니다. Solana 구독은 ingest만 하고 Redis로 팬아웃합니다. Express HTTP load-test 서버는 없고, 주문 부하는 scripts/load-orders.ts 원샷입니다.

전체 구조

BrowserTrade UIapps/webNext.jsrealtimeSSE fan-outtrade-apiRESTRedispub/sub + cacheingestSolana watchMongoDB영속 저장SolanaManifest DEX

강조 테두리: ingest / realtime (스케일 병목 해결 지점)

Railway replica (수평 확장)

replica = 같은 서비스를 복사본 여러 대로 띄우는 것. 코드가 아니라 인스턴스 개수를 늘린다. 1 = 서버 1대, 3 = 같은 프로세스 3대.

복제해도 되는 것

realtime — SSE 연결 부하 분산 (Redis 구독만)
trade-api — REST 주문·잔고 (무상태)
web — Next UI

함부로 복제하면 안 되는 것

ingest — 인스턴스마다 Solana WS가 또 열려 마켓당 구독이 N배. RPC가 먼저 터짐. 기본은 1대, 늘리려면 마켓 샤딩 등 설계가 필요.

순서: ① 역할 분리(이미 함) → ② 동접이 커지면 realtime · trade-api부터 replica → ③ ingest는 마켓당 1 유지. Online·레이트리밋이 프로세스 메모리면 replica마다 한도가 따로 잡힌다.

서비스 역할 · replica

서비스포트하는 일replica메모
apps/web4000Trade UI만 (Solana/SSE 로직 없음). 메인 `/`복제 OKNext 인스턴스 늘려도 됨
trade-api4001prepare / send / verify / balances / candles / fills · 서버 서명 주문복제 OK무상태에 가깝다. REST 부하 시 복제
realtime4002SSE fan-out · presence · Online · daily_visitors복제 OKSSE 연결 부하를 나눔. Redis만 공유
ingest4003마켓당 Solana WS 1개 → Redis publish · fills/candles 쓰기복제 금지복제하면 같은 마켓 구독이 N배 → RPC가 먼저 터짐
load-test원샷: scripts/load-orders.ts (HTTP 서버 아님)원샷기동 시 동시 주문 후 종료. replica 의미 없음

데이터 계층

Redis

실시간

역할

메인 DB가 아님. 중계·캐시·짧은 상태.


  • book:{market} pub/sub
  • fill:{market} pub/sub
  • presence / 최신 호가 스냅샷

MongoDB

영속

역할

진짜 저장소. DB revolution.


  • candle_bars차트
  • fills체결
  • daily_visitors방문
  • 마켓 목록·Open Orders는 코드/온체인 (Mongo 아님)

MongoDB 컬렉션

candle_bars

차트

봉 1개 = 문서 1개. 레거시 candle_series는 조회 시 1회 마이그레이션.


쓰기: ingest · scripts/backfill-fills
읽기: trade-api GET /api/markets/candles

fills

체결

FillLog 1건 = 문서 1개. 테이프·내역·24h Volume.


쓰기: ingest (기동 이후) · backfill
읽기: trade-api fills / volume24h / history

daily_visitors

방문

날짜(KST)별 presence SSE 연결 누적. Online(실시간)과 별개.


쓰기: realtime /presence/stream 연결 시 +1
읽기: GET /stats/daily-visitors

두 가지 주요 흐름

1. 실시간 호가/체결

  1. Solana → ingest (구독 1개)
  2. ↓ publish
  3. Redis channels
  4. ↓ subscribe
  5. realtime → SSE → Browser
  6. ↘ fills / candle_bars → Mongo

2. 주문 / 잔고

  1. Browser → trade-api prepare
  2. 지갑 서명 (Phantom / MetaMask)
  3. trade-api tx/send → Solana
  4. verify / balance 조회
  5. 부하 실측: Railway load-test → scripts/load-orders.ts (서버 지갑)

왜 이렇게 쪼개나

Before

Next 한 프로세스에 UI + REST + SSE + Solana 구독 → 동접 3000이면 SSE 수천 + 동일 마켓 구독 수천 중복

After

Solana는 ingest만, 유저 연결은 realtime이 Redis로 분산. trade-api·realtime·web은 replica로 늘리고, ingest는 마켓당 1 유지.


Railway 배포 단위

모노레포 1개 Git → 앱 서비스 + Redis/Mongo 플러그인. Config: railway.*.toml

webtrade-apirealtimeingestload-test (원샷)RedisMongoDB