루프 엔지니어링(Loop Engineering)

TMT

https://addyosmani.com/blog/loop-engineering/

루프 엔지니어링은 에이전트에게 프롬프트를 던지는 ‘나’라는 사람을 대체하는 일입니다. 대신 프롬프트를 던지는 시스템을 설계하는 것이죠. 여기서 말하는 루프는, 어떤 목적을 정해두고 AI가 그 목표를 달성할 때까지 계속 반복해서 일을 진행하는 재귀적인(goal-recursive) 목표라고 볼 수 있습니다. 저는 이것이 앞으로 우리가 코딩 에이전트와 협업하는 방식의 미래가 될 수 있다고 생각합니다. 다만 아직 초기 단계이고, 저 역시 회의적인 부분이 있으며, 토큰 비용에는 반드시 주의를 기울여야 합니다(토큰이 넉넉한지, 빠듯한지에 따라 사용 패턴이 완전히 달라질 수 있습니다). 그래서 루프 엔지니어링이 정확히 무엇이고, 어떤 의미를 가지는지 풀어서 설명해 보려 합니다.

Peter Steinberger는 최근 이런 이야기를 했습니다. “이제는 코딩 에이전트에 직접 프롬프트를 던져서는 안 됩니다. 에이전트를 프롬프트하는 루프를 설계해야 합니다.” 비슷하게, Anthropic의 Claude Code를 이끄는 Boris Cherny는 이렇게 말했죠. “저는 이제 Claude에 직접 프롬프트하지 않습니다. Claude를 프롬프트하고 뭘 해야 할지 스스로 결정하는 루프들을 돌리고 있어요. 제 일은 루프를 쓰는 겁니다.”

그렇다면 이게 실제로는 무슨 뜻일까요?

지난 2년 정도는 코딩 에이전트에게서 유용한 결과를 얻는 방법이 ‘좋은 프롬프트를 쓰고, 충분한 컨텍스트를 제공하는 것’이었습니다. 어떤 내용을 입력하고, 결과를 읽은 뒤, 다음 내용을 다시 입력하는 식이죠. 에이전트는 도구이고, 당신은 그 도구를 매번 쥐고서 턴을 주고받으며 일했습니다. 이 단계는 이제 어느 정도 끝났거나, 적어도 많은 사람들이 곧 끝나리라고 보고 있습니다.

이제는 작은 시스템을 하나 만듭니다. 이 시스템은 스스로 할 일을 찾아내고, 일을 나눠 주고, 결과를 검수하고, 무엇이 끝났는지 기록하고, 그다음에 할 일을 스스로 정합니다. 그리고 이 시스템이 에이전트들을 ‘찌르는(poke)’ 역할을 하도록 두는 거죠. 저는 예전에 이와 비슷한 개념인 에이전트 하네스 엔지니어링(agent harness engineering)에 대해 쓴 적이 있습니다. 이는 단일 에이전트가 돌아가는 환경을 설계하는 일이고, 또 팩토리 모델(factory model) — 즉 소프트웨어를 만들어내는 전체 시스템 — 에 대해서도 썼습니다. 루프 엔지니어링은 이들보다 한 층 위에 있습니다. 하네스가 타이머 위에서 돌아가고, 작은 헬퍼들을 스폰하며, 스스로 먹이를 가져다 주는 구조라고 보면 됩니다.

흥미로웠던 지점은, 이게 더 이상 ‘툴을 하나 더 만든다’는 문제가 아니라는 점입니다. 1년 전만 해도 루프를 만들고 싶다면 온갖 bash 스크립트를 잔뜩 써서 평생 직접 관리해야 했고, 그건 철저히 나만의 것이었습니다. 그런데 이제는 이런 부품들이 그냥 제품 안에 기본 탑재됩니다. Steinberger가 정리한 목록은 Codex 앱의 기능과 거의 1:1로 대응되고, Claude Code에도 거의 그대로 겹칩니다. 그리고 한 번 이 공통된 형태가 보이기 시작하면, 더 이상 “어느 툴이 낫냐”를 두고 싸우지 않게 됩니다. 그 대신, 어떤 툴에 앉아 있든 여전히 잘 동작하는 ‘루프’를 설계하는 데 집중하게 되죠.

