루프 엔지니어링

🏷️ 정보 에이전트 도구

이 글은 애디 오스마니의 'Loop Engineering'을 토대로 작성했습니다.

AI 코딩 도구를 쓰다 보면 한 가지 패턴에 갇힙니다. 프롬프트를 던지고, 결과를 보고, 다시 프롬프트하고, 또 결과를 봅니다. 일을 처음부터 끝까지 내가 손에 쥐고 한 턴씩 끌고 갑니다.

에이전트를 프롬프트하는 사람 자리를 내가 비우고, 그 일을 대신할 시스템을 설계하라.

일을 찾고, 나눠주고, 검사하고, 무엇을 했는지 적어두고, 다음에 뭘 할지 결정하는 작은 시스템이죠. 프롬프트 엔지니어링이 한 번의 대화를 잘 짜는 기술이라면, 루프 엔지니어링은 그 대화를 자동으로 반복하는 고리를 짜는 기술입니다.

프롬프트에서 루프로

기존 방식에서 나는 운전자입니다. 에이전트에게 "이 버그 고쳐줘"라고 하고, 답을 받고, "테스트도 추가해줘"라고 하고, 다시 답을 받습니다. 매 단계가 내 입력에 묶여 있습니다. 내가 자리를 비우면 고리도 멈춥니다.

루프 방식에서 나는 설계자입니다. "매일 아침 CI 실패를 훑어서, 고칠 만한 것은 격리된 작업 공간에서 수정 초안을 잡고, 다른 에이전트가 검토한 뒤 통과하면 PR을 열어라" 같은 흐름을 한 번 정의합니다. 그다음은 시스템이 알아서 돕니다. 한 번 설계했을 뿐, 그 단계 중 어느 것도 직접 프롬프트하지 않은 상태가 됩니다.

이 자리는 하네스보다 한 층 위입니다. 하네스 엔지니어링이 에이전트 한 번의 실행이 들어가는 환경, 즉 어떤 도구를 쥐어주고 무엇을 "끝"으로 칠지를 짜는 일이라면, 루프 엔지니어링은 그 하네스를 타이머 위에 올리고, 작은 조수들을 띄우고, 스스로를 먹여 돌게 만드는 바깥 시스템입니다.

1년 전이라면 루프를 짜려고 bash 더미를 쓰고 평생 그걸 혼자 유지보수해야 했지만, 지금은 부품들이 제품 안에 그냥 들어 있습니다. Claude Code와 Codex의 구성이 거의 똑같아서, 어느 도구냐를 따지는 대신 어느 쪽에 앉아도 도는 고리를 설계하면 됩니다.

이게 가능하려면 고리를 이루는 부품이 더 필요합니다. 다섯 가지 부품에 기억(상태)을 더한 여섯 가지를 하나씩 보겠습니다.

정말 루프가 필요한가?

잠시만요, 어쩌면 대부분의 개발자에게는 아직 루프가 필요 없습니다.

루프는 설정 비용을 여러 번의 실행에 나눠 갚는 구조입니다. 그래서 그 비용을 정당화하는 조건이 따로 있습니다.

네 조건 중 하나라도 빠지면 루프는 돌려받는 것보다 더 많이 듭니다. 그래서 좋은 첫 루프와 나쁜 첫 루프가 갈립니다. CI 실패 분류, 의존성 업데이트 PR, 린트·수정 패스, 플래키 테스트 재현처럼 주니어가 체크리스트와 테스트 스위트로 해낼 만한 일은 루프에 잘 맞습니다. 반대로 아키텍처 재설계, 인증·결제 코드, 프로덕션 배포, "끝"이 판단의 문제인 모호한 작업은 사람이 의자에 앉아 있어야 합니다.

여기서 경제성이 갈립니다. 중간 규모 작업 한 건의 단일 에이전트 루프가 5만~20만 토큰, 오케스트레이터에 전문가 셋을 붙인 플릿 루프는 50만~200만 토큰을 씁니다. 매일 도는 스케줄 루프라면 주당 수백만 토큰입니다. 토큰이 사실상 공짜인 사람에게는 당연한 기술이지만, 종량제 요금을 쓰는 사람에게는 무모한 기술이 되는 이유가 여기 있습니다. 루프는 설계가 어려운 게 아니라 감당이 어렵습니다.

루프의 구성 요소

자동화

루프는 누군가 방아쇠를 당겨야 돕니다. 그 방아쇠를 사람이 아니라 일정이 당기게 하는 것이 자동화입니다.

Claude Code에는 일정 간격으로 도는 /loop와 조건이 충족될 때까지 도는 /goal이 있습니다.

OpenAI Codex 앱에는 주기를 설정하는 Automations 탭과, 발견된 일이 쌓이는 Triage 인박스가 있습니다.

