본문으로 건너뛰기
Koding

cron이랑 뭐가 달라? — 업무 태스크로 직접 짜 보고 알게 된 루프 엔지니어링의 핵심

Jaewoo KimJaewoo Kim · 조회 0 · 9분 읽기
목차

2026년 6월부터 루프 엔지니어링이라는 말이 부쩍 눈에 띄게 되었습니다. 발단은 OpenClaw의 작자 Peter Steinberger의 게시글로, "이제 코딩 에이전트에 프롬프트를 쓰지 마라. 에이전트에 프롬프트를 쓰는 루프를 설계하라"는 취지의 것. Claude Code를 이끄는 Boris Cherny도 "이제 Claude에 직접 지시를 내리지 않는다. 루프가 Claude에 프롬프트하고 있다"고 말했고, 다음 날에는 Google의 Addy Osmani가 개념을 정리해 Loop Engineering이라 이름 붙였다고 합니다.

개념 해설 글은 이미 많이 있습니다. 다만 읽기만 해서는 솔직히 감이 안 왔어요. "그거, cron으로 정기 실행하는 거랑 뭐가 달라?"라는 의문이 끝까지 사라지지 않았기 때문입니다.

그래서 제 업무 태스크로 하나 짜 봤습니다. 이 글은 그 과정에서 "그냥 정기 실행"과 "루프"의 차이가 어디에 있는지를 알게 되기까지의 기록입니다. 실제로 만든 파일 한 벌(샘플)도 실었습니다.

먼저 용어만

본문에서 쓰는 말을 처음에 정리해 둡니다. 아는 분은 건너뛰세요.

용어의미
cron(크론)Linux와 Mac에 옛날부터 있는 정시 실행 구조. "매일 아침 9시에 이 커맨드를 실행하라" 같은 예약을 할 수 있다. crontab이라는 설정 파일에 쓴다
claude -pClaude Code(Anthropic의 AI 코딩 툴)를 대화 화면 없이 커맨드 한 방으로 실행하는 모드. 헤드리스 실행이라고도 부른다. 스크립트에 넣을 수 있다
MCPAI가 외부 서비스(이번엔 Slack)를 읽고 쓰기 위한 연결 규격. 팔 같은 것
SKILL.mdClaude에 기억시키고 싶은 절차와 주의점을 써 두는 파일. 매번 말로 설명하는 대신 이걸 읽힌다
permalinkSlack의 각 메시지에 붙는 고유 URL. "이 게시글을 확인했다"는 증거로 쓸 수 있다
state.json이번에 자작한 상태 파일. 지난 실행 결과를 다음 회차에 넘기기 위한 기억 저장소

소재로 삼은 태스크

저는 직업훈련학교에서 신입 연수를 보고 있습니다. 신입 멤버에게는 매일 Slack 전용 채널에 진척을 올리게 하고 있는데, 전원 몫을 매일 아침 눈으로 확인하는 게 은근히 귀찮았습니다. 누가 언제 올렸나, 멈춰 있는 사람은 없나. 확인 자체는 5분이면 끝나지만 잊는 날도 있고, 보는 사람 기분에 따라 기준이 흔들립니다.

루프 엔지니어링 해설 글에서도 로그 순회 점검은 루프에 알맞은 태스크의 첫손에 꼽혀 있었습니다. 판정 기준을 숫자로 만들 수 있고(48시간 게시가 없으면 요확인), 증거도 남길 수 있고(Slack permalink), 틀려도 아무것도 부서지지 않는다. 연습 상대로는 딱 좋습니다.

참고로 이후 샘플에 나오는 이름은 전부 가명(홍길동 등)으로 바꿔 두었습니다.

처음 만든 것. 그리고 "이거 cron 아닌가?"

첫 버전은 이랬습니다.

  1. SKILL.md에 절차를 쓴다(채널을 찾는다, 24시간 치를 읽는다, 사람별 마지막 게시를 추출한다, 48시간 넘으면 요확인)
  2. claude -p 로 그 스킬을 비대화 실행하는 셸 스크립트를 쓴다
  3. crontab에 등록해 매일 아침 9시 반에 돌린다