다섯 가지 구성 요소, 그리고 한 가지 메모리

하나의 루프를 구성하려면 다섯 가지가 필요하고, 거기에 정보를 기억해 둘 한 곳이 더 필요합니다. 먼저 목록부터 나열하고, 그다음에 각각을 연결해 보겠습니다.

  1. 스스로 정해진 주기에 따라 돌아가며, 자동으로 탐색(discovery)과 티어링(분류, triage)을 해 주는 자동화(Automations)
  2. 둘 이상의 에이전트가 병렬로 작업해도 서로를 밟지 않도록 하는 워크트리(Worktrees)
  3. 에이전트가 추측으로 때려 맞추지 않도록, 프로젝트에 관한 지식을 밖에다 써 두는 스킬(Skills)
  4. 이미 사용 중인 도구들과 연결해 주는 플러그인과 커넥터(Plugins and connectors)
  5. 아이디어를 내는 에이전트와 검증하는 에이전트를 분리하기 위한 서브 에이전트(Sub-agents)

그리고 여섯 번째로, 메모리가 있습니다. 단일 대화 세션 밖에 존재하면서, 무엇이 끝났고 무엇이 다음인지 적어 두는 마크다운 파일이나 Linear 보드 같은 것 말이죠. 너무 단순해 보여서 별거 아닌 것 같지만, 오래 돌아가는 모든 에이전트가 의존하는 바로 그 트릭입니다. 저는 이 점을 long-running agents에서 자세히 다뤘습니다. 모델은 각 실행(run) 사이의 내용을 죄다 잊어버리기 때문에, 메모리는 컨텍스트 안이 아니라 디스크 위에 있어야 합니다. 에이전트는 잊지만, 레포는 잊지 않습니다.

현재 두 제품 모두 이 다섯 가지를 다 갖추고 있습니다.

프리미티브루프에서의 역할Codex 앱Claude Code
Automations스케줄 기반 탐색 및 티어링Automations 탭: 프로젝트, 프롬프트, 주기, 환경을 선택; 결과는 Triage 인박스로 수집; /goal로 “완료될 때까지 실행”예약 작업과 cron, /loop, /goal, 훅(hooks), GitHub Actions
Worktrees병렬 기능을 서로 격리스레드당 내장 워크트리git worktree, –worktree, 서브에이전트에 isolation: worktree 설정
Skills프로젝트 지식을 코드화Agent Skills (SKILL.md), $이름 호출 또는 암시적으로 사용Agent Skills (SKILL.md)
Plugins / connectors기존 도구들과 연결커넥터(MCP)와 배포용 플러그인MCP 서버와 플러그인
Sub-agents아이디어 도출 및 검증.codex/agents/ 내 TOML로 정의된 Subagents.claude/agents/의 작업 서브에이전트, 에이전트 팀(agent teams)
State진행 상황 추적커넥터를 통한 Markdown 또는 LinearMarkdown(AGENTS.md, 진행 파일) 또는 MCP를 통한 Linear

이름은 조금씩 다르지만, 결국 제공하는 능력은 같은 기능입니다. 이제 각각을 하나씩 살펴보겠습니다. 실제로는 이 디테일들이 루프를 단단하게 붙들어 주기도 하고, 모르게 여기저기 새게 만들기도 하기 때문입니다.

자동화(Automations), 루프의 심장박동

자동화는 루프를 ‘한 번 돌려 본 것’이 아니라 진짜 루프로 만들어 주는 요소입니다. Codex 앱에서는 Automations 탭에서 자동화를 하나 만들고, 어떤 프로젝트에 대해, 어떤 프롬프트를, 어떤 주기로, 로컬 체크아웃에서 돌릴지 백그라운드 워크트리에서 돌릴지 정합니다. 무언가를 찾아낸 실행(run)은 Triage 인박스로 들어오고, 아무것도 못 찾은 실행은 그냥 스스로 아카이브됩니다. 꽤 편리하죠. OpenAI 내부에서는 이런 자동화를 일일 이슈 티어링, CI 실패 요약, 커밋 브리핑 작성, 지난주에 새로 생긴 버그 찾기 같은 지루한 일들에 활용합니다. 그리고 자동화는 스킬을 호출할 수도 있기 때문에, 반복 작업에 거대한 설명 블록을 그대로 붙여 넣는 대신 $skill-name⁠을 호출하는 식으로 유지보수성을 확보합니다. 아무도 다시 열어 보지 않을 스케줄에 끝도 없이 긴 지시문을 붙여두는 대신 말입니다.

