모델이 전부가 아니었다 — Coding Agent를 진짜로 작동시키는 것

8년간 만들지 못한 소프트웨어를 AI와 함께 3개월 만에 출시한 개발자가 있다. 같은 LLM을 쓰는데, 어떤 환경에서는 결과물이 나오고 어떤 환경에서는 나오지 않았다. 차이는 모델이 아니었다.


2026년 3월, Google의 PerfettoSQL 메인테이너인 Lalit Maganti가 syntaqlite를 출시했다. SQLite의 400개 문법 규칙을 파싱하는 완전한 개발 도구 — parser, formatter, LSP, VS Code extension, Python/JavaScript 바인딩, 웹 플레이그라운드까지. 약 250시간, 3개월의 작업이었다. 혼자서.

이 프로젝트를 그는 8년간 하고 싶었지만 하지 못했다. SQL을 “SQLite가 실제로 파싱하는 것과 정확히 같은 방식으로” 파싱해야 한다는 요건이 있었고, 이를 위해서는 SQLite의 밀도 높은 C 소스코드에서 문법 규칙을 하나하나 추출해야 했다. 어렵고 지루한 작업이 동시에 필요한, 영원한 사이드 프로젝트 후보였다. AI가 이것을 바꿨다. Aider로 시작해서, Roo Code를 거쳐, Claude Code로 정착했다.

그런데 흥미로운 건 여기서부터다. 첫 한 달간 그는 “vibe coding”을 했다. 거의 모든 설계와 구현을 Claude에게 위임했다. 500개 이상의 테스트를 생성했고, 접근 방식이 실현 가능하다는 것을 증명했다. 그러나 만들어진 것은 유지보수 불가능한 스파게티 코드였다. 그는 전부 버리고 처음부터 다시 시작했다. 같은 모델이었다. 같은 AI였다. 그런데 두 번째에는 결과가 달랐다. 왜?

이 질문에 대한 구조적 답변이 같은 주, Sebastian Raschka의 Components of a Coding Agent에서 제시되었다. Claude Code나 Codex CLI 같은 coding agent가 실제로 어떻게 구성되어 있는지를 분해한 이 글은, 하나의 핵심 통찰로 수렴한다 — “겉보기에 ‘모델의 품질’인 것의 상당 부분은 실제로는 ‘맥락의 품질’이다.”

모델은 엔진이다. 하지만 엔진만으로는 자동차가 달리지 않는다. 엔진을 감싸는 구조가 실제 성능을 결정한다. Raschka는 이 구조를 Agent Harness라고 부른다.

Harness라는 단어는 원래 “마구(馬具)“를 뜻한다. 말의 힘을 마차나 쟁기에 전달하기 위해 말의 몸에 씌우는 장치다. 말 자체의 능력을 바꾸지는 않지만, 그 힘이 어디로 향하는지, 어떤 방식으로 전달되는지를 결정한다. Harness가 없는 말은 그냥 들판을 뛰어다닌다. 힘은 있지만 방향이 없다. Harness가 있는 말은 밭을 갈고, 짐을 나르고, 사람을 태운다. 같은 힘이 구조를 통해 목적을 갖게 된다.

소프트웨어 엔지니어링에서도 이 단어는 비슷한 맥락으로 쓰인다. Test harness는 테스트를 실행하고 결과를 수집하는 프레임워크다. 테스트 코드 자체의 품질을 바꾸지는 않지만, 테스트가 올바른 환경에서 올바른 순서로 실행되고, 결과가 체계적으로 보고되도록 보장한다. Agent Harness도 같은 논리다. LLM이라는 강력하지만 방향 없는 엔진을 감싸서, 맥락을 수집하고, 도구를 검증하고, 기억을 관리하고, 행동의 범위를 제한하는 소프트웨어 계층이다. 모델을 더 똑똑하게 만드는 것이 아니라, 모델의 똑똑함이 올바른 방향으로 발휘되도록 구조화하는 것이다.

Maganti의 첫 번째 시도가 실패하고 두 번째가 성공한 이유도, 모델이 아니라 그 모델을 어떤 구조로 사용했느냐의 차이였다.


1. Coding Agent의 해부 — 엔진 밖의 여섯 가지