crontab의 내용은 한 줄입니다.

30 9 * * 1-5 /home/kazu/shinsotsu-tracker/scripts/run_daily.sh

의미는 "평일(1-5=월~금) 9시 30분에 이 스크립트를 실행하라". 이걸로 매일 아침 진척 목록이 알아서 로그에 떨어지게 되었습니다.

돌아갑니다. 그런데 만든 본인이 제일 위화감을 느꼈어요. 이거, AI를 부르고 있을 뿐인 정기 배치 아닌가?

이 의문, 사실 정곡을 찌른 것이었습니다. X에서도 "그냥 모자 쓴 cron job이잖아"라는 싸늘한 목소리가 있었다고 하고요. 그래서 해설 글을 다시 읽고 알게 된 것은, cron과 루프를 가르는 것은 기동 방법이 아니라는 점이었습니다.

갈림길은 기억의 순환이었다

첫 버전의 무엇이 문제였나. 매일 아침의 실행이 전날의 결과를 일절 참조하지 않았다는 것입니다.

매일 0에서 전원을 다시 조사한다. 어제 "이 사람은 게시글이 안 보인다"고 판정한 사람을 오늘도 다시 7일 치를 거슬러 찾는다. 어제 통지한 요확인자에게 오늘도 같은 통지를 보낸다. 실행할 때마다 결과를 버리고 있으니 원환이 이어지지 않는다. 정시 실행의 직선을 매일 늘어놓고 있을 뿐이었습니다.

고치는 법은 단순해서, state.json이라는 상태 파일을 하나 만들었습니다. 실물(가명판)은 이렇습니다.

{
  "last_run": "2026-07-04",
  "members": {
    "홍길동": { "last_post": "2026-07-03T09:11:37+09:00", "status": "ok", "consecutive_missing_days": 0 },
    "김철수": { "last_post": "2026-07-03T09:01:14+09:00", "status": "ok", "consecutive_missing_days": 0 },
    "이영희": { "last_post": "2026-06-29T09:08:56+09:00", "status": "escalated", "consecutive_missing_days": 4, "escalated_on": "2026-07-03" },
    "박민수": { "last_post": null, "status": "unresolved", "note": "표기 불일치/다른 채널/미참여 가능성" },
    "최민지": { "last_post": null, "status": "unresolved", "note": "위와 같음" },
    "정수빈": { "last_post": null, "status": "unresolved", "note": "위와 같음" }
  },
  "lessons": [
    "이름만으로 대조하면 표시명 흔들림으로 누락된다. 풀네임으로 대조한다",
    "24시간 창만으로 미보고라 단정하지 않는다. 반드시 거슬러 확인한다"
  ]
}

status는 3종류. ok(최근 게시 있음), escalated(요확인으로 통지 완료), unresolved(게시글 자체가 안 보여 원인 미특정). lessons에는 실행하며 배운 주의점이 쌓여 갑니다.

이 파일을 실행 전에 읽는다. 상태에 따라 움직임을 바꾼다. 실행 후에 다시 써 둔다.

  • unresolved인 사람은 매번 7일 치를 거슬러 오르는 낭비를 그만두고 그대로 둔다
  • escalated 완료인 사람에게는 재통지하지 않는다(매일 같은 알림이 오면 인간이 무시하게 된다)
  • ok인 사람은 최근 24시간만 본다

이걸 넣은 순간 동작이 달라졌습니다. 쓸데없는 탐색이 사라지고 통지의 노이즈도 사라졌다. 어제의 결과가 오늘의 움직임을 바꾼다. 이 순환이 생기고 나서야 루프라고 부를 수 있다는 걸 몸으로 이해한 순간입니다. 모델은 매번 잊지만, 파일은 잊지 않더군요.

참고로 cron 자체는 악역이 아닙니다. 해설 글에서도 Automations(자동 기동)는 정당한 부품으로 자리 잡고 있습니다. 심장 박동은 매일 아침 cron이 친다. 하지만 기억과 판단은 루프 쪽이 가진다. 이 분담이 답이었습니다.

그래도 모자랐던 2가지