Claude Code는 스케줄과 훅(hook)을 통해 비슷한 지점에 도달합니다. /loop⁠로 프롬프트나 명령을 정해진 간격으로 돌릴 수 있고, 크론 작업을 예약할 수도 있고, 에이전트 라이프사이클의 특정 지점에서 셸 커맨드를 쏘는 훅을 정의할 수도 있습니다. 혹은 이 전체를 GitHub Actions로 밀어 올려서, 노트북을 덮은 뒤에도 계속 돌게 만들 수도 있죠. 핵심 아이디어는 완전히 같습니다. 자율적인 작업을 정의하고, 주기를 부여하고, 그 결과가 알아서 당신에게 도달하게 해서, 당신이 직접 이곳저곳을 돌아다니며 확인하지 않아도 되게 만드는 것입니다.

여기에는 루프 전체를 관통하는 개념에 더 직접 닿아 있는 또 다른 “세션 내” 프리미티브가 하나 더 있습니다. /loop⁠가 주기적으로 다시 도는 것이라면, /goal⁠은 당신이 적어둔 조건이 실제로 참이 될 때까지 계속 진행합니다. 매 턴이 끝날 때마다 별도의 작은 모델이 “이제 끝났는가?”를 확인하기 때문에, 코드를 작성한 에이전트와 그것을 채점하는 에이전트가 동일하지 않습니다. 예를 들어 “test/auth 안의 모든 테스트가 통과하고, 린트가 깨끗하다” 같은 조건을 주고 자리를 떠날 수 있습니다. Codex에도 같은 기능이 있으며, 마찬가지로 /goal⁠이라는 이름을 씁니다. 검증 가능한 종료 조건이 만족될 때까지 여러 턴에 걸쳐 계속 작업을 이어 가고, 중지·재개·초기화 같은 조작도 지원합니다. 이름은 같고, 두 툴에 공통으로 존재하는 동일한 프리미티브입니다. 이 글 전체에 걸쳐 반복해서 나오게 될 “양쪽 툴에 같은 패턴이 있다”는 점을 잘 보여 주는 예입니다.

이렇게 해서 ‘일감이 떠오르는’ 부분이 마련됩니다. 루프의 나머지 구성 요소는 이 일감에 실제로 작동하는 역할을 맡습니다.

워크트리(Worktrees), 병렬이 곧바로 혼돈이 되지 않도록

에이전트를 한 개 이상 돌리는 순간부터, 파일 충돌이 바로 문제로 떠오릅니다. 두 에이전트가 같은 파일을 동시에 수정하면, 서로 한마디 상의도 없이 같은 코드 라인에 커밋을 박아 넣는 두 명의 엔지니어와 똑같은 골칫거리가 됩니다. git worktree는 이 문제를 해결해 줍니다. 하나의 레포 히스토리를 공유하지만, 각기 다른 브랜치에 독립된 워킹 디렉터리를 제공해 주기 때문에, 한 에이전트의 수정이 다른 에이전트의 체크아웃을 물리적으로 건드릴 수 없게 됩니다.

Codex는 이 worktree 지원을 제품 내부에 아예 내장해서, 여러 개의 스레드가 동시에 같은 레포를 건드리더라도 서로를 방해하지 않게 합니다. Claude Code 역시 git worktree⁠--worktree⁠ 플래그를 통해 세션을 독립된 체크아웃에서 열 수 있으며, 서브 에이전트에 isolation: worktree⁠ 설정을 붙여서 각 헬퍼가 깨끗한 체크아웃을 받아 쓰고, 끝난 뒤에는 스스로 정리(clean-up)하도록 할 수 있습니다. 저는 이런 병렬 작업의 ‘사람 쪽 이야기’를 the orchestration tax라는 글에서 다룬 적이 있습니다. 워크트리는 기계적인 충돌을 없애 주긴 하지만, 여전히 당신이 병목이라는 사실은 변하지 않습니다. 실제로 몇 개의 에이전트를 병렬로 돌릴 수 있는지는 도구가 아니라, 당신이 검토(review)에 쓸 수 있는 대역폭이 결정합니다.