Raschka는 Coding Agent를 구성하는 여섯 가지 핵심 요소를 식별한다. 이것들은 모델이 아니다. 모델을 감싸는 소프트웨어 계층이다. 하나씩 뜯어보자.

첫째, Live Repo Context. 에이전트가 사용자의 요청을 처리하기 전에, 현재 작업 환경의 “안정적 사실들”을 수집한다. Git 브랜치 상태, 레포지토리 구조, 프로젝트 문서(README, AGENTS.md), 진행 중인 변경사항. “테스트를 고쳐줘”라는 요청을 처리하려면, 어떤 테스트 프레임워크를 쓰는지, 테스트 명령어가 무엇인지부터 알아야 한다. 이 맥락이 없으면 모델은 진공 상태에서 추측한다.

둘째, Prompt Shape와 Cache Reuse. 매 상호작용마다 프롬프트를 처음부터 재구성하는 것이 아니라, 안정적 접두사(일반 지침, 도구 정의, 작업 환경 요약)와 동적 요소(최근 대화, 사용자 요청)를 분리한다. 이 아키텍처가 prompt caching을 가능하게 하고, 불필요한 재처리를 방지한다.

셋째, Tool Access와 Validation. 모델이 임의의 명령을 생성하는 대신, 구조화된 도구 정의를 통해 행동한다. 모델이 도구 호출을 지시하면, harness가 도구 인식 여부, 인자 유효성, 권한 요건, 파일 경로 범위를 검증한다. 자유도를 줄이는 것이 역설적으로 신뢰성을 높인다.

넷째, Context Bloat 최소화. 긴 맥락은 비용이 크고 노이즈를 유발한다. Harness는 두 가지 전략으로 이에 대응한다. Clipping은 장황한 도구 출력을 잘라내고, Transcript Reduction은 전체 세션 이력을 요약본으로 압축하되 최근 이벤트는 더 풍부하게 유지한다. 같은 파일을 반복 읽는 것도 중복 제거된다.

다섯째, Structured Session Memory. 에이전트는 두 겹의 저장소를 유지한다. Working Memory는 현재 중요한 정보의 정제된 요약이다. Full Transcript는 모든 요청, 도구 출력, 응답의 완전한 기록이다. 세션을 닫았다 다시 열어도 작업을 이어갈 수 있는 이유가 여기에 있다.

여섯째, Bounded Subagents. 병렬 처리나 부수 작업을 위해 하위 에이전트를 생성하되, 주 에이전트보다 더 제한된 권한으로 운영한다. 읽기 전용 작업만 허용하거나, 재귀 깊이를 제한하거나, 작업 범위를 명시적으로 한정한다.

여섯 가지 요소의 공통점이 보이는가. 모델의 추론 능력을 향상시키는 것이 하나도 없다. 전부 모델이 추론할 수 있는 조건을 정비하는 것이다. Raschka의 비유를 빌리면, LLM은 엔진이고, reasoning model은 강화된 엔진이며, Agent Harness는 그 엔진을 효과적으로 사용하게 해주는 것이다. 같은 엔진도 어떤 차체에 올리느냐에 따라 경주차가 되기도 하고 고장난 수레가 되기도 한다.


2. 한 개발자의 실험 — Harness가 실제로 만든 차이

Raschka의 프레임워크는 이론적으로 깔끔하다. 그렇다면 실전에서는 어떤가? Maganti의 3개월은 이 여섯 가지 요소가 실제로 어디서 작동하고 어디서 한계에 부딪히는지를 정밀하게 보여주는 필드 리포트다.

Harness가 빛난 영역: 관성의 돌파.

8년간 프로젝트가 시작되지 못한 이유는 기술력 부족이 아니었다. Maganti는 Google에서 PerfettoSQL을 유지보수하는 전문가다. 문제는 “어렵고 동시에 지루한” 작업의 조합이었다. SQLite의 400개 문법 규칙을 C 소스코드에서 추출하는 일은, 고도의 집중력이 필요하면서도 반복적이다. 인간의 동기 부여 구조가 처리하기 어려운 조합이다.

