/foreign-pitcher-predict

클로드와 함께 자동화 파이프라인을 완성하다

by randahlia

37개의 코드


그동안 예측력을 맞춰내는 일에만 몰두하다 보니 이미 많은 코드가 쌓여 있었다. scripts 폴더를 열어보니 37개의 파이썬 파일이 있었다. 01_extract_kbo_data.py부터 37_optimized_war_model.py까지. 각각이 지난 몇 일간 클로드와 함께 개발한 흔적이다.

AI 바이브코딩은 이런 점에서 유용하다. 내가 몇년간 시도하다가 실패하고, 에러코드만 죽어라 발생시키던 일을 단 몇일동안의 채팅(?) 으로 이뤄냈다. 물론 클로드 코드와의 대화창만을 썼던것은 아니다. Deep Research, 구글 검색 및 관련 글감 조회등 많은 개인적 공부를 같이 병행해야 했다.

대략 이런 구성이었다. 01번부터 07번까지는 초기 시도들이다. 18명의 성공 투수만으로 분석하던 시절, 생존자 편향 문제로 폐기된 코드들. 08번부터 15번은 데이터를 128명으로 확장하고 생존 분석을 추가하던 시기의 코드다. 16번부터 19번은 생존 확률 Band 시스템을 만들던 때, 20번부터 25번은 4-Group 피처를 도입하던 때. 26번부터 28번이 현재 핵심 시스템이고, 29번부터 37번은 시퀀싱 피처를 실험하던 코드들이다.


구단에서 일할 때는 사실 R을 썼었고, 이걸 다루는 사람이 나 혼자였어서 클린 코드 원칙같은걸 알지도 못했고 지키지도 않았다. 디라커라는 구단 분석 시스템을 재구축할 때, 시스템 구축 전문가 분들께 보고서 포맷을 설명하기 위해서 코드를 정리해서 주석을 달아 전달해 드렸던게 전부같다.(10년전의 나는 OOP라는 개념을 알지도 못했고, import라는 개념에 대해 매우 무지했다) 하지만 이제는 나는 채팅만 하는 사람이니, 체계적인 원칙을 주고 그대로 만들어 달라고 했다. 분석할 때마다, 그리고 새로운 단계가 추가될 때 마다 새 파일을 만들었다. 뭐가 되고 안 되는지 추적하기 위해서. 프로젝트가 끝나면 폴더에 온갖 버전의 스크립트가 쌓여 있다. 하지만 이대로는 아무도 쓸 수 없다. 파일명, 단계, 주석 등으로 어느정도 구분할수는 있지만, 혹여나 나중에 공개할 필요가 있을 때에 나 말고는 어떤 파일이 뭘 하는지 모를수도 있었다(나라고 다 알겠어. 내가 만든게 아닌데). 정리가 필요했다.



핵심 5개로 정리하다


37개를 5개로 정리했다. 각 스크립트의 역할을 분석하고, 중복되는 기능을 합치고, 필수적인 것만 남겼다.


05_generate_report.py가 메인이다. 통합 리포트를 생성하는 스크립트. 이 파일이 나머지 네 개를 호출한다. 26_feature_extraction_4group.py는 4-Group 피처를 추출한다. 투수의 구종을 4개 그룹으로 묶고, 각 그룹별로 15개 특성을 계산한다. 시퀀싱 피처도 여기서 추출한다. 27_similarity_analysis_4group.py는 유사도 분석을 담당한다. 후보 투수와 KBO 128명 투수 사이의 코사인 유사도를 계산하고, 같은 타입 내에서 가장 유사한 투수 5명을 찾는다. 28_pitcher_type_classifier.py는 투수를 6가지 타입으로 분류한다. 삼진 마스터, 파워 직구형, 변화구 장인, 제구형, 그라운드볼 유도형, 밸런스형. 16_survival_probability_bands.py는 생존 확률을 계산한다. 50이닝, 100이닝, 150이닝을 소화할 확률과 재계약 확률까지.

