콘텐츠로 이동

Ralph 기법: 무한 루프의 실전

Geoffrey Huntley가 공개한 Ralph 기법의 핵심은 단 한 줄의 셸 명령이다.

Terminal window
while :; do cat PROMPT.md | claude-code; done

이것이 전부다. while : 는 무한 루프, cat PROMPT.md | claude-code 는 프롬프트 파일을 읽어 Claude Code에 파이프로 전달한다. 매 이터레이션마다 PROMPT.md 를 새로 읽기 때문에, 루프 도중 프롬프트를 편집하면 다음 이터레이션부터 즉시 반영된다.

이 단순함 뒤에 정교한 원칙들이 있다.

Ralph의 이름 — 단순함을 존중하라

섹션 제목: “Ralph의 이름 — 단순함을 존중하라”

“Ralph”는 The Simpsons의 Ralph Wiggum에서 따왔다. 가장 단순하고, 어리숙하지만, 이상하게 효과적인 캐릭터다. Huntley의 요점은 아이러니를 담고 있다. AI 에이전트에게 필요한 것이 정교한 오케스트레이션 프레임워크가 아니라, 단순하고 규율 있는 루프일 수 있다.

Claude Code 자체가 강력한 에이전트 루프를 내장하고 있다. Ralph는 그 위에 외부 루프 하나를 추가할 뿐이다. 내부 루프(Claude Code)는 한 번의 태스크를 해결하고, 외부 루프(while)는 그것을 계속 반복한다.

Ralph 루프는 한 번에 하나의 일만 한다. “전체 앱을 만들어라”가 아니라 “로그인 폼 유효성 검사를 추가하라”. 범위가 좁을수록 성공률이 높고, 실패했을 때 원인을 파악하기 쉽다.

나쁜 PROMPT.md:
"인증, 대시보드, 알림 시스템, 결제 모듈을 구현하라"
좋은 PROMPT.md:
"이메일 입력 필드에 RFC 5322 형식 유효성 검사를 추가하라.
실패 시 '올바른 이메일 주소를 입력하세요' 메시지를 표시하라.
완료 후 npm test를 실행해 기존 테스트가 통과하는지 확인하라."

PROMPT.md는 단순한 지시 한 줄이 아니다. 에이전트가 작업을 완료하는 데 필요한 모든 컨텍스트를 담은 문서다.

┌─────────────────────────────────────────────────────────────────┐
│ PROMPT.md 구조 예시 │
└─────────────────────────────────────────────────────────────────┘
## 목표
로그인 폼에 이메일 유효성 검사를 추가한다.
## 현재 상태
- src/components/LoginForm.tsx 에 폼 컴포넌트 있음
- 현재 이메일 필드는 빈 값 검사만 함
- 테스트: src/__tests__/LoginForm.test.tsx
## 완료 조건
- [ ] 올바른 이메일 형식만 허용
- [ ] 잘못된 형식 시 인라인 오류 메시지 표시
- [ ] 기존 테스트 통과 유지
- [ ] 새 유효성 검사 테스트 추가
## 제약
- React Hook Form 라이브러리 사용 중
- 외부 이메일 검증 라이브러리 추가 금지
- TypeScript strict mode 준수

원칙 3: 스펙을 안정적 컨텍스트로

섹션 제목: “원칙 3: 스펙을 안정적 컨텍스트로”

PROMPT.md는 루프가 돌아가는 동안 **변경되지 않는 진실의 원천(source of truth)**이다. 에이전트가 코드를 수정하고, 파일을 생성하고, 테스트를 실행해도 PROMPT.md는 그대로다. 다음 이터레이션이 시작될 때 에이전트는 다시 명확한 목표를 확인한다. 이것이 5-5절에서 다룬 “신선 컨텍스트(fresh context)” 전략이다.

Ralph 루프에서 루프를 종료하는 신호는 테스트 통과다. PROMPT.md에 “npm test 통과”를 완료 조건으로 명시하면, 에이전트는 테스트가 통과할 때까지 코드를 수정한다. 테스트가 곧 루프의 종료 조건이 되는 것이다.

Terminal window
# 테스트를 백프레셔로 활용하는 PROMPT.md 끝부분 예시
## 완료 기준
마지막 행동으로 반드시 `npm test` 실행하고,
모든 테스트가 통과한 상태에서 종료하라.
실패한 테스트가 있으면 수정 재실행하라.

Ralph 루프는 각 이터레이션이 깨끗한 컨텍스트로 시작한다. 이전 이터레이션의 실패, 중간 결과, 내부 추론이 누적되지 않는다. PROMPT.md와 현재 코드베이스 상태만이 다음 이터레이션의 입력이다. 이것은 맥락 부패(context rot)를 자연스럽게 방지한다.

원칙 6: 모델이 아니라 프롬프트를 튜닝하라

섹션 제목: “원칙 6: 모델이 아니라 프롬프트를 튜닝하라”

루프가 원하는 대로 작동하지 않을 때, Ralph 기법은 모델을 바꾸거나 복잡한 오케스트레이션을 추가하는 대신 PROMPT.md를 개선하라고 권고한다. 지시가 모호했는가? 완료 조건이 불명확했는가? 제약이 빠져 있었는가? 대부분의 경우 문제는 루프 구조가 아니라 프롬프트의 명확성에 있다.

while :; do cat PROMPT.md | claude-code; done
┌───────┴──────────────────────────────────────────┐
│ 이터레이션 1 │
│ PROMPT.md 읽기 → claude-code 실행 │
│ (Claude Code 내부 루프: 파일읽기·수정·테스트) │
│ → 테스트 실패 → 종료 (실패 상태로) │
└───────┬──────────────────────────────────────────┘
│ 자동으로 다음 이터레이션 시작
┌───────┴──────────────────────────────────────────┐
│ 이터레이션 2 │
│ PROMPT.md 다시 읽기 (신선 컨텍스트) │
│ 이전 실패와 무관하게 새로 시작 │
│ 현재 코드 상태 + PROMPT.md 목표로 작업 │
│ → 테스트 통과 → 종료 (성공 상태로) │
└───────┬──────────────────────────────────────────┘
(루프 수동 중단 또는 성공 조건 감지)

Ralph 기법은 모든 상황에 맞지 않는다. 다음 조건에서 특히 효과적이다.

  • 반복적 코딩 작업: 유사한 패턴을 여러 파일에 적용하거나, 점진적 기능 추가
  • 테스트 주도 개발: 테스트가 명확한 종료 조건 역할을 할 수 있을 때
  • 범위가 잘 정의된 태스크: 무엇이 완료인지 PROMPT.md에 명확히 쓸 수 있을 때
  • 프롬프트를 반복 개선할 때: 루프가 돌아가는 동안 PROMPT.md를 수정해 즉시 테스트 가능

반면 장시간의 계획 수립, 다중 에이전트 협업, 외부 API 실시간 통합이 필요한 복잡한 작업에는 더 정교한 루프 구조가 필요할 수 있다.

다음 챕터에서는 Ralph 기법을 확장한 스펙 주도 루프 설계를 살펴본다. PROMPT.md를 넘어, specs 디렉터리와 AGENTS.md를 활용한 더 체계적인 접근이다.

참고 자료