AI 코딩 에이전트의 테스트 PASS를 그대로 믿을 수 있을까

AI 에이전트 생태계 구축기· 10 / 10
- 1 — 멋진 비서를 만들기 위한 뜬구름 시도 끝에, LLM Wiki부터 세웠다
- 2 — LLM Wiki를 지식 뇌로 쓰게 해주는 두 가지 자동화
- 3 — 지식 뇌와 연결한 첫 번째 비서 알림 기능
- 4 — 지식 뇌에 손발을 붙였다: LLM Wiki와 헤르메스 연결하기
- 5 — AI 대화 기록으로 블로그 작성하기
- 6 — AI 자동화와 사람의 판단을 잇는 우편함 만들기
- 7 — 텔레그램에서 슬랙으로 헤르메스 사무실 옮기기
- 8 — AI가 쓴 블로그에 필요한 이미지를 더한 방법
- 9 — 헤르메스로 자동화를 운영하며 접한 사소한 문제들
- 10 — AI 코딩 에이전트의 테스트 PASS를 그대로 믿을 수 있을까
요즘 나는 코딩 에이전트와 꽤 많은 작업을 한다. 구현 속도도 빨라졌고, 혼자였다면 미뤘을 정리와 테스트도 함께 진행할 수 있게 됐다. 다만 결과가 늘어날수록, 테스트 통과와 빌드 성공만으로는 안심하기 어려운 순간도 많아졌다.
그래서 내가 직접 만든 Taskery를 계속 손봤다. Taskery는 AI 코딩 작업을 기획·구현·검증·마무리 단계로 나누고, 각 작업의 기준과 결과를 남기게 하는 도구다. 구현한 세션과 다른 세션이 테스트를 맡도록 분리한 것도, 구현자가 자기 결과를 쉽게 통과시키지 못하게 하기 위해서였다.
그런데 막상 써 보니, 검증 단계를 여러 겹 둔다고 해서 결과가 저절로 믿을 만해지지는 않았다. 테스트가 통과했다는 말은, 그 테스트에 넣은 조건에서는 문제가 없었다는 뜻일 뿐이었다.
Taskery에 검증 단계를 둔 이유
Taskery의 흐름은 단순하다. 작업을 먼저 정리하고, 구현한 뒤, 다른 세션이 다시 확인하고, 마지막으로 작업을 닫는다.
| 단계 | 하는 일 | 이 단계가 필요한 이유 |
|---|---|---|
| 기획 | 작업 범위와 완료 기준 정리 | 무엇을 만들고 무엇을 확인할지 먼저 합의 |
| 구현 | 코드 작성과 기본 검사 | 문법·빌드·기존 테스트의 즉시 실패 확인 |
| 독립 검증 | 구현과 다른 세션이 실제 동작 확인 | 구현 과정의 가정을 한 번 끊고 다시 확인 |
| 마무리 | 결과 기록과 작업 반영 | 무엇을 확인했고 무엇이 남았는지 보존 |
여기서 독립 검증은 구현 세션과 다른 세션이 테스트 계획을 읽고 실행하는 단계다. 이 방식은 실제로 구현 중 놓친 문제를 잡아냈다. 다만 처음에는 구현한 세션이 테스트 시나리오까지 함께 작성했다. 요구를 잘못 이해하면 그 해석이 코드와 테스트 시나리오에 같이 들어갈 수 있었다. 그래서 테스트 단계 자체를 여러 번 고쳐야 했다.
테스트 단계에서 실제로 고친 문제들
한 번에 완성한 규칙은 없었다. 실제 프로젝트를 진행하며 문제가 드러날 때마다, 그 문제가 다시 통과하지 못하도록 Taskery의 테스트 흐름을 보완했다.
테스트 시나리오를 모호하게 작성한 문제
처음에는 테스트 계획에 “정상 동작하는지 확인”처럼 적힌 경우가 있었다. 이 문장은 고장이 나도 무엇이 달라져야 실패인지 알 수 없다. 테스트를 맡은 쪽도 코드를 읽고 그럴듯하면 PASS를 줄 여지가 생긴다.
그래서 자동 테스트에는 실행 방법과 관측 가능한 결과를 한 쌍으로 적게 했다. 예를 들어 “저장이 잘 되는지”가 아니라, “값을 저장한 뒤 앱을 다시 열었을 때 같은 값이 남아 있는지”처럼 쓴다. 정답이 취향에 가까운 화면의 미세한 차이라면 억지로 자동 판정하지 않고, 사람 검수 항목으로 남긴다.
구현을 맡은 쪽이 테스트 시나리오도 작성한 문제
구현 세션이 테스트 계획까지 작성하면, 요구를 잘못 이해한 방식이 구현과 시험 문제에 함께 들어갈 수 있다. 그 상태에서 테스트 담당만 바꾸는 것은 충분한 분리가 아니었다.
검증 세션을 따로 띄워도, 그 세션이 구현 쪽이 만든 테스트 계획과 입력을 그대로 받아 쓰면 같은 가정을 다시 확인할 수 있다. 검증을 맡은 사람이 다른지만으로는 충분하지 않았다.
그래서 해석의 여지가 있는 작업은 구현 계획을 세우기 전에 별도 세션이 테스트 계획을 먼저 작성하도록 바꿨다. 그 원문도 보존해 둔다. 이후 구현 과정에서 테스트 기준이 바뀌었다면, 무엇이 왜 달라졌는지 표시하지 않은 변경은 그대로 통과시키지 않는다.
테스트 입력이 가능한 경우를 모두 확인하지 못한 문제
한 프로젝트에서 시간값을 비교하는 기능을 테스트할 때, 0초, 2초, 4초처럼 정수 값만 넣었다. 그래서 테스트는 모두 통과했다. 하지만 실제 사용에서는 22.4초처럼 소수점이 있는 값도 들어왔고, 이 경우 메모 표시가 사라지는 버그가 있었다.
정수 값만 확인한 테스트는 가능한 시간값 중 일부만 확인한 것이었다. 그런데도 전체 기능이 문제없다고 판단했고, 확인하지 않은 소수점 값에서 버그가 남았다. 이후에는 테스트가 통과했는지보다 실제 사용에서 가능한 입력 경우를 얼마나 확인했는지를 먼저 본다.
핵심 동작 대신 겉모습만 테스트한 문제
어떤 기능은 특정 조건에서 실행되지 않아야 했다. 그런데 테스트는 그때 버튼이 비활성으로 보이는지만 확인했다. 버튼 모양은 바뀌었으니 테스트는 통과했지만, 정작 막아야 할 기능이 실제로 실행되지 않는지는 검사하지 않았다.
겉모습만 정상인 것을 보고 핵심 동작까지 정상이라고 판단한 셈이다. 그래서 이제는 버튼 상태가 아니라, 막아야 할 기능이 실제로 실행되지 않는지를 직접 검사한다. 핵심 테스트는 기능을 잠시 되돌렸을 때 정말 실패하는지도 확인한다.
작은 수정에도 무의미한 전체 테스트를 반복한 문제
검수에서 한 줄을 고칠 때마다 전체 테스트를 다시 실행한 적이 있었다. 한 작업에서 전체 테스트가 여섯 번 이상 반복됐고, 나는 수십 분을 기다려야 했다. 대부분은 방금 고친 부분과 관계없는 검사였고, 이미 확인한 결과를 다시 보는 일이었다.
그래서 수정 중에는 바뀐 부분의 테스트와 기본 검사만 돌리고, 전체 테스트는 수정이 끝난 뒤 한 번만 실행하도록 바꿨다. 테스트가 결함을 찾는 데보다 기다리는 데 더 많은 시간을 쓰게 해서는 안 된다고 봤다.
통과를 믿기 위해 바꾼 기준
문제를 고칠 때마다 테스트를 하나 더 붙이는 식으로 끝내지는 않았다. 어떤 초록불이 왜 잘못된 신호였는지에 맞춰, 테스트 계획·입력·실행·판정을 각각 손봤다.
| 검증에서 생긴 문제 | Taskery에서 보완한 방식 |
|---|---|
| 합격 기준이 모호함 | 실행 방법과 관측 가능한 결과를 함께 작성 |
| 구현자와 테스트 계획의 해석이 같음 | 별도 세션이 테스트 계획 작성, 원문과 변경 내용 대조 |
| 테스트가 일부 입력 경우를 빼먹음 | 실제 사용에서 가능한 입력 경우를 함께 확인 |
| 핵심 동작 대신 겉모습만 확인 | 실제 동작을 직접 검사하고, 고의 실패로 실효성 확인 |
| 무의미한 전체 테스트를 반복 | 수정 중 국소 검사, 마무리 시 전체 검사 |
검증 결과도 단순히 PASS와 FAIL 둘로만 보지 않게 했다. 코드가 잘못된 경우, 테스트 기준이 부족한 경우, 사람이 직접 봐야 하는 경우, 실행 환경 때문에 아직 돌리지 못한 경우는 다음에 해야 할 일이 모두 다르다. 이것을 한 가지 실패로 묶으면, 고쳐야 할 곳도 흐려진다.
검증 장치도 계속 운영해야 했다
테스트 통과는 기능이 정상임을 보여주는 중요한 근거다. 문제는 Taskery에서 일부 입력만 확인하거나, 핵심 동작 대신 버튼 모양만 보는 부실한 테스트도 통과로 남았다는 점이었다. 결과만 보면 기능 전체가 검증된 것처럼 보였지만, 실제로는 확인하지 않은 경우가 남아 있었다.
정수 시간만 확인한 테스트, 버튼 모양만 확인한 테스트, 무의미한 전체 테스트 반복은 각각 테스트 범위·확인 대상·실행 비용에 구멍이 있던 사례였다. 그래서 Taskery는 이런 문제가 드러날 때마다 테스트 계획과 실행 방식을 보완해 왔다.
앞으로도 테스트를 더 많이 돌리는 것이 아니라, 무엇을 확인해야 하는지와 그 검사가 실제로 충분한지를 계속 점검하며 사용할 생각이다.