개인적으로 이번에 만들고자 한 신규 외국인투수 분석 기능에서, 이 '생존 확률'을 핵심 기능이라고 생각하고 개발했다. 투수들의 퍼포먼스를 시즌단위로 나누어둔 이유도 그런 까닭에서였다. '아~ 이정도 퍼포먼스면 50이닝(조기퇴출)도 못버티는구나. 이정도면 한시즌을 계속 선발로 뛸수있구나' 라는 기준이 필요했다. 이것을 토대로 구간별 예상 WAR을 계산해두면, expected WAR(xWAR)을 좀더 명확하게 제공할수 있으리라 생각했었다.

나머지 32개는 개발 히스토리로 남겨두기로 했다. 삭제하지 않았다. 나중에 왜 이런 결정을 했는지 추적할 때 필요하니까.(사실은 확인되지 않은 의존성 관계가 여전히 남아있을 가능성이 있고, 한명의 선수를 분석하는데 다양한 중간결과물 형태의 csv가 생성되고 지워지는데, 중간 과정이 의도치않게 증발하였을 경우 추적하기 위함이었다.) 최대한 히스토리를 남겨두고, 이전의 아이디어가 다시 필요하다면 다시 꺼내쓰면 될 일이었다.



클로드 코드의 슬래시 명령어


이제 이 5개 스크립트를 한 줄 명령어로 실행할 수 있게 만들 차례다. 클로드 코드에는 "슬래시 명령어"라는 기능이 있다. `.claude/commands/` 폴더에 마크다운 파일을 만들면, `/명령어` 형태로 실행할 수 있다. 보통은 파이썬 파일을 실행하면, 내가 원하는 형태의 csv결과 파일을 만들어준다. 그러면 나는 이걸 또 열어서 수치를 확인하고, 나름의 인사이트를 가지고 글을 쓰는 형태다. 구단에서도, AI센터에서도(정확히는 나는 NLP센터 소속이었다. AI센터와 NLP센터 두개의 조직이 있었으므로 정확한 정보를 위해 남겨둔다)

이러한 '자동화된 보고서형 컨텐츠'에 너무 목이 말라있었지만 그때 당시만 해도 AI를 활용해서 이정도 결과물을 만들어 내기는 쉽지 않았다.(누가 야구 보고서 콘텐츠 만드는데 개발자 수십명을 붙여주겠는가.)


`foreign-pitcher-predict.md`라는 파일을 만들었다. 200줄이 넘는 상세한 프롬프트다. 클로드가 어떤 순서로 분석을 진행해야 하는지, 어떤 스크립트를 호출해야 하는지, 결과를 어떻게 해석해야 하는지 모두 적어놓았다. 이 파일 하나가 전체 분석 파이프라인을 정의한다.


구단에서 일할 때 이런 걸 만들고 싶었다. 분석 요청이 들어오면 몇 시간, 길면 며칠씩 걸렸다. 데이터 추출하고, 정리하고, 분석하고, 리포트 쓰고. 그 과정을 자동화하면 얼마나 좋을까 늘 생각했다. 지금은 가능하다. 클로드가 있으니까.



분석 파이프라인


클로드 코드 내에서, `/foreign-pitcher-predict`를 실행하면 어떤 일이 벌어지는지 설명해보겠다.(ChatGPT에서 쓰는 MCP와 비슷한 구조다.)


/foreign-pitcher-predict 를 실행하면

먼저 foreign_pitchers 폴더를 뒤진다. 분석 가능한 후보 목록이 쭉 뜬다. "M을 분석해줘"라고 말하면 해당 투수의 데이터 파일을 찾아서

26_feature_extraction_4group.py를 돌린다. 26개 핵심 피처와 시퀀싱 피처가 계산된다.

그다음 28_pitcher_type_classifier.py가 돌아가면서 6가지 타입 중 하나로 분류한다.

