월드컵을 통째로 서비스에 담아보기로 했다
월드컵 RAG 개발기 1편 — 프로젝트 개요, 아키텍처, 그리고 "숫자는 코드가, 문장은 LLM이"라는 설계 결정.
연재 · 월드컵 RAG 프로젝트 개발기 (1/5) 2026 북중미 월드컵을 데이터로 다루는 웹 서비스를 혼자 만들고 운영하며 겪은 것들.
- 월드컵을 통째로 서비스에 담아보기로 했다 ← 지금 글
- 크롤러가 죽었다 — 데이터 수집 잔혹사
- 배포했더니 데이터가 사라졌다 — Railway 운영의 함정들
- LLM은 숫자를 지어낸다 — 환각과 싸운 기록
- 손흥민 리포트 제작기 — 이벤트 데이터로 기사급 리포트 만들기
뭘 만들었나
2026 북중미 월드컵을 앞두고, 경기 데이터를 모아서 프리뷰·리뷰 기사를 자동 생성하고, 챗봇으로 질문에 답하는 웹 서비스를 만들기로 했다. 목표는 단순했다 — 경기가 끝나면 몇 분 안에 데이터 기반 리뷰가 올라오고, “이번 조별리그에서 크로스 성공률이 제일 낮았던 팀은?” 같은 질문에 근거를 갖고 답하는 것.
최종 형태는 이렇다:
- Streamlit 멀티페이지 앱 — 관리자/기자 역할 구분, 경기 리뷰·커스텀 리뷰·프리뷰·챗봇·데이터 관리 페이지
- 데이터: sofascore 경기 통계(팀/슛맵/선수 CSV) + WhoScored 이벤트 데이터(경기당 1,500여 개 이벤트) + 웹 기사 수집(48개 본선 팀, 2,400여 건)
- 생성: OpenAI API로 리뷰·프리뷰·챗봇 답변. RAG로 수집 기사를 컨텍스트로 주입
- 배포: Railway (커스텀 도메인), 영구 데이터는 Volume에
211커밋을 지나 지금은 본선 96경기(조별리그 72 + 32강 16 + 16강 8) 데이터가 올라가 있고, 대회 진행에 맞춰 매일 업데이트 중이다.
시작은 Hugging Face Spaces였다
처음엔 Streamlit 템플릿을 복제해서 HF Spaces에 올렸다. 프로토타입엔 충분했지만 곧 한계가 왔다. 재시작마다 데이터가 날아가서 HF Dataset 동기화로 버텼는데, 서비스가 커지니 어색한 구조가 됐다. 결국 Railway로 옮기고 Dockerfile 배포 + Volume 마운트 구조로 정리했다. (이 과정에서 겪은 함정들은 3편에서.)
가장 중요한 설계 결정 — 숫자는 코드가, 문장은 LLM이
이 프로젝트에서 제일 잘한 결정을 하나 꼽으라면 이거다.
초기에 LLM에게 원시 데이터를 주고 “표로 정리해서 리뷰 써줘”라고 하면, 표의 숫자를 지어냈다. 합계가 안 맞고, 없는 선수가 등장하고, 슛 분포를 골 시간으로 착각했다. 몇 번 데인 후 역할을 완전히 갈랐다:
- 집계·표·수치 = 파이썬 코드가 확정 생성 (pandas로 계산, 마크다운 표까지 코드가 만듦)
- LLM = 확정된 수치를 “인용”만 — 프롬프트에 “이 표를 그대로 복사해 넣어라, 재계산 금지”를 박았다
이 원칙은 이후 모든 기능(리뷰, 챗봇, 기자용 리포트)의 뼈대가 됐다. 자세한 건 4편에서 다룬다.
역할 기반 워크플로
기자가 실제로 쓰는 도구가 되려면 “생성 → 검토 → 발행” 흐름이 필요했다. 그래서:
- admin: 데이터 검수·승인, 경기 공개 여부, 발행 권한
- reporter: 커스텀 리뷰 작성(관점·형식·분량 지정), 본인 초안 열람
커스텀 리뷰는 “관전 포인트를 자유 서술로 입력 → LLM이 청크(섹션) 구성을 제안 → 스트리밍 생성” 구조다. 형식(서술형/개조식/요약)과 분량(짧게/보통/길게)을 고르게 했더니 실사용 만족도가 확 올라갔다.
이 연재에서 다룰 것
만들면서 “이건 글로 남겨야겠다” 싶었던 순간이 많았다:
- 크롤러가 403으로 전멸하고, 알고 보니 기관망 IP가 Cloudflare에 차단돼 있었던 날 (2편)
- 배포만 하면 승인·발행 상태가 사라져서 한참 헤맨 끝에 찾은 휘발성 디렉토리 (3편)
- 맥에서 올린 한글 폴더가 서버에서 두 개로 보이던 유니코드 정규화(NFC/NFD) 사건 (3편)
- LLM이 골 시간을 슛 분포에서 “추론”해서 지어내던 문제와 단일 출처 원칙 (4편)
- 손흥민 3경기 리포트를 만들며 발견한 WhoScored 데이터의 함정들 — 후반 시계는 45분부터 다시 시작한다 (5편)
한 편씩, 삽질의 기록을 정리해 본다.