본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 05. 12:12

당신의 테스트 스위트가 당신에게 거짓말을 하고 있습니다

요약

AI 에이전트가 코드를 작성한 후 테스트를 생성할 때 발생하는 '자기 확증적 루프'의 위험성을 경고합니다. 코드가 수행하는 동작을 단순히 문서화하는 테스트가 아닌, 명세(Spec)를 기반으로 한 검증 체계의 중요성을 강조합니다.

핵심 포인트

  • 코드가 작성된 후 작성된 테스트는 버그를 문서화할 뿐이다
  • AI 에이전트는 코드와 테스트를 동시에 생성하며 오류를 확장시킨다
  • 테스트는 구현(Implementation)이 아닌 명세(Spec)로부터 시작되어야 한다
  • 단순 프롬프팅만으로는 테스트의 사각지대를 해결할 수 없다

AI 지원 개발(AI-assisted development)에서 가장 위험한 순간:

테스트 스위트(test suite)가 초록색(pass)입니다.

당신은 에이전트(agent)를 실행했습니다. 에이전트는 기능을 구현했고, 테스트를 작성했습니다. 모든 테스트를 통과했습니다. CI(지속적 통합)는 만족스럽습니다. 당신은 배포합니다.

2주 후, 프로덕션(production) 환경에서 빈 리스트를 마주하며 크래시(crash)가 발생합니다.

이것은 제가 엔지니어들이 빠져드는 것을 계속 목격해 온 실패 모드(failure mode)이며, 저 또한 빠져들었던 방식입니다.

코드가 작성된 후에 작성된 테스트는 테스트가 아닙니다. 그것은 코드가 우연히 수행한 동작에 대한 문서화(documentation)일 뿐입니다. 즉, 코드에 버그가 있을 때(조용히 무시된 중복 웹훅(webhook), 처리되지 않은 빈 입력값, 모델이 가정하여 배제해 버린 실패 모드 등), 그 코드를 바탕으로 작성된 테스트는 실패하지 않습니다. 오히려 통과합니다. 테스트는 그 버그를 문서화된 동작으로 고착시켜 버립니다.

CI 신호는 초록색으로 변합니다. PR(Pull Request)은 승인됩니다. 명세(spec)는 조용히 위반됩니다.

가장 최악인 점은 테스트 스위트가 고장 난 것이 아니라는 사실입니다. 그것은 요청받은 대로 정확히 수행하고 있습니다. 즉, 코드가 수행하는 대로 코드가 작동하는지 확인하는 것입니다. 유일하게 누락된 것은 명세(spec)입니다. 테스트가 강제했어야 할 바로 그 명세 말입니다.

이제 이 루프에 에이전트(agent)를 추가해 보십시오.

에이전트가 코드를 생성합니다. 에이전트는 생성된 코드와 함께 자신이 만든 테스트를 실행합니다. 테스트는 통과합니다. 에이전트는 성공을 보고합니다. 그리고 다음 기능으로 넘어갑니다.

사람이 테스트 목록을 보고 다음과 같이 질문하는 지점은 존재하지 않습니다: "잠깐, 명세(spec)가 여기에 실제로 반영되어 있나요? 빈 입력값에 대해 테스트했나요? 중복 웹훅에 대해 테스트했나요? 절대 일어나서는 안 되는 상황에 대해 테스트했나요?"

그러한 멈춤(pause)이 없다면, 테스트 스위트는 그저 자기 확증적인 루프(self-confirming loop)일 뿐입니다. 코드는 A라고 말합니다. 테스트는 A가 맞다고 말합니다. 에이전트는 A가 검증되었다고 보고합니다. 하지만 명세(spec)는 B는 시스템에 절대 들어올 수 없다고 말하고 있습니다.

테스트는 작성된 목적대로 잡아낼 뿐입니다. 그 이상은 아무것도 하지 못합니다.

인간이 코드를 작성한 후에 테스트를 작성할 때도 동일한 패턴이 나타납니다. 대부분의 엔지니어들은 이를 인정할 것입니다. AI와의 차이점은 양(Volume)과 속도(Speed)입니다. 코드를 작성하고 테스트를 만드는 주니어 엔지니어는 하루에 아마 두세 개의 결과물을 만들어낼 것입니다. 하지만 에이전트(Agent)는 20개를 만들어냅니다. 동일한 사각지대가, 과거 새벽 2시에 운영 환경(Production)에서 버그를 발견하며 겪었던 고통스러운 경험(Scarring) 없이 그대로 확장됩니다.