검증 역의 분리(Maker-Checker)

작업한 에이전트가 "전원 몫 확인했습니다"라고 말한다. 그걸 믿어도 되나. 안 되는 모양입니다. 어느 글이든 작업 역의 완료 보고는 자기 신고이지 증명이 아니라고 못을 박고 있었습니다. 실제로 몇몇 기술 블로그 글에서는 리뷰 역 에이전트가 "테스트의 기댓값 쪽을 조작해 통과한 것으로 만들어 둔" 부정을 검출한 사례가 소개되어 있어서, 이건 좀 무서웠어요.

그래서 2단 구성으로 했습니다. Slack을 읽고 정리하는 Maker(작업 역)와 별도로, 검증 전용 Checker(검증 역)를 다른 모델(Haiku. 빠르고 싼 모델)로 돌립니다. 스크립트 해당 부분 발췌가 이겁니다.

# ── Checker(검증 역·다른 모델·읽기만) ──
checker_out=$(claude -p "당신은 이 성과물을 쓰지 않은 독립된 검증자입니다.
일단 의심부터 해 주세요. 다음 관점만으로 검증하고, 첫 줄에 PASS 또는
FAIL이라고만 써 주세요.

검증 관점:
1. 대상자 전원에게 판정이 붙어 있는가
2. 판정에 permalink가 첨부되어 있는가
3. state.json이 올바른 JSON이고 members가 누락되지 않았는가
4. 지난번 escalated/unresolved였던 사람이 증거 없이 ok로
   고쳐 써지지 않았는가

지난번 state.json: ${PREV_STATE}
검증 대상: ${maker_result}" \
  --model claude-haiku-4-5 --max-turns 3 --max-budget-usd 0.10)

첫머리의 "당신은 이 성과물을 쓰지 않았다"는 한 문장은, 자기 작업에 관대해지는 확증 편향을 말로 상쇄하기 위한 정석이라고 합니다. 관점 4가 우리 태스크에서의 채점표 조작 감지에 해당합니다. 요확인이었던 사람을 증거 없이 멋대로 ok로 되돌리지 않았는지를 봅니다.

Checker가 FAIL을 내면 지적을 Maker에 돌려 재실행. 2번 실패하면 state.json을 갱신하지 않고 사람에게 넘깁니다. 부서진 상태를 기억에 남기지 않기 위해서입니다.

상한의 선언

또 하나는 비용과 횟수의 천장입니다. Uber가 직원의 AI 툴 이용에 월 1,500달러 상한을 넣었다, 연간 예산을 4개월 만에 다 썼기 때문이다, 라는 보도가 있었다고 해서 웃어넘길 수가 없습니다. 스크립트 첫머리에, 루프를 쓰기 전에 정지 조건부터 썼습니다.

# ── 정지 조건(먼저 쓴다) ─────────────────
MAX_ATTEMPTS=2            # Checker FAIL 시 재시도 상한
MAX_TURNS=15              # 1회 claude -p 의 턴 상한
MAX_BUDGET_PER_RUN=0.50   # 1회 호출 비용 상한(USD)
DAILY_BUDGET_USD=2.00     # 일일 누계. 80% 도달 시 실행 스킵
STUCK_DAYS_LIMIT=14       # unresolved가 이 일수 이어지면 구조 자체를 재검토

그리고 세세한 이야기지만, claude -p(헤드리스 실행)는 2026년 6월부터 구독과는 별도의 미터 과금이 되어 있습니다. 정기 실행에 올린다면 이 과금 체계는 먼저 확인해 두는 편이 좋습니다.

대응표. 어느 파일이 루프의 어느 부품인가

여기까지의 구성물을 Addy Osmani식 6개 모듈과 설계 절차 5단계에 대응시키면 이렇습니다. 직접 만든 것이 어느 부품에 해당하는지는 이 표를 만들고 나서야 정리됐습니다.

6개 모듈과의 대응

모듈역할이번 구현
Automations(자동 기동)정해진 시각·이벤트에 알아서 기동한다crontab(평일 9:30)
Skills(절차의 고정)매번의 설명을 생략하기 위한 지식 파일SKILL.md(절차, 48시간 규칙, 과거의 함정)
Connectors(외부 연결)외부 서비스를 읽고 쓰는 팔Slack MCP
Memory(기억)대화 밖에 상태를 남겨 다음 회차에 넘긴다state.json(status, lessons)
Sub-agents(견제 역)작업 역과 다른 눈으로 검증한다Checker(Haiku를 이용한 다른 모델 검증)
Worktrees(병행 작업)여러 태스크를 격리해 동시에 돌린다미사용(6명 규모에서는 과하다고 판단)

설계 절차 5단계와의 대응

단계내용이번 구현
1. 목표 계약골과 인수 기준, 금지 사항을 먼저 정한다48시간 규칙, permalink 필수, 본인에게 연락 금지
2. 실행에이전트에 작업시킨다Maker(claude -p + SKILL.md)
3. 증거 피드백완료 보고에 검증 가능한 증거를 붙이게 한다판정별 Slack permalink
4. 정지 조건합격, 인계, 중단의 출구를 정한다4분류 판정표+재시도 상한+예산 가드
5. 경험의 환원배운 것을 다음 바퀴에 반영한다lessons 추가, SKILL.md 갱신

만드는 순서도 이 표 그대로였습니다. 다짜고짜 자동 기동부터 만들면 불안정한 것을 매일 돌리기만 하게 된다. 손으로 한 번 도는 것(단계 2)을 만들고, 지식을 고정하고(Skills), 증거와 정지 조건을 갖추고, 마지막에 cron을 잇는다. 이 순서가 손이 다시 가는 일이 가장 적었습니다.

인간의 일은 어디에 남는가

지금 제가 이 루프에 대해 하는 일은 3가지뿐입니다. 요확인 기준을 정한다(48시간). 통지가 왔을 때 본인에게 말을 걸지 말지 판단한다. 미해결이 2주 이어지면 구조 자체를 재검토한다.

매일 아침 Slack을 열어 눈으로 확인하는 일은 사라졌습니다. 대신 판정 기준과 예외 처리를 설계하는 일이 남았다. Osmani의 맺음말에, 기동 버튼만 누르는 사람이 아니라 엔지니어로 남을 작정인 사람으로서 루프를 만들라는 취지의 문장이 있는데, 이건 그 말대로라고 생각합니다. 루프에 맡긴 순간 속을 이해하는 노력을 그만두면, 실수도 고속으로 양산될 뿐이니까요.

참고로 이 루프는 보고만 합니다. 본인에게 독촉을 자동화하는 것도 기술적으로는 가능하지만, 하지 않고 있습니다. 해설 글의 용어로 말하면 L1(보고만)입니다. 감시당하는 느낌을 신입에게 주는가 아닌가는 기술이 아니라 운용의 문제이고, 거기는 아직 제 안에서 결론이 나지 않았습니다. 자율도를 올리는 건 L1의 보고가 신뢰할 만하다고 확인된 뒤로 충분하다고 생각합니다.

정리

  • cron으로 매일 아침 AI를 기동하는 것만으로는 정시 실행이지 루프가 아니다. 지난 결과가 이번 움직임을 바꾸는 기억의 순환(state.json)이 들어가야 비로소 루프가 된다
  • 작업 역의 완료 보고는 자기 신고. 다른 모델의 검증 역(Maker-Checker)과 조작 감지 관점을 넣는다
  • 정지 조건과 예산은 루프를 쓰기 전에 정한다. 재시도 상한, 턴 상한, 일일 예산
  • 첫 태스크는 판정을 수치화할 수 있고, 틀려도 부서지지 않는 읽기 전용을 고른다

구성은 SKILL.md(절차와 금지 사항), state.json(기억), 셸 스크립트(Maker→Checker→상태 갱신 제어)의 3파일뿐입니다. 화려하진 않지만, 이 작은 크기로도 루프의 틀은 한차례 배울 수 있었습니다. 다음은 사내 주간 보고 시스템의 미제출 체크에 같은 틀을 이식할 예정입니다.

이 글이 도움이 됐다면 추천해 주세요

관련 글

댓글 0