AI가 이 관성을 돌파한 방식은 흥미롭다. 추상적 불확실성을 구체적 프로토타입으로 바꿔준 것이다. “이 접근이 될까?”라는 막연한 질문이, “이 코드가 이 테스트를 통과하나?”라는 검증 가능한 질문으로 전환되었다. Harness의 관점에서 보면, Live Repo Context가 현재 코드베이스 상태를 파악하고, Tool Access가 테스트 실행을 자동화하고, Session Memory가 이전 시도의 결과를 보존한 것이다. 모델이 “더 똑똑해서”가 아니라, 모델이 작업할 수 있는 맥락이 구조적으로 갖춰져 있었기 때문이다.

Harness가 빛난 영역: 도메인 격차의 압축.

Maganti가 syntaqlite를 위해 배워야 했던 것들이 있다. Wadler-Lindig pretty-printing 알고리즘, VS Code extension API, Rust 도구 체인, WASM 빌드 파이프라인, PyPI/crates.io 패키징. 각각을 전통적 방식으로 학습하면 며칠씩 걸렸을 것이다. AI는 이것을 집중된 대화 몇 번으로 압축했다. 그가 “teaching assistant” 역할이라고 표현한 것이 이것이다.

Harness 프레임워크로 해석하면, 이것은 Prompt Shape와 Context Management의 영역이다. 사용자가 이미 아는 것(PerfettoSQL, 파서 설계)과 모르는 것(Rust 빌드 시스템)의 경계를 harness가 효과적으로 관리한다. 안정적 맥락(사용자의 전문 영역)은 프롬프트 접두사에 놓고, 동적 맥락(새로 배우는 내용)을 효율적으로 주입하는 구조가 학습 속도를 높인 것이다.

Harness가 빛난 영역: “경제적으로 정당화할 수 없던 것”의 구현.

Maganti의 가장 솔직한 고백 중 하나다. AI 없이는 에디터 확장, 웹 플레이그라운드, 다중 언어 바인딩 같은 기능은 만들지 않았을 것이라고 했다. 핵심 파서만 만들고 나머지는 생략했을 것이다. ROI가 나오지 않으니까. AI가 구현 비용을 극적으로 낮추면서, 이전에는 비용 대비 효과가 맞지 않던 기능들이 경제적으로 실현 가능해졌다. 제품의 완성도가 올라간 것은 모델이 더 똑똑해서가 아니라, Subagent를 통한 병렬 작업 분배와 Session Memory를 통한 맥락 보존이 “한 사람이 여러 영역을 동시에 진행하는” 것을 가능하게 했기 때문이다.


3. Harness가 해결하지 못한 것 — 설계는 다른 문제다

여기까지 읽으면 “그럼 Harness가 만능인가?”라는 질문이 떠오를 것이다. 답은 명확하게 아니오다. Maganti의 경험은 Harness의 한계를 Harness의 가능성만큼이나 선명하게 드러낸다.

설계 결정에서의 실패. Maganti는 공개 API 설계에서 AI가 “total mess”를 만들었다고 말한다. 이것은 모델의 능력 부족일까? 아니다. 구조적 한계다. Raschka의 여섯 가지 요소를 다시 보자. Live Repo Context는 현재 코드의 상태를 알려준다. Tool Access는 테스트를 돌려준다. Verification은 빌드가 통과하는지 확인해준다. 그런데 “이 API가 사용자에게 좋은 경험을 제공하는가?”를 검증할 도구는 없다. Harness의 여섯 가지 요소 중 어떤 것도 이 질문에 답하지 못한다.

구현(implementation)과 설계(design)의 차이는 여기에 있다. 구현에는 “정답”이 존재한다. 코드가 컴파일되거나 안 되거나, 테스트가 통과하거나 실패하거나. 이 이진적 검증 가능성이 Harness의 Tool Access와 Verification을 강력하게 만든다. 모델이 잘못된 코드를 생성해도, 테스트가 실패하면 피드백 루프가 작동한다.

설계에는 이런 검증 메커니즘이 없다. “이 API가 6개월 후에도 사용하기 편한가?”는 테스트로 확인할 수 없다. “이 추상화 수준이 적절한가?”는 컴파일러가 판단하지 못한다. Maganti의 표현을 빌리면, “구현에는 정답이 있다. 설계에는 없다.” Harness는 정답이 있는 영역에서 강력하고, 정답이 없는 영역에서 무력하다.

