본문으로 건너뛰기
← 블로그 목록으로
테크

자동화가 저장한 파일이 0바이트가 되는 두 가지 이유 — AI 지침서가 4일 동안 비어 있었던 일

공유

저는 Claude Code와 Codex, 두 AI 에이전트에게 일을 나눠 맡깁니다. 둘은 일을 시작할 때 같은 지침서 파일 하나(AGENTS.md)를 읽습니다. 저를 어떻게 부를지, 절대 하면 안 되는 일, 제가 지난번에 고쳐 준 사실까지 거기에 적혀 있습니다.

지난 8월, 이 파일이 0바이트였습니다. 2만 1천 바이트 남짓이던 지침서가 통째로 비어 있었고, 그 상태로 4일이 지났습니다. 그보다 열흘 전에는 에이전트들이 쌓아 온 지식 파일이 항목 349개에서 1개로 덮어써졌습니다.

둘 다 해킹이나 고장이 아니었습니다. 프로그램이 파일을 다시 저장하는 평범한 한 번에서 일어났습니다. 자동화로 파일을 고쳐 쓰고 계신다면 같은 길이 열려 있을 수 있어서, 사고가 지나간 경로와 막은 방법을 적어 둡니다.

내용이 사라진 바인더와, 지식을 한 장씩 나눠 담은 카드 상자 — 이 글을 위해 만든 그림
내용이 사라진 바인더와, 지식을 한 장씩 나눠 담은 카드 상자 — 이 글을 위해 만든 그림

첫 번째 사고: 디스크가 가득 찬 날, 지침서가 0바이트가 됐다

지침서 아래쪽에는 '정정 사항' 칸이 있습니다. 제가 "그거 아니고 이게 맞아"라고 고쳐 줄 때마다 프로그램이 이 칸을 새로 쓰고 파일 전체를 다시 저장합니다. 사람이 손대지 않아도 지침서가 스스로 자라도록 만든 장치입니다.

다시 저장하는 코드는 가장 흔한 방식이었습니다. 파일을 열면서 먼저 내용을 비우고, 그다음 새 내용을 써 넣습니다. 평소에는 아무 문제가 없습니다. 비우는 것과 쓰는 것이 한순간에 이어지니까요.

8월 7일은 C 드라이브가 가득 차 있던 때였습니다. 비우는 데는 공간이 필요 없지만, 새로 쓰는 데는 공간이 필요합니다. 쓰기가 중간에 실패하면 남는 것은 비워진 파일뿐입니다. 기록을 맞춰 보면 정황이 이 경로를 가리켰습니다.

