들어가며: 테스트가 개발 속도를 따라잡지 못하는 시대

AI 기반의 에이전틱(Agentic) 소프트웨어 개발이 보편화되면서 코드 작성, 리뷰, 배포 속도가 전례 없이 빨라지고 있습니다. 하지만 이에 발맞춰 테스트 프레임워크도 함께 진화해야 한다는 사실을 간과하기 쉽습니다. 전통적인 테스트 방식은 정적 테스트 스위트(Static Test Suite)에 의존하고, 수동 작성과 지속적인 유지보수를 필요로 하기 때문에 에이전틱 개발 환경에서는 거의 한계에 다다랐습니다.

여기서 주목할 개념이 JiTTest(Just-in-Time Test) 입니다. JiTTest는 LLM(Large Language Model)이 코드 변경 사항이 프로덕션에 반영되기 직전, 그 순간에 맞춰 테스트를 자동 생성하는 방식입니다. 더 이상 개발자가 테스트 코드를 직접 작성하거나 유지보수할 필요가 없습니다. 이 글에서는 Meta(페이스북) 엔지니어링 블로그에서 발표한 'Catching JiTTest' 개념을 중심으로, 전통적 테스트의 문제점과 새로운 접근법을 깊이 있게 살펴보겠습니다.

참고 자료: 이 글은 Meta Engineering 공식 블로그의 내용을 근거로 분석하였습니다.

AI-powered code generation and automated testing in modern CI pipeline System Abstract Visual

전통적인 테스트 방식의 세 가지 한계

1. 정적 테스트 스위트의 유지보수 비용

전통적인 패러다임에서는 새로운 코드가 추가될 때마다 수동으로 테스트를 작성하고, 기존 테스트를 계속 실행하며 업데이트해야 합니다. 문제는 개발자가 현재 코드뿐 아니라 미래의 모든 변경 가능성까지 고려해야 한다는 점입니다.

# 전통적인 방식: 정적 테스트 스위트 예시
def test_user_registration():
    user = create_user("test@example.com", "password123")
    assert user.email == "test@example.com"
    assert user.is_active == True
    # ... 몇 달 후, 이메일 인증 로직이 바뀌면?
    # 이 테스트는 깨지거나, 더 이상 의미 없는 테스트가 됩니다.

2. 거짓 양성(False Positive)과 무의미한 테스트

미래 변경에 대한 불확실성 때문에 테스트가 아무것도 잡지 못하거나, 잡더라도 거짓 양성인 경우가 많습니다. 에이전틱 개발은 코드 변경 속도를 극적으로 높이기 때문에, 테스트 개발 부담은 폭발적으로 증가하고 거짓 양성과 유지보수 비용은 감당할 수 없는 수준이 됩니다.

3. 테스트가 '의도'를 이해하지 못함

기존 테스트는 코드의 '의도(intention)'를 파악하지 못합니다. 단순히 입력-출력 관계만 검증할 뿐, 코드 변경이 원래 의도한 동작을 망가뜨리는지(regression)를 제대로 감지하지 못합니다.

Server infrastructure showing just-in-time test execution architecture

Catching JiTTest: 어떻게 동작하는가?

Catching JiTTest는 코드 변경으로 인한 회귀(Regression)를 잡는 데 특화된 테스트 방식입니다. 핵심은 LLM이 코드 변경의 의도를 추론하고, 그 의도에 기반해 맞춤형 테스트를 즉석에서 생성한다는 점입니다.

작동 프로세스 (6단계)

  1. 새 코드가 코드베이스에 도착 (Pull Request 제출)
  2. 시스템이 코드 변경의 의도를 추론 (LLM이 변경 사항을 분석)
  3. 돌연변이(Mutant) 생성 — 의도적으로 결함을 삽입한 코드 버전을 만들어 어떤 문제가 발생할 수 있는지 시뮬레이션
  4. 테스트 생성 및 실행 — 돌연변이를 잡아낼 수 있는 테스트를 LLM이 즉시 생성하고 실행
  5. 신호 집중 — 규칙 기반 + LLM 기반 평가자가 협력하여 진짜 양성(True Positive)에만 집중
  6. 개발자에게 명확한 리포트 제공 — 예상치 못한 동작 변경에 대한 관련성 높은 보고서가 적시에 전달됨
