Claude Code를 도메인 특화 코딩 에이전트로 바꾸는 방법

TMT

https://blog.langchain.com/how-to-turn-claude-code-into-a-domain-specific-coding-agent/

코딩 에이전트는 LLM이 충분히 학습한 인기 라이브러리를 활용한 코드 작성에는 매우 뛰어납니다. 하지만 커스텀 라이브러리, 새로운 버전의 라이브러리, 내부 API, 혹은 특수 프레임워크를 사용해야 할 때는 성능이 떨어집니다. 이는 도메인 특화 라이브러리나 엔터프라이즈 코드를 다루는 팀에게 문제입니다.

라이브러리(LangGraph, LangChain) 개발자로서, 우리는 코딩 에이전트가 LangGraph와 LangChain 코드를 잘 작성할 수 있도록 만드는 방법에 관심이 많습니다. 다양한 컨텍스트 엔지니어링 기법을 시도해봤고, 일부는 효과가 있었고 일부는 그렇지 않았습니다. 이 블로그 포스트에서는 우리가 진행한 실험과 얻은 교훈을 공유합니다. 가장 큰 결론은 다음과 같습니다.

"고품질의 응축된 정보와 필요 시 더 자세한 내용을 확인할 수 있는 도구를 결합하는 것이 최고의 결과를 낸다"

에이전트에게 원본 문서 접근 권한을 주는 것만으로는 기대만큼 성능이 향상되지 않았습니다. 오히려 컨텍스트 윈도우가 더 빨리 채워졌습니다. LangGraph에 특화된 구조화된 가이드(‎⁠Claude.md⁠)를 제공하는 것이 단순히 문서 도구를 연결하는 것보다 항상 더 좋은 결과를 냈습니다. 가장 좋은 결과는 두 가지를 결합했을 때 나왔습니다. 에이전트가 기본 지식(‎⁠Claude.md⁠)을 갖고 있으면서, 더 자세한 정보가 필요할 때 특정 문서 부분에 접근할 수 있도록 하는 방식입니다.

Image

이 글에서는 다음 내용을 공유합니다:

  • 우리가 테스트한 다양한 Claude Code 구성
  • 생성된 코드를 평가하기 위해 사용한 평가 프레임워크(자신의 라이브러리에도 재사용 가능한 템플릿)
  • 결과 및 주요 시사점

Claude Code Setups

일관성을 위해 Claude 4 Sonnet 모델을 사용하여 네 가지 구성을 테스트했습니다:

Claude Vanilla: 별도의 수정 없이 기본 Claude Code.

Claude + MCP: Claude Code가 MCPDoc 서버에 연결되어 문서 접근 가능.

Claude + Claude.md: LangGraph 특화 가이드가 담긴 ‎⁠Claude.md⁠ 파일을 추가한 Claude Code.

Claude + MCP + Claude.md: 상세 ‎⁠Claude.md⁠와 MCPDoc 서버 모두에 접근 가능한 Claude.

MCP 문서 도구

우리는 코딩 에이전트가 어떤 라이브러리의 문서든 접근할 수 있도록 MCPDoc 서버를 만들었습니다. 오픈소스 MCP 서버로, ‎⁠list_doc_sources⁠와 ‎⁠fetch_docs⁠ 두 가지 도구를 제공합니다. 첫 번째는 사용 가능한 ‎⁠llms.txt⁠ 파일의 URL을 공유하고, 두 번째는 특정 ‎⁠llms.txt⁠ 파일을 읽습니다. 우리의 설정에서는 LangGraph와 LangChain의 Python 및 JavaScript 문서에 접근할 수 있도록 했습니다. MCP 설정에서 자신의 라이브러리 ‎⁠llms.txt⁠ 파일의 URL을 전달하면 쉽게 적용할 수 있습니다.

Image

Claude.md

‎⁠Claude.md⁠는 LangGraph 라이브러리 가이드로, LangGraph 프로젝트 구조의 필수 요구사항(파일 생성 전 코드베이스 검색, 올바른 export 패턴, 배포 모범 사례 등)에 대한 상세 지침을 담았습니다. 단일 및 다중 에이전트 시스템 구축에 필요한 프리미티브(예: ‎⁠create_react_agent⁠, supervisor 패턴, 동적 handoff를 위한 swarm 패턴) 샘플 코드도 포함했습니다. LLM이 어려워하는 스트리밍, Human-in-the-loop 에이전트 구현에 대해서도 광범위한 가이드라인을 추가했습니다.

Image

