View

728x90
반응형

 

 

증권 개발 로그는 밀리초 단위로 찍지 않으면 망한다. 3시 1분 22.473초에 들어간 주문이 3시 1분 22.891초에 체결됐다고 가정해 보자. 이 418ms 동안 무엇이 일어났는지 증명하지 못하면 민원이 들어왔을 때 대응이 불가능하다. "왜 내 주문이 늦게 체결됐냐"고 고객이 따지는데 로그가 초 단위라면 "둘 다 22초였다"로 끝난다. 반박 근거가 존재하지 않는다.

 

일반 커머스 백엔드라면 초 단위 로그로도 버틸 만하다. 그러나 증권 개발 로그는 밀리초 아래까지 찍지 않으면 장애 상황에서 RCA 자체가 불가능하다. 거래소 매칭 자체가 ms 단위로 돌아가는데 회원사 시스템만 초 단위로 로그를 남긴다면 눈을 감고 운전하는 것과 다름없다.

 

이 글에서 다룰 내용은 세 가지다. (1) 왜 증권만 밀리초여야 하는가, (2) 로그에 무엇을 남겨야 하는가, (3) 남기지 않으면 실제로 무엇이 터지는가. 특히 자동매매를 돌리는 개인 개발자도 본인의 거래 내역을 증명하지 못해 증권사 민원팀과 싸우다 지는 사례가 많은데, 이를 방지하려면 로깅부터 제대로 구축해야 한다.

 

증권 도메인이 왜 밀리초 단위로 움직이는가

 

증권 바닥은 일반 서비스와 시간 감각 자체가 다르다. 커머스에서 "응답 1초"는 그럭저럭 허용 가능한 수준이지만, 증권에서 1초는 이미 수십 개의 이벤트가 지나간 뒤다.

 

KRX 매칭엔진과 회원사 시스템 사이의 시간 차

 

한국거래소(KRX) 차세대 매매체결시스템 EXTURE+는 주문 체결 응답을 수십 마이크로초~수 밀리초 수준으로 돌려준다. 회원사(증권사) 게이트웨이는 여기서 받은 응답을 내부 시스템으로 전파하는데, 이 과정만 해도 네트워크 왕복과 큐잉, 내부 파싱으로 수십 ms가 더 붙는다.

 

고빈도 매매(HFT)를 하는 외국계 IB는 아예 KRX 부산 IDC에 코로케이션을 박아두고 케이블 길이까지 줄이면서 마이크로초 단위 싸움을 벌인다. 일반 개인 개발자 수준에서 마이크로초까지 갈 필요는 없다. 다만 최소 밀리초는 찍어야 본인 시스템이 몇 ms 걸리는지 측정이라도 가능하다.

 

같은 초에 이벤트 수십 개가 발생하면 순서 보장이 되지 않는다

 

 

출처: timestamping.co.kr

 

초 단위 로그가 왜 무너지는지는 단순하다. 같은 초에 이벤트가 10개 찍히면 순서를 알 수 없다. 코스피 종목 중 체결 빈도가 높은 종목은 1초에 호가 갱신이 수십 번 일어난다. 실시간 시세가 찍히고, 내 주문이 나가고, 체결이 돌아오고, 잔량이 갱신되는 네 가지 이벤트가 같은 초에 모두 들어오면 로그만 보고는 인과관계 추적이 불가능하다.

 

아래 표는 정밀도별로 무엇을 놓치는지 정리한 것이다.

 

정밀도 구분 가능한 이벤트 증권 도메인 활용도 저장 비용
초 (s) 같은 초 이벤트 순서 불가 쓸모없음 낮음
밀리초 (ms) 네트워크 왕복, API 레이턴시 측정 실무 최소선 낮음
마이크로초 (μs) 커널-유저 영역 이벤트 구분 HFT/DMA 수준 중간
나노초 (ns) CPU 명령어 단위 연구/성능 튜닝 높음

 

결론은 밀리초가 실무 최소선이라는 점이다. 마이크로초는 HFT가 아니라면 오버엔지니어링이고, 초는 사실상 로그를 찍지 않는 것과 다를 바 없다.

 

밀리초 로그가 없으면 터지는 3가지 시나리오

 

이론만으로는 와닿지 않으므로 실제로 터졌을 때 어떻게 되는지 시나리오 세 개로 정리한다. 이를 읽고 본인 프로젝트에 밀리초 로그가 없다면 오늘 바로 패치하기를 권한다.

 

시나리오 1: 민원 대응 실패

 