타입이 정해지면 27_similarity_analysis_4group.py가 같은 타입 내에서 가장 비슷한 KBO 투수 5명을 찾는다.

그리고 05_generate_report.py의 v5.1 모델이 Per 150 IP 기준 WAR과 FIP를 예측한다.

마지막으로 16_survival_probability_bands.py가 50, 100, 150 이닝 생존 확률과 재계약 확률을 계산한다.

csv 결과 파일들을 기반으로 Claude Opus4.5모델이 정해진 템플에 맞게리포트를 생성한다.

result 폴더에 투수 이름으로 마크다운 리포트가 떨어진다.

전체 과정은 길어야 2~3분 내외다.


전체 과정이 2-3분 안에 끝난다. 구단에서 며칠 걸리던 작업이 몇 분으로 줄었다. 물론 나 혼자 쓰는 도구지만, 이 정도면 충분히 개인 컨텐츠를 만들만한 수준이라 생각했다. 가장 중요한것은, csv 여러개를 내가 굳이 일일이 살펴보지 않아도 마크다운 형태의 보고서를 AI가 만들어 준다는 사실이다. 물론 이 과정에 오기까지 수많은 할루시네이션(AI가 없는 정보를 생성하는 행위)을 겪었고, 할루시네이션을 피하기 위해 리포트 생성 프롬프트를 매우 엄격하게 작성해 두어야만 했다. 대표적인 가이드로는


CRITICAL : NO HALLUCINATION POLICY

스크립트에서 생성하지 않는 투수 이름을 만들지 말것

스크립트 출력에 없는 섹션을 추가하지 말것

팀 정보나 투수 경력을 추측하지 않을 것

스크립트 출력 파일을 그대로 사용할 것

선수 프로필 정보를 채우기 위한 웹 검색을 하지 않을 것

보고서 양식은 엄격히 템플릿 구조를 따를 것

정도가 있었다.(물론 각 항목별 세부 지침이 별도로 존재한다)



리포트 출력 예시


투수 M을 분석하면 이런 결과가 나온다.


OVR 등급 30(40). 야구에서 주로 쓰는 20-80 스케일을 선수 평가에 녹여냈다. WAR 예측값과 생존확률을 종합해서 20-80스케일로 환산한 점수다. 별 하나짜리면 Replacement 등급이다. 솔직히 낮다. Per 150 IP WAR은 1.3, 90% 신뢰구간이 -0.5에서 3.1. 불확실성이 크다. Per 150 IP FIP는 5.27, 신뢰구간 4.36에서 6.19. 역시 넓다. 투수 타입은 삼진 마스터. K% 22.5%로 분류됐다. 직구 티어는 S등급, 151.5km/h. 이건 확실히 좋다. 가장 유사한 투수는 알드레드(2024), 유사도 51.6%.


생존 확률 Band를 보면 상황이 더 명확해진다. P(50+ IP) 85.2%, 여기까지는 괜찮다. P(100+ IP) 58.6%, 절반을 조금 넘긴다. P(150+ IP) 34.4%, 풀 시즌을 완주할 확률은 3분의 1 수준이다. 1백만 달러를 주고 야심차게 영입하는 선수가 이정도 수치가 나온다고? 하는 의문이 들지만, 일단 현재 상태에서는 '프로세스가 문제없이 구동되는 상태'를 확인하는게 중요했다.



두 가지 WAR의 의미


리포트에는 두 가지 WAR을 표시하게끔 설정해두었다. 이 구분이 중요하다.


Per 150 IP WAR은 순수한 능력치다. 이 투수가 건강하게 150 이닝을 던진다고 가정했을 때 창출할 WAR. 부상 없이 풀 시즌을 소화했을 때의 가치다. M의 경우 1.3이다. 풀시즌을 소화할때 1.3


