KBKRW 외부 송금 부하 시험 보고서 (동시 사용자 50명·100명)

요약

처리 성능: 동시 사용자 100명에서 완료 처리량 초당 3.61건, 성공률 95.02%

처리 규모: 두 회차 합계 2,355건 송금 완료, 원장·체인 불일치 신규 발생 0건

시험 조건: 요청 접수부터 온체인 확정과 원장 반영까지 전 구간을 지나는 외부 송금

동시 사용자 50명과 100명 두 규모로 외부 송금 부하 시험을 수행하였다. 두 회차 모두 목표 성공률 95%를 충족하였다.

다만 이 결과는 시험 도중 발견한 결함 하나를 고친 뒤에 얻은 것이다. 최초 시험은 시작 7초 만에 전면 중단되었고 완료 건수가 0이었다. 원인은 목적지와 금액이 같은 두 출금이 동시에 서명될 때 트랜잭션이 완전히 동일해져 서명값이 겹치는 것이었다. 그 충돌 한 건이 출금 처리기 전체를 영구 정지시켰다. 상위 저장소에 이미 수정이 있었으나 시험 환경에 반영되어 있지 않았다. 해당 수정을 적용한 이미지를 새로 만들어 배포한 뒤 재시험하였다.

재시험에서는 같은 충돌이 50명 회차에서 29회, 100명 회차에서 53회 발생하였으나 처리기는 한 번도 멈추지 않았다. 매번 새 블록해시로 다시 서명해 정상 처리되었다.

1. 시험 개요

본 시험은 KB PoC 앱의 외부 송금 경로가 동시 사용자 부하에서 정상 동작하는지 확인하기 위하여 수행하였다. 외부 송금을 대상으로 삼은 이유는 KB가 요구한 처리 경로를 모두 지나는 흐름이 외부로 나가는 거래뿐이기 때문이다. 충전, 환전, 행내 송금, 행내 결제는 원장에서 끝나며 체인을 타지 않는다.

부하는 앱 서버 내부에서 발생시켰다. 부하 발생기를 앱 서버의 도커 네트워크에 직접 붙여 백엔드 컨테이너를 호출하였으므로 로드밸런서와 외부 구간의 지연이 측정치에 섞이지 않는다.

체인 접속은 전용 유료 엔드포인트를 사용하였다. 직전까지 쓰던 무료 RPC는 크레딧이 소진되어 있었으므로 시험에 앞서 교체하고 정상 동작을 확인하였다.

2. 결과 요약

항목50명 회차100명 회차
동시 사용자50명100명
지속 시간5분5분
송금 요청 접수1,109건1,246건
송금 완료1,068건1,184건
성공률96.30%95.02%
완료 처리량초당 3.41건초당 3.61건
완주 시간 중앙값12.35초25.80초
완주 시간 p9521.71초32.61초
출금 처리기 정지0회0회
원장·체인 불일치 신규0건0건

부하를 두 배로 올렸을 때 완료 처리량은 초당 3.41건에서 3.61건으로 거의 변하지 않았고 완주 시간만 두 배가 되었다. 현재 구성이 초당 3.5건 부근에서 포화한다는 뜻이다.

3. 시험 환경

3.1 구성

항목
대상KB PoC 앱 백엔드
부하 발생기k6, 앱 서버 내부 컨테이너로 실행
접속 경로도커 네트워크 직결, 로드밸런서와 외부 구간 미경유
체인솔라나 테스트넷
체인 접속전용 RPC 엔드포인트
대상 경로외부 송금 전 구간

3.2 시험 조건

항목
시험 계정50명 회차 50개, 100명 회차 73개
송금 목적지10개 주소 순환 배정
건당 금액5,000 KBKRW
트래블룰미적용, 임계 1,000,000 KBKRW 미만
완주 판정거래 상태가 완료로 바뀔 때까지 1초 간격 조회, 최대 3분
상관키요청 메모에 회차·사용자·반복 번호를 기록