스킬(Skills), 매번 프로젝트를 처음부터 설명하지 않기 위해

스킬은, 매 세션마다 같은 프로젝트 컨텍스트를 금붕어처럼 처음부터 다시 설명하는 일을 멈추게 해 주는 장치입니다. 두 툴 모두 같은 포맷을 사용합니다. 폴더 하나 안에 SKILL.md⁠를 두고, 그 안에 지시문과 메타데이터를 적습니다. 그리고 선택적으로 스크립트나 참고 자료, 에셋 등을 함께 넣을 수 있습니다. Codex에서는 $⁠나 /skills⁠로 호출하거나, 현재 작업이 스킬 설명과 잘 맞아떨어지면 알아서 스킬을 실행합니다. 이 때문에 스킬 설명은 화려한 문구보다 ‘지루하리만큼 정확한’ 설명이 훨씬 더 좋습니다. Claude Code도 같은 방식으로 동작하며, 저는 이 패턴을 agent skills 글에서 정리해 두었습니다.

스킬은 또, 당신의 의도가 반복해서 토큰 비용을 갉아먹지 않게 해 주는 공간이기도 합니다. 저는 the intent debt에서, 에이전트는 매 세션을 완전한 백지 상태에서 시작하고, 당신의 의도에 빈틈이 있으면 그 부분을 자기가 지어낸 추측으로 채운다고 주장했습니다. 스킬은 이런 의도를 “바깥에 적어두는” 장치입니다. 컨벤션, 빌드 단계, “우리가 이 방식을 쓰지 않는 이유는 그때 그 사건 때문이다” 같은 것들을, 에이전트가 매 실행마다 읽어 보게 되는 한 장짜리 문서로 옮겨 놓는 거죠. 스킬이 없으면 루프는 매 사이클마다 프로젝트 전체를 다시 추론해 내야 하지만, 스킬이 있으면 의도가 누적(compound)됩니다.

여기서 헷갈리지 말아야 할 한 가지가 있습니다. 스킬은 ‘어떻게 쓸 것인가’를 정의하는 저작 포맷이고, 플러그인은 그것을 ‘어떻게 배포할 것인가’에 관한 것입니다. 레포를 넘나들며 스킬을 공유하거나, 몇 가지 스킬을 하나로 묶어 배포하고 싶다면 플러그인으로 패키징합니다. Codex에서도 그렇고, Claude Code에서도 마찬가지입니다.

플러그인과 커넥터(Plugins and connectors), 루프가 실제 도구를 건드리게 만드는 것

파일 시스템만 볼 수 있는 루프는 아주 작은 루프입니다. MCP 기반의 커넥터를 사용하면, 에이전트가 이슈 트래커를 읽고, 데이터베이스를 쿼리하고, 스테이징 API를 두드리고, Slack에 메시지를 남길 수 있습니다. Codex와 Claude Code는 둘 다 MCP를 사용하기 때문에, 한 쪽을 위해 만든 커넥터는 대개 다른 쪽에서도 그대로 동작합니다. 그리고 플러그인은 커넥터와 스킬을 묶어서, 동료가 당신이 쓰는 설정을 “처음부터 기억을 더듬어 재구성”하지 않고도 한 번에 설치할 수 있게 해 줍니다.

이게 “여기 고쳤어요”라고 말만 하는 에이전트와, 실제로 PR을 열고, Linear 티켓을 연결하고, CI가 초록불이 되면 채널에 직접 알림까지 보내는 ‘루프’의 차이입니다. 커넥터가 있기 때문에 루프는 “할 수 있다면 이렇게 하겠다”라고 설명만 하는 것이 아니라, 실제로 당신의 환경 안에서 그 일을 수행할 수 있습니다.