더 곤란했던 것은 그다음입니다.

  • 빈 파일은 오류를 내지 않습니다. 읽는 쪽은 "지침이 없구나" 하고 넘어갈 뿐입니다.
  • 비어 버린 지침서는 그날 정오, 다른 파일들을 함께 올리는 스냅샷 커밋에 묶여 git에 그대로 올라갔습니다. 커밋은 파일이 비었는지 따지지 않습니다.
  • 두 에이전트는 그 뒤 4일 동안 함께 읽는 지침서에 아무것도 없는 상태로 일을 시작했습니다.
  • 8월 11일, 직전 정상본(8월 6일 저장분)에서 지침서를 되살렸습니다.

    두 번째 사고: 여럿이 파일 하나를 쓰다가 지식 349개가 1개로

    7월 28일 저녁에는 같은 작업 폴더에서 세 작업(Claude 둘, Codex 하나)이 동시에 돌고 있었습니다. 에이전트들이 쌓은 지식은 파일 하나에 통째로 들어 있었습니다. 사고는 다섯 고리로 이어졌습니다.

  • 동시 작업 중 git으로 최신본을 받아오다가, 그 지식 파일 안에 충돌 표시가 끼어들었습니다.
  • 지식 파일을 읽는 코드가 해석에 실패했는데, 경고 한 줄만 남기고 빈 상태로 계속 진행했습니다.
  • 그 작업이 새 지식 1개를 추가했습니다.
  • 저장 관문은 "항목이 0개보다 많으면 저장"이었습니다. 1개는 통과했고, 349개짜리 파일을 1개짜리로 덮어썼습니다.
  • 저장 단계의 실패도 조용히 넘어가도록 되어 있어서, 그대로 커밋까지 흘러갔습니다.
  • 다섯 고리 중 하나만 끊겼어도 일어나지 않았을 일입니다.

    되찾는 길은 git 기록에 있었습니다. 기록 안에 남은 그 파일의 과거 판본을 전부 꺼내 항목 수와 저장 시각으로 줄을 세우고, 가장 최신이면서 가장 많은 판본을 찾았습니다. 거기에 사고 뒤 새로 생긴 항목만 골라 덮어쓰지 않고 합쳤습니다. 잃은 것 없이 351개로 돌아왔습니다.

    막은 방법: 관문을 세우고, 저장 단위를 쪼갰다

    처음에는 관문을 세웠습니다.

  • 시작할 때 읽기에 실패했다면 저장 자체를 금지합니다.
  • 저장하려는 항목 수가 이전보다 10% 넘게 줄면 저장을 거부하고, 거부된 내용은 따로 파일로 남겨 둡니다. 정말 비우려는 경우에만 별도 명령으로 허락합니다.
  • 그런데 관문은 막는 장치일 뿐, 여럿이 한 파일을 두고 다투는 구조는 그대로였습니다. 그날 저는 "여러 작업을 동시에 돌리기 좋게 구조를 바꾸면 안 되나?"라고 물었고, 저장 방식을 바꿨습니다.

  • 지식 하나 = 파일 하나. 349개를 한 파일에 담지 않고 카드 한 장씩 따로 저장합니다.
  • 저장은 "있으면 고치고, 없으면 추가"만 합니다. 내 쪽에 없는 카드는 건드리지 않습니다.
  • 지우기는 지우라는 명령을 따로 내렸을 때만 합니다.
  • 이렇게 바꾸니 빈 상태로 시작한 작업이 남의 지식을 지우는 일이 구조적으로 불가능해졌습니다. 잠금 장치를 더 단단히 만드는 것보다 다툴 거리를 없애는 쪽이 간단했습니다.

    지침서 쪽은 저장 순서를 바꿨습니다.

  • 새 내용을 원본이 아니라 임시 파일에 먼저 씁니다.
  • 임시 파일을 다시 읽어 내용이 맞는지 확인한 뒤, 그때 원본과 바꿔 끼웁니다. 쓰다가 실패해도 원본은 그대로 남습니다.
  • 써 넣을 내용이 비어 있으면 아예 쓰지 않습니다.
  • 지침서가 이미 비어 있으면 갱신을 건너뜁니다. 빈 파일 위에 덮어쓰면 손실이 굳어 버리기 때문입니다.
  • 세션을 시작할 때마다 지침서가 0바이트인지 자동으로 확인합니다. 확인을 사람의 기억에 맡기지 않습니다.
  • 자동으로 다시 쓰는 파일이 있다면 확인할 다섯 가지

    AI 에이전트의 지침 파일이든, 매일 갱신되는 설정 파일이든, 스크립트가 고쳐 쓰는 데이터 파일이든 같습니다.

  • 저장 방식 — 원본 위에 바로 쓰는가, 임시 파일에 쓴 뒤 바꿔 끼우는가. 파이썬이라면 임시 파일에 쓰고 os.replace로 교체하는 방식이 기본입니다. 원본 위에 바로 쓰면 실패하는 순간 빈 파일이 남습니다.
  • 빈 내용 거부 — 쓰려는 내용이 비어 있으면 멈추게 합니다. 정상적인 갱신이 빈 파일을 만들 일은 거의 없습니다.
  • 급감 관문 — "0보다 크면 저장"처럼 절대 기준을 두면 1로 뚫립니다. "이전보다 얼마 이상 줄면 멈춤"처럼 직전 상태와 비교합니다.
  • 읽기 실패는 크게 멈춘다 — 읽다가 실패했는데 빈 상태로 계속 가는 코드는, 결국 지우는 코드입니다. 경고 한 줄로 넘기지 말고 작업을 세웁니다.
  • 여럿이 쓰면 쪼갠다 — 에이전트나 예약 작업 여러 개가 같은 파일을 쓴다면, 순서를 맞추려 애쓰기 전에 파일을 단위별로 나눌 수 있는지 봅니다.
  • 하나 덧붙이면, 두 번 모두 git 기록 덕분에 되찾았습니다. 빈 지침서를 실어 나른 것도 커밋이었지만, 되살릴 판본을 남겨 둔 것도 그 전날까지 쌓인 커밋이었습니다. 자동으로 고쳐 쓰는 파일일수록 변경 기록이 남는 곳에 두는 것이 가장 싼 보험입니다.

    마치며

    두 사고 모두 큰일처럼 보이지 않는 저장 한 번에서 시작됐습니다. 자동화를 늘릴수록 사람이 파일을 직접 열어 볼 일은 줄어듭니다. 그래서 "잘 저장됐겠지"를 믿는 대신, 저장이 실패해도 원본이 남는 순서와 비어 있으면 알려 주는 점검을 장치로 박아 두었습니다.

    자동화가 저장한 결과를 공개 화면까지 확인하는 절차는 자동화는 저장에서 끝나지 않습니다에, 블로그 사진을 사이트 코드에 넣었다가 사이트가 멈춘 일은 Vercel 배포 용량 초과로 사이트가 멈췄습니다에 정리해 두었습니다.

    보통리가 만드는 서비스

    자동 저장 파일이 0바이트가 되는 이유와 막는 법 | 보통리