이것이 바로 프롬프팅(Prompting)을 통해 이 문제를 해결하려는 시도가 통하지 않는 이유이기도 합니다. "좋은 테스트를 작성해줘"라는 조언은 모델이 받아들이기는 하지만 실제로는 즉시 무시됩니다. 왜냐하면 모델에게 요청된 작업(기능을 작동시키고 이를 검증하는 것)은 테스트가 통과하기만 하면 충족되기 때문입니다. 테스트가 통과하는 것 자체가 곧 검증(Verification)이 되어버립니다. 모델은 검증이 다른 곳에서부터 와야 한다는 사실을 알 방법이 없습니다.

유일하고 정직한 해결책은 순서(Order)에 있습니다.

테스트는 코드가 아니라 명세(Spec)로부터 나와야 합니다. "빈 입력에 대해 X를 반환한다", "ID가 중복된 웹훅(Webhook)은 거부한다", "알 수 없는 상태에서는 명확하게 실패한다"와 같은 수락 기준(Acceptance criteria)은 구현(Implementation)이 존재하기 전에 실패하는 테스트가 되어야 합니다. 모델은 그 테스트들을 만족시키기 위해 코드를 작성합니다. 테스트는 코드에서 나온 것이 아닙니다. 코드가 수행하기로 되어 있었던 작업으로부터 나온 것입니다.

테스트가 먼저 오게 되면, 모델은 버그를 생성함과 동시에 그 버그를 정당화하는 테스트를 함께 만들어낼 수 없습니다. 빈 입력 케이스는 이미 빨간색 막대(Failing test)로 존재합니다. 중복 웹훅은 이미 단언(Assertion)으로 존재합니다. 모델은 자신이 우연히 만들어낸 결과물이 아니라, 명세가 요구한 사항을 충족시켜야만 합니다.

실제로, 아무도 자발적으로 이렇게 하지 않습니다.

더 나은 방법을 몰라서가 아닙니다. 에이전트가 몇 초 만에 구현을 해낼 수 있는 순간에, 실패하는 테스트를 먼저 작성하는 것은 느리게 느껴지기 때문입니다. 지난 20년 동안의 모든 TDD(테스트 주도 개발) 관련 기사들은 이 부분을 인정해 왔습니다. 그 순간에는 단계를 건너뛰는 것이 사소해 보입니다. 그리고 규율(Discipline)은 무너집니다.

인간의 경우, 이러한 건너뛰기는 최소한 눈에라도 보입니다. PR(Pull Request)을 보면 테스트가 코드와 같은 커밋에 들어있거나, 심지어 코드보다 나중에 들어온 것을 확인할 수 있습니다. 하지만 에이전트의 경우, 이러한 건너뛰기는 보이지 않습니다. 테스트는 존재하고, 통과합니다. 아무도 그 테스트가 검증(Verification)이 아니라 정당화(Ratification)에 불과하다는 사실을 알아차리지 못합니다.

제가 믿게 된 것은, 모델이 테스트를 건너뛰는 비용이 거의 없을 정도로 충분히 빨라지면, 테스트 우선(test-first) 방식은 더 이상 하나의 규율(discipline)로서 달성 가능한 영역이 아니라는 점입니다. 그것은 구조적(structural)이어야 합니다. 워크플로의 어떤 부분은 테스트보다 먼저 작성된 코드 수정 사항을 수용하기를 거부해야 합니다. 그것만이 제가 일관되게 경계선을 지켜내는 것을 본 유일한 방법입니다.

그것이 훅(hook)이든, CI 체크(CI check)든, 머지(merge)를 거부하는 에이전트(agent)든, 혹은 프리 커밋 게이트(pre-commit gate)든 상관없습니다. 메커니즘보다는 원칙이 더 중요합니다. 모델은 자신이 직접 검증(verification)까지 생성해낸 코드를 배포해서는 안 됩니다. 테스트는 반드시 코드보다 더 오래되어야 하며, 코드가 영향을 미치지 않은 소스(source)로부터 작성되어야 합니다.

이 규율은 TDD(테스트 주도 개발)가 아닙니다. 이 규율은 테스트 대상 코드와 동일한 컨텍스트(context)에서 생성된 테스트를 수용하기를 거부하는 것입니다.

AI 보조 워크플로(AI-assisted workflow)에서, 이 차이는 의미 있는 '통과(green)' 상태의 테스트 스위트와 아무런 의미가 없는 '통과' 상태의 테스트 스위트를 가르는 기준이 됩니다.

여러분의 팀은 AI가 생성한 PR(Pull Request)에서 '코드 작성 후 테스트(tests-after-code)' 현상을 어떻게 방지하고 있나요? 그 구분이 제대로 지켜지고 있다고 믿으시나요?

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0