코드베이스 이해의 상실. 더 미묘한 문제도 있었다. AI가 대량의 코드를 생성하면서, Maganti는 여러 차례 자기 코드베이스에 대한 감각을 잃었다. 아키텍처는 알고 있었지만, 일상적 세부사항 — 어떤 함수가 어디에 있는지, 컴포넌트 간 연결이 어떻게 되어 있는지 — 을 놓쳤다. 이것은 Harness의 Session Memory가 해결하지 못하는 문제다. Harness는 에이전트의 맥락을 보존하지만, 인간 개발자의 멘탈 모델까지 보존하지는 못한다.

결과적으로 프롬프트가 점점 장황해지고 모호해졌다. 자기가 원하는 것을 정확히 표현하지 못하니까 더 많은 단어를 쓰게 되고, 더 많은 단어가 더 모호한 출력을 만들고, 모호한 출력이 코드베이스 이해를 더 저하시키는 악순환이었다. AI와 함께 일할 때의 역설적 위험 — 생산성이 올라갈수록 인간의 이해가 내려가는 — 이 여기에 있다.

시간 감각의 부재. Maganti가 지적한 또 하나의 한계는, AI에게 “temporal awareness”가 없다는 것이다. 이전에 왜 어떤 결정을 내렸고 나중에 왜 번복했는지를 모른다. 숙련된 시니어 엔지니어가 조직을 떠날 때 사라지는 것과 비슷하다 — 기술이 아니라 맥락이 사라진다. Harness의 Session Memory는 현재 세션의 맥락을 보존하지만, 수개월에 걸친 설계 결정의 이력과 그 이유까지 보존하지는 못한다.


4. 두 번의 빌드가 증명한 것

Maganti의 경험에서 가장 핵심적인 대목은 두 번의 빌드 사이에 있다.

첫 번째 빌드(2025년 12월~2026년 1월): 한 달간 vibe coding. AI에게 설계와 구현 모두를 위임. 500개 이상의 테스트를 생성. 접근 방식이 실현 가능하다는 것을 증명. 하지만 결과물은 유지보수 불가능한 스파게티 코드. 전부 폐기.

두 번째 빌드(2026년 2월~3월): Rust로 처음부터 재작성. 인간이 설계를 주도하고, AI를 “autocomplete on steroids”로 사용. 상시적 리팩토링. 모든 생성 코드를 즉시 리뷰. 6주 만에 핵심 기능 완성, 이후 패키징과 문서화를 거쳐 출시.

같은 사람이, 같은 모델로, 같은 프로젝트를 두 번 만들었다. 차이는 무엇이었나?

첫 번째에서는 인간이 Harness의 역할을 하지 않았다. 맥락 관리, 도구 검증, 품질 게이트 — 이 모든 것을 모델에 맡겼다. 모델은 주어진 맥락 안에서 그럴듯한 코드를 대량 생산했지만, 장기적 일관성을 유지할 구조가 없었다.

두 번째에서는 인간이 Harness가 되었다. 설계 결정을 내리고(Brainstorming), 생성된 코드를 즉시 검토하고(Verification), 맥락이 흐트러지면 리팩토링으로 복구하고(Context Management), AI의 작업 범위를 명시적으로 제한했다(Bounded Subagent의 인간 버전).

Raschka의 프레임워크와 Maganti의 경험을 겹쳐놓으면, 하나의 원칙이 드러난다. Harness는 존재해야 한다. 소프트웨어로 구현되든, 인간의 규율로 작동하든. 모델만 놓고 “좋은 결과를 내라”는 것은, 엔진만 놓고 “달려라”라고 말하는 것과 같다.

Maganti 자신이 이것을 한 문장으로 요약했다. “AI는 구현에 있어 믿을 수 없는 force multiplier다. 하지만 설계의 대체재로서는 위험하다.” 이 문장은 정확하다. 그리고 이 문장이 정확한 이유를 Raschka의 프레임워크가 구조적으로 설명한다 — Harness의 여섯 가지 요소가 모두 구현 영역에서의 맥락 관리에 최적화되어 있고, 설계 영역에서의 판단은 다루지 않기 때문이다.


5. 리팩토링은 선택이 아니라 생존 조건이다

