Scaffolded Test-First Prompting: 첫 실행에서 올바른 코드 얻기
It looks like only the source line was provided. Could you please share the text you’d like translated into Korean? Once I have the content, I’ll keep the source line unchanged and translate the rest while preserving all formatting, markdown, and technical terms.
AI‑지원 코딩 실패를 줄이는 Scaffolded Test‑First Prompting
AI를 사용해 코딩을 도와줄 때 가장 흔한 실패 원인은 모델이 게으른 것이 아니라 목표가 흐릿하기 때문입니다.
수정을 요청하면, 어시스턴트가 “올바른” 의미를 추측하고, 겉보기엔 그럴듯하지만 약간 빗나간 결과를 반환합니다: 잘못된 엣지 케이스, 잘못된 파일, 잘못된 추상화, 잘못된 의존성, 혹은 올바른 아이디어를 너무 광범위하게 구현한 경우 등.
그 실패율을 낮추는 간단한 방법은 “수정”을 먼저 묻지 않는 것입니다.
먼저 테스트를 요청하세요.
이를 Scaffolded Test‑First Prompting이라 부릅니다: 어시스턴트에게 실행 가능한 실패 테스트를 작성하게 한 뒤, 그 테스트를 통과시키는 가장 작은 구현을 요구하는 작은 워크플로우입니다. 화려하지는 않지만, 모호한 코딩 요청을 실행 가능한 계약으로 바꿔 줍니다.
왜 효과가 있는가
코드 작업에서 대부분의 프롬프트 문제는 실제로 명세 문제입니다.
예를 들어:
독일 로케일용 통화 포맷터를 고쳐 주세요.
여기에는 숨겨진 질문이 열두 개 정도 있습니다:
- 어떤 파일에 해당 동작이 구현돼 있나요?
- 정확히 어떤 포맷을 기대하나요?
- 어떤 런타임이나 테스트 프레임워크를 사용하고 있나요?
- 외부 라이브러리를 사용할 수 있나요?
- 해결책이 최소한이어야 하나, 아키텍처 수준이어야 하나?
- “완료”의 기준은 무엇인가요?
테스트는 이러한 질문에 prose(문장)보다 명확하게 답합니다. 어시스턴트에게 모호한 의도가 아니라 관찰 가능한 목표를 제공하죠.
이로 인해 즉시 얻는 이점 세 가지:
- 정확성을 실행 가능하게 만든다. 결과를 토론하지 않고 바로 실행해 볼 수 있습니다.
- 범위가 작아진다. 하나의 어서션만으로 충분할 때 전체 레포를 리팩터링할 가능성이 줄어듭니다.
- 리뷰가 쉬워진다. 변경이 실패한 테스트 케이스에 의해 정당화되므로 손으로 설명할 필요가 없습니다.
3단계 워크플로우
1. 최소한의 실행 가능한 컨텍스트 제공
어시스턴트가 작업 위치를 파악할 수 있을 정도의 정보만 제공하세요.
포함 내용:
- 해당 파일 또는 모듈
- 사용 언어/런타임
- 테스트 프레임워크
- 구체적인 입력/출력 예시 하나
- “새로운 의존성 금지”와 같은 제약 조건
예시
I have formatCurrency(amount, locale) in src/format.js.
Runtime: Node 22.
Tests use Jest.
Expected behavior: formatCurrency(12.5, 'de-DE') should return '12,50 €'.
No new dependencies.
이 정도면 충분합니다. 세 문단짜리 배경 설명은 필요 없습니다.
2. 실패 테스트만 요청
핵심 단계입니다. 아직 구현을 요구하지 마세요. 기존 프로젝트 관례를 따르고, 당신이 확인하고 싶은 동작만 검증하는 집중된 테스트 파일 하나를 요청합니다.
예시 프롬프트
Create a Jest test for src/format.js that asserts
formatCurrency(12.5, 'de-DE') returns '12,50 €'.
Only return the test file contents and any minimal config change required.
Do not implement the function yet.
왜 단계를 나누는가?
어시스턴트가 코드를 만들기 전에 기대 동작을 생각하도록 강제하기 때문입니다. 이 한 단계만으로도 큰 편차를 크게 줄일 수 있습니다. 또한 바로 실행 가능한 아티팩트를 얻으니, 테스트 자체가 깨졌거나 모호하거나 관례와 맞지 않을 경우 구현 전에 바로 잡을 수 있습니다.
3. 테스트를 통과시키는 가장 작은 수정 요청
테스트를 실행하고 실패하면, 실패 출력 전체를 붙여넣고 그 정확한 케이스를 만족시키는 가장 작은 코드 변경을 요구합니다.
예시 프롬프트
This test fails with the following output:
[paste stack trace]
Implement the smallest change in src/format.js that makes this test pass.
Avoid unrelated refactors.
Return a unified diff.
이러한 프레이밍이 중요합니다. “가장 작은 변경”과 “관련 없는 리팩터링 금지”는 장식이 아니라, 어시스턴트를 광범위한 재작성 대신 검토 가능한 패치로 이끌기 위한 지시입니다.
구체적인 예시
문제: CSV 내보내기 버그 – 필드에 개행 문자가 포함되면 스프레드시트 앱에서 열 때 깨짐.
모호한 프롬프트는 다음과 같습니다:
CSV 내보내기 이스케이프를 고쳐 주세요.
그런 요청은 거의 광범위한 수정을 유도할 가능성이 높습니다. (이하 내용은 다음 파트에 이어집니다.)
uce a mushy answer.
부드러운 답변을 내세요.
A scaffolded version looks like this instead.
대신에 구조화된 버전은 다음과 같습니다.
Step 1 – 컨텍스트
File: src/export/csv.ts
Runtime: Node 22
Tests: Vitest
Bug: values containing newlines are split into multiple rows in spreadsheet apps
Constraint: preserve the newline; do not replace it with spaces
Step 2 – 테스트 먼저
import { describe, it, expect } from 'vitest';
import { toCsvRow } from '../../src/export/csv';
describe('toCsvRow', () => {
it('quotes fields containing newlines', () => {
const row = toCsvRow(['hello\nworld', 'ok']);
expect(row).toBe('"hello\nworld",ok');
});
});
Step 3 – 최소 구현 요청
Now the assistant has a precise target:
이제 어시스턴트는 정확한 목표를 갖게 됩니다:
- 줄바꿈을 보존한다
- 필드를 따옴표로 감싼다
- 다른 필드는 변경하지 않는다
That is a much tighter problem than “fix CSV export.” You are far more likely to get a correct, local patch instead of a speculative rewrite.
이는 “CSV 내보내기 수정”보다 훨씬 구체적인 문제입니다. 추측에 기반한 전체 재작성보다 올바른 로컬 패치를 받을 가능성이 훨씬 높습니다.
왜 이것이 “코드 작성” 프롬프트보다 자주 더 효과적인가
Direct implementation prompts encourage the model to jump straight to a solution shape. Sometimes that works, but often the assistant commits too early.
직접 구현 프롬프트는 모델이 바로 해결책 형태로 뛰어들게 합니다. 때때로 효과적이지만, 종종 어시스턴트가 너무 일찍 커밋하게 됩니다.
Test‑first prompting delays that commitment. It makes the assistant define success in visible behavior before it chooses structure. That tends to improve outcomes in a few specific ways:
테스트‑우선 프롬프트는 그 커밋을 지연시킵니다. 어시스턴트가 구조를 선택하기 전에 가시적인 동작으로 성공을 정의하게 합니다. 이는 몇 가지 구체적인 방법으로 결과를 개선합니다:
- 불필요한 추상화가 줄어듭니다
- 불필요한 의존성이 감소합니다
- 기존 관례를 더 잘 보존합니다
- 디버깅이 쉬워집니다. 실패한 단언이 탐색 범위를 좁히기 때문입니다
It also helps you think better. Writing or reviewing the test forces clarity. You may notice that the expected output is wrong, the edge case is underspecified, or the real problem is slightly different than you thought.
또한 당신이 더 잘 생각하도록 돕습니다. 테스트를 작성하거나 검토하면 명확성이 확보됩니다. 기대 출력이 잘못됐거나, 경계 사례가 충분히 지정되지 않았거나, 실제 문제가 생각한 것과 약간 다를 수 있음을 발견할 수 있습니다.
이 패턴에 대한 좋은 프롬프트
Below are a few template prompts you can adapt:
아래는 적용할 수 있는 몇 가지 템플릿 프롬프트입니다:
| 목표 | 프롬프트 |
|---|---|
| Provide context | “src/date.js에 parseDate(str) 함수가 있습니다. 런타임: Node 20. 테스트는 Mocha를 사용합니다. 기대: parseDate('2023‑01‑01')은 UTC 자정을 나타내 |
일관된 가이드라인
- “먼저 하나의 집중된 실패 테스트를 작성하세요.”
- “기존 프로젝트 규칙을 사용하세요.”
- “아직 구현하지 마세요.”
- “테스트 파일 내용만 반환하세요.”
- “이 테스트를 통과하게 하는 가장 작은 변경만 구현하세요.”
- “관련 없는 리팩터링을 피하세요.”
- “통합 diff를 반환하세요.”
이러한 작은 제약들을 모두 합치면 워크플로우가 훨씬 더 신뢰할 수 있게 됩니다.
일반적인 실수
한 번에 너무 많은 요청
버그 하나, 테스트 하나, 패치 하나.
한 번에 세 가지 엣지 케이스, 리팩터링, 업데이트된 문서를 요청하면 주요 이점인 긴밀한 피드백을 놓치게 됩니다.
환경을 지정하지 않은 프롬프트
“테스트 작성”이라고만 하고 Jest, Vitest, pytest, 파일 레이아웃 등을 명시하지 않으면 올바른 아이디어가 잘못된 형식으로 나오게 됩니다.
실행하지 않은 테스트를 받아들임
생성된 테스트도 코드이므로 실행해 보세요. 깨진 테스트 파일은 계약이 아니라 보기 좋게 포맷된 또 다른 환상에 불과합니다.
구현이 커지게 두기
테스트가 존재하면 선을 지키세요. “가장 깔끔한 장기 리디자인”이 아니라 가장 작은 통과 가능한 변경을 요청하세요. 정확성이 확보된 후 언제든 리팩터링할 수 있습니다.
사용하지 말아야 할 때
이 패턴은 동작이 명확하고 정확성이 중요한 경우에 빛을 발합니다. 다음과 같은 경우에는 덜 유용합니다:
- 탐색적 프로토타이핑
- 개방형 디자인 작업
- 원하는 동작이 아직 변하고 있는 대규모 리팩터링
그런 경우에는 사양‑우선 또는 계획‑우선 프롬프트가 더 적합할 수 있습니다.
마무리
스캐폴드된 테스트‑우선 프롬프트는 모호한 의도를 실행 가능한 목표로 대체하기 때문에 작동합니다.
AI에게 “fixed”가 무슨 뜻인지 추측하도록 요청하는 대신, 당신은:
- 실패하는 예시를 정의합니다.
- 그 예시를 실행 가능하게 만듭니다.
- 그 예시를 만족시키는 가장 작은 코드 변경을 요청합니다.
이 습관은 보통 더 빠른 수렴, 더 깔끔한 패치, 그리고 덜 이상한 우회 경로를 가져다줍니다.
코딩에 매일 AI를 사용한다면, 이것은 채택하기 가장 쉬운 워크플로우 업그레이드 중 하나입니다: 먼저 테스트하고, 두 번째에 패치하고, 모두 실행합니다.