한 줄로
실시간 시세 WS 프레임을 파싱하면서 한 틱의 필드 수를 46으로 하드코딩해 둔 것이 버그의 원인이었다. 실제 프레임의 필드 수가 46이 아니면 두 번째 틱부터 46칸씩 건너뛰며 어긋나, 종목코드 자리에 가격(249500)이 들어왔다. 상수를 다른 숫자로 바꾸는 대신 프레임이 스스로 알려주는 값을 쓰도록 고쳤다.
어긋남은 왜 두 번째 틱부터인가
한 프레임 안에 여러 틱이 들어오고, 틱들은 구분자 없이 이어 붙은 하나의 필드 배열로 온다. 파서는 “한 틱이 N개 필드”라는 값을 알아야 배열을 잘라 틱 경계를 찾는다. 이 N이 46으로 코드에 박혀 있었다.
첫 번째 틱은 배열의 맨 앞에서 시작하므로 N이 틀려도 시작 위치는 맞다. 잘못된 N은 다음 틱의 시작 위치부터 영향을 준다. 그래서 N이 틀린 순간에도 첫 틱은 항상 정상으로 보인다.
오래 숨어 있던 이유
오류(2,243건)와 정상 저장(1,268건)이 동시에 났다. 첫 번째 틱은 밀릴 수가 없으니 3분봉이 계속 쌓였고, 봉 차트만 보면 시스템은 멀쩡해 보인다. 로그에는 오류와 정상 저장이 함께 남았다.
망가진 값은 이런 모양이었다.
['005930','0','10']
종목코드 자리에 가격(249500)이 들어온 사례도 있었다. 파싱은 “실패”하지 않고 그럴듯한 쓰레기를 만들어 낸다.
상수를 다른 숫자로 바꾸지 않았다
46을 실제 값으로 고치는 것은 지금은 맞지만 다음 스펙 변경 때 같은 사고를 낸다. 그때도 첫 틱은 정상으로 보일 수 있다.
그래서 프레임이 스스로 알려주는 값을 쓰도록 했다.
한 틱의 필드 수 = 전체 필드 수 ÷ 틱 수
나누어떨어지지 않으면 — 즉 이 계산 자체를 신뢰할 수 없으면 — 첫 틱만 처리하고 경고를 남긴다. 밀린 값을 그대로 내보내면 엉뚱한 종목의 손절선이 갱신되기 때문이다. 조용히 틀린 값을 흘려보내는 것보다 하나만 처리하고 남은 것을 버리는 쪽이 낫다.
검증 — 운영 컨테이너 안에서 대조
운영 컨테이너 안에 같은 프레임을 넣고 수정 전후를 대조했다.
| 필드폭 | 수정 전 | 수정 후 |
|---|---|---|
| 40·50·59 | 세 폭 모두 잘못된 값 발생. 폭별 출력은 노트에 구분되지 않음 | 세 폭 모두 정상 |
수정 전 출력이 운영 로그에서 보던 그 값과 같다는 점이 중요하다. 재현하지 못한 채 고쳤다면 “고쳤다고 생각하는 것”에 그친다.
아직 검증하지 못한 것
실시간 틱 검증은 미완이다. 장 마감(17:47) 후라 틱이 흐르지 않아 재시작 후 저장된 봉이 0건이었다. 월요일 개장 후 오류 건수를 다시 봐야 한다.
배포는 17:50, 장 마감 후에 했다. 확인한 것은 틱 오류·ERROR 0건, 손절선 047810 133,056 보존, blacklist 32종목 유지, 자산 5,100,907원, WS 구독 30종목. origin/main보다 4커밋 앞선 상태로 두고 push는 하지 않았다.
남는 교훈
“부분적으로 작동하는 고장”이 가장 오래 산다. 전부 실패하면 즉시 보이지만, 오류와 정상 저장이 함께 있으면 저장 성공만 보고 고장을 놓칠 수 있다. 이 버그에서 첫 틱이 구조적으로 절대 밀릴 수 없었다는 점이 정확히 그 은폐 조건이었다.
그리고 하드코딩된 상수를 고칠 때는 값을 갱신할 것인지 값의 출처를 바꿀 것인지를 먼저 정해야 한다. 46을 올바른 숫자로 바꾸는 수정은 버그를 없애지만 버그가 생기는 구조는 그대로 남긴다.