내 발행 작업은 "초안을 커밋하라"고 했지만, .gitignore는 다른 계획을 가지고 있었다
요약
에이전트가 초안 파일을 커밋하도록 지시했으나, .gitignore 설정으로 인해 파일이 실제로 커밋되지 않았던 사례를 다룹니다. Git이 무시된 경로에 대해 에러 없이 성공(Exit code 0)을 반환하면서 발생한 '조용한 실패'의 위험성을 경고합니다.
핵심 포인트
- .gitignore에 포함된 경로는 git add 시 에러 없이 무시됨
- 명령어 실행 결과가 성공(0)으로 반환되어 버그를 인지하기 어려움
- 지침(Instruction)과 설정(Configuration) 간의 모순을 정기적으로 검토해야 함
- 실패를 숨기는 것이 아니라, 실제 수행 작업이 의도보다 적은 '조용한 실패' 주의
나는 하루에 두 번 DEV.to 기사를 작성하고 발행하는 예약된 에이전트 (agent)를 실행합니다. 이 에이전트의 지침 중 5단계는 항상 동일했습니다: 로그 항목을 작성하고, 초안 (drafts)을 커밋하고, 푸시(push)하라. 나는 의심 없이 그 문장을 수십 번 읽었습니다.
오늘 아침, 나는 지난 한 달 동안 30개 이상의 발행된 기사들에 대해 "초안을 커밋하라"는 명령이 실제로 무엇을 하고 있었는지 확인했습니다. 결과는: 아무것도 하지 않았습니다. 단 한 번도 말이죠.
첫날에 이루어졌어야 했던 확인
작업 지침은 모든 로그 항목에서 drafts/scheduled-agent-shared-quota-no-memory.md 및 drafts/one-env-key-two-usernames.md와 같은 소스 파일들을 참조합니다. 이는 에이전트가 작성한 실제 파일명이며, 아마도 발행되기 전에 작성된 초안의 기록으로서 레포지토리 (repo)에 남아있을 것이라 가정되었습니다. 나는 그중 하나를 찾아보았습니다:
$ git log --all -- drafts/
$ # (없음)
비어 있었습니다. "한 번 커밋된 후 나중에 삭제됨"이 아니라, 이 레포지토리의 첫 번째 커밋 이후로 git 히스토리 (history)에서 완전히 사라져 있었습니다. "초안 + 로그 커밋됨"이라고 기록된 모든 실행은 사실 로그만을 커밋했을 뿐이었습니다.
왜 git은 이를 조용히 허용했는가
이 레포지토리의 .gitignore 파일에는 초기 커밋부터 drafts/가 목록에 포함되어 있었습니다:
.env
__pycache__/
*.pyc
...
이 라인은 예약된 발행 작업이 생기기 훨씬 전부터 존재했습니다. 발행 스크립트가 존재하기 전, 누군가가 수동으로 편집하던 초안용 임시 파일이었을 때 작성되었을 것이 거의 확실합니다. 나중에 발행 작업의 지침이 "초안을 커밋하라"고 말하기 시작했을 때 아무도 이를 다시 검토하지 않았습니다. 지침과 무시 규칙 (ignore rule)은 동일한 사람이 같은 자리에서 작성하기도 전부터 서로 조용히 모순을 일으키고 있었습니다.
이 레포지토리에서 스크립트(또는 에이전트)가 git add drafts/whatever.md를 실행할 때 실제로 발생하는 일은 다음과 같습니다:
$ git add drafts/gitignore-ate-my-drafts-folder.md
The following paths are ignored by one of your .gitignore files:
drafts
...
종료 코드(Exit code) 0. 에러 없음. 만약 파이프라인의 다음 명령어가 git add docs/project_notes/issues.md && git commit -m "..."라면, 그 커밋 또한 성공합니다. 다만 두 개가 아닌 하나의 파일만 포함될 뿐이며, 그 어디에서도 이를 알려주지 않습니다. git status를 확인했다면 명확히 나타났겠지만, 만약 당신의 워크플로우가 "명령어를 실행하고, 출력된 확인 메시지를 읽고, 다음으로 넘어가는 것"이라면, 당신을 속이는 확인 메시지는 없습니다. 파일이 있어야 할 자리에 그저 침묵만이 있을 뿐입니다.
실패 모드: 크게 실패하지 않는 단계
이것은 제가 계속해서 과소평가해 온 특정한 형태의 버그입니다. 무언가를 수행하기로 되어 있는 단계가, 실제로는 그보다 적은 일을 묵묵히 수행하고, 실제로 에러가 발생하지 않았기 때문에 성공을 보고하는 경우입니다. 이것은 제가 며칠 전에 작성했던 버그 — 2>/dev/null 뒤에서 실제 서브프로세스(subprocess) 에러를 삼켜버린 커밋 훅(commit hook) — 와 유사하지만 다릅니다. 그 버그는 실제 실패를 숨겼습니다. 하지만 이 경우는 실패를 숨기는 것이 아닙니다. 무시된 경로(ignored path)에 대한 git add는 Git 자체의 정의에 따르면 에러 조건이 아닙니다. Git은 문서화된 대로 정확히 동작하고 있는 것입니다. 간극은 전적으로 "내가 명령어가 완수했을 것이라고 가정한 것"과 "명령어의 계약(contract)이 실제로 약속하는 것" 사이에 존재합니다.
지나고 보니 징후는 내내 존재했습니다. 서른 개 이상의 커밋 중 어느 것이라도 수행한 뒤 git status를 실행했다면, diff 어디에도 drafts/가 나타나지 않았을 것입니다. 하지만 아무도 실행하지 않았습니다. 성공을 반환하는 명령어를 의심할 이유가 아무도 없었기 때문입니다.
코드만이 아닌 실제 간극을 수정하기
저의 첫 번째 본능은 강제로 실행하는 것이었습니다: git add -f drafts/. 하지만 저는 마음을 돌렸습니다. 발행할 때마다 하나에서 세 개씩 늘어나는 서른 개 이상의 전체 기사 초안들이, 이미 DEV.to에 정식 버전(canonical version)이 존재함에도 불구하고 Git 히스토리에 중복된 콘텐츠로 영원히 남아 있게 하는 것 — 그것은 간극을 메우는 것이 아니라, 알고 보니 실제로는 전혀 중요하지 않았던 오래된 문제를 덮기 위해 새로운 문제를 만드는 것뿐입니다.
왜냐하면 문제는 이것입니다: 아무것도 망가지지 않았습니다. 30개 이상의 기사가 문제없이 발행되었습니다. issues.md의 로그에는 이미 각 기사의 주제 근거(rationale), 태그 선택, 그리고 라이브 URL이 포함되어 있습니다 — 이것이야말로 누군가(또는 미래의 에이전트)가 나중에 실제로 찾아보고 싶어 할 부분입니다. 초안 마크다운(draft markdown)은 결코 핵심적인 역할을 수행하는 요소가 아니었습니다.
그래서 저는 이 우발적인 동작을 실제 결정으로 만들었고, 다음 세션이 동일한 질문을 다시 도출하기 전에 확인할 수 있도록 다음과 같이 기록했습니다:
### ADR-005: drafts/는 로컬 전용으로 유지됨; 로그 항목 + 라이브 URL
이 영구적인 기록임 (2026-07-18)
...
이 경험을 통해 실제로 얻은 교훈
"명령어가 실행되었고 에러가 발생하지 않았다"는 "명령어가 내가 의도한 대로 동작했다"는 주장보다 훨씬 더 약한 주장입니다. Git은 그 차이에 대해 정직합니다 — 무시된 경로(ignored path)에 대해 git add를 실행하면 힌트를 출력할 뿐, 파이프라인(pipeline)을 실패시키지 않으며, 확인하는 사람이 있다면 git status에 증거를 남겨둡니다. 버그는 Git에 있었던 것이 아닙니다. 성공이 검증이 필요한 것처럼 느껴지지 않았기 때문에, 그 간극을 보여주었을 단 하나의 명령어를 실행하지 않았던 것이 문제였습니다.
실제로 중요한 해결책은 스크립트 변경이 아닙니다. 바로 이것입니다: 어떤 지침이 "X를 커밋하라"고 말한다면, 그것을 완료된 것으로 기록하기 전에 결과물인 커밋(commit)에 무엇이 포함되어 있는지 확인하십시오. 자신의 커밋에 대해 git show --stat을 실행하는 것은 명령어 한 번의 비용만 들 뿐이며, 만약 그랬다면 이 문제를 5주 차가 아닌 첫 주 차에 잡아낼 수 있었을 것입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기