eXpected WAR은 생존 확률을 반영한 실제 기대값이다. 능력에 확률을 곱한 값. M의 경우 P(150+ IP)가 34%이므로, eXpected WAR은 대략 0.8 정도가 된다. 이게 더 현실적인 수치다. "이 투수를 영입하면 평균적으로 얼마나 기여할까?"에 대한 답이다. 50이닝 미만일 확률이 30%고, 50이닝 미만일 때의 기대 WAR이 1이라면, 50이닝 미만에 대한 xWAR은 0.3인 셈. 50이닝 미만, 50~100이닝, 150+이닝 세가지 시나리오에 대한 확률과 기대값을 가중치로 두고 계산한 것이 xWAR이다.


구단의 선수평가에서 선수출신 프런트와 비선출 프런트의 의사소통 과정에서 가장 빈번한 일이 바로 이것이다. 선수출신들은 "아 쟤 폼좋다. 잘한다" 정도로도 서로 의사소통이 된다. 하지만 대부분의 프런트, 그리고 단장/대표이사는 '왜 좋은지' 모른다.

뭐가 어떻게 좋은지

너무나도 궁금하다는 것. 그래서 역대 선수들을 기준점으로 20~80스케일의 재능 스카우팅 리포트 템플릿을 만들고, 구단 전체가 사용할수 있게끔 했었다.(파워 스케일이 80점이 되려면 이승엽 심정수 박병호 레벨은 되어야 한다. 컨택 스케일이 80이 되려면 장효조 이정후 양준혁 수준은 되어야 한다 등). 그 이후에, 유망주에 대한 리포트에서는 현재 수준과 미래 수준에 대해 기록하되, 미래 수준의 가능성을 같이 기록할수 있게끔 했다. "송민구라는 아마추어 선수가 현재는 35의 재능이지만, 75의 가능성까지도 볼 수 있다. 하지만 그 가능성이 현실화 있는 확률은 A(80%이상)/B/C/D/E(10%미만) 이다."

송민구(35/75(D))

너무나도 직관적인 시스템이다. 재능이 있기는 한데, 현실적으로 리스크가 높은 픽이 될거다 라는 정보까지 한줄에 다 담고 있는 것. 능력과 리스크를 분리해서 보여주면 더 나은 의사결정이 가능하다는 것을 그때 배웠다. 그래서 이 시스템 또한 그때의 경험을 살려 설계했다.



계약 가치 평가


eXpected WAR을 기반으로 계약 가치도 평가할 수 있다. KBO 시장 기준으로 1 WAR은 대략 5.5억 원 정도로 추정한다. 최근 5년간 FA계약 규모와 WAR을 역산한 값인데, 계약 규모나 선수의 등급에 따라 4억에서 7억까지 편차가 있다. 물론 전체 선수를 대상으로 하면 1WAR당의 금액은 낮아진다(신인급이나 저연차의 스타급 선수들은 상대적으로 연봉이 낮으므로). 외국인선수는 사실상의 베테랑 FA로 간주해야 한다고 생각했다. 그래서 연봉에 합당하는 퍼포먼스는 FA의 평균적 WAR 기대치로 설정했다. 신규 외국인 투수가 받을 수 있는 최대 연봉이 $1M, 약 15억 원이다.


계산해보자. xWAR 2.5인 투수가 있다면, 선수 가치는 2.5 × 5.5억 = 13.75억 원이다. 15억 원 계약이라면 가치 비율은 91.7%. Fair Deal, 적정 수준이다. 130% 이상이면 Bargain, 구단이 잘한 계약. 90-130%면 Fair Deal, 적정. 60-90%면 Slight Overpay, 약간 높음. 60% 미만이면 Overpay, 과다 지불.



M의 경우를 보자. xWAR 약 0.8이다. 선수 가치는 0.8 × 5.5억 = 4.4억 원. $1M(15억) 대비 29%에 불과하다. 현재 데이터 기준으로는 Overpay다. "절대 영입해서는 안되는 수준. 명백히 후보군을 다시 살펴봐야 하는 수준" 이다.



