Skip to content

Repository files navigation

실시간 주식 거래 어플리케이션

Goal

  • 초당 수천건의 거래에 해당하는 주식거래 데이터를 사용자가 실시간 Tick 으로 볼 수 있음
  • 실시간 거래 이상 내역을 확인 할 수 있음
  • 지난 거래내역을 분/시간/일별로 조회 할 수 있음

Architecture

Overview

flowchart TD
    Source[Data Source] --Incoming--> PostgreSQL[PostgreSQL with TimescaleDB extension]
    App[Real-time Stock Application] --Fetch Data--> PostgreSQL
    Debezium --CDC--> PostgreSQL
    Debezium --producer--> Kafka[Apache Kafka]
    App --consumer--> Kafka
    Flink[Apache Flink] --consumer--> Kafka
    App --Table API--> Flink
    User <--Web Socket / SSE / RestAPI--> App
Loading

Design Decisions

Why Event-Driven Architecture?

  • 거래 데이터 생성, 저장, 분석, 알림이 각각 독립적으로 확장 가능하도록 구성
  • 하나의 컴포넌트 장애가 전체 시스템에 미치는 영향을 최소화

Why Separate Real-time and Batch Processing?

  • Real-time (WebSocket): 사용자가 즉시 거래 변화를 확인할 수 있도록
  • Batch (REST API): 대용량 히스토리 데이터 조회 시 안정적인 성능 보장

Why CDC over Application-level Event Publishing?

  • 데이터 정합성 보장: 트랜잭션 커밋과 동시에 이벤트 발행
  • 기존 레거시 시스템 변경 최소화

성능 요구사항 (Performance Requirements)

Production 환경 기준

# 시나리오 HW 사양 대상 메트릭 요구사항
1 실시간 호가 전송 (WebSocket) 4 CPU / 8GB RAM / 25Gbps NIC 1,000개 동시 연결, 초당 5,000건 거래 데이터 End-to-End 지연시간 < 100ms (DB 커밋 → Client 수신)
2 거래 데이터 삽입 (Bulk Insert) 8 CPU / 16GB RAM / SSD 배치당 10,000건 거래 데이터 처리시간 < 500ms (asyncpg COPY 메서드 사용)
3 히스토리 조회 (REST API) 4 CPU / 8GB RAM / TimescaleDB 30일치 데이터 조회 (약 1,296,000건) 응답시간 < 2초 (TimescaleDB 파티셔닝 활용)

#11 대량 데이터 조회 성능 측정

측정 일시: 2026-07-10 (KST)

실행 환경 및 스펙

구분 측정 환경
호스트 macOS 26.5.1 (Build 25F80), arm64
하드웨어 MacBook Pro (MacBookPro18,3), Apple M1 Pro 10코어(8P+2E), 메모리 16GB
Docker Docker Desktop 29.6.1, aarch64, 6 CPU, 메모리 약 9.7GiB
애플리케이션 런타임 Python 3.13.14, FastAPI 0.139.0, asyncpg 0.31.0, NumPy 2.5.1
데이터베이스 PostgreSQL 16.9, TimescaleDB 2.20.3
측정 도구 hyperfine 1.20.0, k6 2.0.0

실행 구성

  • docker/data-pipeline/docker-compose.yaml의 TimescaleDB, Kafka, Debezium, Kafka UI, Flink JobManager/TaskManager를 함께 실행했다.
  • FastAPI는 호스트의 127.0.0.1:8000에서 실행하고 TimescaleDB는 localhost:5432로 연결했다.
  • stock_tradesevent_time 기준 hypertable이며 청크 간격은 7일이다. 측정 범위에 포함된 청크는 6개였고 compression이 활성화되어 있었다.
  • stock_trades에서 기본 키를 포함한 인덱스 7개를 확인했다.
  • Debezium의 dbz_publicationstock_trades를 대상으로 하며 connector와 task가 실행 중인 상태에서 측정했다.
  • continuous aggregate 비교를 위해 stock_trades_1min을 생성하고 측정 구간을 수동 refresh했다.

테스트 데이터 및 조회 조건

항목
시드 삽입 건수 1,296,000건
시드 배치 크기 10,000건
시드 시간 범위 2026-06-10 11:34:44.227022 UTC ~ 2026-07-10 11:34:44.227022 UTC
기존 데이터 포함 조회량 1,508,344건
원본 조회 위 30일 범위, limit=1000
분 집계 조회 위 30일 범위, granularity=minute, limit=1000

현재 REST API의 pagination 상한에 따라 30일 범위의 첫 1,000건 응답을 측정했다. 분 집계 API는 stock_trades를 직접 집계하며 continuous aggregate 조회는 SQL로 별도 측정했다.