루프 엔지니어링에서는, 사용자가 수동으로 작업을 확인하지 않아야 합니다.

워크트리

여러 에이전트가 동시에 코드를 만지면 충돌합니다. 두 에이전트가 같은 파일을 쓰는 상황이 대표적입니다.

git worktree는 같은 저장소 히스토리를 공유하면서도 작업 디렉터리만 분리해주는 표준 git 기능입니다.

Claude Code는 git worktree, --worktree 플래그, isolation: worktree 설정으로 이를 다룹니다.

Codex는 스레드마다 워크트리를 자동으로 띄웁니다. 덕분에 에이전트 여러 개를 병렬로 돌려도 서로의 작업을 덮어쓰지 않습니다.

스킬

매번 같은 맥락을 다시 설명하는 건 비효율입니다. 루프가 돌 때마다 반복할 수 없습니다.

스킬은 SKILL.md 파일에 지침·관례·빌드 절차·프로젝트 이력을 적어두는 것입니다. 작업 설명에 맞으면 암묵적으로 매칭됩니다. intent debt, 즉 에이전트가 빈 지식을 자신 있는 추측으로 메우는 문제를 줄여줍니다.

플러그인과 커넥터

에이전트가 코드만 고치고 끝나면 반쪽입니다. 이슈 트래커를 읽고, 데이터베이스를 조회하고, 스테이징 API를 때리고, 슬랙에 알리는 것까지 해야 고리가 닫힙니다.

이 연결은 Model Context Protocol 위에 올라갑니다. "여기 수정안이 있어"와 "PR을 열고, Linear 티켓을 링크하고, CI가 초록불이 되면 채널에 알림을 보낸다"의 차이입니다. 플러그인은 이런 커넥터와 스킬을 묶어 팀에 배포할 수 있게 합니다.

서브에이전트

한 모델이 자기 작업을 자기가 채점하면 신뢰하기 어렵습니다. 그래서 만드는 에이전트와 검증하는 에이전트를 분리합니다.

Claude Code는 .claude/agents/에 에이전트를 정의하고 팀으로 묶어 작업을 주고받게 합니다. Codex는 .codex/agents/에 TOML로 정의하며 모델과 추론 강도를 따로 설정할 수 있습니다. 전형적인 분담은 탐색(explorer) → 구현(implementer) → 검증(verifier)입니다. /goal이 내부적으로 쓰는 패턴도 이것입니다. 새 모델이 일이 끝났는지 판정합니다.

기억: 대화 밖에 두는 상태

모델은 실행 사이에 모든 것을 잊습니다. 그래서 무엇을 했고, 무엇을 시도했고, 무엇이 열려 있는지를 대화 컨텍스트가 아니라 디스크에 적어둬야 합니다.

마크다운 파일, Linear 보드, 외부 저장소 어디든 좋습니다. 모델은 실행 사이에 모든 것을 잊으므로, 기억은 컨텍스트가 아니라 디스크에 있어야 합니다. 이 블로그 작업에서 핸드오프 문서(raw/drafts/_handoff-*.md)를 남기는 것과 정확히 같은 발상입니다.

열린 고리와 닫힌 고리

부품이 같아도 고리를 푸는 방식은 둘로 갈립니다. 이 구분이 2026년 현재 가장 실용적인 분기점입니다.

열린 고리는 탐색형입니다. 에이전트에게 목표만 주고 마음껏 돌아다니게 둡니다. 여러 경로를 시도하고, 내가 명세하지 않은 것까지 발견하고 만들어냅니다. 토큰이 무제한인 사람들이 하는 일입니다. 대신 토큰을 무섭게 태우고, 기준이 느슨한 프로젝트를 겨누면 빠르고 지저분하고 비싼 슬롭 기계가 됩니다.

닫힌 고리는 경계가 있습니다. 사람이 처음부터 끝까지의 경로를 먼저 설계합니다. 명확한 목표, 정해진 단계, 단계마다의 평가, 그리고 멈추거나 사람에게 되넘기는 지점. 에이전트는 여전히 고리를 돌지만 내가 짠 틀 안에서 돕니다. 경로가 빡빡한 만큼 일반 예산으로도 돌아가고, 매 패스가 다음 패스를 먹이며 매번 조금씩 나아집니다. 품질 게이트가 없으면 AI는 표류하고, 있으면 개선됩니다.

권하는 순서는 닫힌 고리부터입니다. 신뢰할 만하게 도는 빡빡한 시스템을 먼저 짜고, 품질 게이트를 갖춘 다음에야 고리를 열어야 합니다.