Maganti가 두 번째 빌드에서 발견한 것 중 하나는, AI 코드 생성 시대에 리팩토링의 의미가 근본적으로 달라졌다는 것이다. 그는 이렇게 표현했다. “AI로 산업적 규모의 코드를 생성하고 있다면, 끊임없이 리팩토링해야 한다(you have to refactor constantly).”

왜 그런가? 전통적 개발에서 리팩토링은 “코드가 충분히 쌓인 후” 하는 정리 작업이었다. 기술 부채가 임계점에 도달하면 한번 크게 치우는 것. 하지만 AI가 코드를 생성하는 속도에서는, 기술 부채가 쌓이는 속도도 산업적이다. 한 시간에 수백 줄이 생성된다. 그중에는 모델이 “그럴듯하지만 최적이 아닌” 패턴으로 작성한 것들이 섞여 있다. 하루만 방치하면 그 위에 다른 코드가 올라가고, 일주일 후에는 뿌리를 뽑기 어려운 구조적 문제가 된다.

Maganti의 첫 번째 빌드가 정확히 이 궤적을 따랐다. AI가 빠르게 코드를 생성했고, 500개 이상의 테스트가 통과했고, 모든 것이 “작동”했다. 하지만 한 달 후 그가 마주한 것은 자신조차 이해할 수 없는 코드베이스였다. 테스트가 통과한다는 사실이 구조적 건강성을 보장하지 않았다. 그는 이것을 “false reassurance”라고 불렀다 — 테스트가 주는 거짓된 안심감.

Harness 관점에서 이것은 Context Bloat 문제의 변형이다. Raschka가 설명한 Context Bloat 최소화 전략은 프롬프트 수준에서의 노이즈 관리다. 하지만 코드베이스 자체의 bloat — 불필요한 추상화, 일관성 없는 패턴, 모델이 만들어낸 “합리적이지만 중복된” 구조 — 는 프롬프트 관리로 해결되지 않는다. 이것은 인간이 개입해야 하는 영역이다.

두 번째 빌드에서 Maganti가 한 것은, 리팩토링을 작업 흐름의 핵심 루프에 넣은 것이다. 코드 생성 → 즉시 리뷰 → 리팩토링 → 다음 생성. 사후 정리가 아니라 동시 진행. 이것이 작동한 이유는 AI가 리팩토링 비용도 극적으로 낮추었기 때문이다. “잘못된 접근의 비용이 극적으로 줄어든다. 빠르게 구조를 바꿀 수 있으니까.” 그의 표현이다. 역설적으로, AI가 코드를 빠르게 만들어서 리팩토링이 필수가 되었는데, AI가 리팩토링도 빠르게 해주기 때문에 지속 가능하다. AI가 만든 문제를 AI가 해결하되, 인간이 “언제 리팩토링할지”를 판단하는 구조.

이 패턴에서 Harness가 해야 할 역할이 하나 더 보인다 — Raschka의 여섯 가지에는 명시적으로 포함되지 않은 것이지만, “코드베이스의 구조적 건강성을 모니터링하고, 리팩토링 시점을 제안하는” 기능이다. 복잡도가 임계치를 넘거나, 일관성 없는 패턴이 반복되거나, 중복 코드가 누적될 때 에이전트가 “지금 정리할 때입니다”라고 알려주는 것. 현재의 coding agent 대부분은 이 기능을 갖추고 있지 않다. 사용자가 요청하면 리팩토링을 하지만, 자발적으로 리팩토링의 필요성을 감지하지는 못한다.


6. 중독의 구조 — Harness가 보호해야 할 것

Maganti의 회고에서 가장 솔직하고, 가장 덜 논의되는 부분이 있다. AI 코딩의 “addiction-like” 패턴이다.

프롬프트를 입력하고 결과를 기다리는 과정이 슬롯머신과 비슷한 구조를 띤다는 것이다. 결과가 훌륭할 때도 있고 쓸모없을 때도 있다. 이 불확실성이 “한 번만 더” 하게 만든다. 특히 피로할 때 이 패턴이 강화된다. 수면 부족 → 모호한 프롬프트 → 나쁜 결과 → 좌절 → “한 번만 더” → 더 깊은 피로. Maganti는 이것을 자기 경험으로 고백했다.

