5.3 테스트 용이성(Testability)을 위한 프롬프트 모듈화 설계
전통적인 소프트웨어 공학에서 유닛 테스트(Unit Test)의 핵심은 코드를 독립적이고 검증 가능한 작은 조각(Unit)으로 쪼개는 ’모듈화(Modularization)’에 있다. 테스트하기 어려운 스파게티 코드는 유지보수성에 대한 사형 선고와도 같다. 이와 동일한 원칙이 AI 기반 소프트웨어 개발의 ’프롬프트 엔지니어링(Prompt Engineering)’에도 엄격하게 적용되어야 한다.
LLM 애플리케이션 개발 초기에는 페르소나 설정, 배경 지식(Context), 제약 조건, 출력 포맷 지시사항을 수천 토큰 길이의 단일 거대 프롬프트(Monolithic Prompt)로 때려 넣는 우를 범하기 쉽다. 이러한 거대 프롬프트는 작은 지시어 변경 하나가 시스템 전체의 동작 지그재그(Zig-zag)를 유발하며, 어떤 문장이 어떤 결함을 발생시켰는지 역추적하는 것을 불가능하게 만든다. 즉, ’테스트 불가능(Untestable)’한 상태에 빠지는 것이다.
결정론적 오라클을 구축하기 위해서는 프롬프트를 소프트웨어 아키텍처 관점에서 분해(Decomposition)하고, 각각의 구성 요소를 격리하여 테스트할 수 있도록 설계해야 한다.
1. 프롬프트 컴포넌트 분리 (Separation of Concerns)
거대 프롬프트는 단일 책임 원칙(SRP, Single Responsibility Principle)을 위배한다. 테스트 가능한 프롬프트는 목적에 따라 명시적인 템플릿 컴포넌트로 분리되어야 하며, 실행 시점에 코드 레벨에서 조립(Composition)되어야 한다.
다음과 같은 논리적 블록으로 모듈화하라:
- System Persona (시스템 페르소나): LLM의 역할과 근본적인 어조를 정의한다. (예:
당신은 엄격한 보안 코드 리뷰어다.) - Task Instruction (핵심 작업 지시): 모델이 수행해야 할 구체적이고 단일한 행동을 정의한다. (예:
주어진 파이썬 코드에서 SQL Injection 취약점을 찾아라.) - Context Loading (컨텍스트 로딩 - RAG/데이터): 동적으로 주입되는 배경 지식이나 과거 대화 내역의 영역이다.
- Constraints & Safeguards (제약 조건 및 안전장치): 하면 안 되는 행동, 길이 제한, 반환 불가 조건 등을 강제한다.
- Output Format (출력 구조 정의): JSON 스키마, XML 태그 등 기계 파싱이 가능하도록 형태를 규정한다.
2. 모듈별 독립적 단위 테스트 전략
프롬프트가 모듈화되면, 비로소 **단위 기능별 테스트 격리(Test Isolation)**가 가능해진다. 개발자는 전체 애플리케이션을 구동하지 않고도 특정 프롬프트 컴포넌트의 신뢰도만을 추적하는 오라클을 설계할 수 있다.
- Output Format 테스트: ’Task Instruction’이나 ’Context’에 무의미한 더미(Dummy) 데이터를 주입하고, 반환된 응답이 사전에 정의된 JSON Schema 오라클과 100% 일치하는지(Syntax Parsing)만을 무한 반복 테스트한다.
- Constraints 테스트 (적대적 테스트): ‘Context Loading’ 영역에 일부러 프롬프트 인젝션 공격 유도문이나 PII(개인식별정보), 금지어를 주입하여 ‘Constraints & Safeguards’ 모듈이 제대로 방어하여 응답을 거부(Reject)하는지를 검증한다.
이러한 격리 테스트는 API 호출 비용(Token Cost)을 극단적으로 낮추고, 실패(Flaky)의 원인을 특정 모듈로 정확히 좁혀준다.
3. 동적 변수 주입과 형틀(Template) 버전 관리
모듈화된 프롬프트는 코드(Code)처럼 선언적으로 다루어져야 한다. f-string이나 템플릿 엔진(예: Jinja2)을 사용하여 동적 변수({{user_input}})를 정의하고, 입력 변수의 경계값 분석(Boundary Value Analysis) 기반 테스트를 수행하라. 완전히 빈 텍스트, 엄청나게 긴 특수문자열, 타겟 언어가 아닌 외국어가 주입되었을 때 조합된 프롬프트 모듈이 어떻게 붕괴하는지 오라클을 통해 추적해야 한다.
더불어, 분리된 각 모듈을 GitHub과 같은 버전 관리 시스템에 개별 파일(예: task_instruction_v1.2.txt, output_format_v2.0.json)로 저장하라(Prompt-as-Code). 특정 Output Format 파일의 변경이 기존의 파서(Parser) 유닛 테스트를 깨트린다면, 곧바로 롤백(Rollback)할 수 있는 체계가 바로 테스트 용이성 설계의 종착지다.