테스트 내용

  1. POST /api/v1/stock/generatemode=historical, totalRecords=1296000, days=30, batchSize=10000을 전달하고 완료 상태까지 대기했다.
  2. hypertable, 청크, 인덱스와 조회 범위 건수를 확인하고 ANALYZE stock_trades를 실행했다.
  3. 원본 범위 조회와 stock_trades 직접 분 집계에 EXPLAIN (ANALYZE, BUFFERS, VERBOSE)를 실행했다.
  4. stock_trades_1min을 refresh한 뒤 동일 범위의 continuous aggregate 조회 실행계획을 측정했다.
  5. hyperfine으로 원본 API와 분 집계 API를 각각 3회 워밍업 후 20회 측정했다.
  6. k6에서 5 VU로 30초 동안 각 iteration마다 원본 API와 분 집계 API를 순차 호출해 응답시간 분포를 측정했다.

측정 결과

구분 측정 항목 결과
데이터 생성 1,296,000건 COPY 삽입 46.920초
SQL 원본 조회 실행시간 0.582ms
SQL 직접 분 집계 실행시간 6,587.098ms
Continuous aggregate 30일 범위 refresh 16.515초
Continuous aggregate 조회 실행시간 18.732ms
hyperfine 원본 API 평균 ± 표준편차 34.03 ± 9.47ms
hyperfine 원본 API 최소 ~ 최대 26.04 ~ 62.25ms
hyperfine 분 집계 API 평균 ± 표준편차 7,279.46 ± 313.02ms
hyperfine 분 집계 API 최소 ~ 최대 6,945.30 ~ 8,148.48ms
k6 원본 API 평균 / 중앙값 / p95 / 최대 70.986 / 42.767 / 169.352 / 171.514ms
k6 분 집계 API 평균 / 중앙값 / p95 / 최대 13.138 / 13.397 / 13.773 / 13.813초
k6 HTTP 요청 / 실패 / check 30건 / 0건 / 30건 성공

Tech Stack

  • FastAPI - 0.115.12
    • 비동기 처리로 수천 건의 동시 IO 연결을 효율적으로 처리
  • PostgreSQL with TimescaleDB extension - 16.9
    • 시계열 데이터 압축과 파티셔닝으로 대용량 거래 데이터의 저장/조회 성능을 최적화
  • Debezium: 3.0.0.Final
    • 데이터베이스 변경사항을 실시간으로 캐치하여 애플리케이션 로직 수정 없이 CDC를 구현
  • Apache Kafka - 4.0.0
    • 초당 수천 건의 거래 데이터를 버퍼링하고 여러 컨슈머가 독립적으로 처리할 수 있는 확장성
  • Apache Flink - Scala 2.12
    • 스트리밍 데이터에서 실시간 이상 거래 탐지를 위한 복잡한 윈도우 기반 집계 연산을 처리

How It Works

Data Pipeline

  1. Data Source 에서 PostgreSQL 에 거래내역 데이터를 저장한다
    • Data Source 라고 지칭하는 거래 데이터는 서비스 레벨에서 구현하여 모킹 함
    • POST http://<HOST>/api/v1/stock/generate
  2. 저장된 거래내역은 Change Data Capture 를 통해 Kafka 로 스트리밍된다
  3. Flink 에서는 거래내역을 실시간으로 통계 및 연산을 수행하여 이상 거래 여부를 판단한다

Local Docker Data Pipeline

docker/data-pipeline/docker-compose.yaml — PostgreSQL(TimescaleDB), Kafka, Debezium, Flink를 한 스택으로 띄운다.

사전 준비

docker/data-pipeline/.env.compose.local 생성:

POSTGRES_PASSWORD=<your-password>
POSTGRES_DB=stock

POSTGRES_USER를 넣지 않으면 슈퍼유저는 postgres다.

아래 파일의 사용자명·비밀번호를 위 POSTGRES_PASSWORD와 맞춘다.

파일 넣을 값
postgres/init/02-cdc.sql CDC용 replication role (예: debezium)
postgres/pg_hba.conf 위 role 이름 (replication 허용 줄)
debezium/postgres-stock-connector.json database.* → CDC role, transforms.timescaledb.database.*postgres + POSTGRES_PASSWORD

01-ddl.sql, 02-cdc.sqlpostgres_data volume이 처음 만들어질 때만 실행된다.

실행

cd docker/data-pipeline
docker compose up -d
./debezium/register-connector.sh

register-connector.shhttp://localhost:8083postgres-stock-connector.json을 등록한다. Compose에 포함되지 않는다.

구성

서비스 역할 호스트 포트
postgres-stock TimescaleDB, 01-ddl.sql·02-cdc.sql로 초기화 5432
broker Kafka (KRaft) 9094
debezium-connect PostgreSQL CDC → Kafka 8083
kafka-ui Kafka UI 8080
jobmanager Flink JobManager 8081
taskmanager Flink TaskManager

네트워크 stock_trade_network. 데이터는 postgres_data, kafka_data volume에 저장된다.

접속

호스트에서 실행 Compose 내부
PostgreSQL localhost:5432 postgres-stock:5432
Kafka localhost:9094 broker:9092

구현 기술 상세