규모로 보면 또 둘로 나뉩니다. 단일 에이전트 루프는 한 에이전트가 발견·계획·실행·검증·반복을 혼자 도는 것으로, 초안을 자기가 다시 고치는 사람에 가깝습니다. 플릿 루프는 오케스트레이터에게 목표를 주면 그것이 일을 쪼개 전문가 에이전트에게 넘기고, 전문가는 다시 서브에이전트에게 좁은 일을 맡기는 구조입니다. 팀 하나가 프로젝트를 처음부터 끝까지 돌리는 모양인데, 트리의 모든 에이전트가 똑같은 발견→계획→실행→검증→반복 고리를 돕니다. 단일 에이전트가 잘 도는 걸 확인하기 전에 플릿부터 띄우는 건, 닫힌 고리 없이 고리를 여는 것과 같은 실수입니다.

하루가 어떻게 도는가

부품을 다 모으면 이런 고리가 됩니다.

  1. 자동화가 매일 아침 한 번 돕니다.
  2. 분류(triage) 스킬이 어제의 CI 실패, 열린 이슈, 최근 커밋을 읽습니다.
  3. 발견한 것을 마크다운이나 Linear에 적습니다.
  4. 고칠 만한 항목은 격리된 워크트리를 열고, 서브에이전트가 수정 초안을 잡습니다.
  5. 두 번째 서브에이전트가 프로젝트 스킬과 테스트에 비춰 검토합니다.
  6. 커넥터가 PR을 열고 티켓을 자동으로 갱신합니다.
  7. 처리 못 한 항목은 사람이 볼 수 있게 분류 인박스에 남습니다.
  8. 상태 파일이 다음 고리를 위해 진행 상황을 기억합니다.

한 번 설계했을 뿐, 이 단계 중 어느 것도 직접 프롬프트하지 않았다는 게 결과입니다.

고리가 빨라질수록 위험한 것들

원문은 글의 절반을 경고에 씁니다. 이 부분이 진짜 무게중심입니다. 위험은 두 결로 나뉩니다. 하나는 고리 자체가 조용히 망가지는 기술적 실패이고, 다른 하나는 고리가 잘 돌수록 오히려 커지는 사람 쪽의 위험입니다.

조용히 실패하는 고리

이 그림이 매끄럽게 돌려면 한 가지가 깨지지 않아야 합니다. 게이트입니다. 완료됐을 때만 완료 토큰을 내보내야 하는 에이전트가 그 토큰을 너무 일찍 내보내고, 고리는 일을 반쯤 하다 빠져나갑니다. 단단한 게이트가 없으면 고리는 조용히 실패하면서 계속 토큰만 태웁니다. 엔지니어 제프리 헌틀리가 기록하고 이름 붙인 이 실패 모드를 랠프 위검(Ralph Wiggum) 루프라고 부릅니다.

이 일이 벌어지는 조건은 세 가지입니다. 진짜 검증자가 없을 때(그냥 "검토해줘"라고 부탁받은 두 번째 에이전트는 객관적 신호가 아니라 함께 끄덕이는 두 번째 낙관론자입니다), 완료 조건이 물렁할 때(테스트·빌드·타입체크가 아니라 에이전트의 판단으로 "끝"을 정의할 때), 단단한 멈춤이 없을 때(성공이 검증돼서가 아니라 외부의 무언가가 죽일 때까지 도는 경우)입니다. 해법은 의견을 가진 검증자가 아니라 작업을 떨어뜨릴 수 있는 객관적인 무언가, 즉 통과하거나 실패하는 테스트, 컴파일되거나 안 되는 빌드, 0이나 0이 아닌 값을 돌려주는 린터입니다.

측정된 다른 실패 모드도 알아둘 만합니다.

관측 가능성(observability) 쪽에서 떠오르는 발상 하나가 이 문제의 다른 절반을 짚습니다. 프로덕션에서 고리가 깨졌을 때 트레이스는 무슨 일이 있었는지는 보여줘도 왜 깨졌고 무엇을 고쳐야 하는지는 알려주지 않습니다. 그래서 고친 실패를 매번 회귀 테스트로 잠가두자는 접근이 나옵니다. 디버깅한 실패 트레이스 하나하나가 새 테스트 케이스가 되면, 게이트는 실제 프로덕션 실패에서 자라납니다. 사이클이 돌수록 하네스가 깨지기 어려워지는 셈입니다. 게이트는 한 번 짜고 끝이 아니라 썩기 때문에, 이렇게 스스로 단단해지는 구조가 중요합니다.

잘 돌수록 커지는 위험

이쪽 위험은 고리가 망가져서 생기는 게 아닙니다. 고리가 잘 돌수록 더 날카로워집니다.