서브 에이전트(Sub-agents), 만드는 사람과 검증하는 사람을 분리하기

루프 안에서 구조적으로 가장 중요한 장치는, 단연 ‘작성자’와 ‘검증자’를 분리하는 것입니다. 코드를 쓴 모델이 자기 숙제를 채점하면 점수가 터무니없이 후해집니다. 지시문이 다른 두 번째 에이전트, 때로는 완전히 다른 모델이 들어오면, 첫 번째 에이전트가 스스로를 설득해 버린 부분들을 잡아낼 수 있습니다.

Codex는 당신이 요청할 때만 서브 에이전트를 스폰하고, 동시에 돌린 뒤, 결과를 다시 하나의 답변으로 합쳐 줍니다. .codex/agents/⁠ 안에 TOML 파일로 각 에이전트를 정의하는데, 이름, 설명, 지시문과 함께 선택적으로 모델과 reasoning effort를 지정할 수 있습니다. 그래서 탐색자는 빠른 읽기 전용 모델로 두고, 보안 리뷰어는 강력한 모델에 높은 effort를 주는 식으로 조합할 수 있습니다. Claude Code도 .claude/agents/⁠ 안의 서브 에이전트와, 작업을 서로 넘겨 주는 에이전트 팀(agent teams)을 통해 같은 구조를 제공합니다. 두 툴에서 자주 쓰는 전형적인 분리는, 하나는 탐색(explore), 하나는 구현(implement), 하나는 스펙에 맞게 검증(verify)하는 식의 3분 역할입니다.

저는 이런 구조를 the code agent orchestra, 그리고 adversarial code review에서 두 번이나 강조한 바 있습니다. 특히 루프 안에서 이 구조가 중요한 이유는, 루프가 당신이 지켜보지 않는 동안에도 돌아가기 때문입니다. 믿을 수 있는 검증자를 따로 두어야만, 안심하고 자리를 떠날 수 있습니다. 물론 서브 에이전트를 추가하면 토큰은 더 많이 듭니다. 각 에이전트가 자기 모델과 툴 호출을 따로 하기 때문이죠. 따라서 두 번째 의견이 값어치를 하는 지점에 집중해서 써야 합니다. Claude Code의 /goal⁠이 내부적으로 하는 일도 사실상 이와 같습니다. 작업을 수행한 에이전트가 아니라, 새 모델이 “끝났는지”를 판정하는 것, 즉 ‘만드는 쪽’과 ‘검증하는 쪽’을 종료 조건 수준에서까지 분리하는 셈입니다.

루프 하나가 실제로 생겼을 때의 모습

이제 이 모든 것을 한데 모으면, 단일 스레드가 작은 컨트롤 패널로 변합니다. 제가 자주 쓰는 하나의 형태를 예로 들어 보겠습니다.

매일 아침 레포에서 자동화가 한 번씩 돌아갑니다. 이 자동화의 프롬프트는 티어링 스킬을 호출하고, 이 스킬은 전날 CI 실패 내용, 열린 이슈들, 최근 커밋들을 읽은 뒤, 발견 사항을 마크다운 파일이나 Linear 보드에 정리해 씁니다. 그중 실제로 할 만한 일감마다 루프는 개별 워크트리를 열고, 한 서브 에이전트를 보내서 수정안을 작성하게 합니다. 그리고 두 번째 서브 에이전트는 이 초안을 프로젝트 스킬과 기존 테스트에 비추어 검토합니다.

커넥터들은 루프가 직접 PR을 열고 티켓을 업데이트할 수 있게 해 줍니다. 루프가 처리하지 못한 일들은 제 티어링 인박스로 떨어집니다. 상태 파일(state file)은 이 모든 것의 척추 역할을 하며, 무엇을 시도했고, 무엇이 통과했고, 무엇이 아직 열려 있는지를 기억합니다. 그래서 다음 날 아침 실행(run)은 오늘 멈춘 지점에서 그대로 이어서 시작할 수 있게 됩니다.