고객이 "내가 3시 1분 22초에 시장가로 넣었는데 왜 10틱이나 슬리피지가 났냐"고 민원을 넣었다. 증권사 CS팀은 개발팀에 로그 조회를 요청한다. 그런데 로그가 초 단위라면 어떨까?

 

  • 22초에 내 주문 접수
  • 22초에 시세 갱신
  • 22초에 체결 돌아옴

 

이게 전부다. 고객 주문이 시세 갱신보다 먼저 들어갔는지 나중에 들어갔는지 증명할 수 없다. 민원팀은 "시장 변동성 때문에 어쩔 수 없다"고 답변하고 마무리하지만, 고객이 금감원 민원으로 올리면 그때부터 피곤해진다. ms 단위 로그가 있으면 "22.473초 주문 접수 → 22.481초 시세 갱신 → 22.891초 체결"을 그대로 찍어 보여줄 수 있다. 증빙의 차원이 다르다.

 

시나리오 2: 장애 근본 원인 분석(RCA) 불가

 

어느 날 아침 9시 개장 직후 주문 체결 지연이 갑자기 터졌다. 평소 200ms 걸리던 것이 800ms를 넘어간다. 고객 불만이 폭주한다. 서비스를 내려야 하는가?

 

로그가 초 단위라면 어디서 밀렸는지 추적이 불가능하다. 지연이 다음 중 어디에서 발생했는지 구분되지 않는다.

 

  1. 클라이언트 → API 게이트웨이 구간
  2. 게이트웨이 → 주문 라우터 구간
  3. 라우터 → 거래소 FIX 세션 구간
  4. 거래소 응답 후 내부 전파 구간

 

세 군데에 ms 타임스탬프가 찍혀 있으면 어느 구간에서 지연이 났는지 1분 안에 드러난다. "게이트웨이 수신 ~ 거래소 송신 사이에서 600ms 밀림 → 큐 적체 의심"으로 좁혀지고, 하드웨어 교체든 튜닝이든 빠르게 움직일 수 있다. 로그가 없으면 전체 서버를 재부팅하고 기도할 수밖에 없다.

 

시나리오 3: 금감원 감사 / 내부 감사 대응 불가

 

 

출처: lawlac.kr

 

전자금융거래법과 전자금융감독규정상 금융회사는 전자금융거래 내역과 관련 기록을 일정 기간 보관해야 한다. 이 "기록"에는 당연히 로그도 포함된다. 보관 기간은 거래 종류에 따라 통상 5년 수준이다(정확한 기간은 감독규정 최신 개정본 확인이 필요하다).

 

감사가 들어왔는데 주문 처리 로그가 초 단위로만 남아 있다면 "밀리초 단위 시간 기록 체계 미비"로 지적받기 딱 좋다. 내부 감사에서도 마찬가지다. 특히 위·변조 가능성 차단 관점에서 이벤트 순서를 시간으로 증명할 수 있어야 하는데, 초 단위 로그로는 이것이 불가능하다.

 

개인 개발자라 하더라도 "자동매매 시스템을 돌리다가 증권사에서 갑자기 계좌 정지를 당했는데 내 매매가 정상이었다"를 증명하려면 본인 쪽 로그도 ms 단위여야 한다. 증권사 로그와 맞춰볼 수 있어야 비로소 싸움이 성립한다.

 

증권 시스템 로그에 반드시 들어가야 하는 필드

 

여기서부터는 실무다. 증권 개발 로그를 밀리초로 찍을 것이라면 어떤 필드를 설계해야 하는지 정리한다.

 

시간 관련 필드 3종 세트

 

시간 필드는 최소 세 개가 필요하다.

 

  • client_ts: 클라이언트(또는 자동매매 봇)에서 주문을 생성한 시각
  • gateway_ts: 자사 게이트웨이가 주문을 수신한 시각
  • exchange_ts: 거래소에서 응답을 돌려준 시각

 

이 세 개가 있어야 구간별 지연을 쪼갤 수 있다. 예컨대 gateway_ts - client_ts는 네트워크 왕복 시간이고, exchange_ts - gateway_ts는 자사 내부 + 거래소 처리 시간이다. 문제가 생겼을 때 어디를 파야 할지 즉시 드러난다.

 

가끔 server_received_ts, order_router_ts 같은 중간 필드를 더 박아두기도 하나, 너무 많으면 로그 스키마 관리가 어려워진다. 핵심 3개 + 구간별 차이(gateway_latency_ms, exchange_latency_ms)를 미리 계산해서 넣는 것이 실무상 편리하다.

 

추적(trace) 필드

 

분산 시스템이라면 trace_id가 필수다. 주문 하나가 게이트웨이 → 라우터 → FIX 어댑터 → 체결 처리기 → 알림 → DB 저장까지 돌면서 로그를 수십 줄 찍는데, trace_id가 없으면 나중에 이를 모을 방법이 없다.

 

증권 특화 추적 필드는 다음과 같다.

 

  • order_id: 자사 주문번호
  • original_order_id: 정정/취소 주문의 원주문번호
  • exchange_order_id: 거래소에서 부여한 주문번호
  • request_id: API 호출 단위 ID
  • trace_id: 분산 트레이싱 ID (OpenTelemetry를 쓰면 자동)

 

민감정보 마스킹

 

로그에 계좌번호 풀 스트링을 찍으면 개인정보보호법 이슈에 바로 걸린다. 계좌번호는 뒤 4자리만 찍거나 SHA256 해시로 저장하는 것이 기본이다. 고객 식별자도 마찬가지로 해시나 내부 user_id로만 로깅한다.

 

JSON 로그 예시

 

평문 로그는 파싱도 어렵고 필드 누락 체크도 어렵다. 구조화 로그(JSON)를 쓰는 것이 기본이다. 증권 주문 로그 한 줄의 예시는 다음과 같다.

 

{
  "trace_id": "7f3a2b1c-9e4d-4f8a-b2c3-d5e6f7a8b9c0",
  "event": "order_placed",
  "client_ts": "2026-04-23T14:01:22.473Z",
  "gateway_ts": "2026-04-23T14:01:22.481Z",
  "exchange_ts": "2026-04-23T14:01:22.891Z",
  "gateway_latency_ms": 8,
  "exchange_latency_ms": 410,
  "order_id": "ORD-20260423-000123456",
  "exchange_order_id": "KRX-998877",
  "account_hash": "a3f5b9c2d1e4...",
  "account_last4": "4567",
  "symbol": "005930",
  "side": "BUY",
  "order_type": "MARKET",
  "qty": 10,
  "price": null,
  "status": "FILLED",
  "filled_qty": 10,
  "avg_fill_price": 72500,
  "slippage_ticks": 2,
  "node": "gw-prod-03",
  "version": "v2.14.1"
}

 

이 정도 스키마라면 민원 대응, 장애 RCA, 감사 대응을 모두 커버할 수 있다. 본인 프로젝트 로깅 구조가 이보다 빈약하다면 오늘 중에 보강할 것을 권한다.

 

NTP 동기화가 되어 있지 않으면 밀리초 로그는 의미가 없다

 

 

출처: hamradio.my

 

ms 로그를 열심히 찍어도 서버마다 시계가 틀어져 있으면 그 로그는 쓸모없다. 게이트웨이 서버 시간이 라우터 서버보다 50ms 빠르다면 이벤트 순서가 뒤집혀 보인다. "라우터에서 먼저 보낸 주문이 게이트웨이에서 나중에 수신된 것처럼" 기록된다.

 

NTP와 PTP 중 무엇을 써야 하는가

 

  • NTP(Network Time Protocol): 일반적인 시간 동기화 프로토콜. 인터넷 기준 수 ms 오차, LAN 기준 1ms 이하까지도 가능. 대부분의 증권 시스템에 충분하다
  • PTP(Precision Time Protocol): 하드웨어 타임스탬프 기반. 마이크로초 수준 동기화. HFT가 아니라면 오버킬이다

 

일반 자동매매나 일반 증권사 백엔드는 NTP로 충분하다. 리눅스에서는 chronyd를 쓰는 것이 요즘 표준이며(과거 ntpd보다 가볍고 복구가 빠르다), Windows 서버도 w32time을 제대로 구성하면 LAN 내부에서 1ms 수준을 유지한다.

 

실제 사례: 서버 시간 30초 차이가 났던 전설

 

유명한 이야기 중에 특정 기업의 장애 포스트모템을 보면 "NTP 설정이 빠진 서버끼리 30초 차이가 나서 세션 만료 로직이 이상하게 돌아갔다"는 사례가 있다. 증권 바닥에서도 이런 일이 가끔 발생한다. 신규 서버를 프로비저닝하고 NTP 구성을 빼먹으면 며칠 뒤 시간이 수 초씩 튄다. 이 상태에서 주문 로그를 남기면 밀리초고 뭐고 모두 무의미하다.

 

체크리스트만 언급하자면 (1) 모든 서버 NTP 동기화 필수, (2) chrony sources 주기적 확인, (3) 장애 발생 시 시간 드리프트 우선 점검, (4) 컨테이너 구동 시 호스트 시계 공유 설정 확인이다. 이 네 가지만 지키면 대부분의 문제는 발생하지 않는다.

 

로그 수집과 보관은 어떻게 설계해야 하는가

 

로그를 잘 찍어도 보관이 엉망이면 감사 시점에 꺼내지 못한다. 증권 개발 로그가 밀리초 단위로 쌓이면 양이 어마어마하게 나온다. 하루에 GB 단위는 기본이다.

 

전자금융거래법 관점에서 5년 보관이 현실

 

전자금융거래법과 감독규정은 전자금융거래 기록을 일정 기간 보관하도록 규정한다. 실무에서는 주문/체결 관련 로그는 5년 보관을 기본 가정으로 깔고 간다. 다만 정확한 기간과 적용 범위는 최신 감독규정 및 내부 규정을 기준으로 법무/컴플라이언스 팀과 확정해야 한다(이 글에서 특정 연수를 단정하지 않는다).

 

핫/콜드 스토리지 분리

 

모든 로그를 ELK(Elasticsearch)에 다 넣으면 디스크 비용으로 망한다. 실무에서는 다음과 같이 나눈다.

 

  • : 최근 7~30일. Elasticsearch나 OpenSearch에 넣고 대시보드/알림을 돌림
  • : 30~90일. 검색 빈도가 낮은 구간. 저렴한 SSD나 S3
  • 콜드: 90일~5년. 압축하여 S3 Glacier나 오브젝트 스토리지에 보관. 필요할 때만 쿼리(Athena, Presto)

 

이 구조가 비용 대비 효율이 가장 좋다. 증권사 내부에서도 유사하게 운영된다.

 

실시간 알림 연동 기준

 

로그 수집만 하고 알림을 걸지 않으면 장애를 사후에 알게 된다. 증권 시스템은 실시간 알림이 필수다. 기준 예시는 다음과 같이 설정할 수 있다.

 

  • 주문 레이턴시 P99가 500ms 초과 → Slack 경고
  • 체결 실패율 1% 초과 → PagerDuty 긴급 호출
  • NTP 동기화 실패 → Slack 경고
  • 거래소 FIX 세션 끊김 → PagerDuty 긴급 호출

 

개인 개발자라도 자동매매를 돌린다면 최소 Slack 웹훅 하나는 박아둘 것을 권한다. 새벽에 봇이 이상한 주문을 날리고 있는데 아침에 앱을 열어 보고 알게 되면 이미 늦다.

 

결국 로그는 증권 개발자의 보험이다

 

 

출처: youthpress.net

 

길게 썼지만 핵심은 세 가지로 요약할 수 있다.

 

  1. 왜 밀리초인가: 거래소 매칭이 ms 단위로 돌아가고, 같은 초에 이벤트 수십 개가 발생해 순서 보장이 되지 않기 때문이다. 증권 개발 로그 밀리초 단위는 타협 불가 영역이다
  2. 무엇을 남길 것인가: client_ts/gateway_ts/exchange_ts 3종 시간 필드, trace_id 계열 추적 필드, 민감정보 마스킹을 구조화 JSON으로 찍는 것
  3. 어떻게 보관할 것인가: NTP 동기화 전제 위에서 핫/콜드 티어를 분리하고, 5년 가정으로 콜드 스토리지를 깔고, 실시간 알림 임계치를 박는 것

 

증권 개발에서 로그는 사실상 보험이다. 평상시에는 쓸데없어 보이고 디스크만 잡아먹는 듯하지만, 사고가 터지면 이것의 유무로 갈린다. 민원이 들어왔을 때 증빙이 가능한지, 장애 RCA가 가능한지, 감사 대응이 가능한지가 모두 로그에 달려 있다.

 

자동매매 시스템을 돌리는 개인 개발자든, 증권사 백엔드 조직이든 이 원칙은 동일하다. 지금 본인 프로젝트의 로그 스키마를 열어보고 (1) 타임스탬프가 ms 이상의 정밀도인지, (2) 시간 필드가 최소 2~3개 있는지, (3) trace_id가 존재하는지, (4) NTP 동기화가 정상인지 이 네 가지만 점검해도 절반은 간 것이다. 본인 서비스에 맞는 JSON 스키마를 설계해 봤다면 댓글로 공유해 주기를 환영한다. 로깅이 탄탄해야 밤에 잠이 편하다.

728x90
반응형
Share Link
reply
«   2026/05   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31