금액을 트래블룰 임계 아래로 둔 것은 의도한 조건이다. 임계를 넘기면 상대 기관 검증이 붙는데 현재 상대 기관이 응답하지 않아 전 건이 보류에 묶인다. 그 경우 송금 처리 성능 자체를 측정할 수 없다.

100명 회차의 계정이 73개인 이유는 신규 가입 축하금을 지급하는 재원이 시험 준비 중 소진되었기 때문이다. 잔여 450,099 KBKRW인데 가입 1건당 1,000,000 KBKRW가 필요하여 계정을 100개까지 늘리지 못하였다.

3.3 노드월렛 서비스

서비스역할
coordinator출금 승인, 서명 요청, 브로드캐스트, 확정 처리
approver정책 평가와 승인 판정
executor실행키 서명
chain-relay체인 조회와 입금 감지
PostgreSQL원장, 체인, 정책 데이터베이스

4. 최초 시험의 실패와 원인

4.1 실패 양상

동시 사용자 50명으로 수행한 최초 시험은 실패하였다. 요청 100건이 모두 정상 접수되고 승인 판정까지 통과하였으나 완료된 건이 하나도 없었다.

구간도달 건수
요청 접수100건
승인 판정100건
서명34건
브로드캐스트33건
온체인 확정0건

승인과 서명 사이에서 처리가 끊겼다. 앱 거래 100건은 전량 진행 중 상태로 남아 사용자 화면에서는 송금이 끝나지 않은 것으로 보였다.

4.2 원인

출금 처리기가 원장의 트랜잭션 서명 유일성 제약을 위반하며 정지하였다. 시험 개시 7초 만이었다.

충돌한 두 건은 목적지와 금액이 같았고 같은 순간에 처리되어 같은 블록해시를 사용하였다. 솔라나 트랜잭션의 서명은 트랜잭션 내용에서 결정되므로 내용이 완전히 같으면 서명값도 같아진다.

항목출금 A출금 B
요청 시각07:13:38.99707:13:38.999
목적지동일동일
금액5,000 KBKRW5,000 KBKRW
서명값동일동일
결과전송 기록원장 기록 실패, 처리기 정지

출금 트랜잭션에 건마다 달라지는 값이 들어 있지 않아 목적지와 금액이 같은 두 출금이 같은 블록해시 유효 구간에 서명되면 반드시 충돌한다. 이번 시험은 목적지 10개에 사용자 50명, 금액 고정이었으므로 충돌은 필연이었다.

두 번째 문제는 오류 처리 방식이었다. 한 건의 원장 기록 실패가 해당 건의 실패로 끝나지 않고 출금 처리기 전체를 정지시켰다. 그 결과 뒤이어 접수된 정상 요청까지 승인 상태에서 멈추었다.

4.3 조치

상위 저장소에 해당 결함의 수정이 이미 있었다. 중복 서명이 발생하면 정지하는 대신 새 블록해시로 다시 서명해 재시도하도록 바꾼 수정이다. 시험 환경의 이미지에는 이 수정이 반영되어 있지 않았고 레지스트리의 최신 이미지에도 없었다.

시험 환경이 사용하는 빌드 브랜치에 해당 수정만 반영한 이미지를 만들어 배포하였다. 트래블룰 관련 기능 등 시험 환경 전용 구현이 빠지지 않았음을 배포 전에 확인하였다.

5. 재시험 결과

5.1 구간별 소요 시간

원장에 기록된 시각을 기준으로 산출하였다.

구간50명 중앙값50명 p95100명 중앙값100명 p95
요청 접수 → 원장 반영11.19초20.52초24.77초31.66초
승인 판정0.26초0.28초0.26초0.29초
서명6.01초8.39초18.58초20.87초
브로드캐스트0.03초0.04초0.03초0.04초
온체인 확정4.09초12.24초4.92초14.05초

5.2 결함 수정의 동작 확인

항목50명 회차100명 회차
중복 서명 충돌 발생29회53회
충돌로 인한 처리기 정지0회0회