검증은 여전히 내 책임입니다. 사람 없이 도는 고리는 사람 없이 실수도 합니다. 검증 서브에이전트가 "끝났다"를 의미 있게 만들어주긴 하지만, "끝났다"는 주장이지 증명이 아닙니다. 고리가 초록불을 켰다고 해서 정답이라는 보장은 없습니다.

이해 부채(comprehension debt)가 쌓입니다. 고리가 내가 쓰지 않은 코드를 빨리 내보낼수록, 존재하는 것과 내가 실제로 파악한 것 사이의 간극이 커집니다. 고리의 산출물을 읽는 일은 타협 대상이 아닙니다. 빨리 도는 고리일수록 읽기를 더 해야 합니다.

인지적 항복(cognitive surrender)이 유혹합니다. 같은 고리 설계가 정반대 결과를 냅니다. 내가 이해한 일을 가속하는 데 쓰면 레버리지가 되고, 이해 자체를 회피하는 데 쓰면 독이 됩니다. 편안한 자세가 곧 위험한 자세입니다. 고리는 사려 깊은 가속과 인지 회피를 구분하지 못합니다. 그 구분은 나만 할 수 있습니다.

토큰 비용을 경계해야 합니다. 서브에이전트는 추가 토큰을 태웁니다. 토큰이 넉넉한지 빠듯한지에 따라 사용 패턴이 크게 달라지므로, 두 번째 의견이 정말 중요한 곳에만 서브에이전트를 씁니다.

보안도 세금처럼 따라옵니다. 무인으로 도는 고리는 무인으로 방치된 공격 표면이기도 합니다. 고리는 사람이 읽는 속도보다 빠르게 PR을 엽니다. 보안 검사(SAST, 의존성 감사, 시크릿 스캔)가 게이트에 들어 있지 않으면 안전하지 않은 코드가 자동으로 머지됩니다. 자동 설치되는 스킬은 그 설명문에 숨은 프롬프트 인젝션을 그대로 물려받으니 출처를 감사해야 하고, 긴 실행 중의 장황한 디버그 로그는 내가 보지도 않는 로그 곳곳에 시크릿을 흩뿌립니다. 읽기 전용으로 시작한 권한에 "딱 하나만" 쓰기 권한을 더한 뒤 다시 들여다보지 않는 것도 전형적인 함정입니다. 권한은 주기적으로 다시 감사하는 게 맞습니다.

결론

그래서 네 조건을 통과해 정말 고리를 짤 사람에게 줄 처방은 단순합니다. 화려한 것 이전에, 작동하는 가장 작은 고리를 먼저 만듭니다. 부품 넷이면 충분합니다. 일정에 따라 돌고 명확한 조건에서 멈추는 자동화 하나, 매번 다시 설명하지 않도록 프로젝트 맥락을 담은 스킬 하나, 오늘의 진행을 기억해 내일이 이어받게 하는 상태 파일 하나, 그리고 나쁜 작업을 자동으로 떨어뜨리는 게이트 하나. 순서도 중요합니다. 먼저 수동 실행 한 번을 믿을 만하게 만들고, 그걸 스킬로 굳히고, 고리로 감싸고, 그다음에 스케줄에 올립니다. 건너뛰면 아무도 이해하지 못하는 시스템에 돈을 내게 됩니다. 따라가야 할 지표는 태운 토큰도, 시도한 작업 수도 아닌 "받아들여진 변경 한 건당 비용"입니다. 받아들여지는 비율이 절반 아래라면 고리가 덜어준 검토 일을 내가 다시 하고 있다는 뜻이고, 그 고리는 지고 있는 겁니다.

레버리지 지점이 프롬프트 설계에서 고리 설계로 옮겨간 것은 맞습니다. 그런데 일이 쉬워지는 게 아니라 더 구조적인 것이 됩니다. 똑같은 고리를 짠 두 사람이 정반대 결과를 얻습니다. 차이는 고리가 아니라 그것을 짠 사람의 의도에 있습니다. 고리 자체는 그 의도를 구별하지 못하니까요.

이 글의 가치는 다섯 부품 목록이 아니라 후반부 경고에 있습니다. 자동화·워크트리·서브에이전트는 도구일 뿐이고, 어느 도구를 쓸지는 Claude Code든 Codex든 비슷합니다. 정작 중요한 건 고리를 돌려놓고 읽기를 멈추는 순간 무슨 일이 벌어지는가입니다. 검증과 이해를 남에게(혹은 고리에게) 넘기는 순간, 속도는 올라가도 내가 책임질 수 있는 범위는 줄어듭니다. 고리를 짜되, 그냥 시작 버튼을 누르는 사람이 아니라 끝까지 엔지니어로 남을 사람처럼 짜야 한다는 것. 애디 오스마니의 결론은 거기에 있습니다.