특히, 흔히 발생하는 실수와 안티패턴에 대한 포괄적 섹션이 매우 유용했습니다. 예를 들어, 잘못된 ‎⁠interrupt()⁠ 사용, 잘못된 상태 업데이트 패턴, 타입 가정 오류, 과도하게 복잡한 구현 등입니다. 이런 실수는 주로 라이브러리의 구버전이나 다른 프레임워크 패턴과 혼동해서 발생했습니다.

LangGraph 특화 코딩 표준(구조화된 출력 검증, 올바른 메시지 처리, 프레임워크 통합 디버깅 패턴 등)도 포함했습니다. Claude가 웹 도구에 접근할 수 있으므로, 각 섹션 끝에 참고용 문서 URL과 탐색 가이드라인을 추가했습니다.

이 파일은 ‎⁠llms.txt⁠와 다르게, 페이지의 모든 내용을 담은 일반 텍스트 파일이 아니라, 처음부터 시작할 때 가장 중요한 응축된 정보를 담고 있습니다. 결과에서 보듯, ‎⁠llms.txt⁠만 전달하면 LLM이 더 많은 컨텍스트와 탐색 지침 없이 혼란을 겪는 경우가 많았습니다.

각 Claude Code 구성의 성능을 살펴보기 전에, 우리가 사용한 평가 프레임워크를 공유합니다.

Evaluations

우리는 단순 기능뿐 아니라 코드 품질에 가장 크게 기여하는 요소를 측정하는 것이 목표였습니다. Pass@k와 같은 인기 지표는 기능만 평가하며, 모범 사례는 상황에 따라 다릅니다.

기술적 요구사항과 주관적 요소(코드 품질, 설계 선택, 선호하는 방식 준수 등) 모두를 확인하는 작업별 평가 하니스(harness)를 구축했습니다.

평가는 세 가지 범주로 나뉩니다:

Smoke Tests

기본 기능을 확인합니다. 코드가 컴파일되는지, ‎⁠.invoke()⁠ 메서드를 노출하는지, 예상 입력 상태를 처리하는지, ‎⁠AIMessage⁠ 객체와 같은 출력 구조를 반환하는지 등을 테스트합니다.

점수는 가중치 합산 방식으로 계산합니다:

Score = Σᵢ wᵢ × cᵢ

여기서 _wi_는 테스트 _i_의 가중치, _ci_는 테스트 결과(이진값)입니다.

Task Requirement Tests

작업별 기능을 검증합니다. 배포 설정 파일 검증, 외부 API(웹 검색, LLM 제공자 등)로의 HTTP 요청 확인, 각 코딩 작업별 단위 테스트 등이 포함됩니다. 점수 계산 방식은 smoke test와 동일하게 가중치 합산입니다.

Code Quality & Implementation Evaluation

이 범주는 LLM-as-a-Judge를 활용해 이진 테스트로는 잡히지 않는 부분을 평가합니다. 더 나은 접근법을 따른 구현이 단순히 컴파일·실행되는 것보다 높은 점수를 받도록 했습니다. 코드 품질, 설계 선택, LangGraph 추상화 활용 등은 미묘한 평가가 필요합니다.

각 작업별로 전문가가 작성한 코드를 검토해 평가 기준(rubric)을 만들었습니다. Claude Sonnet 4(‎⁠claude-sonnet-4-20250514⁠)를 온도 0으로 설정해, 생성된 코드를 기준에 따라 평가하고, 컴파일·런타임 오류는 사람이 주석으로 기록했습니다.

루브릭에는 두 가지 기준이 있습니다:

Objective Checks: 코드에 대한 이진적 사실(특정 노드 존재, 올바른 그래프 구조, 모듈 분리, 테스트 파일 없음 등). LLM judge가 각 체크에 대해 불리언 응답을 반환하고, 가중치 합산으로 점수를 계산합니다.

Subjective Assessment: 전문가 코드와 비교해 코드의 질을 정성적으로 평가하며, 컴파일·런타임 오류 로그를 사람이 주석으로 전달합니다. LLM judge가 문제를 심각도(치명적, 주요, 경미)별로 분류하고, 두 가지 차원(정확성 위반, 품질 문제)에서 평가합니다.

이때는 페널티 기반 점수제를 사용합니다:

Score = Scoreₘₐₓ - Σₛ (nₛ × pₛ)

여기서 Score_max_는 최대 점수, _ns_는 심각도 _s_별 위반 개수, _ps_는 해당 심각도 페널티 가중치입니다.

최종 점수는 객관적·주관적 결과를 모두 합산합니다:

Score = Σᵢ wᵢ × cᵢ + Σₛ (Scoreₘₐₓ,ₛ - Σₛ (nₛ × pₛ))

