01 — RESEARCH ·R-003 ·2026.06.11

배포했더니 데이터가 사라졌다 — Railway 운영의 함정들

월드컵 RAG 개발기 3편 — Volume vs 휘발 디렉토리, NFC/NFD 한글 폴더명, iCloud가 git을 멈출 때.

목차
  1. 했다: Railway 배포
  2. 사건 1: “재배포할 때마다 승인을 다시 해야 돼”
  3. 사건 2: 로컬엔 한 경기인데 서버엔 두 경기
  4. 사건 3: git이 4분째 멈춰 있다
  5. 사건 4: 홈 화면이 가짜 일정을 그리고 있었다
  6. 배운 것

연재 · 월드컵 RAG 프로젝트 개발기 (3/5)

  1. 월드컵을 통째로 서비스에 담아보기로 했다
  2. 크롤러가 죽었다 — 데이터 수집 잔혹사
  3. 배포했더니 데이터가 사라졌다 — Railway 운영의 함정들 ← 지금 글
  4. LLM은 숫자를 지어낸다 — 환각과 싸운 기록
  5. 손흥민 리포트 제작기 — 이벤트 데이터로 기사급 리포트 만들기

했다: Railway 배포

HF Spaces에서 Railway로 옮기고 Dockerfile 배포 + 커스텀 도메인을 붙였다. push하면 자동 빌드되고, railway ssh로 컨테이너에 들어가 검증할 수 있어 운영이 편해졌다. 그런데.

사건 1: “재배포할 때마다 승인을 다시 해야 돼”

리뷰를 쓰고 admin이 승인·발행까지 마쳤는데, 재배포하면 승인 상태가 초기화된다는 제보가 왔다. 발행된 리뷰 본문(.md)은 살아 있는데 승인 상태만 사라졌다.

원인은 저장 위치였다. Railway에서 컨테이너 파일시스템은 재배포 시 이미지 상태로 리셋된다. 영속돼야 하는 건 Volume 마운트 경로에 있어야 한다:

  • 리뷰 본문 → reports/ (Volume) ✅ 살아남음
  • 승인 상태 파일 → data/ (이미지 영역) ❌ 리셋

승인 상태 파일을 reports/ 밑으로 옮기고, 구버전 경로에서 자동 마이그레이션하는 폴백을 넣었다. 이후 프로젝트 철칙: “영구 저장은 reports/(Volume)에만. data/는 배포마다 새로 깔리는 읽기 전용 소스.”

덤으로 경기명이 바뀌면(예: 대회명 표기 변경) 승인 키가 어긋나 “승인한 적 없음”으로 보이는 문제도 있었다 — 상태 키는 이름이 아니라 안정적인 식별자로 잡아야 한다.

사건 2: 로컬엔 한 경기인데 서버엔 두 경기

경기 데이터를 올렸는데 배포본에서만 같은 경기가 두 번 보였다. 로컬은 정상. 범인은 유니코드 정규화였다.

macOS 파일시스템은 한글·악센트 문자를 NFD(자소 분해)로 저장하고, 리눅스는 NFC(완성형)를 쓴다. Curaçao, Türkiye처럼 악센트가 든 폴더명이 한 번은 NFD로, 한 번은 NFC로 올라가면 사람 눈엔 같은 이름인데 파일시스템엔 서로 다른 두 폴더가 된다.

해결: 업로드 전 폴더명을 NFC로 정규화하고, 배포 후 검증 스크립트가 “NFC 기준 중복 0건”을 확인한다. 이 체크는 이후 모든 데이터 추가 루틴의 고정 단계가 됐다.

nfc = [unicodedata.normalize('NFC', name) for name in folders]
dups = [x for x in set(nfc) if nfc.count(x) > 1]  # 반드시 빈 리스트여야 함

사건 3: git이 4분째 멈춰 있다

어느 날 git status가 몇 분씩 걸리고 commit이 안 끝났다. 디스크도 CPU도 멀쩡한데.

원인은 iCloud Drive의 dataless 파일이었다. 맥이 용량 확보를 위해 파일 본문을 클라우드로 올려버리면(로컬엔 메타데이터만), git이 그 파일을 읽는 순간 iCloud 다운로드를 기다리며 무한 블록된다. 프로젝트 폴더가 iCloud 동기화 대상에 걸쳐 있으면 벌어지는 일.

해법: dataless 파일을 강제로 실체화(brctl download 등)하거나 iCloud 범위 밖으로 옮기고, 급할 땐 git plumbing 명령으로 인덱스를 우회해 커밋했다. 이후 git이 이유 없이 멈추면 iCloud부터 의심한다.

git index/pack이 손상돼 clone본에서 objects를 통째로 이식해 복구한 적도 있다. 원격(GitHub)이 곧 백업이라는 당연한 사실이 제일 든든했다.

사건 4: 홈 화면이 가짜 일정을 그리고 있었다

본선 일정 데이터(groups.json)를 추첨 전 예상 일정으로 만들어 둔 걸 잊고 있었다. 실제 대회가 진행되자 홈에는 “6/18 체코-남아공”처럼 실제로 없는 경기가 뜨고, 진짜 6/18 경기(잉글랜드-크로아티아)는 매칭이 안 됐다.

FIFA 공식 일정표 기준으로 72경기 전체(날짜·홈/원정)를 재작성했고, 이때 무결성 검증을 자동화했다 — 12개 조 × 6경기, 각 팀 정확히 3경기, 수집된 경기 전부 스코어 매칭. 일정 데이터는 “예상”과 “확정”을 절대 섞으면 안 된다.

곁가지로, 데이터를 올려도 홈이 안 바뀌는 문제는 @st.cache_data(ttl=600) — 10분 캐시였다. 대회 기간엔 1분으로 줄였다. 캐시 때문에 “반영 안 됨” 문의를 받는 건 웹 서비스의 클리셰지만 직접 겪으면 또 당한다.

배운 것

  • PaaS 배포에서 어떤 경로가 살아남는지를 기능 만들기 전에 확정하라. “어디에 저장되나”가 곧 기능 명세다.
  • macOS ↔ 리눅스를 오가는 파일 파이프라인은 NFC 정규화 + 배포 후 중복 검증을 기본 장착.
  • 개발 장비의 편의 기능(iCloud)이 도구 체인(git)을 죽일 수 있다. 프로젝트 폴더는 동기화 범위 밖에.
  • 배포 후 검증 루틴(부팅 200 → 데이터 수 → 중복 0 → 신규 경기 매칭)을 스크립트로 고정해 두면 심리적 비용이 0이 된다.