AI 시대의 TDD
인프런 TDD 밋업을 듣고
요즘 개발자들은 GitHub Copilot, ChatGPT, Claude 같은 AI 코딩 도구들이 부쩍 익숙해진 덕분에 참 바빠진 것 같다. 프롬프트 몇 줄만 입력하면 함수도 알고리즘도 금방 나오는 것이 생산성이 크게 올라간 걸 느낀다.
하지만 여기서 AI가 짜준 코드를 우리는 얼마나 믿을 수 있을까? 바로 그 지점에서 테스트 주도 개발을 다시금 주목해야할 필요를 느껴 밋업을 듣게 되었다.
LLM 기반 코딩의 불확실성
AI 코딩 도구는 결국 LLM 즉 대규모 언어모델이다. 이 모델은 프롬프트를 해석해서 확률적으로 다음 토큰을 이어 붙이는 방식으로 동작한다.
이를 강연에서는 “비결정성” 이라고 표현했다. 물론 우리의 삶에서는 많은 부분이 비결정적으로 동작하지만 우리가 원하는 소프트웨어 프로그램 개발은 결정적으로 작동해아한다는 점이 중요한 것 같다.
Kent Beck은 이런 LLM을 “예측 불가능한 지니(genie)”에 비유해 소원을 들어줄 것 같지만 의도를 잘 못 이해하고 자기 멋대로 행동할 때가 있다고 설명했다. 실제로 AI 코딩 도구를 활용하다 보면 내 의도와 다른 로직으로 풀거나 코드를 몽땅 지워버리거나 하는 등의 위험천만한 일을 벌이곤 한다.
이렇듯 LLM 기반 코딩에는 언제나 예측 불가능성과 불확실성이 따라다닌다.
그래서 오히려 TDD가 중요하다
사람이 짠 코드든 AI가 생성한 코드든 결국 우리가 믿을 수 있는 건 적절한 검증 절차 뿐이다. 그리고 그 핵심은 테스트다.
이런 맥락에서 TDD의 장점이 뚜렷해진다. 미리 테스트를 작성해 두면 그 테스트 자체가 명확한 기준이자 시스템의 기대 행동을 정의한 문서 역할을 하게 된다. 내가 의도한 기능이 제대로 작동하는지 AI가 작성한 코드가 기존 흐름을 해치지 않는지를 매번 검증할 수 있다.
무엇보다 테스트는 내가 통제할 수 있는 유일한 영역이다. AI가 코드를 짜는 건 이제 자연스러운 일이 됐지만 그 코드를 믿을 수 있는지는 전혀 다른 문제다. 결국 중요한 건 그 코드가 내가 원하는 대로 동작하는가?이고 테스트가 그 답을 줄 수 있다.
이런 방향에서 우리가 생각해볼 수 있는 설계
AI의 도움을 받아 개발하는 시대에 중요한 건 무엇을 만들 것인가에 집중하는 사고방식이다. 더 이상 모든 구현을 사람이 일일이 짜지 않아도 되는 지금, 개발자는 시스템의 목적을 명확히 정의하고 그 목적을 뒷받침할 요구사항과 인터페이스, 테스트 시나리오를 중심으로 설계를 진행해야 한다.
- 제품 목적 이해 → 요구사항 구체화 → 인터페이스 설계 → TDD → 구현
- 요구사항 구체화 → 테스트 시나리오 목록 → LLM 코딩
이 순서를 따르다 보면 자연스럽게 무엇(What, 인터페이스 정의나 도메인 규칙 등)에 집중하고 어떻게(How, 내부 실제 구현)는 LLM에게 위임하는 구조가 만들어진다. 고수준 언어가 등장하면서 저수준 메모리 제어나 포인터 연산이 인간의 관심사에서 멀어진 것과도 비슷하다. 이제 우리가 LLM에게 맡길 수 있는 추상화 단계가 늘어난 만큼 우리는 더 상위 레벨의 문제에 집중해야 한다.
관심사의 방향이 구현에서 목적으로 전환되면 개발자는 더 본질적인 문제(이 시스템은 왜 존재하는가, 어떻게 동작해야 하는가)에 몰입할 수 있다. 이건 단순한 편의성의 문제가 아니라 설계 철학의 변화다.
작고 격리된 모듈 설계
LLM을 통해 코드를 생성할 때 가장 효과적인 방식 중 하나는 작고 격리된 단위로 코드를 나누는 설계다.
- 시스템을 마치 세포처럼 쪼개고
- 각 단위에서 테스트 가능하고 명확한 책임을 갖도록 구성하고
- 그 단위 안에서 LLM이 코드를 작성하도록 유도한다면
비교적 일관되고 신뢰할 수 있는 출력을 얻을 수 있다.
작게 나눈 모듈은 테스트를 통해 연결성과 역할을 검증할 수 있고 이는 전체 시스템이 요구 명세를 충실히 따르고 있는지 확인할 수 있다.
물론 지나치게 세분화된 설계는 복잡하다. 그렇지만 TDD는 이 설계가 잘 동작하는지 계속 피드백을 준다. 복잡한 설계가 가치가 있는지 검증하고 다듬을 수 있도록 도와주는 역할을 하는 것이다.
Kent Beck이 말한 TDD의 순환 절차
밋업에서 공유된 Kent Beck의 TDD 순서는 AI와 함께 개발할 때 특히 유용하게 다가온다
- 다루고자 하는 테스트 시나리오의 목록을 작성한다.
- 목록에서 정합된 한 가지 항목을 실제 실행 가능한 구체적인 테스트로 전환한다.
- 전환된 테스트(및 이전 테스트들)를 통과하도록 코드를 변경한다.
- 선택적으로 리팩터링하여 구현 설계를 개선한다.
- 목록이 비워졌는가?
- 아니오 → 2단계로 돌아감
- 예 → 종료
이 과정을 참고해서 각각의 테스트를 잘 정의하고 이를 통과하는 과정을 거친다면 이 강의에서 말한 TDD를 잘 실현할 수 있을 것 같다.
마무리: 기준을 세우는 개발자, 생성하는 AI
우리는 더는 모든 코드를 손으로 짜지 않아도 되지만 코드가 무엇을 해야 하는지를 명확히 정의하고 그것이 지켜지고 있는지를 꾸준히 검증하는 일은 여전히 우리 몫이다.
코드를 빠르게 만드는 건 이제 누구나 할 수 있는 시대지만 좋은 시스템을 만드는 건 여전히 개발자의 철학과 기준에서 시작되는 것 같다는 것을 느낀 강연이었다.
끝!
ps. 연사님 링크드인 글들도 재밌게 봤다.