이것이 Harness 설계와 무슨 관련인가? 현재의 Harness 아키텍처는 에이전트의 행동을 구조화하는 데 집중하지만, 인간 사용자의 행동에는 개입하지 않는다. Raschka의 여섯 가지 요소 어디에도 “사용자의 작업 패턴을 모니터링하고, 비생산적 루프를 감지한다”는 항목은 없다.

이전 에세이에서 다룬 anti-sycophancy의 관점에서 보면, 이것은 같은 문제의 다른 면이다. Sycophancy는 모델이 사용자의 의견에 맹목적으로 동의하는 것이다. 피로한 사용자가 모호한 프롬프트를 입력했을 때, 에이전트가 “이 프롬프트는 너무 모호합니다. 구체적으로 무엇을 원하시나요?”라고 되물어야 하는가, 아니면 “알겠습니다”라고 실행해야 하는가? RLHF 최적화된 모델은 후자를 선택할 가능성이 높다. 사용자의 즉각적 요청에 동의하는 것이 reward를 높이기 때문이다. 하지만 피로한 상태에서의 모호한 요청을 그대로 실행하는 것은, 장기적으로 코드베이스의 품질과 사용자의 건강 모두를 훼손한다.

Agent Harness의 다음 진화가 여기에 있을지 모른다. 코드의 맥락뿐 아니라 사용자의 맥락까지 관리하는 것. 오전 10시의 날카로운 프롬프트와 새벽 2시의 흐릿한 프롬프트를 구분할 수 있는 harness. “지금 프롬프트 품질이 떨어지고 있습니다. 잠시 쉬는 건 어떨까요?”라고 말할 수 있는 에이전트. 이것은 기능의 문제가 아니라, 에이전트가 사용자와 맺는 관계의 문제다.


7. 질문들

Maganti의 3개월과 Raschka의 프레임워크가 합쳐서 던지는 질문은 하나다. 당신은 Harness를 가지고 있는가?

이 질문에는 층위가 있다.

첫째, 당신의 AI 도구에 소프트웨어적 Harness가 있는가? Live Repo Context가 맥락을 수집하고, Tool Validation이 행동을 검증하고, Session Memory가 연속성을 보장하는 구조가 있는가? 채팅창에 프롬프트를 넣고 결과를 받는 것은 Harness가 없는 상태다. Coding agent를 쓰고 있다면 이미 어느 정도의 harness는 있지만, 그것이 얼마나 정교한지는 도구마다 다르다.

둘째, 인간 차원의 Harness — 당신 자신의 규율 — 가 있는가? Maganti의 첫 번째 빌드가 실패한 이유는 도구의 부재가 아니라 규율의 부재였다. 생성된 코드를 즉시 리뷰하는가? 리팩토링을 작업 루프에 포함하고 있는가? 설계 결정을 AI에 위임하지 않고 직접 내리고 있는가? 코드베이스에 대한 멘탈 모델을 유지하고 있는가?

셋째, Harness의 한계를 인식하고 있는가? Harness는 구현 영역에서 강력하고, 설계 영역에서 무력하다. 이 경계를 알고 있으면 각 영역에 적합한 전략을 쓸 수 있다. 모르면 Maganti의 첫 번째 빌드를 반복한다.

Raschka의 논문이 보여주는 것은 구조다. Maganti의 경험이 보여주는 것은 그 구조의 실전 적용과 한계다. 둘을 합치면 이런 그림이 그려진다 — Coding Agent의 성능은 모델의 함수가 아니라 모델 × Harness × 인간 규율의 함수다. 어느 하나가 0이면 전체가 0이다. 가장 강력한 모델도 Harness 없이는 스파게티 코드를 만들고, 가장 정교한 Harness도 인간의 설계 판단 없이는 “작동하지만 유지보수 불가능한” 결과물을 낸다.

8년간 만들지 못한 것을 3개월 만에 만든 것은 모델의 힘이었다. 첫 한 달의 결과를 전부 버리고, 두 번째 시도에서 성공한 것은 Harness의 힘이었다. 그리고 그 Harness는 소프트웨어에 절반, 인간에게 절반이 있었다.


참고문헌