충돌은 수정 전과 같은 빈도로 계속 발생하였으나 처리기는 한 번도 멈추지 않았다. 매번 새 블록해시로 다시 서명해 정상 처리되었다. 수정이 의도대로 동작함을 확인한 근거다.

5.3 실패 건의 성격

회차실패 건수체인 전송 여부
50명41건전량 서명 이전 종료, 체인 미전송
100명62건전량 서명 이전 종료, 체인 미전송

두 회차의 실패는 모두 서명 이전 단계에서 끝나 체인에 나가지 않았다. 원장에 서명값이 남지 않은 것으로 확인하였다. 자금 이동 없이 실패로 정확히 기록되었으므로 원장과 체인이 어긋난 건은 새로 생기지 않았다.

6. 병목

부하를 두 배로 올렸을 때 구간별로 반응이 갈렸다.

구간50명 대비 100명
승인 판정변화 없음
서명3.1배
브로드캐스트변화 없음
온체인 확정1.2배

승인 판정과 브로드캐스트는 부하가 두 배가 되어도 그대로였고 온체인 확정도 거의 변하지 않았다. 서명만 3배로 늘었다. 100명 회차의 완주 시간 25.80초 가운데 18.58초가 서명 대기다.

체인도 정책 평가도 아니고 서명이 한계라는 뜻이다. 처리량을 더 올리려면 서명 처리 능력을 먼저 늘려야 한다.

7. 남은 문제

최초 시험에서 처리기가 정지한 뒤 재기동하는 과정에서 원장과 체인이 어긋난 건이 76건 생겼다. 체인에서는 정상 처리되었으나 원장에는 실패로 기록된 건들이다.

전송까지 갔으나 확정 기록이 없는 건을 재기동 시 실패로 단정한 결과다. 솔라나에서는 전송한 트랜잭션이 뒤늦게 확정될 수 있으므로 확정 여부를 체인에 다시 묻지 않고 실패로 확정하면 이런 불일치가 발생한다.

이 현상에 대한 수정도 상위 저장소에 있으나 이번 배포에는 넣지 않았다. 처리기가 정지하지 않으면 재기동할 일이 없어 새로 발생하지는 않지만 이미 생긴 76건은 체인 결과를 기준으로 원장을 정정해야 한다.

8. 종합 의견

동시 사용자 100명 규모에서 외부 송금은 성공률 95.02%, 완료 처리량 초당 3.61건으로 목표를 충족하였다. 요청 접수와 승인 판정은 두 회차 모두 1초 안쪽에 처리되었고 체인 전송과 확정도 부하에 크게 흔들리지 않았다.

다만 현재 구성은 초당 3.5건 부근에서 포화한다. 부하를 두 배로 올려도 처리량이 늘지 않고 대기 시간만 길어졌으며 그 대기는 대부분 서명 구간에서 발생하였다. 더 높은 처리량이 필요하면 서명 처리 능력 확충이 선행되어야 한다.

이번 시험에서 드러난 결함은 동시 요청이 겹치는 순간에만 나타나는 종류였다. 소규모 시험에서는 보이지 않았고 동시 사용자 3명 조건에서는 전 건이 정상 완주하였다. 규모를 갖춘 부하 시험이 아니었으면 발견되지 않았을 결함이다.

9. 측정 근거

구간별 소요 시간은 부하 발생기의 측정값이 아니라 원장에 기록된 시각을 사용하였다. 요청 접수와 원장 반영은 앱 데이터베이스에서, 승인·서명·전송·확정은 노드월렛 원장에서 추출하였다. 회차 구분은 요청 메모에 심은 상관키로 하였으므로 다른 회차의 거래가 섞이지 않는다.

온체인 결과는 솔라나 표준 조회 방식으로 확인하였으며 조회 시 과거 기록까지 포함하도록 지정하였다.

자료내용
50명 회차 원자료앱 원장, 출금 단계, 스크리닝 기록
100명 회차 원자료앱 원장, 출금 단계, 스크리닝 기록
실시간 대시보드 녹화회차별 영상 각 5분 30초