여기서 실제로 당신이 한 일을 잘 들여다보십시오. 당신은 이걸 한 번 설계했습니다. 그 뒤로는 어떤 단계도 “프롬프트를 직접 치면서” 진행하지 않았습니다. 이것이 Steinberger가 말한 주장 전체의 실체화입니다. 그리고 Codex든 Claude Code든, 구성 요소가 같기 때문에 본질적으로 같은 루프를 만들 수 있습니다.

루프가 여전히 대신해 주지 않는 일들

루프는 일의 양상을 바꾸긴 하지만, 당신을 쓸모없게 만들지는 않습니다. 게다가 루프가 좋아질수록, 더 쉬워지는 대신 오히려 더 날카로워지는 문제들이 세 가지 있습니다.

검증(Verification)은 여전히 당신 몫입니다. 사람이 보지 않는 동안 돌아가는 루프는, 사람이 보지 않는 동안 실수도 저지르는 루프입니다. 만들기용 서브 에이전트와 검증용 서브 에이전트를 분리하는 이유 전체가, 루프가 말하는 “끝났습니다(done)”라는 말에 무게를 실어 주기 위해서입니다. 그래도 여전히 “끝났다”는 건 주장(claim)일 뿐, 증명(proof)은 아닙니다. 저는 code review in the age of AI에서 같은 문장을 반복해서 말하곤 했습니다. “당신의 일은, 실제로 동작함을 확인한 코드를 배송하는 것이다.”

이해도(understanding)는, 방치하면 계속 무너집니다. 루프가 당신이 직접 쓰지 않은 코드를 빨리 배송할수록, 존재하는 시스템과 당신이 실제로 이해하고 있는 것 사이의 간극은 더 벌어집니다. 이것이 comprehension debt입니다. 매끄럽게 돌아가는 루프는, 당신이 루프가 만든 코드를 제대로 읽어 보지 않는 한, 이 부채를 더 빨리 키우는 역할을 할 뿐입니다.

그리고 편안한 자세가 가장 위험한 자세입니다. 루프가 혼자 잘 돌아가기 시작하면, 의견을 갖는 일을 포기하고 그저 결과를 받아들이고 싶은 유혹이 커집니다. 저는 이것을 cognitive surrender라고 불렀습니다. 루프를 설계하는 행위 자체는, 판단력을 가지고 하면 해독제이지만, 생각을 피하기 위해 하면 오히려 불을 붙이는 가속제입니다. 같은 행동이지만, 전혀 반대의 결과를 가져올 수 있습니다.

루프를 만들되, 엔지니어로 남아 있기

저는 이것이 앞으로 우리의 일이 어떻게 진화할지 보여 주는 예고편 같은 것이라고 생각합니다. 그렇다고 해서 제가 직접 코드를 리뷰하지 않거나, 완전히 자동화된 루프에만 코드를 고치는 일을 맡긴다면, 제 제품의 품질은 분명 떨어질 것입니다. 아마 시간이 지날수록 점점 더 깊은 구덩이를 파고 들어가는 악순환에 빠지게 될 겁니다.

그러니 루프를 마음껏 구성해 보되, 에이전트에게 직접 프롬프트를 던지는 방식 또한 여전히 유효하다는 점을 잊지 말아야 합니다. 결국 중요한 건 둘 사이의 균형입니다.

또 루프는, 사용하는 사람에 따라 전혀 다른 결과를 만들어 냅니다. 두 사람이 완전히 동일한 루프를 만들었더라도, 결과가 완전히 반대가 될 수 있습니다. 한 사람은 자신이 깊이 이해하고 있는 일을 더 빨리 진행하기 위해 루프를 씁니다. 다른 사람은 그 일을 이해하지 않기 위해 루프를 씁니다. 루프는 이 둘을 구분하지 못합니다. 하지만 당신은 압니다.

이 점 때문에 루프 설계는 프롬프트 엔지니어링보다 더 어렵지, 더 쉬워진 것이 아닙니다. Cherny가 말하려는 요점은 일이 쉬워졌다는 게 아니라, 지렛대(leverage)가 걸리는 지점이 옮겨졌다는 것입니다.

루프를 만드십시오. 다만, 단지 ‘시작 버튼을 누르는 사람’이 아니라, 끝까지 엔지니어로 남을 사람이라는 태도로 루프를 설계하십시오.

Edit this page