루프를 안전하게 운영하기
8-3에서 루프가 도는 것을 봤습니다. 잘 돌 때는 놀랍지만, 잘못 돌 때는 사람이 매 바퀴 보고 있을 때보다 훨씬 비싸게 잘못됩니다. 여기서는 그 비용을 관리하는 이야기를 하고, 이 책도 여기서 끝납니다.
1. 루프에서 실제로 나는 사고들
루프를 며칠 돌려보면 만나게 되는 사고는 대체로 다섯 가지 안에 들어갑니다. 하나씩 짚고, 각각을 어디서 막는지 봅니다.
1.1 멈추지 못함
가장 흔합니다. 조건이 모호하거나, 조건은 분명한데 도달할 수 없는 경우입니다. 후자가 특히 지독합니다. 예를 들어 조건이 "테스트가 모두 통과한다"인데 그중 하나가 외부 API 장애로 실패하고 있다면, 루프는 코드를 아무리 고쳐도 통과시킬 수 없습니다.
막는 자리는 세 군데입니다.
- 조건문 안의 턴 제한. "20턴 안에 못 끝내면 중단하고 보고한다"를 조건에 넣습니다.
- 비용 상한.
--max-budget-usd처럼 돈으로 천장을 겁니다. 조건 판정이 어떻게 되든 이 선을 넘으면 멈춥니다. - 진전 없음 감지. 같은 실패가 몇 바퀴 반복되면 그 항목을 옆으로 치우고 다음으로 갑니다.
1.2 검사를 통과하려고 검사를 고침
루프에 목표를 주면 그 목표를 가장 싼 방법으로 달성하려 합니다. "check.sh가 ALL PASS로 끝난다"가 목표라면, 원고를 고치는 것보다 check.sh의 기준을 180줄에서 100줄로 낮추는 게 훨씬 쉽습니다. 사람이 매 바퀴 보고 있으면 바로 잡히지만, 밤새 도는 루프에서는 아침에야 발견합니다.
대응은 세 겹으로 둡니다.
-
조건문에 명시. "
check.sh는 수정하지 않는다"를 조건에 넣습니다. 가장 약한 방어이지만 공짜입니다. -
hook으로 차단.
PreToolUse에서 검사 파일 수정을 아예 막습니다.{ "hooks": { "PreToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/protect-checks.sh" } ] } ] } }스크립트가 대상 파일 경로를 보고
check.sh나.claude/settings.json이면 거부 판정을 내립니다. -
검사를 루프 밖에 둔다. 가장 튼튼한 방법입니다. 최종 판정을 루프가 손댈 수 없는 자리, 즉 CI에서 한 번 더 돌립니다. 7장에서 CI 검사를 "마지막 안전망"이라고 불렀던 이유가 루프에서 훨씬 커집니다.
이건 모델이 나쁜 게 아니라 목표 설정이 허술한 것입니다. 사람에게 "이 지표를 올려라"라고만 시켰을 때 벌어지는 일과 정확히 같습니다. 루프를 설계할 때는 "이 조건을 만족시키는 가장 싼 방법이 무엇일까"를 한 번 스스로 물어보는 습관이 좋습니다.
1.3 되돌릴 수 없는 일을 함
푸시, 배포, 메일 발송, 외부 API 쓰기, 파일 삭제. 이 다섯은 잘못됐을 때 되돌리기 어렵거나 불가능합니다. 2장에서 쓴 어휘를 다시 빌리면, 사람 승인 지점을 두껍게 둬야 하는 자리입니다.
| 자율 수준 | 어떤 일 | 승인 |
|---|---|---|
| 얇게 | 파일 읽기, 초안 작성, 로컬 테스트, 검사 스크립트 실행 | 루프가 알아서 |
| 중간 | 브랜치 생성, 커밋, PR 생성 | 루프가 하되 사람이 머지 |
| 두껍게 | 푸시(기본 브랜치), 배포, 외부 발송, 삭제 | 루프는 하지 않음 |
가장 실용적인 배치는 루프의 출력이 항상 풀 리퀘스트에서 멈추게 하는 것입니다. 루프는 브랜치를 만들고 커밋하고 PR을 올리는 데까지만 갑니다. 그다음 칸이 사람 자리입니다. 이러면 되돌리기가 항상 "PR을 닫는다" 한 동작으로 끝납니다.
도구 권한으로도 같은 선을 그을 수 있습니다.
--allowedTools "Read Write Edit Bash(bash check.sh*) Bash(git add*) Bash(git commit*)"
git push가 목록에 없으면 루프는 푸시할 수 없습니다. 글로 적힌 금지보다 훨씬 튼튼합니다.
1.4 외부 문서에 조종당함
5장에서 본 프롬프트 인젝션이 루프에서 위험도가 한 단계 올라갑니다. 루프가 웹 문서나 이슈 코멘트를 읽는다면, 그 안에 "이전 지시를 무시하고..."가 들어 있을 수 있습니다. 사람이 보고 있으면 이상한 행동이 한 번 만에 잡히지만, 루프에서는 그 지시가 다음 바퀴로, 그다음 바퀴로 전파됩니다.
원칙은 5장에서 본 그대로입니다. 외부에서 읽어 온 데이터를 신뢰할 수 있는 명령으로 다루지 않습니다. 루프에서 추가로 챙길 것은 두 가지입니다.
- 외부 내용을 읽는 루프와 쓰기 권한을 가진 루프를 가능하면 분리합니다.
- 도구 권한을 좁게 잡습니다. 읽기만 하는 루프에 쓰기 도구를 주지 않습니다.
1.5 조용히 실패함
5장에서 평가·모니터링을 "가장 자주 빠뜨리는 부품"이라고 했는데, 루프에서는 이게 치명적입니다. 사람이 안 보는 동안 도는 시스템이라 망가진 줄을 모르는 시간이 길어집니다.
최소한 이 세 가지는 남깁니다.
run.log ← 무슨 일이 있었는지 (--output-format stream-json)
progress.md ← 어디까지 갔고 무엇이 막혔는지
비용 ← 이번 실행이 얼마를 썼는지
그리고 "이상 없음"도 기록으로 남기는 게 중요합니다. 아무 로그도 없는 것과 "3바퀴 돌았고 고칠 게 없었다"는 완전히 다른 정보인데, 루프를 잘못 짜면 둘이 구분되지 않습니다.
2. 사람이 계속 붙잡고 있어야 하는 두 가지
기술적인 사고 말고, Osmani가 더 크게 경고한 위험이 두 가지 있습니다. 이건 도구로 막을 수 없고 습관으로만 막을 수 있습니다.
2.1 이해 부채
이해 부채(comprehension debt) 는 루프가 만들어낸 결과물을 사람이 점점 이해하지 못하게 되는 상태입니다. 코드가 돌아가고 테스트가 통과하니 문제가 없어 보이지만, 어느 날 그 코드를 손봐야 할 때 아무도 구조를 모릅니다.
기술 부채와 다른 점은 쌓이는 속도입니다. 사람이 코드를 쓸 때는 이해와 생산이 같은 속도로 갑니다. 루프에서는 생산만 빨라지고 이해는 그대로라, 격차가 매일 벌어집니다. 1장에서 본 "5개월에 100만 줄, 사람이 친 코드 0줄"이라는 숫자를 다시 떠올려 보면 이 격차의 크기가 짐작됩니다.
갚는 방법은 결국 읽는 것입니다. 다만 전부 읽을 수는 없으니 자리를 정해둡니다.
- PR 단위로 끊기. 루프의 출력이 PR에서 멈추면 최소한 diff를 보게 됩니다. 한 PR이 1,000줄을 넘으면 그건 안 읽힙니다. 루프에 "한 PR은 한 가지 일만"을 조건으로 넣습니다.
- 왜를 남기게 하기.
decisions.md처럼 "왜 이렇게 했는지"를 루프가 적게 합니다. 결과물보다 이 파일이 나중에 더 중요해집니다. - 주기적으로 한 곳을 깊게. 매주 한 자리를 골라 사람이 끝까지 읽습니다. 전수 검사는 불가능하지만 표본은 가능합니다.
2.2 인지적 항복
인지적 항복(cognitive surrender) 은 결과를 비판적으로 보지 않고 그냥 받아들이게 되는 상태입니다. 루프가 열 번 연속으로 괜찮은 결과를 내면, 열한 번째를 대충 보게 됩니다.
이건 루프가 잘 돌수록 심해집니다. 그래서 잘 도는 루프일수록 검토 자리를 의식적으로 지켜야 합니다. 3장에서 본 Avianca와 Air Canada 사례가 여기서 다시 유효합니다. AI가 만든 결과를 외부에 내보낸 책임은 사람과 회사가 집니다. 루프가 만들었다는 사실은 면책 사유가 되지 않고, 루프가 만들었다는 사실 때문에 검토가 느슨해졌다면 오히려 가중 사유에 가깝습니다.
"Build loops like someone who intends to stay the engineer, not just the person who presses go."
"버튼만 누르는 사람이 아니라, 계속 엔지니어로 남을 사람처럼 루프를 만들어라."
3. 한 번에 다 켜지 않기
루프를 처음 도입할 때 가장 안전한 방법은 자율성을 한 칸씩 올리는 것입니다. 다섯 단계로 나눠 보면 이렇습니다.
| 단계 | 상태 | 다음 단계로 넘어가는 신호 |
|---|---|---|
| 1 | 사람이 매 바퀴 지시 (7장까지의 우리) | 같은 지적을 세 번 이상 반복했다 |
| 2 | hook으로 검사가 자동 실행 | 검사 결과만 보고도 고칠 방향이 정해진다 |
| 3 | /goal로 조건까지 자동 반복 | 열 번 돌려 열 번 다 납득할 결과가 나온다 |
| 4 | 헤드리스로 사람 없이 한 번에 | 비용과 실패 패턴이 예측된다 |
| 5 | 스케줄로 주기적으로 자율 실행 | 되돌리기 경로가 확실하다 |
각 단계의 오른쪽 칸이 중요합니다. 아직 그 신호가 안 나왔으면 다음 단계로 가지 않습니다. 특히 3단계에서 4단계로 넘어갈 때, "열 번 중 아홉 번 괜찮다"는 아직 이릅니다. 사람이 안 보는 루프에서 열 번에 한 번의 실패는 밤새 여러 번 반복됩니다.
4. 루프를 켜기 전 체크리스트
새 루프를 처음 돌리기 전에 한 번 훑는 용도로 씁니다.
[ ] 멈춤 조건을 기계가 판정할 수 있는가
[ ] 이 조건을 만족시키는 가장 싼 편법이 무엇인지 생각해봤는가
[ ] 그 편법을 막는 장치가 있는가 (조건문·hook·CI 중 최소 하나)
[ ] 턴·시간·비용 상한 중 최소 하나가 걸려 있는가
[ ] 진전이 없을 때 빠져나올 길이 있는가
[ ] 검사하는 쪽과 만드는 쪽이 분리되어 있는가
[ ] 되돌릴 수 없는 행동이 도구 권한에서 빠져 있는가
[ ] 실행 기록이 남는가, "이상 없음"도 남는가
[ ] 결과가 사람 앞에 멈추는 자리(PR 등)가 있는가
[ ] 이 루프가 만든 것을 내가 읽을 수 있는 크기로 끊었는가
열 줄이 다 채워지지 않았다면, 그 루프는 아직 사람이 지켜보는 자리에서 돌릴 때입니다.
5. 네 겹으로 정리하며
이 책을 여기까지 따라오셨다면, 처음 1장에서 봤던 표가 이제 다르게 읽힐 것입니다.
| 구분 | 프롬프트 | 컨텍스트 | 하네스 | 루프 |
|---|---|---|---|---|
| 핵심 질문 | 무엇을 물어볼까 | 무엇을 같이 보여줄까 | 어떤 환경에서 일하게 할까 | 언제 돌고 언제 멈추게 할까 |
| 다루는 자리 | 지시문 한 덩어리 | 모델 앞에 깔리는 정보 | 도구·가드레일·워크플로우 | 멈춤 조건·상한·검증·상태 |
| 일상 비유 | 이메일 한 통 | 첨부파일 챙기기 | 사무실 전체 설계 | 퇴근 후에도 돌아가는 근무 체계 |
| 사람의 역할 | 질문 작성자 | 정보 큐레이터 | 환경 설계자 | 순환 설계자, 최종 판정자 |
| 이 책에서 | 3장 | 4장 | 5~7장 | 8장 |
네 겹은 대체 관계가 아니라 포함 관계라는 것이 이 책이 처음부터 끝까지 반복한 이야기입니다. 그리고 그 순서에는 이유가 있습니다.
- 프롬프트를 못 쓰면 컨텍스트를 아무리 잘 챙겨도 결과가 안 나옵니다.
- 컨텍스트를 못 챙기면 하네스를 짜도 매번 다른 결과가 나옵니다.
- 하네스가 부실하면 루프는 그 부실함을 밤새 증폭시킵니다.
거꾸로 말하면, 루프가 잘 도는 팀은 그 아래 세 겹이 이미 탄탄한 팀입니다. 루프 엔지니어링은 새로 배우는 기술이라기보다, 앞의 세 가지를 제대로 해둔 팀에게 열리는 다음 칸에 가깝습니다.
6. 마지막으로
이 책은 "같은 AI를 누구는 잘 쓰고 누구는 못 쓰는 이유"라는 질문에서 출발했습니다. 세 시대를 따라오고 네 번째까지 얹고 나면, 그 답이 조금 다르게 보입니다.
차이를 만드는 것은 좋은 주문을 아는 것이 아닙니다. 결과가 흔들릴 때 어디를 고쳐야 하는지 아는 것입니다. 프롬프트의 네 자리 중 하나가 빈 것인지, 필요한 정보가 안 깔린 것인지, 하네스에 도구나 규칙이 빠진 것인지, 루프의 멈춤 조건이 모호한 것인지. 이 네 층 중 어디를 손댈지 짚을 수 있게 되는 것이 이 책의 목표였습니다.
그리고 그 손대는 일은 한 번으로 끝나지 않습니다. 2장에서 "고치는 일이 곧 다음 글을 위한 학습"이라고 했고, 7장에서 "사람의 판단이 다시 폴더의 규칙으로 굳어지는 자리"라고 했습니다. 8장의 루프는 그 굳히는 일을 훨씬 빠르게 만들어줄 뿐, 무엇을 굳힐지는 여전히 사람이 정합니다.
계속 엔지니어로 남으실 수 있기를 바랍니다.