테스트
참고한 아티클다가오는 루프[AX 공식] '다가오는 루프'를 지배하는 법: 에이전트 자동화 속에서 코드 아키텍처 지키기
인공지능(AI)이 스스로 코드를 짜고, 테스트를 돌리고, 에러를 수정하는 '코딩 에이전트'의 시대가 열렸습니다. 최근에는 에이전트 내부의 작동을 넘어, 시스템 외부에서 작업 큐를 관리하고 성공할 때까지 무한히 실행을 반복하는 '하네스 레벨 루프(Harness-level loop)'가 새로운 트렌드로 자리 잡고 있죠.
하지만 이 강력한 루프를 그대로 방치하면 치명적인 부작용을 마주하게 됩니다. AI가 에러를 해결하기 위해 코드 곳곳에 임시방편적인 예외 처리(fallback)와 땜질식 패치를 덕지덕지 붙여놓기 때문입니다. 결국 '테스트는 통과하지만, 사람은 도저히 이해할 수 없는 누더기 코드'가 탄생하고 맙니다.
어떻게 하면 이 무한한 기계적 효율성을 누리면서도, 인간의 통제력과 깨끗한 아키텍처를 유지할 수 있을까요? 한 스타트업의 레거시 코드 포팅(Porting) 프로젝트 사례를 통해 그 구체적인 AX 공식을 소개합니다.
1. 문제 상황: 굴러는 가는데 만지기 무서운 '스파게티 코드'의 탄생
기존에 Go 언어로 작성된 데이터 파싱 유틸리티를 Rust로 포팅하는 작업을 진행했습니다. 작업의 효율을 극대화하기 위해 최신 코딩 에이전트 도구를 하네스(Harness) 시스템에 물려 무한 루프를 돌렸습니다. 에이전트가 코드를 포팅하고, 컴파일 에러를 만나면 스스로 수정하고, 단위 테스트가 성공할 때까지 계속 반복하도록 설정한 것이죠.
결과는 놀라웠습니다. 반나절 만에 수십 개의 모듈이 Rust로 완벽히 포팅되었고, 테스트도 100% 통과했습니다. 하지만 코드를 검토하던 시니어 엔지니어는 경악을 금치 못했습니다.
- 과도한 방어 코드의 남발: 조금이라도 널(Null) 값이 들어오거나 타입이 안 맞으면, 근본적인 데이터 구조를 고치지 않고 모든 라인에
try-catch나Option::unwrap_or같은 예외 대응 코드를 수십 개씩 덧붙여 놓았습니다. - 이해 불가능한 아키텍처: 전체적인 시스템 불변식(Invariant) 없이, 오직 당장의 컴파일 에러를 피하기 위해 우회로를 만들다 보니 전체 아키텍처가 엉망이 되었습니다.
- 인지적 의존성: 코드가 왜 이렇게 짜였는지 주니어 개발자는 물론이고 AI조차 명확히 설명하지 못하는 '유기체적 코드'가 되어 있었습니다.
2. 접근법 및 가설: 루프의 목줄을 쥐는 '아키텍처 가드레일'
우리는 하네스 레벨 루프를 아예 차단하는 대신, 루프의 실행 조건과 규칙을 인간이 제어하는 '통제된 루프(Controlled Loop)' 아키텍처를 설계하기로 했습니다.
[가설] "하네스 루프의 검증(Verify) 단계에 '코드 복잡도 제한'과 '아키텍처 비평가(Critic) 프롬프트'를 가드레일로 추가하고, 루프 반복 횟수가 일정 수준을 넘어가면 인간에게 에스컬레이션(Escalation)되도록 설계하면, 속도는 유지하면서 유지보수 가능한 코드를 얻을 수 있을 것이다."
3. 적용 과정: 3단계 가드레일 하네스 루프 구축
기존의 단순한 '작업 -> 시도 -> 판단 -> 반복' 루프에 인간의 엔지니어링 규칙을 주입하는 3단계 시스템을 구축했습니다.
[작업 대기]
↓
[에이전트 실행] → 코드 생성/수정
↓
[하네스 검증] ── (1) 테스트 통과 여부 검증
↓
[아키텍처 검증] ─ (2) 가드레일 작동 (순환 복잡도, LLM Critic 검사)
↓
[조건 분기] ──── (3) 통과시 종료 / 실패시 루프 회수 판단
├─ 루프 3회 이하: 피드백 주입 후 [에이전트 실행]으로 회귀
└─ 루프 4회 이상: 정지 후 [인간 개발자 개입 (Escalation)]1단계: 하네스 레벨에 '아키텍처 비평가(Critic)' 배치
테스트를 통과했더라도 무조건 통과 판정을 내리지 않고, 2차 LLM(Critic)을 통해 코드의 품질을 검사하는 단계를 추가했습니다. 이때 비평가 LLM에게는 아래와 같은 엄격한 시스템 프롬프트를 부여했습니다.
"너는 엄격한 시니어 소프트웨어 아키텍트다. 코드가 에러를 회피하기 위해 임시방편적인 방어 코드(과도한 예외 처리, 중복 래핑)를 썼는지 검사하라. 만약 발견된다면 테스트가 통과했더라도 실패(Fail)를 반환하고, 에이전트에게 '구조적 아키텍처 개선'을 요구하라."
2단계: 기계적 가드레일(Static Analysis) 연동
코드의 복잡도를 정량적으로 제한했습니다. 복잡도 분석 도구를 루프 내에 배치하여 다음과 같은 규칙을 강제했습니다.
- 순환 복잡도(Cyclomatic Complexity)가 이전 버전보다 20% 이상 증가하면 반려
- 중첩 깊이(Nesting Depth)가 3단계를 초과하면 루프 반려
3단계: '인간-인-더-루프(Human-in-the-Loop)' 에스컬레이션 트리거
에이전트가 갈 길을 잃고 땜질식 패치를 반복하는 임계점을 '루프 3회'로 설정했습니다. 3번의 시도 동안 아키텍처 가드레일을 통과하지 못하면, 루프를 일시 중지하고 인간 개발자에게 슬랙(Slack)으로 즉시 알림을 보냅니다.
개발자는 AI가 막다른 길에 다다른 맥락을 파악한 뒤, "이 구조 대신 공통 인터페이스를 추출해서 적용해줘"라는 단 한 줄의 전략적 가이드를 던져주고 다시 루프를 재개시킵니다.
4. 적용 결과 및 변화
새로운 가드레일 하네스 루프를 적용해 프로젝트를 완수한 결과, 속도와 품질 측면에서 매우 고무적인 성과를 거두었습니다.
루프 통제 여부에 따른 코드 품질 및 생산성 비교
| 평가 항목 | 무제한 루프 에이전트 (Before) | 가드레일 하네스 루프 (After) |
|---|---|---|
| 작업 완료 속도 | 평균 25분 (초고속) | 평균 45분 (가드레일 검사로 약간 증가) |
| 코드 가독성 점수 | 4.2 / 10점 (난해함) | 8.5 / 10점 (깔끔함) |
| 함수당 평균 줄 수(LoC) | 84줄 (방어 코드로 비대해짐) | 32줄 (간결함 유지) |
| 평균 중첩 깊이 (Depth) | 4.2단계 (임시 예외 처리 남발) | 1.8단계 (구조적 우회 최소화) |
| 인간의 시스템 이해도 | 10% 미만 (블랙박스 상태) | 85% 이상 (원하는 아키텍처 반영) |
무한 루프에 고삐를 채우자, 결과물은 완전히 달라졌습니다. 이전에는 굴러가기만 하는 기괴한 코드가 나왔다면, 가드레일을 적용한 후에는 인간 시니어 개발자가 작성한 것과 구별하기 힘들 정도로 정돈된 Rust 코드가 생성되었습니다.
5. 마치며: 하네스 엔지니어가 되어야 하는 이유
'다가오는 루프'의 흐름을 거스를 수는 없습니다. 기계가 제공하는 엄청난 속도의 생산성을 외면하는 기업은 경쟁에서 뒤처질 수밖에 없으니까요.
하지만 기계에게 설계의 통제권까지 넘겨주어서는 안 됩니다. 진짜 영리한 AI 적용 방식(AX)은 루프 자체를 통제할 수 있는 정교한 하네스를 엔지니어링하는 것입니다. 기계가 지칠 줄 모르고 달리는 루프 속에서, 인간의 지혜와 좋은 설계 원칙이 계속해서 이정표를 제시할 수 있도록 시스템을 구축해 보세요.
댓글
0댓글을 남기려면 로그인이 필요해요.
아직 댓글이 없어요. 첫 댓글을 남겨보세요.