<!-- HUMANIZE-SUMMARY v1.6.1 run_id: 2026-08-24-001 route: light (route_hint=light, risk_band=medium score 5) · 강도 보수 genre: report (격식체 -다 유지) metrics: char_in: 5738 char_out: 5735 change_rate: 0.6% self_check: 6/6 grade: A categories: # before → after C-11 연결어미 뒤 쉼표 (★ ending_comma_rate z=+2.66 S1 트리거): 7 → 0 A-18 관형절 다중 중첩 문장: 3 → 2 A-1/A-2/A-7/A-8/A-16 번역투 어휘: 0 → 0 (원문에 없음) D-1~D-7 AI 관용구·hype: 0 → 0 H-1 문두 접속사 5회+: 0 → 0 ('다만' 2회 — 임계 미만, 보존) I-3 '~다는 뜻이다': 2 → 2 (규칙 허용 한도 2회 이내 — 보존) J-1/J-2/C-5 볼드·따옴표·이모지: 0 → 0 untouched: - 마크다운 표 11개 전량 (수치·항목명·구분선 원형) - 헤딩 텍스트 전량 - 수치·시각·고유명사 (KBKRW·솔라나·노드월렛·coordinator·approver·executor·chain-relay·PostgreSQL·k6·RPC·KB PoC) self_check: - 고유명사·수치·인용·내용 앵커 100% 보존: OK - 변경률 30% 이하: OK (0.6%) - 장르 이탈 없음: OK (기술 보고서 유지) - register 보존: OK ('-하였다' 원형 유지, '했→하였' 상향 없음, 하향 없음) - S1 잔존 0건: OK (C-11 7건 전량 해소) - 인공 표현 추가 없음: OK (신규 어휘 삽입 0, 문장 분리 시 접속 표현 신설 없음) highlights: - id: C-11 + A-18 before: "…서명값이 겹치는 것이었고, 그 충돌 한 건이 출금 처리기 전체를 영구 정지시켰다." after: "…서명값이 겹치는 것이었다. 그 충돌 한 건이 출금 처리기 전체를 영구 정지시켰다." - id: C-11 + A-18 before: "상위 저장소에 이미 수정이 있었으나 시험 환경에 반영되어 있지 않아, 해당 수정을 적용한 이미지를 새로 만들어 배포한 뒤 재시험하였다." after: "상위 저장소에 이미 수정이 있었으나 시험 환경에 반영되어 있지 않았다. 해당 수정을 적용한 이미지를 새로 만들어 배포한 뒤 재시험하였다." - id: C-11 + A-18 before: "…전 건이 보류에 묶이며, 그 경우 송금 처리 성능 자체를 측정할 수 없다." after: "…전 건이 보류에 묶인다. 그 경우 송금 처리 성능 자체를 측정할 수 없다." - id: C-11 before: "출금 트랜잭션에 건마다 달라지는 값이 들어 있지 않아, 목적지와 금액이…" after: "출금 트랜잭션에 건마다 달라지는 값이 들어 있지 않아 목적지와 금액이…" - id: C-11 before: "…이 수정이 반영되어 있지 않았고, 레지스트리의 최신 이미지에도 없었다." after: "…이 수정이 반영되어 있지 않았고 레지스트리의 최신 이미지에도 없었다." residual_findings: - id: A-1 severity: S1(조건부) detail: "'이 현상에 대한 수정도' — 1회 단발이라 A-1 '남발' 임계 미달. 보수 강도 지침에 따라 보존." - id: A-11 severity: S2 detail: "'확인하기 위하여 수행하였다' — 1회. 보고서 격식 register의 정상 표현으로 판단, 보존." - id: lexical_diversity z=-3.48 severity: 정보 detail: "'송금·원장·체인·처리기' 반복은 기술 보고서의 용어 일관성 요구. 동의어 치환은 내용 앵커 훼손이므로 미조치." grade_reason: "A — S1(C-11) 잔존 0건, 자체검증 6항 전부 통과, 표·수치·헤딩 무손상. 변경률 0.6%는 A 기준 밴드(10~25%) 하한 미만이나, 이는 과소 윤문이 아니라 light 경로 입력(어휘형 AI 티 0건)에 보수 강도를 적용한 결과다." over_polish_aborted: false -->

