Claude Code의 결과를 실제로 개선하는 XML 프롬프팅 시스템
요약
Claude Code 사용 시 에이전트의 성능을 극대화하기 위해 XML 구조를 활용한 프롬프팅 전략을 제안합니다. 단순한 명령 대신 XML 태그를 통해 작업 범위, 제약 사항, 컨텍스트를 명확히 정의함으로써 에이전트의 추측을 방지하고 결과물의 품질을 높일 수 있습니다.
핵심 포인트
- 에이전트의 품질은 모델 성능보다 작업 설정(Context)에 좌우됨
- XML 태그를 사용하여 작업 범위를 명시하고 체크리스트 역할을 부여
- 긴 세션의 컨텍스트 오염을 방지하기 위해 새로운 세션에서 시작 권장
- 단일하고 밀도 높은 자기 완결적 프롬프트 작성의 중요성
몇 달 동안 Claude Code를 매일 사용해 오면서, 사람들이 듣고 싶어 하지 않는 사실을 하나 말씀드리자면 이렇습니다. 에이전트가 내놓는 결과물의 품질은 모델과는 거의 상관이 없으며, 거의 전적으로 당신이 작업을 어떻게 설정하느냐에 달려 있습니다. 더 똑똑한 모델로 교체하는 것은 아주 약간의 변화만 줄 뿐입니다. 작업을 잘 구성하는 것은 훨씬 더 큰 변화를 가져옵니다. 그리고 아니요, 제가 말하는 것은 전문가들이 말하는 방식의 "프롬프트 엔지니어링 (Prompt Engineering)"이 아닙니다. 마법의 단어나 "10배 개발자처럼 행동하라" 같은 그런 서커스 같은 것들 말입니다. 제가 말하는 것은 아무도 하고 싶어 하지 않는 지루한 구조적 부분입니다. 에이전트에게 무엇을 건드리지 말아야 할지 알려주는 것. 에이전트가 파일을 찾아 헤매게 만드는 대신 실제 파일을 직접 지목해 주는 것. 이미 내린 결정을 다시 논쟁하지 않도록 미리 적어두는 것. 그런 것들 말입니다. 이는 지루한 작업이며, 솔직히 바로 그 점 때문에 효과가 있습니다. 거의 아무도 하지 않기 때문이죠.
제가 계속 마주쳤던 실패 모드는 다음과 같습니다. 당신은 "로그인 엔드포인트에 속도 제한 (rate limiting)을 추가해 줘"라고 한 줄로 요청을 입력하고, 그것으로 충분하다고 느낍니다. 하지만 그렇게 짧게 작성하면 당신이 이미 알고 있는 모든 정보가 당신의 머릿속에만 갇힌 채 남게 됩니다. 그 기능이 어느 파일에 있는지, 지난주에 슬라이딩 윈도우 (sliding window) 방식을 사용하기로 했던 결정, 프론트엔드는 건드리면 안 된다는 사실 등 말이죠. 에이전트는 그중 아무것도 알지 못하므로 빈틈을 채우려 할 것이고, LLM이 빈틈을 채우는 방식은 그저 확신을 가지고 추측하는 것뿐입니다. 한 줄짜리 요청은 기본적으로 후속 질문을 할 수 없는 사람에게 보내는 메모와 같습니다. 당신에게 슬랙 (Slack)으로 다시 물어볼 방법이 없는 새로운 계약자에게 그 한 줄을 건네준다고 생각해 보세요. 그들은 무엇을 할까요? 추측할 것입니다. 여기서도 마찬가지입니다. 다만 에이전트는 즉시 추측하고, 당신이 멈추기도 전에 그 추측을 바탕으로 코드를 작성하기 시작한다는 점이 다를 뿐입니다.
이 문제의 나머지 절반은 긴 세션 (long sessions)에 있습니다. 어떤 상황인지 아실 겁니다... 작업 A를 시작했다가 작업 B로 흘러가고, 버그를 잡고, 마음을 바꾸고, 계속 진행합니다. 메시지가 40개쯤 쌓이면 에이전트는 3개 전 작업의 반쯤 죽어가는 컨텍스트 (context)를 질질 끌고 다닙니다. 이미 되돌린 결정을 어설프게 기억하고 있습니다. 한 시간 전에 더 이상 신경 쓰지 않기로 한 파일에 대해 여전히 "인지"하고 있습니다. 그리고 이런 표류는 일어나는 동안에는 눈에 보이지 않습니다.
그것은 diff(차이점)가 잘못 돌아왔을 때, 그리고 당신이 앉아서 "잠깐, 왜 이걸 건드렸지?"라고 생각하게 될 때 눈에 보입니다. 그래서 제가 찾아낸 해결책은 두 가지인데, 그 방식이 너무 단순해서 다소 어리석게 느껴질 수도 있습니다. 첫째, 해당 작업을 위해 단일하고 밀도 높은, 자기 완결적인 XML 프롬프트 (XML prompt)를 작성합니다. 둘째, 히스토리가 전혀 없는 완전히 새로운 세션에 그것을 붙여넣습니다. XML을 사용하는 이유는 태그가 마법 같아서가 아닙니다. 마법은 아니며, 일반적인 마크다운 (Markdown)도 잘 작동합니다. 태그는 당신이 건너뛸 수 없는 체크리스트 역할을 합니다. 빈 <scope> 태그는 당신이 내용을 채울 때까지 그저 거기 앉아 당신을 응시하지만, 빈 문단은 당신이 그냥 지나쳐 버리게 만듭니다. 그것이 실제 비결입니다. 이 형식은 당신이 평소라면 건너뛰었을 질문들을 억지로 끌어냅니다. 다음은 해당 rate-limit (속도 제한) 작업에 대한 프롬프트의 대략적인 모습입니다:
<claude_code_prompt>
<role> FastAPI + Postgres 백엔드의 시니어 엔지니어. 스스로 작업하되, 코드에 체크인하지 않은 사항은 절대 주장하지 말 것. 정보가 부족한가? 그렇다고 말할 것. 추측으로 대충 덮어버리지 말 것. </role>
<mission> POST /api/login에 IP당 rate limiting (속도 제한) 추가: 60초당 5회 시도 후 429 반환. </mission>
<scope>
<in>/login 핸들러와 해당 throttle (스로틀) 미들웨어</in>
<out>다른 엔드포인트, DB 스키마, 또는 프론트엔드는 건드리지 말 것</out>
</scope>
<context> Redis는 이미 app.state.redis에 연결되어 있음. 지난주에 토큰 버킷 (token bucket) 대신 슬라이딩 윈도우 (sliding window) 방식을 선택함. 이는 결정된 사항이므로 다시 논의하지 말 것. </context>
<code_anchors>
<primary_file>auth/login.py:42 - 보호해야 할 핸들러</primary_file>
<pattern_to_reuse>middleware/throttle.py:18 - 이 리미터를 복사해서 사용하고, 새로 만들지 말 것</pattern_to_reuse>
</code_anchors>
<success_criteria>
- 60초 이내의 6번째 요청은 429를 반환해야 함
- 기존 로그인 테스트가 여전히 통과해야 함
</success_criteria>
<verification_commands>pytest tests/auth -q</verification_commands>
</claude_code_prompt>
이것이 실제 구조입니다. 영리한 기술은 없으며, 에이전트 (Agent)가 필요로 하는 모든 것이 당신의 머릿속과 40개의 오래된 메시지에 흩어져 있는 대신 한곳에 모여 있을 뿐입니다. 각 태그가 왜 그 자리에 있어야 하는지 빠르게 살펴보겠습니다. 이 중 어느 것도 임의로 정해진 것이 없기 때문입니다.
미션(Mission)은 단 한 문장, 단 하나의 결과물이어야 하며, 그 제약 조건이 실제로 효과를 발휘합니다. 만약 미션이 한 문장에 담기지 않는다면, 그것은 하나의 작업이 아니라 두 개의 작업이며, 분리해야 합니다. 모호한 미션은 매번 모호한 diff (차이점)를 만들어냅니다. 범위(Scope), 특히 <out> 태그는 모두가 건너뛰지만 가장 중요한 부분입니다. "DB 스키마나 프론트엔드는 건드리지 마세요."라는 내용을 빠뜨리면, 에이전트는 자신이 어디까지 개입할지를 스스로 결정하며, 그 범위는 매우 넓어집니다. 그 한 줄의 차이가 30줄짜리 diff와 400줄짜리 diff를 가릅니다. 후자의 경우, 에이전트가 작업 중에 무언가를 개선하고 있다고 판단하여 여러분의 인증(auth) 모듈 절반을 "친절하게" 리팩터링(refactoring)해 버릴 수도 있습니다. 코드 앵커(Code anchors)는 환각 (hallucination)을 방지하는 핵심 요소입니다. "관련 파일"이라고 지칭하면 에이전트는 찾아보려 할 것이고, 찾아본다는 것은 추측을 의미하며, 그러다 보면 가끔은 이름만 그럴싸하고 실제로는 맞지 않는 파일에 도달하게 됩니다. auth/login.py:42와 같이 명시하면 에이전트가 방황할 여지가 없습니다. 복사할 패턴을 전달하는 것도 마찬가지입니다. "rate limiter를 작성하세요"보다는 "middleware/throttle.py:18에 있는 limiter를 재사용하세요"가 훨씬 낫습니다. 그래야 에이전트가 나중에 삭제하고 싶어질 새로운 스타일을 발명하는 대신, 여러분의 기존 코드와 일치하는 코드를 작성하기 때문입니다. 성공 기준 (Success criteria)과 검증 명령 (verify command)은 사람들이 간과하는 부분입니다. 에이전트에게 pytest tests/auth -q와 같이 실제로 실행할 명령을 주면, 에이전트는 자신의 작업물을 스스로 점검합니다. 여러분이 diff를 확인하기도 전에 스스로 저지른 실수 중 절반 정도는 잡아내고 수정할 것입니다. 마치 치트키를 쓰는 것 같지만, 사실은 그렇지 않습니다. 여러분은 단지 에이전트에게 '자신이 생각하는 완료 시점'이 아니라 '실제로 완료된 시점'을 알 수 있는 방법을 제공했을 뿐입니다. 하지만 저를 가장 놀라게 했던 부분은 새로운 세션 (fresh session) 활용법이었습니다. 지금까지 쌓아온 모든 컨텍스트 (context)를 버리는 것이 역행하는 것처럼 느껴질 수 있습니다. 하지만 긴 세션은 "풍부한 컨텍스트"가 아니라 대부분 축적된 쓰레기일 뿐입니다. 밀도 높은 프롬프트 (prompt)와 함께 깨끗한 세션을 여는 것은, 브라우저 탭 40개를 모두 닫고 필요한 3개만 여는 것과 같습니다. 에이전트의 모든 주의력이 배경에서 조용히 썩어가는 것 없이, 상세히 기술된 단 하나의 작업에만 집중하게 됩니다. 결과물은 훨씬 더 집중된 형태로 돌아오며, 이는 매우 확연한 차이를 보입니다.
단점도 있습니다. 단점이 분명히 존재하며, 어떤 기술이든 공짜라고 파는 사람은 무언가를 팔고 있는 것이기 때문입니다. 초기 작업량이 더 많습니다. 한 줄짜리 명령어를 바로 날리는 대신, 프롬프트(prompt)를 작성하는 데 몇 분을 더 써야 합니다. 당연히 오타 수정이나 이름 변경(rename) 같은 사소한 작업에는 과한 방식이며, 그런 경우에는 저도 여전히 한 줄만 입력하고 넘어갑니다. 이 방식은 실제로 움직이는 부품(moving parts)이 있는 모든 작업에 적합합니다. 더 뼈아픈 단점은, 잘못된 사실을 바탕으로 구축된 프롬프트는 모호한 프롬프트보다 더 나쁘다는 점입니다. 모호한 프롬프트는 에이전트(agent)를 조심스럽게 만들어, 확답을 피하거나 질문을 던지게 합니다. 하지만 자신만만한 잘못된 프롬프트는 에이전트를 자신만만하게 틀리게 만듭니다. 핸들러(handler)가 실제로는 88번 라인에 있는데 42번 라인을 가리키면, 에이전트는 당신을 믿고 잘못된 지점에서 작업을 수행하다가 완전히 탈선해 버립니다. 따라서 file:line 정보는 반드시 실제와 일치해야 합니다. 재미있게도, 바로 그 이유 때문에 저는 경로를 기억해서 입력하는 것을 그만두고 무언가가 코드를 먼저 읽도록 하기 시작했습니다. 그리고 결국 그 방식에 도달했습니다. 매번 스켈레톤(skeleton)을 직접 손으로 쓰는 것에 질려버려서, 이를 /prompt라는 슬래시 명령어(slash command)로 감싸버렸습니다. 제가 원하는 것을 평이한 영어로 설명하면, 그것이 실제 코드를 읽고, 실제 file:line 정보가 이미 삽입된 채로 채워진 XML을 저에게 돌려줍니다. 지루함과 잘못된 경로 문제를 한 번에 해결합니다. 하지만 시작할 때 반드시 그것이 필요한 것은 아니며, 솔직히 말씀드리면 권장하지도 않습니다. 그냥 이 빈 템플릿(template)을 가져가서 직접 채워 넣으세요:
이것이 전부입니다. 각 태그를 정직하게 채우기만 해도, 대부분의 한 줄짜리 프롬프트는 이미 저 멀리 뒤처지게 됩니다. 처음 몇 번은 직접 손으로 해보세요. 어디서 문제가 발생하는지 몸소 느껴보시기 바랍니다.
그 원리가 이해되고 수동 작업이 짜증 나기 시작할 때, 바로 그때가 명령어로 감싸야 할 시점이지 그 전이 아닙니다. 태그 자체가 핵심은 아니었습니다. 에이전트가 추측하기 전에 태그에 답하도록 강제하는 것... 그것이 핵심입니다. 이 내용이 도움이 되길 바랍니다. /u/cryptoviksant 제출 [link] [comments]
AI 자동 생성 콘텐츠
본 콘텐츠는 r/ClaudeAI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기