첫 번째 항은 객관적 체크, 두 번째 항은 모든 주관적 카테고리의 평가입니다.

각 Claude Code 구성을 작업별로 세 번씩 실행해 편차를 반영했습니다. 모든 점수는 전체 가능 점수 대비 백분율로 보고하며, 작업별 평균을 냅니다.

자신의 라이브러리에도 이 방식을 LangSmith 플랫폼을 활용해 코딩 에이전트 구성을 비교할 수 있습니다.

Results

세 가지 LangGraph 작업에 대해 점수를 평균내어 Claude Code 구성을 비교했습니다. 아래 차트는 전체 점수를 보여줍니다:

Image

가장 흥미로운 점은 Claude + ‎⁠Claude.md⁠가 Claude + MCP보다 더 좋은 성능을 보였다는 것입니다. ‎⁠Claude.md⁠에는 MCP 서버가 제공할 수 있는 정보의 일부만 포함되어 있었음에도 불구하고 말입니다. 트레이스 분석 결과, Claude가 MCP 도구를 기대만큼 자주 호출하지 않았기 때문입니다. 작업에 두세 개의 연결된 페이지를 따라가야 할 때도, MCP를 한 번만 호출하고 메인 페이지에서 멈추는 경우가 많았는데, 이 페이지는 표면적인 설명만 제공하고 필요한 세부 정보는 없었습니다.

반면 Claude + ‎⁠Claude.md⁠ + MCP는 문서를 더 효과적으로 활용했습니다. 트레이스에서 MCP 도구를 더 자주 호출하고, 필요할 때 웹 검색 도구도 트리거하는 것을 확인했습니다. 이는 각 섹션 끝에 참고 URL을 추가한 ‎⁠Claude.md⁠의 영향이었습니다.

MCP 도구 자체도 도움이 되지 않은 것은 아닙니다. 기본 문법과 개념을 잡아주는 역할로 점수를 약 10% 포인트 올려줬습니다. 하지만 작업 완성도와 코드 품질 측면에서는 ‎⁠Claude.md⁠가 더 중요했습니다. 가이드에는 피해야 할 실수와 따라야 할 원칙이 포함되어 있어, Claude Code가 더 깊이 생각하고 라이브러리의 다양한 부분을 탐색하도록 도왔습니다. 단순히 상위 설명에서 멈추지 않게 해줬습니다.

이 결과는 코딩 에이전트 구성에 관심 있는 모든 사람에게 몇 가지 교훈을 줍니다.

Key Takeaways

결과를 통해 얻은 시사점은 다음과 같습니다. 자신의 라이브러리에 맞게 코딩 에이전트를 커스터마이즈하려 한다면 참고할 만합니다:

컨텍스트 과부하: 문서의 ‎⁠llms.txt⁠ 파일을 대량으로 덤프하면 컨텍스트 윈도우가 금방 채워집니다. 성능 저하와 비용 증가로 이어질 수 있습니다. 우리의 MCP 서버는 페이지 전체를 가져오는 단순 구현이었는데, 한 번만 호출해도 Claude Code에서 컨텍스트 윈도우 경고가 떴습니다. 문서가 충분히 방대하다면, 관련 스니펫만 뽑아오는 스마트한 검색 도구를 만드는 것이 좋습니다.

Claude.md가 가장 큰 효과: MCP 서버나 별도 도구보다 설정이 쉽고, 운영 비용도 저렴합니다. 작업 #2에서 Claude + ‎⁠Claude.md⁠는 Claude MCP 및 Claude + ‎⁠Claude.md⁠ + MCP보다 약 2.5배 저렴했습니다. 성능도 더 좋았습니다. Claude MCP보다 저렴하면서 더 좋은 성능을 내므로, Claude Code 커스터마이즈를 시작할 때 좋은 선택이 될 수 있습니다.

좋은 지침을 작성하라. ‎⁠Claude.md⁠(혹은 ‎⁠Agents.md⁠)에는 핵심 개념, 고유 기능, 라이브러리의 주요 프리미티브를 강조해야 합니다. 실패한 실행을 수동으로 검토해 반복되는 실수를 찾아 가이드에 추가하세요. 우리 경우 LangGraph에서 Streamlit을 활용한 비동기 작업, 에이전트가 ‎⁠asyncio⁠ 통합에 자주 실패하는 부분을 다뤘습니다. 개발 서버를 띄우는 디버깅 단계도 추가해, Claude Code가 서버에 요청을 보내 결과를 검증할 수 있도록 했습니다. 인기 코드 생성 도구는 보통 7~10k 토큰의 긴 시스템 프롬프트를 사용합니다. 지침에 공을 들이면 효과가 큽니다.

