테스트 113개 다 통과했는데 실제론 0개를 긁었다 — AI가 짠 수집기의 함정
AI에게 수집기(웹에서 데이터를 긁어오는 코드) 두 개를 맡겼더니, 테스트 113개를 전부 통과시켰다. 초록불. 그런데 실제로 돌려보니 한쪽은 0개를 긁었다.
전에 이 블로그에서 이 트렌드 수집 도구의 데이터 소스가 어떻게 죽는지 썼다. 이번엔 거기에 기능을 붙이다가 더 불편한 걸 배웠다. 테스트는 통과했는데 코드는 작동하지 않았다 — 이게 어떻게 동시에 참일 수 있나.
한 줄이면 이렇다.
추정한 입력에 맞춰 짠 테스트는 그 추정이 맞다는 것만 증명한다. 실제 세계가 추정과 같은지는 아무도 검증하지 않았다.
누가 읽으면 좋은가
- AI(Claude·Cursor 등)에게 코드를 맡기고 테스트 초록불을 받아본 사람
- 특히 외부 API나 웹페이지를 긁는 코드를 AI에 시켜본 사람
- “테스트 다 통과했으니 됐겠지”로 넘어간 적 있는 사람
113개 초록불, 라이브에선 0개
무슨 일이었나부터. 트렌드 도구에 “해외에서 잘나가는 신제품”을 자동으로 잡아낼 수집기 두 개를 붙이려 했다. 하나는 Show HN(해커뉴스 신제품 게시판), 하나는 Product Hunt(신제품 등록 사이트).
작업은 서브에이전트에 맡겼다. 테스트를 먼저 쓰고 구현하는 방식으로, 규칙도 빡빡하게 줬다.
에이전트는 코드와 테스트를 짜서 “113개 통과”를 보고했다.
여기서 멈췄으면 끝이었다. 초록불이니까.
그런데 안 믿고 실제로 돌렸다. Show HN은 실제 런칭 5건을 정확히 긁어왔다.
Product Hunt는 0개였다. 대신 에러 하나 — “제품을 하나도 파싱하지 못함”.
테스트는 왜 통과했나
이게 핵심이다. 0개를 긁는 코드의 테스트가 어떻게 113개나 통과했나.
답은 단순했다. 에이전트는 Product Hunt의 실제 응답을 한 번도 본 적이 없다.
줄 수도 없었다. 그 응답은 실제로 한 번 긁어봐야 나온다. 그래서 “이렇게 생겼겠지” 하고 가짜 샘플을 만들었고, 그 샘플에 맞춰 파싱 코드를 짰고, 다시 그 샘플로 테스트를 돌렸다.
테스트는 “추정이 추정과 일치하는가”만 확인한 셈이다. 실제 사이트가 그 추정과 같은지는 본 적이 없다. 초록불은 코드가 제 가정 안에서 앞뒤가 맞는다는 뜻이지, 바깥세상에서 작동한다는 뜻이 아니었다.
공정하게 말하면, 에이전트는 이 셀렉터(긁을 위치를 지정하는 규칙)가 “실제 출력을 대조하지 않은 추정”이라고 스스로 경고는 달아뒀다. 그 경고를 무시하지 않은 게 그나마 다행이었다.
추정이 빗나간 세 지점
실제 응답을 떠서 추정과 맞춰봤다. 세 군데가 어긋나 있었다.
| 항목 | 에이전트 추정 | 실제 응답 |
|---|---|---|
| 링크 경로 | /posts/{이름} |
/products/{이름} |
| 제품명 | [이름] |
[1. 이름] (순위 번호가 붙음) |
| 추천 수 | ▲ 1.2K 파싱 |
응답에 숫자가 아예 없음 |
세 번째가 특히 컸다. 추천 수를 긁으려 했는데, 그 데이터는 응답 텍스트에 처음부터 존재하지 않았다. 없는 걸 긁으려는 코드를, 테스트는 통과시켰다.
셀렉터를 실제 구조로 고치자 라이브에서 6건이 정상으로 들어왔다. 고치는 데 든 시간은 짧았다. 문제는 고치는 게 아니라, 고쳐야 한다는 걸 아는 것이었다.
초록불은 신뢰가 아니라 시작점
고친 도구로 원래 하려던 작업을 돌렸다. 해외에서 돈 버는 제품과 국내 공백을 맞춰보는 일이다.
Hypefury(스레드·X 예약 도구, 월 7만 달러 매출)나 Supademo(제품 데모 도구, 연 300만 달러 매출) 같은 후보가 떴지만 국내에선 이미 다 막혀 있었다. 그건 다른 글의 몫이다.
이 글의 교훈은 하나다. AI가 만든 코드의 초록불은 신뢰의 끝점이 아니라 검증의 시작점이다.
위임은 빨라졌지만, “실제로 작동하는가”를 확인할 책임은 그대로 내게 남는다. 둘은 분리된다.
특히 바깥을 긁는 코드라면 규칙은 더 단순해진다. 한 번은 실제 데이터로 돌려봐라. 테스트 초록불은 그다음이다.
하나 살린 건 있다. 0개를 긁었을 때 코드가 조용히 빈 결과를 넘기지 않고 에러로 시끄럽게 실패했다. 실패를 숨기지 않고 즉시 드러내는 이런 설계를 fail-fast라고 부른다.
그 덕에 “0개”를 바로 발견했다. 만약 실패를 삼키고 빈 목록을 반환했다면, 며칠 뒤 “요즘 Product Hunt가 한산하네” 하고 넘어갔을 거다.
한계
- 한 도구, 한 사례다. 모든 AI 코드가 이렇게 빗나간다는 일반화가 아니다. 바깥을 긁는 코드가 특히 취약하다는 관찰이다.
- 내부 로직만 다루는 코드라면 테스트의 신뢰도는 더 높다. 이 이야기는 “바깥세상을 가정하는 코드”에 한정된다.
- 라이브 검증을 어떻게 자동화할지는 아직 숙제다. 매번 손으로 돌려보는 건 임시방편이다.
참고
- 내 AI 설정을 점검했더니 절반이 죽어 있었다 — 있다 ≠ 작동한다
- 구글이 막은 6개 도구 — 트렌드 수집기의 소스 결정
- AGENTS.md를 명시적으로 쓰면 달라지는 것 — 측정 기록(fail-fast)
저자 — Jason 황재승
cd4761.blogspot.com에서 개발 자동화와 AI 도구 운영 기록을 쓴다. 8년 차 프론트엔드. 코드를 AI에 맡기되, 작동은 직접 돌려보고 확인하려 한다.
태그: #AI코딩 #테스트 #코드검증 #AI수집기 #failfast #TDD #코드위임 #자동화
댓글
댓글 쓰기