백테스팅으로 검증하다


시스템이 실제로 작동하는지 검증해야 했다. 클로드와 함께 백테스팅을 설계했다. 2021-2024 데이터로 학습하고, 2025 투수들을 예측해보는 방식이다. 2025 시즌이 끝났으니 실제 결과와 비교할 수 있다.

* 참고 : 개인적 연구의 일환으로, 파크팩터, wOBA, wRC+ 및 WAR까지 직접 구현하고 있습니다. 이 모듈을 만들던 때 기준으로 폰세의 WAR은 저렇게 나왔습니다.(그동안 많은 수정이 있었어서, 지금은 달라졌습니다.) 이 글, 그리고 이 시리즈에서 언급되는 모든 WAR은 자체 계산 수치임을 밝힙니다.


성공 투수(WAR 4.0 이상) 예측 결과를 보자. 폰세는 예측 WAR 7.17, 실제 WAR 9.38이었다. 오차 -2.21. 과소평가했다. 후라도는 예측 5.91, 실제 4.57. 오차 +1.34. 약간 과대평가. 와이스는 예측 5.96, 실제 5.71. 오차 +0.25. 거의 정확했다. 성공 투수 3명의 MAE(평균 절대 오차)는 0.77 WAR. 상당히 정확한 편이다.


하지만 조기 방출 투수들은 예측이 어려웠다. 65 이닝 미만으로 잘린 투수들. 시스템은 "150 IP 기준 능력"을 예측한다. 하지만 그들은 150 이닝을 던지지 못했다. 부상, 멘탈 붕괴, 팀과의 불화. 이런 건 데이터로 예측할 수 없다. 65 이닝만 던지고 잘린 투수와 예측값을 비교하는 건 공정하지 못하다.


시스템 신뢰도는 B+ 정도라고 평가했다. 성공 투수 식별 능력은 우수하다. 실패 투수 예측에는 한계가 있다. 데이터의 한계이기도 하고, 야구라는 스포츠의 본질적인 불확실성이기도 하다.



혼자서 여기까지 왔다



시스템이 완성됐다. 개인적 숙원사업이, 우여곡절 끝에 드디어 결실을 맺었다. 조직에 있을 때는 리소스가 없어서, 우선순위가 밀려서, 승인이 안 나서 못 했던 것들. 실력이 모자라서 제대로 해내지 못했던 것들을, 비록 혼자서 할지라도 AI덕분에 완성해내고 있다는 사실. 프로그래밍 이란게 원래 '어? 어~ 어? 어!'라는 과정을 거치는 경우가 많지만, 이제는 대화창을 통해 해결해 나가고 있다는게 정말 "어?" 할 만한 놀라운 것이었다.


물론 완벽하지 않다. R² = 0.36이라는 건 WAR 변동의 64%를 아직 설명하지 못한다는 뜻이다. 부상은 예측할 수 없고, 멘탈도 마찬가지다. 128명이라는 샘플로 과적합 위험도 있다. 하지만 동전 던지기보다는 낫다. '될놈은 된다'의 단계에서 '얼마나 잘될거 같아'정도는 볼 수 있는 수준이라는 것에 만족했다.



다음 편 예고



5편에서는 이 한계들을 솔직히 인정하려고 한다. R² = 0.36이 의미하는 것. 예측할 수 없는 영역들. 앞으로 개선하고 싶은 것들. 데이터의 한계를 인정하는 것도 분석의 일부다.


그리고 이 프로젝트를 진행하면서 느낀 것들. AI와 함께 일한다는 것, 9년간의 경험을 개인 프로젝트로 실현한다는 것, 혼자서 연구한다는 것의 의미.


**[5편: 한계와 미래 - 데이터의 한계를 인정하는 것도 분석이다]**




이전 03화구속은 8위