에이전트를 리뷰할 수는 없습니다. 계획(Plan)을 리뷰해야 합니다.
요약
AI 에이전트가 IaC(Infrastructure as Code)를 다룰 때 발생하는 '리뷰된 계획'과 '실제 실행 계획' 사이의 불일치 문제를 다룹니다. 에이전트 자체를 리뷰하는 대신, 검토된 계획(plan)이 그대로 실행되도록 보장하는 하네스(Harness) 구조의 필요성을 강조합니다.
핵심 포인트
- 에이전트가 작성한 코드와 실제 실행되는 plan 사이의 간극이 위험 요소임
- 신뢰의 단위는 에이전트가 아닌, 검토된 특정 계획(plan)이어야 함
- 프롬프트 기반의 지침보다 CI 게이트를 통한 기술적 보증이 중요함
- AI 시대의 IaC는 기존 도구를 교체하는 것이 아니라 하네스로 감싸야 함
AI 시대의 Terraform을 위한 하네스 (Harness).
저는 현재 하나를 구축하고 있습니다. 한동안 저는 코드로서의 인프라 (Infrastructure-as-Code, IaC)를 위한 프라이빗 SDK 및 컴파일러로서의 하네스를 개발해 왔습니다. 이는 변경을 제안하는 주체(사람, 에이전트, CI)와 실제로 프로덕션에 도달하는 것 사이의 계층 역할을 합니다. 이 포스트는 도구에 관한 것이 아닙니다. 그 밑바탕에 깔린 사고방식, 그리고 이를 구축하면서 제가 가장 확신하게 된 몇 가지 요소들에 관한 것입니다. (작업 과정에서의 기록 — 현재까지 도달한 지점이며, 조언이 아닙니다.)
저를 이 길로 이끈 문제는 설명하기 쉽지만, 과소평가하기 쉬운 문제입니다.
최근에 그 문제의 한 사례가 발생했습니다. 한 에이전트가 Terraform을 수정했습니다. PR (Pull Request)은 깔끔해 보였습니다 — 정돈된 diff, 합리적인 리소스 이름, 그리고 제가 요청한 것과 정확히 일치해 보이는 plan 출력 결과까지. 승인이 났습니다. 그런데 적용 (apply) 시점에, 리뷰되었던 것과는 다른 plan이 실행되었습니다. apply 과정에서 그 사이 변경된 state (상태)를 기준으로 다시 plan이 생성되었고, 프로덕션에 영향을 미친 diff는 그 누구도 읽었던 diff와 완전히 달랐습니다.
그때는 아무것도 망가지지 않았습니다. 하지만 그 아찔했던 순간이 바로 이 하네스가 존재하는 이유 전체입니다.
왜냐하면 위험 요소는 결코 "에이전트가 잘못된 HCL을 작성한다"는 것이 아니었기 때문입니다. 에이전트는 완벽하게 좋은 HCL을 작성하며, 저 또한 그렇게 하도록 허용합니다. 진짜 위험은 _사람이 리뷰한 plan_과 실제로 실행되는 plan 사이의 거리입니다. 그리고 에이전트가 대량으로 변경 사항을 제안하게 되는 시점이 오면, 저는 그 거리를 확실히 좁히는 것을 가장 우선순위에 두고자 합니다.
현재 제가 내린 결론 (그리고 계속 수정해 나갈 것으로 예상되는 내용):
- AI 시대의 IaC (Infrastructure as Code)에 필요한 것은 단순히 _적용(apply)_할 수 있는 AI가 아닙니다. 인간이든 에이전트든 모든 변경 사항이 **동일한 경계(same boundary)**에서 평가되고, 오직 검토된 계획(plan)만이 배포되는 구조가 필요합니다.
- 신뢰의 단위는 에이전트가 아닙니다. 그것은 바이트 단위로 일치하도록 묶인, 특정하고 검토된 **계획(plan)**입니다.
- 에이전트를 리뷰할 수는 없습니다. 오직 계획(plan)만을 리뷰할 수 있습니다.
- 에이전트에 대한 지침(Instructions)은 깨질 수 있습니다. CI 게이트(gate)는 말로 설득해서 멈출 수 없습니다. 가이드는 프롬프트(prompt)에 넣고, 보증은 게이트(gate)에 넣으십시오.
- Terraform/OpenTofu는 사라지지 않습니다. 그것들을 교체하는 것이 아니라, 하네스(harness)로 감싸는 것입니다.
이제 당신의 리포지토리에는 인간이 아닌 작성자가 있습니다
수년 동안 IaC는 편안한 형태를 유지해 왔습니다. 인간이 HCL을 작성하고, 인간이 계획(plan)을 읽으며, 동일한 인간이나 팀원이 적용(apply)을 실행하는 방식이었습니다. 작성자와 적용자는 모두 느리고, 수가 적으며, 책임을 지는 존재인 '사람'이라는 동일한 종류의 존재였습니다. 작성자를 검토하는 것과 변경 사항을 검토하는 것은 거의 동일한 행위였습니다.
그 가정이 조용히 깨지고 있으며, 이는 키보드를 잡고 있는 주체가 누구냐에 따라 깨지고 있습니다. GitHub의 Copilot coding agent, OpenAI의 Codex, GitHub Actions에서의 Claude Code — 이제 에이전트들이 리포지토리 내에서 이슈를 생성하고, 브랜치를 푸시하며, 리뷰 코멘트에 답변합니다. 또한 MCP나 AGENTS.md와 같은 표준들은 에이전트들이 당신의 문서, 상태, 컨벤션(conventions)을 읽고 그에 따라 행동할 수 있도록 존재합니다. 중요한 세부 사항은 이러한 도구들 중 어느 하나가 아닙니다. 그 함의가 중요합니다. 즉, IaC 리포지토리는 더 이상 인간만이 편집하는 장소가 아니라는 점입니다. 그곳은 에이전트 또한 변경 사항을 제안하는(propose changes) 장소입니다.
그리고 여기서 여러분이 리뷰만으로는 해결할 수 없는 부분이 등장합니다. 여러분은 한 에이전트의 PR(Pull Request)을 주의 깊게 읽을 수는 있지만, _에이전트 그 자체_를 리뷰할 수는 없습니다. 에이전트는 분기마다 신뢰를 쌓아가는 동료가 아닙니다. 에이전트는 이전 작업이 건전했는지 여부와 상관없이, 다음 PR을 똑같이 자신만만하게 여는 하나의 프로세스입니다. 자신감은 평면적이지만, 정확성은 그렇지 않습니다.
리뷰됨(Reviewed)과 적용됨(Applied)은 서로 다른 두 가지 사실입니다
따라서 "에이전트가 Terraform을 작성하게 하라"는 해결책은 쉬운 절반만을 해결할 뿐입니다. 정말 중요한 나머지 절반은 이것입니다: 무엇이 변경 사항을 평가하며, 그것이 모두에게 동일한 것인가?
terraform plan은 원격 상태(remote state)를 읽고 여러분의 설정(config)에 대한 차이점(diff)을 생성합니다. 그 계획(plan)을 저장하고 적용(apply)하면, Terraform은 추가 프롬프트 없이 정확히 해당 작업들을 실행합니다. 하지만 계획을 저장하지 않고 적용 시점에 다시 계획(re-plan)을 세운다면, 그 간극이 다시 벌어집니다. 상태(state)가 드리프트(drift)되었을 수도 있고, 변수(variable)가 이동했을 수도 있으며, 실행되는 차이점(diff)은 더 이상 처음에 읽었던 차이점이 아니게 됩니다. 이것이 바로 발생할 뻔한 위험(near-miss)을 한 문장으로 요약한 것입니다.
그러므로 결합(binding)할 가치가 있는 것은 PR이 아니라 계획(plan)입니다. 이를 **계획 지문(plan fingerprint)**이라고 부릅시다. 이는 저장된 계획에 대한 식별자로서, 해당 계획이 검토된 결과(findings) 및 서명(signature)과 함께 결합됩니다. 이를 통해 적용(apply) 단계에서 지문이 승인 기록과 일치하지 않는 모든 것을 거부할 수 있습니다. 계획, 결과, 서명이라는 이 핵심 요소가 바로 제가 오늘 의지하고 있는 부분입니다. 제가 식별자에 포함시키고 싶은 범위는 더 넓습니다. 계획 JSON, 변수, 정책 번들(policy bundle), 워크스페이스(workspace), 프로바이더 락(provider lock), 그리고 툴체인(Terraform/OpenTofu 버전, 프로바이더 버전, 러너 이미지, 상태 버전) 등이 포함됩니다. 왜냐하면 계획은 그것을 생성한 환경만큼만 재현 가능하기 때문입니다. 저는 여전히 포함될 범위를 확장하고 있습니다. 솔직한 상태를 말씀드리자면, 견고한 핵심(core)이 있고 그 주변부(edge)가 계속 확장되고 있는 단계입니다.
초기에 언급해둘 주의 사항이 하나 있습니다. 지문(fingerprint)은 공개될 수 있지만, 그 뒤에 있는 아티팩트(artifact)는 아마도 공개되지 않아야 합니다. 저장된 계획과 계획 JSON에는 민감한 값(sensitive values)이 포함될 수 있으므로, 이를 PR 댓글에 붙여넣을 대상이 아닌 민감한 빌드 아티팩트(sensitive build artifacts)로 취급해야 합니다.
게이트(gate)가 단호하게 말할 수 있게 만들고 싶다는 사실은, 문단 형태보다는 다음과 같은 모습에 더 가깝습니다:
IAC.PLAN_DRIFT approval=#4821 severity=high quality=verified
claim: applied plan ≠ approved plan
cause: apply re-planned against state changed after approval
...
패턴 매칭(pattern-match)은 "이것은 검토된 계획이 아닐 수도 있으니, 다시 확인해 주세요"와 같이 모호하게 말할 수밖에 없습니다. 반면 지문 비교(fingerprint comparison)는 한 가지를 단호하게 말합니다. "이것은 당신이 승인한 계획이 아닙니다." 엄격한 검증이 오히려 더 짧은 과정입니다.
네 개의 박스로 이루어진 하네스 (harness)
저는 계속해서 똑같은 네 개의 박스를 그리고 있으며, 이제는 이것들이 부수적인 것이 아니라고 인정하기로 했습니다.
Author 무엇을 원하는가 / 왜 (인간과 에이전트가 작성함)
Compiler 어떻게 구현되는가 (현재의 HCL: Terraform / OpenTofu)
Observer 지금 무엇이 사실인가 (상태(state), 드리프트(drift), DB, 감사(audit), 비용, 이력))
...
- Author (작성자) — 의도(intent). 무엇을 원하는가, 그리고 왜 그러한가. 인간과 에이전트 모두 이곳에 자유롭게 작성합니다.
- Compiler (컴파일러) — 번역(translation). 의도를 당신이 실행할 엔진으로 변환합니다. 설계상 교체 가능하지만, 현재는 HCL 엔진(Terraform/OpenTofu)이 그 역할을 합니다.
- Observer (관찰자) — 현실(reality). 이에 대해서는 아래에서 더 자세히 다루겠습니다. 사람들이 가장 과소평가하여 구축하는 계층입니다.
- Evaluator (평가자) — 판단(judgment). "아니오"라고 말할 수 있는 유일한 계층입니다.
핵심적인 아이디어는 다음과 같습니다. Author와 Compiler는 원하는 만큼 똑똑하거나 혹은 엉성해도 상관없습니다. 그들이 생성한 그 어떤 것도 Evaluator를 통과하지 않고서는 프로덕션(production)에 도달할 수 없기 때문입니다. 당신은 안전의 기준을 "작성자를 신뢰하기"에서 "계획을 검증하기"로 옮기게 됩니다. 이것이 바로 작성자가 에이전트로 변하는 상황에서도 살아남을 수 있는 정확한 전략입니다. 제가 구축하고 있는 것의 대부분은 Compiler와 Evaluator 박스에 존재하며, 나머지 두 박스는 그것이 세상과 연결되는 지점입니다.
그리고 그 밑단에 있는 엔진들에 대해 명확히 말씀드리자면: Terraform, OpenTofu, 그리고 Pulumi는 진정으로 훌륭합니다. 저는 매일 이 도구들을 사용하며, 제가 만드는 하네스(harness)는 이들을 대체하는 것이 아니라 이들을 감싸는 형태입니다. 하지만 이들 중 그 어떤 것도 AI-first(AI 우선)로 설계되지 않았습니다. 각각은 여전히 인간 작성자(human author), 인간 검토자(human reviewer), 그리고 실행 프롬프트(apply prompt) 단계의 인간을 가정합니다. 이는 비판이라기보다는 발전의 여지(headroom)에 가깝습니다. 엔진들은 훌륭하며, 그 상위 계층 — 그리고 궁극적으로 엔진 자체 — 이 에이전트가 신뢰하고 구동할 수 있는 무언가로 진화할 수 있는 실제적인 여지가 여전히 남아 있습니다.
Observer는 모니터링을 하는 것이 아닙니다
제가 보기에 가장 많이 생략되는 계층은 Observer입니다. 보통은 "대시보드가 있다"는 식으로 뭉뚱그려지곤 합니다. 하지만 AI 시대의 하네스에서 Observer는 구체적인 역할을 수행합니다. 모든 행위자(actor) — 인간이든 에이전트든 — 가 세상에 대해 제안을 하기 전에, 근거가 확실한(grounded) 세계의 모습을 제공하는 것입니다. 클라우드를 인간만이 다룰 때도 드리프트(Drift)는 이미 문제였습니다. 에이전트와 외부 도구들이 API를 통해 무언가를 변경하기 시작하면, Observer가 볼 수 없는 대역 외 변경(out-of-band change)은 하네스 전체가 물려받게 되는 사각지대가 됩니다. (최근 연구에서는 대역 외 변경 사항을 다시 코드로 조정(reconciling)하는 데 LLM 에이전트를 투입하고 있는데, 이는 이 문제가 아직 해결되지 않았음을 말해줍니다.)
제가 Observer가 실제로 수집하기를 원하는 것들은 다음과 같습니다:
- Terraform/OpenTofu 상태(state) 및 클라우드 제공업체 인벤토리
- 드리프트 탐지(drift detection), 그리고 CloudTrail / 감사 로그(audit logs)
- DB 스키마 상태
- 비용 데이터 및 보안 태세(security posture)
- 정책 위반 이력 및 사고/변경 이력
이를 통해 에이전트는 단순히 "현재의 코드"뿐만 아니라 "실제 환경", "의도(intent)", 그리고 "지난번에 무엇이 잘못되었는지"를 참조하게 됩니다.
하나의 게이트, 측면 출입문은 없다
A 하네스는 측면 출입문(side door)이 없을 때에만 작동합니다. 노트북을 사용하는 인간, 리포지토리(repo) 내의 에이전트, 로컬 CLI, 그리고 CI 러너(runner) 모두에게 동일한 경계가 적용되어야 합니다.
구체적으로, 세 가지 원칙이 그 대부분을 담당합니다:
- 어디서나 하나의 정책 번들 (One policy bundle, everywhere). 노트북의
pre-commit에서 실행되는 규칙은 CI에서 계획(plan)을 검증하는 규칙과 동일한 규칙이자 동일한 버전이어야 합니다. 만약 로컬과 CI가 서로 다르게 동작한다면, 로컬 경로는 우회로(bypass)가 되어버립니다. - PR이 아닌 지문(fingerprint)을 승인하세요. 계획(plan)을 저장하고, 해시(hash)화하십시오. 그리고
apply단계에서는 서명된 승인을 포함한 지문을 가진 계획_만_ 소비하도록 하십시오. PR은 승인된 이후에 그 내부의 계획이 변경될 수 있지만, 지문은 그럴 수 없습니다. - 계획(plan) 역할과 적용(apply) 역할을 분리하세요. 계획을 제안하는 작업과, 승인된 지문 외에는 아무것도 수용하지 않는 적용 작업을 분리해야 합니다. HCP Terraform은 이미 이 방향을 지향하고 있습니다. 실행 단계(run stages), 정책 검사(policy checks), 작업 실행(run tasks), 그리고 분리된 계획/적용 권한(plan/apply permissions) 등이 그러합니다. 이 프레임워크(harness)는 새로운 기본 요소(primitives)를 만드는 것이 아니라, 그 요소들을 우회하는 것을 거부하는 것입니다.
이전 — 가공되지 않은 AI x Terraform
AI Agent --HCL--> terraform apply --> production
...
프롬프트 안의 규칙은 단지 바람(wish)일 뿐입니다
제가 가장 피하고 싶은 실패 모드는 문장 속에 존재하는 안전성입니다.
AGENTS.md에 적힌 "공개 S3 버킷을 생성하지 마세요"는 하나의 바람입니다. 반면, OPA가 계획(plan) JSON을 CI를 실패시키는 결정으로 변환하는 것은 사실(fact)입니다. 의도는 같지만, 지탱할 수 있는 하중(load-bearing capacity)은 완전히 다릅니다. AGENTS.md와 MCP는 작성자(Author) 및 컴파일러(Compiler) 계층을 더 강력하게 만들지만(더 나은 컨텍스트, 더 적은 멍청한 차이점), 그 무엇도 안전하게 만들지는 않습니다. 그것들은 단지 결과물이 좋을 가능성을 높일 뿐이며, 이는 안전하다는 주장과는 다릅니다. 지침(Instructions)은 프롬프트 인젝션(prompt injection), 혼란스러운 도구 호출(tool call), 혹은 그냥... 작동하지 않는 모델에 의해 깨질 수 있습니다. 따라서 가이드는 AGENTS.md에 두고, 보증(guarantee)은 평가자(Evaluator)에 두어야 합니다.
제가 활용하는 평가자(Evaluator) 규칙은 다음과 같습니다:
delete/replace/drop을 포함하는 차이점(diffs) → 자동 적용 금지- 퍼블릭 인그레스(public ingress) → 명시적이고 기록된 예외 사항이 없으면 거부
- 높은 DB 민감도(high DB sensitivity) → 암호화, 감사 로그(audit log), 백업, 프라이빗 네트워크가 필요하며, 그렇지 않으면 계획(plan) 실패
- 계획 지문(plan fingerprint) ≠ 승인 기록(approval record) → 적용 거부
- 누락된 / 만료된 / 권한이 부족한 승인 → 적용 거부
제가 원하지만 아직 출시하지 못한 규칙 하나는, 사람이 작성한 변경 사항보다 에이전트가 작성한 변경 사항에 대해 더 엄격한 정책 프로필(policy profile)을 적용하는 것입니다. 즉, 느낌(vibe)에 따라 누군가를 덜 신뢰하는 것이 아니라, 작성자 유형(author type)에 따라 기계적으로 라우팅하는 것입니다.
그리고 에이전트가 CI 내부에 자리 잡게 되면 공격 표면(attack surface)이 확장됩니다 (OWASP는 정확히 이 문제를 위해 Agentic-AI 위험 목록을 유지하고 있습니다). 제가 출시할 때 반드시 지키는 최소한의 네 가지 원칙은 다음과 같습니다:
- 읽기 전용 도구와 변경(mutating) 도구를 분리하십시오. 에이전트가 제공업체 문서와 상태(state)를 읽을 수 있게 하되, 적용(apply)급 작업을 직접 넘겨주지 마십시오.
- 계획(plan)과 적용(apply)을 분리하십시오. 에이전트는 계획 단계까지만 제안합니다. 적용은 서명된 승인과 CI 게이트(gate)를 거쳐야 합니다.
- CI 작업 권한을 분리하십시오. 계획 작업(plan jobs)은 계획 역할(plan role)을 부여받고, 적용 작업(apply jobs)은 적용 역할(apply role)을 부여받으며 승인된 계획 외에는 아무것도 수락하지 않습니다.
- 정책을 프롬프트가 아닌 코드에 넣으십시오. 퍼블릭 S3를 사용하지 말라고 _요청_하지 마십시오. 계획에서 이를 탐지하고 실패 처리하십시오.
핵심 관통선(through-line): 에이전트를 신뢰할지 말지를 묻는 것을 멈추고, 대신 그 출력을 기계적으로 평가 가능하게 만드십시오.
destroy는 인프라의 DROP COLUMN입니다
이 지점에서 이 글은 일반적인 "AI + Terraform" 포스트를 벗어납니다. 왜냐하면 데이터베이스 전문가들이 이미 이 문제의 한 버전을 해결했기 때문입니다.
DROP COLUMN은 파괴적인 변경(destructive change)입니다. 되돌릴 수 없고(irreversible), 하위 호환성이 없으며(backward-incompatible), 한 번에 배포해서는 안 되는 바로 그런 작업입니다. Atlas는 이러한 파괴적인 변경을 감지하여 CI(지속적 통합)를 실패 처리합니다. 확장-축소 패턴(expand-contract pattern) — 즉, 새로운 형태를 추가하고, 마이그레이션(migrate)한 다음, 그제서야 별도로 기존 것을 제거하는 방식 — 은 위험한 부분이 안전한 부분과 함께 움직이지 않도록 하기 위해 존재합니다. Bytebase와 같은 도구들은 누군가가 프로덕션(prod) 환경에 직접 접근하게 두는 대신, 이 모든 과정을 리뷰(review), 승인(approval), 롤아웃(rollout), 그리고 감사(audit)의 과정으로 감싸줍니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기