# Catching JiTTest의 개념적 예시 (의사 코드)
# 실제로는 LLM이 이 과정을 자동으로 수행합니다.

def catching_jittest(original_code, changed_code):
    # 1단계: 의도 추론
    intention = llm_infer_intention(original_code, changed_code)
    # 예: "사용자 이메일 인증 시간을 5분에서 10분으로 늘림"
    
    # 2단계: 돌연변이 생성 (의도와 다른 결함을 심어봄)
    mutants = [
        mutate(changed_code, "타임아웃을 0으로 설정"),
        mutate(changed_code, "인증 코드 검증 로직 제거"),
        mutate(changed_code, "이메일 전송 API 호출 생략")
    ]
    
    # 3단계: 각 돌연변이를 잡을 테스트 생성 및 실행
    for mutant in mutants:
        test = llm_generate_test(mutant, intention)
        result = run_test(mutant, test)
        if result.fails:
            # 진짜 버그 가능성 높음 → 개발자에게 보고
            report_to_developer(result)

왜 기존 테스트보다 강력한가?

  • 유지보수 비용 제로: 테스트가 코드베이스에 상주하지 않으므로, 업데이트할 필요가 없습니다.
  • 의도 기반 테스트: 단순한 입출력 검증이 아니라 '의도된 동작'을 기준으로 테스트하므로 거짓 양성이 현저히 줄어듭니다.
  • 자동 적응: 코드가 변경되면 테스트도 자동으로 변경됩니다.
  • 인간 개입 최소화: 실제 버그가 잡혔을 때만 사람이 리뷰합니다.

Cloud-based testing platform with LLM integration for agentic development Technical Structure Concept

국내 개발 생태계에서의 적용 맥락

한국의 SI(시스템 통합) 및 스타트업 환경에서 JiTTest 도입을 고려할 때 몇 가지 주의할 점이 있습니다. 첫째, LLM 호출 비용입니다. JiTTest는 PR마다 LLM을 호출하므로, 대규모 조직에서는 비용이 상당할 수 있습니다. 둘째, 레거시 시스템과의 통합 문제입니다. 오래된 Java/Spring 기반의 레거시 코드베이스에서는 LLM이 의도를 정확히 추론하기 어려울 수 있습니다. 셋째, 테스트 신뢰성입니다. LLM이 생성한 테스트가 항상 정확한 것은 아니므로, 초기 단계에서는 사람의 검증이 필요합니다.

이 기술의 한계와 주의사항

  • LLM의 환각(Hallucination) 문제: 잘못된 의도를 추론하면 오히려 잘못된 테스트를 생성할 수 있습니다.
  • 프로덕션 환경에서의 지연: PR이 제출될 때마다 LLM을 호출하면 CI/CD 파이프라인의 전체 시간이 늘어날 수 있습니다.
  • 보안 이슈: 민감한 코드베이스의 의도를 LLM이 분석하는 과정에서 데이터 유출 가능성이 있습니다.

다음 단계 학습 방향

JiTTest에 관심이 생겼다면, 다음 주제를 함께 공부해보세요:

  • LLM 기반 코드 분석 (CodeBERT, GraphCodeBERT 등)
  • 돌연변이 테스팅(Mutation Testing) 기본 개념
  • 에이전틱 AI 에이전트 프레임워크 (LangChain, AutoGPT 등)
  • CI/CD 파이프라인 고도화 (특히 테스트 단계 최적화)

함께 보면 좋은 글

본 콘텐츠는 신뢰할 수 있는 출처를 바탕으로 AI 도구를 활용하여 초안이 작성되었으며, 편집자의 검토를 거쳐 발행되었습니다. 전문가의 조언을 대체하지 않습니다.