31P by xguru 15일전 | ★ favorite | 댓글 5개

TDD(Test-Driven Development)와 LLM의 결합

  • TDD는 프로그램 작성을 시작하기 전, 포괄적인 단위 테스트를 먼저 작성하는 개발 방법론임
  • 테스트가 사실상 사양서 역할을 맡기 때문에, 최종적으로 모든 테스트가 통과하면 코드의 정확성을 어느 정도 증명할 수 있음
  • 전통적으로 TDD는 생산성을 저해하거나 비효율적이라는 비판을 받기도 함
  • 하지만 LLM의 등장으로 테스트 작성과 코드를 반복 수정하는 과정이 훨씬 수월해짐

내가 LLM을 평소에 사용하는 방법

  • Github Copilot 같은 도구를 적극적으로 사용해 왔음
  • LLM은 반복 패턴을 찾고 다음 몇 줄을 자동완성하는 데 능숙하지만, 문제 전체를 깊이 이해하여 완결성 있는 모듈을 단번에 만들어내는 데에는 종종 어려움이 있음
  • 문제 해결에 필요한 맥락을 과도하게 제공하면 모델이 주제에서 벗어나기 쉬움
  • 필요에 따라 정보(오류 출력 등)를 부분적으로만 제공하며 작업을 진행하면, 모델이 디버깅에도 훌륭한 도움을 줌
  • IDE, 터미널, 챗 인터페이스 간 복사-붙여넣기를 반복하는 과정에서 마찰이 생긴다는 점을 체감함

자동화할 수 있을까?

  • 이러한 과정을 자동화하기 위해 직접 event loop 개념을 도입함
  • 첫 번째 프롬프트에 구현할 함수의 사양과 함수 시그니처를 명시하면, 모델은 단위 테스트와 코드의 초안을 제시함
  • 이 코드를 ‘sandbox’ 디렉토리에 저장하고 자동으로 go test를 실행함
  • 테스트가 실패하면, 두 번째(반복) 프롬프트에 기존 코드와 테스트 결과(컴파일 오류나 실패 정보)를 함께 전송함
  • 모델은 이를 바탕으로 수정된 테스트 및 구현 코드를 다시 제안함
  • 모든 테스트가 통과할 때까지 이 과정을 반복함
  • 이 접근법은 맥락을 과도하게 누적하지 않고도 점진적인 개선을 가능케 함
  • 모델이 동일 테스트 케이스에서 반복적으로 실패할 수도 있는데, 그럴 경우 사람이 직접 문제 부분을 짚어 힌트를 제공함
  • LLM이 만든 테스트가 충분히 엄격한지 의심해야 하는 ‘감시자 부재’ 문제를 인지해야 함
  • 동일한 오류나 불완전한 설계를 코드와 테스트가 함께 공유할 가능성이 있음
  • 따라서 인간이 추가로 테스트 케이스를 강화해주는 과정이 중요함
  • 필요하다면, mutation testing 같은 기법을 AI로 실험해볼 수도 있음

LLM 기반 개발과 인지 부하(cognitive load)

  • LLM과 함께 TDD를 적용하면, 일반적인 알고리즘 문제뿐 아니라 실제 종속성이 있는 코드베이스에서도 가능할 것으로 예상함
  • 단, 프로젝트 구조를 더 작은 단위로 쪼개어 유지보수성을 높이고, 각 디렉토리/패키지가 독립적으로 테스트 가능해야 함
  • 각 패키지는 주요 타입 정의(shared.go)와 특정 로직을 담당하는 파일(x.go) 및 테스트(x_test.go)로 분리하여 인지 부하를 줄이는 방식을 권장함
  • AI를 활용하는 과정에서 전체 코드를 매번 모델에 제공하는 대신, 특정 부분만 선택적으로 포함해 모델이 집중하도록 유도함
  • 이는 테스트 커버리지를 높이면서 모듈 간의 결합도를 줄여 장기 유지보수에도 이점을 줌
  • 큰 프로젝트라도 작고 명확한 단위로 쪼개어, 각 단위의 논리를 풍부하게 담되 범위를 최소화하는 구조를 지향함

마무리

  • AI의 발전 속도를 고려하면, 내일 당장 새로운 아키텍처가 등장해서 LLM의 한계를 뛰어넘을 수도 있음
  • 따라서 10만 줄이 넘는 대규모 레거시 코드를 급작스럽게 리팩터링하기보다는, 작은 규모에서부터 TDD와 LLM의 결합 가능성을 탐색해보는 것을 추천함
  • TDD와 LLM의 융합은 코드 자동생성과 테스트 품질 관리 모두에 긍정적인 변화를 줄 수 있을 것으로 기대함

빌더 아이오에서 만든 마이크로 에이전트도 비슷한 접근입니다. https://github.com/BuilderIO/micro-agent 저도 LLM과 TDD를 여러 번 해봤는데, 디자인 시스템 등으로 추상화도 잘 해야하고요. 컨벤션이랑 패턴도 잘 정립되어 있어야 하고요. 테스트케이스는 보통 직접 짜는 편입니다. (인간 말로라도?) 무엇보다 이 글에서도 말하듯이 결합도가 낮고 응집도 높은 모듈을 잘 설계해야 한정된 컨텍스트 윈도우에 맥락을 밀어넣을 수 있더라고요.

다른 개발 전용 AI 서비스는 어떤 파이프라인을 사용하고 있을까 곰곰이 고민하게 되네요.

(이런거 보고있자면)조금 있으면 전뇌를 위해 전기 자극선을 뇌에 삽입 할 것만 같아요.

테스트 코드를 넣는 건 좋은 거 같긴 한데, 이 사람이 만든 프로그램에 메리트는 없는 거 같아요
cline이나 aider에서도 커맨드라인 실행하고 결과 받는 게 돼서 그 프로그램에서 프롬프트만 잘 쓰는 게 다른 편의성을 생각했을 때 나을 거 같습니다

LLM이 작은 범위의 코드는 잘 봐주지만 전체적인 설계, 큰 시야가 아직 아쉬운 편인데,
TDD랑 결합해서 조금씩 개선하는 방식 좋은 것 같네요.