Claude + Claude.md + MCP가 최고: ‎⁠Claude.md⁠가 토큰당 가장 큰 효과를 내지만, MCP 서버와 함께 사용하면 문서를 상세히 읽을 수 있어 최고의 결과를 냅니다. 가이드가 개념을 잡아주고, 문서가 심층 탐구를 돕습니다. 두 가지를 결합하면 도메인 특화 라이브러리에서 최고의 성능을 낼 수 있습니다.

부록에는 작업별 결과와 카테고리별 그래프를 포함했습니다. 작업별 성능이 궁금하다면 참고하세요.

Appendix

Task #1: Text-to-SQL Agent

각 구성에 LangGraph 기반 텍스트-투-SQL 에이전트를 만들도록 요청했습니다. 자연어를 SQL 쿼리로 변환하고, 데이터베이스에서 실행한 뒤 자연어 응답을 반환해야 했습니다. Chinook SQLite 데이터베이스를 원격 URL에서 가져와 인메모리 DB로 설정하는 작업이 포함되었습니다. Claude Code 인스턴스에 전달한 프롬프트는 여기 에서 볼 수 있습니다.

이 작업에서는 smoke test로 LangGraph 기본 기능을 확인했습니다. 작업 요구사항으로 DB 설정, SQL 쿼리 처리(단순 쿼리, join 쿼리, 날짜 범위 쿼리), LLM-as-a-Judge로 코드 설계(원격 URL 가져오기, SQL 생성·실행·응답을 위한 별도 노드 등)를 평가했습니다. LLM-as-a-Judge 프롬프트는 여기에서 확인할 수 있습니다.

구성별 성능 차이는 다음과 같습니다:

Image Image

잘못된 구현은 인메모리 DB를 스레드 간 연결하는 데 실패하거나, LLM 프롬프트에 스키마를 하드코딩하고 원격 URL을 통한 런타임 스키마 읽기를 사용하지 않았으며, LLM 출력에서 SQL 실행을 위한 파싱에 실패해(LLM이 약간 다른 형식의 결과를 생성할 때) 문제가 발생했습니다.

Task #2: Company Researcher

이 작업에서는 각 Claude 구성에 웹 검색(Tavily API)을 활용해 기업을 조사하는 다중 노드 LangGraph 에이전트를 만들도록 요청했습니다. 구조화된 데이터 수집, 병렬 검색 실행, 모든 요청 정보를 수집하는 반영(reflection) 단계 구현이 필요했습니다. 프롬프트는 여기에서 볼 수 있습니다.

기본 기능, Tavily API 통합, 구조화 객체 클래스에 모든 요청 속성 포함 여부를 테스트했습니다. LLM-as-a-Judge는 반영 로직, 최소 검색 쿼리 제한, 병렬 웹 검색 실행 구현 여부를 평가했습니다.

이 작업의 결과는 다음과 같습니다:

Image Image

구현 실패는 주로 상태에 정보를 구조화해 담거나 반영 단계 구현에 관련되어 있었습니다. 반영 노드가 제대로 동작하지 않거나 추가 검색을 트리거하지 못하는 경우가 많았습니다.

Task #3: Categories of Memories

기존 메모리 에이전트 코드를 각 Claude Code 구성에 제공하고, 메모리 저장 방식을 사용자 ID 외에 유형(개인, 업무, 기타)별로 분류하도록 확장, 메시지 유형별로 선택적 메모리 조회, 저장 전 사용자 확인(휴먼 인 더 루프) 단계 추가를 요청했습니다. 일부러 문법 오류도 포함했습니다. 전체 프롬프트는 여기에서 볼 수 있습니다.

테스트에서는 인터럽트 기능 추가, 유형별 저장·조회, 세 가지 카테고리(개인, 업무, 기타) 구현, 인터럽트 로직이 정상적으로 동작해 사용자가 승인할 때만 저장되는지 확인했습니다. LLM-as-a-Judge는 키워드 매칭이 아닌 LLM 기반 분류, 불필요한 파일 생성 여부 등을 평가했습니다.

이 편집 작업의 결과는 다음과 같습니다:

Image Image

대부분의 구현은 인터럽트 기능을 올바르게 구현하지 못했습니다. 잘못된 구현은 단순히 ‎⁠input()⁠ 호출로 터미널 입력을 받거나, 불필요하게 별도 노드를 만들어 과도하게 복잡하게 처리했습니다. 잘못된 구현은 키워드 매칭에 의존해 분류하거나, 우리가 의도적으로 넣은 문법 오류를 거의 잡아내지 못했습니다.

Edit this page

On this Page