TimescaleDB: 시계열 데이터베이스 최적화

  • Hypertable: stock_trades 테이블을 event_time 기준으로 Hypertable로 변환하여 시계열 데이터에 최적화된 파티셔닝을 자동 적용합니다. 이를 통해 대규모 데이터셋에서도 빠른 데이터 삽입 및 범위 기반 조회(Time-based queries) 성능을 보장합니다.
  • 고성능 삽입: 데이터 생성 시 asyncpgcopy_records_to_table 메서드를 사용하여 PostgreSQL의 COPY 명령을 실행합니다. 이는 INSERT를 반복하는 것보다 훨씬 빠른 속도로 대량의 데이터를 벌크 삽입할 수 있게 해주는 핵심 기능입니다.

Debezium & Kafka: 변경 데이터 캡처(CDC) 및 이벤트 스트리밍

  • CDC 설정: Debezium PostgreSQL 커넥터가 stock_trades 테이블의 변경 사항을 실시간으로 감지합니다. publication.name으로 dbz_publication을 사용하여 논리적 디코딩(Logical Decoding)을 통해 변경분을 스트림으로 변환합니다.
  • Kafka 토픽: 감지된 모든 데이터 변경(INSERT, UPDATE, DELETE) 이벤트는 stock.public.stock_trades라는 Kafka 토픽으로 발행(Publish)됩니다. 이 토픽은 실시간 데이터 파이프라인의 중심 허브 역할을 합니다.
  • 느슨한 결합: 이 아키텍처를 통해 데이터베이스와 실시간 처리 시스템(FastAPI, Flink)이 분리됩니다. 데이터베이스는 데이터 저장에만 집중하고, 실시간 처리가 필요한 모든 애플리케이션은 Kafka 토픽을 구독(Subscribe)하여 독립적으로 확장 및 운영될 수 있습니다.

API

Tick

  • GET ws://<HOST>/api/v1/stock/real-time
    • Description: 사용자의 실시간 거래내역(Tick) 을 요청 시 서비스에서 WebSocket 연결을 하여 Kafka 의 메시지를 전달한다
    • 동작: Client 가 WebSocket 으로 Ticker·Tick 주기를 요청하면 Server 가 해당 Ticker 의 Kafka 토픽을 구독해 설정 주기로 호가를 전송하고, 전송 중 Ticker/주기 변경 요청을 받으면 기존 구독을 해제하고 신규 구독으로 전환한다
    • 구현/진행: 개발 항목 #1~#6

Anomaly

  • GET http://<HOST>/api/v1/stock/anomaly
    • Description: 사용자의 거래 이상 거래 탐지 확인 요청 시 SSE 로 발생 내역을 전달한다
    • 동작: Flink 이상 거래 결과 스트림을 subscribe 해 이벤트 타입·발생 시각·ticker·탐지 근거를 포함한 SSE 로 변환하며, keep-alive/heartbeat 와 연결 종료 시 consumer 정리·재연결, 이상 없음/지연/실패 예외 정책을 따른다
    • 구현/진행: 개발 항목 #12~#16 (선행: Flink 배포)

Trades

  • GET http://<HOST>/api/v1/stock
    • Description: 지난 거래내역에 대한 조회 요청 시 RestAPI 로 제공한다
    • 동작: duration·ticker·tradeType·marketCode 조합과 시작/종료 시각 기간 조회, 분/시간/일 집계, pagination/cursor 를 지원하고, count·filters·aggregate metadata 를 포함한 표준 응답과 파라미터·빈 결과·범위 초과 검증 정책을 따른다
    • 구현/진행: 개발 항목 #7~#11

개발 항목

1. 실시간 Tick 스트리밍

  • #1 Kafka consumer 를 애플리케이션 런타임 의존성으로 반영
  • #2 Debezium CDC 메시지 스키마 파싱 로직 구현
  • #3 WebSocket 연결별 ticker / tick 상태 관리
  • #4 수신 이벤트를 tick 주기 기준 candle / high-low 로 집계
  • #5 ticker 변경 시 기존 구독 해제 후 신규 구독 연결
  • #6 데이터 부재 / 지연 / consumer 오류 WebSocket 예외 처리

2. 히스토리 조회

  • #7 필터 조회 API를 range query 로 확장
  • #8 분 / 시간 / 일 aggregation 쿼리 추가
  • #9 pagination / cursor 응답 도입
  • #10 응답 모델 및 validation 표준화
  • #11 대량 데이터 조회 성능 검증

3. 이상 거래 SSE

  • #12 Flink 출력 토픽 / 결과 스트림 스키마 확정
  • #13 이상 거래 이벤트 consumer 구현
  • #14 SSE event / data 포맷 표준화
  • #15 이상 거래 이벤트 필터링 / 직렬화
  • #16 heartbeat / reconnect / graceful shutdown 처리

4. 운영 안정성

  • #17 Kafka / PostgreSQL / Flink 설정을 env.toml 로 통합
  • #18 구조화된 로그와 장애 추적 포인트 추가
  • #19 연결 종료 / 재시도 / backpressure 정책 정리
  • #20 통합 / 회귀 테스트 추가
  • #21 성능 요구사항 점검 시나리오 문서화

About

실시간 주식 거래 데이터 스트리밍 및 이상 거래 탐지 기능을 제공하는 이벤트 기반 데이터 파이프라인

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages