김종인
English백엔드 · 시스템 소프트웨어 개발자의 포트폴리오
- 학력
- KAIST 전산학부 학사, 2027년 2월 졸업 예정
- 경력
- 루센트블록 2018.12 – 2023.12 · 금융 플랫폼 백엔드. 그중 약 2년(2020 – 2021)은 Rust 매칭엔진 개발
사회평론 2026.01 – 03 · 프리랜서 백엔드 개발(Java/Spring, 학습 플랫폼 재구축) - 언어
- 아래 작업은 Rust, TypeScript, Java, JavaScript(Node.js), Python으로 진행했습니다. C는 학교 수업에서 사용했습니다.
문제 해결 1
매칭엔진의 오더북 자료구조 선정: 실행 중 재할당이 없는 구조
주문 가격
→
틱 단위 인덱스
→
크기를 미리 잡은 Vec
가격 슬롯 0 … N
→
가격 슬롯 0 … N
가격별 주문 큐
먼저 온 주문부터
먼저 온 주문부터
① 배열 + 주문 큐 선택
② B+ tree + flyweight
③ dense hash set
문제
- 매칭엔진은 주문이 들어올 때마다 오더북을 읽고 고치므로, 자료구조가 지연 시간에 바로 영향을 줍니다.
- 매칭엔진을 Rust로 다시 만들면서 오더북 구조를 새로 정해야 했습니다.
- 실행 중 메모리 재할당이나 흩어진 메모리 접근이 생기면 지연 시간의 편차가 커질 수 있었습니다.
해결
- C++ 오더북 구현 사례를 분석해 배열 + 주문 큐, B+ tree + flyweight, dense hash set 세 가지 안을 비교했습니다.
- local allocator가 주문 추가, 삭제, 취소, 수량 감소에 주는 영향을 조사하고 메모리 단편화 문제는 팀 회의 안건으로 올렸습니다.
- 메모리를 캐시 라인 단위로 가져오는 하드웨어 특성상 연속된 배열이 유리하다고 판단했습니다. 가격을 틱 단위 인덱스로 바꾸고 크기를 미리 잡은 Rust
Vec을 써서 실행 중 재할당을 없앴습니다.
결과
- 오더북의 공개 함수 테스트와 시나리오 테스트를 만들어 함께 병합했습니다.
- 이 테스트로 결함 두 개를 찾아 고쳤습니다. 주문을 지운 뒤 최우선 가격이 갱신되지 않았고 변경분 메시지에는 수량 0인 항목이 섞였습니다.
문제 해결 2
매칭엔진의 한계 성능을 찾는 벤치마크 도구 설계
테스트 설정
→
벤치마크 도구
Kubernetes 제어
→
Kubernetes 제어
부하 생성 worker
→
Kafka
→
매칭엔진
CPU, 메모리 제한
CPU, 메모리 제한
↓
구간별 로그 수집
→
자동 판정
처리량, 가용성, 재시작, 오류
→
처리량, 가용성, 재시작, 오류
리포트와 대시보드
문제
- 매칭엔진이 버티는 최대 부하와, 코드를 바꾼 뒤 나아지거나 나빠진 점을 같은 조건에서 반복해 확인할 방법이 없었습니다.
- 손으로 측정하면 데이터, 자원, 측정 구간이 매번 달라져 결과를 비교하기 어려웠습니다.
해결
- Kubernetes에서 실행하는 도구를 설계했습니다. 부하 테스트는 worker 수를 단계적으로 늘리고 스트레스 테스트는 매칭엔진의 CPU와 메모리 한도를 바꾸며 성능 테스트는 무작위 주문을 미리 쌓아 두고 처리하게 합니다.
- 미리 정한 기준으로 처리량, 가용성, 재시작, 치명 오류를 자동 판정하고 대시보드 링크가 붙은 리포트를 남기게 했습니다.
- 측정한 지연 값에 음수가 섞여 나오자 엔진보다 측정 데이터를 먼저 살폈습니다. 원인은 구간의 시작이나 끝 로그 유실, 엔진 재시작으로 같은 Kafka 메시지를 다시 처리한 중복, 집계 쿼리가 여러 로그 중 잘못된 시각을 고른 경우였습니다. 구간의 시작과 끝 로그 수와 고유성을 검사해 어긋난 표본은 걸렀고, 시작 시각은 가장 이른 로그를 쓰도록 쿼리를 고쳤습니다. 다시 검사하자 확인했던 음수 구간이 사라졌습니다.
결과
- 배포마다 변경 사항과 실행 결과를 함께 기록해 비교할 수 있게 됐습니다.
- 비동기 런타임의 채널을 바꾼 전후를 반복 측정해 t-검정으로 비교했고 좋아진 구간과 나빠진 구간을 함께 보고했습니다. 두 측정의 로그 유실 비율이 달라 처리량 비교는 이 데이터로 결론 내리지 않았습니다.
- 이후 같은 방식으로 성능 저하를 발견했고 자원 조건을 바꾼 재측정으로 환경 요인을 먼저 배제했습니다.
- 이 도구로 같은 조건에서 반복 측정하며 병목을 하나씩 줄여, 2021년 최적화를 마칠 때 처리량을 초당 100건 안팎에서 약 38,000건으로 높였습니다.
문제 해결 3
규정 대응에 쓰는 블록체인 기록 모듈과 DB 기반 migration
서비스 이벤트
→
메시지 브로커
→
블록체인 기록 모듈
Spring Kafka listener
→
Spring Kafka listener
내부 블록체인 원장
↑
DB의 과거 데이터
→
migration
메시지 재구성
→
메시지 재구성
실패하면 순서 처리 중지
문제
- 규정 대응을 위해 주요 거래 이력을 내부 블록체인 원장에 기록해야 했습니다.
- 기존 데이터도 기록해야 했지만 메시지 브로커의 보존 기간이 지나 과거 이벤트는 남아 있지 않았습니다.
해결
- Spring Kafka listener의 컨테이너 구조, 파티션 할당, offset 초기화 방식을 조사한 뒤 이벤트 종류마다 처리 모듈을 구현했습니다.
- 과거분은 DB에서 메시지를 다시 만들어 보내는 migration으로 설계하고 파티션을 할당받으면 바로 실행하게 했습니다.
- migration이 실패하면 순서 처리를 멈춰 순서가 어긋난 채로 기록이 이어지지 않게 했습니다.
결과
- 테스트에서 문제 두 가지를 찾아냈습니다. 파티션 할당이 멀티스레드로 일어나면 circuit break가 동작하지 않았습니다. migration이 두 번 실행되기도 했습니다.
- 팀과 migration 전략을 논의하고 문서화한 뒤 운영에 적용했습니다.
문제 해결 4
이벤트마다 프로세스를 띄우던 입금 알림을 ZeroMQ 구독으로 전환
데몬 (블록체인 노드)
ZeroMQ publisher
→
ZeroMQ publisher
Notifier
같은 서버의 subscriber
→
같은 서버의 subscriber
DB
입금 내역, 블록
→
입금 내역, 블록
내부 서버
새 블록 수신
→
저장된 hash와 비교
→
갈라진 지점 찾기
확정 전 블록만 비교
→
확정 전 블록만 비교
블록과 입금 내역 다시 맞추기
문제
- 기존에는 입금 이벤트가 생길 때마다 HTTP 요청을 보내는 스크립트를 새 프로세스로 실행했습니다. 관리하는 주소가 늘면 프로세스 생성으로 메모리를 많이 쓸 수 있었습니다.
- 서버가 정보가 필요할 때마다 데몬의 RPC를 호출해야 했습니다.
- 체인이 갈라지면 DB에 저장한 블록과 실제 체인의 블록이 어긋날 수 있었습니다.
해결
- Notifier를 데몬과 같은 서버에서 실행하고 ZeroMQ pub/sub로 트랜잭션과 블록을 구독하게 했습니다. 그만큼 서버 사이의 통신이 줄었습니다.
- 입금 기록과 전달, 확인 수(confirmation)를 채운 블록 전달, 입금이 채굴된 블록 기록, DB와 체인이 어긋났을 때 복구, 네 가지 기능을 구현했습니다.
- 복구할 때는 DB에서 확인 수만큼의 블록만 가져와 비교하고 일치하는 지점을 찾으면 RPC 호출을 멈추게 했습니다. 네트워크 버퍼가 little endian이라 hash 비교가 틀리던 문제도 바이트 순서를 바로잡아 해결했습니다.
결과
- Docker로 테스트용 데몬과 서버를 띄워 네 가지 기능을 각각 테스트하고 결과를 문서로 남겼습니다.
- 체인이 갈라지는 상황을 재현해 다음 블록을 받으면서 어긋난 블록을 찾아 hash와 입금 내역을 다시 맞추는 것을 확인했습니다.