10. 재시험 절차

같은 조건으로 다시 돌리는 방법이다. 회차는 요청 메모에 심는 상관키로 구분되므로, 몇 번을 돌려도 이전 회차와 섞이지 않는다.

10.1 사전 확인

시험 전에 세 가지를 본다. 하나라도 어긋나면 부하가 아니라 환경 탓으로 결과가 망가진다.

확인 항목기준어긋났을 때
체인 접속RPC 상태 조회가 정상 응답엔드포인트 교체 후 재확인
시험 계정 잔액계정당 최소 30만 KBKRW계정 추가 준비 또는 회차 단축
출금 처리기로그가 실제로 흐르는 상태처리기 재기동

세 번째가 특히 중요하다. 이 스택은 처리기가 멈춰도 컨테이너는 정상으로 보이고 상태 점검 응답도 정상을 돌려준다. 살아 있는지는 상태 점검이 아니라 로그가 흐르는지로 판단해야 한다.

10.2 회차 실행

bash run.sh 50 5m        # 동시 사용자 50명, 5분
bash run.sh 100 5m       # 동시 사용자 100명, 5분

부하 발생기를 앱 서버로 올리고, 서버 안에서 실행하고, 끝나면 원장에서 결과를 걷어 집계까지 한 번에 이어진다. 건당 금액을 바꾸려면 AMOUNT, 회차 이름을 지정하려면 RUN_ID를 앞에 붙인다.

10.3 실시간 대시보드

회차가 도는 동안 부하 발생기가 웹 대시보드를 연다. 서버 안에서 열리므로 밖에서 보려면 터널이 필요하다.

ssh -N -L 5665:127.0.0.1:5665 solana-app

브라우저에서 http://127.0.0.1:5665로 들어간다. 대시보드는 회차가 끝나면 사라지지만, 같은 내용의 정적 보고서가 결과 폴더에 남는다.

10.4 녹화

대시보드가 응답하기 시작한 뒤에 녹화를 붙인다. 회차보다 먼저 시작하면 빈 화면이, 늦게 시작하면 앞부분이 빠진다.

PROJECT=k6dash DASH_URL=http://127.0.0.1:5665 DSF=1 WATCH_MS=330000 npm run record
npm run compose

WATCH_MS는 회차 길이보다 넉넉히 잡는다. 짧으면 그래프가 차오르는 도중에 끊긴다.

10.5 결과 회수

영상과 원자료는 부하를 건 워크스테이션에 남는다. 앱 서버가 아니다. 녹화가 가상 화면에 브라우저를 띄워 워크스테이션에서 찍히기 때문이다.

bash fetch-videos.sh                  # 영상만
bash fetch-videos.sh --with-data      # 영상과 회차 원자료

회차마다 파일 이름이 달라지므로 이름을 고정해 받지 않는다. 목록을 먼저 확인하고 받는다.

10.6 실패했을 때 볼 순서

완료 건수가 0이거나 대부분이 진행 중에 머물면 다음 순서로 좁힌다. 위에서부터 보는 편이 빠르다.

순서확인해당하면
1출금 처리기가 정지했는가정지 사유를 로그에서 확인, 재기동
2체인 접속에 요청 제한이나 크레딧 오류가 있는가엔드포인트 교체
3시험 계정 잔액이 남아 있는가계정 보충
4상대 기관 검증이 걸렸는가건당 금액을 트래블룰 임계 아래로

처리기 정지는 재기동으로 풀리지만, 정지 상태에서 전송까지 갔던 건이 실패로 확정되며 원장과 체인이 어긋날 수 있다. 재기동 전에 전송 기록이 있는 건을 먼저 세어 두면 나중에 정정 대상을 특정하기 쉽다.