본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 02. 18:05

매번 코딩 에이전트에게 코드베이스를 다시 설명하는 것에 지쳐서 — 핵심 메모리를 코드 옆 레포지토리에 저장하도록 만들었습니다

요약

코딩 에이전트를 교체할 때마다 발생하는 컨텍스트 손실 문제를 해결하기 위해, 프로젝트의 핵심 메모리를 레포지토리에 Markdown 형식으로 저장하고 MCP를 통해 공유하는 'agent-memory' 프로젝트를 소개합니다.

핵심 포인트

  • 에이전트 교체 시 발생하는 작업 메모리 유실 문제 해결
  • Markdown 기반의 프로젝트 컨텍스트를 코드와 함께 관리
  • MCP(Model Context Protocol)를 활용한 효율적인 컨텍스트 전달
  • 벤더 종속성 없는 오픈 소스 메모리 계층 구축

저는 코딩 에이전트(coding agents)를 끊임없이 바꿉니다.

어떤 달은 Opus가 앞서가고, 다음 달은 어떤 GPT가, Gemini는 점점 좋아지고, Cursor는 변화하며, 로컬 설정(local setups)은 특정 작업들을 수행하기에 충분할 정도로 좋아집니다. 게다가 사용량 제한(usage limits)과 토큰 정책(token policies) 때문에 작업을 계속 진행하기 위해 프로젝트 도중(mid-project)에 도구를 옮겨 다녀야만 합니다.

도구를 바꿀 때마다 매번 똑같이 짜증 나는 실패 패턴이 발생합니다:

새로운 에이전트는 동일한 레포지토리(repo)를 보고 있지만, 프로젝트의 작업 메모리(working memory)를 잃어버렸습니다.

  • 스스로 방향을 잡기 위해 트리(tree)를 다시 스캔합니다.
  • 이미 결정된 사항들을 다시 질문합니다.
  • 로컬 컨벤션(local conventions)을 놓칩니다.
  • 이미 겪었던 실수(footguns)를 반복합니다.

그리고 저는 똑같은 내용을 N번째로 프롬프트(prompt)에 붙여넣습니다:

여기 아키텍처(architecture)가 있고, 우리는 X라고 결정했으니, Y는 하지 마세요.

제가 쌓아온 모든 컨텍스트(context)는 단 하나의 도구 세션(session) 안에 머물러 있었고, 도구를 옮기는 순간 사라져 버렸습니다.

그래서 저 자신을 위한 해결책을 만들었고, 이를 정리하여 오픈 소스로 공개했습니다.

이 프로젝트의 이름은 agent-memory입니다.

문제점

모든 진지한 레포지토리에는 파일만으로는 명확히 알 수 없는 컨텍스트가 존재합니다:

  • 왜 모듈이 특정한 방식으로 구성되었는지
  • 어떤 컨벤션이 이 프로젝트의 로컬 규칙인지
  • 우리가 이미 어떤 버그를 겪었는지
  • 이 브랜치(branch)가 현재 무엇을 완료하려고 하는지
  • 어떤 접근 방식(approach)을 사용하지 않기로 명시적으로 결정했는지
  • 어떤 API가 유혹적이지만 사용해서는 안 되는지
  • 어떤 테스트 명령어가 실제로 중요한지

그러한 컨텍스트 중 일부는 문서(docs)에 속합니다. 일부는 코드 주석(code comments)에 속합니다. 일부는 AGENTS.md, CLAUDE.md, 또는 .cursor/rules에 속합니다.

하지만 그 중 상당수는 살아있는 프로젝트 메모리(living project memory)입니다.

브랜치가 진화함에 따라 변화합니다.

작업하는 동안 발견됩니다.

다음 에이전트에게 유용하지만, 항상 영구적인 문서로 만들 가치가 있는 것은 아닙니다.

그리고 만약 그것이 채팅 세션(chat session)에만 존재한다면, 그것은 기본적으로 일회용입니다.

아이디어: 의도적으로 지루하게

프로젝트의 살아있는 컨텍스트(living context), 즉 현재 작업 상태, 결정 사항, 컨벤션(conventions), 알려진 실수 유발 요소(footguns)를 레포지토리에 커밋되는 일반 Markdown 형식으로 유지하고, 이를 MCP를 통해 에이전트에게 노출하는 것입니다.

그게 전부입니다.

에이전트는 매 세션마다 트리(tree)를 다시 읽는 대신, 단 한 번의 MCP 호출로 관련 컨텍스트를 가져옵니다. 그리고 결정 사항들이 이미 메모리에 존재하며 관련 시점에 나타나기 때문에, 저는 프롬프트(prompt)에 결정 사항들을 일일이 수동으로 입력하는 것을 멈추게 되었습니다.

요약하자면 다음과 같습니다:

  • 메모리가 코드 옆에 존재함
  • Markdown이 신뢰할 수 있는 단일 원천(source of truth)임
  • 에이전트가 MCP를 통해 관련 컨텍스트를 가져옴
  • 영구적인 업데이트는 인간의 검토를 위해 스테이징(staged)됨
  • 로컬 인덱스(local index)를 다시 구축할 수 있음
  • 클라우드 메모리 계층이 없음
  • 벤더 종속성(vendor lock-in)이 없음
  • 불투명한 데이터베이스가 유일한 신뢰 원천이 아님

목표는 간단합니다:

모든 코딩 에이전트에게 매번 똑같은 레포지토리를 다시 가르치는 일을 멈추는 것.

에이전트가 얻는 것

에이전트는 세 가지 MCP 도구를 통해 메모리에 접근합니다:

도구목적
memory.fetch_context현재 작업을 위한 관련 프로젝트 컨텍스트를 가져옴
...

전형적인 워크플로우는 다음과 같습니다:

  1. 에이전트가 작업을 시작합니다.
  2. memory.fetch_context를 호출합니다.
  3. 관련 결정 사항, 컨벤션, 함정(pitfalls), 모듈 노트가 포함된 압축된 팩을 받습니다.
  4. 작업 도중, 영구적으로 기록할 만한 무언가를 발견합니다.
  5. memory.propose_update를 호출합니다.
  6. 업데이트가 스테이징(staged)됩니다.
  7. 사람이 변경 사항(diff)을 검토하고 적용하거나 거부합니다.

에이전트는 제안할 수 있습니다.

사람은 결정합니다.

이 경계가 중요합니다.

사용해 보기

agent-memory는 작은 Go 도구입니다. CGo가 필요 없으며, Apache-2.0 라이선스를 따르고, MCP 서버로서 로컬에서 실행되도록 설계되었습니다.

.mcp.json에 추가하세요:

{
  "mcpServers": {
    "agent-memory": {
...

또는 직접 실행하세요:

npx -y @xchucx/agent-memory mcp --root .

레포지토리에서 메모리를 초기화하세요:

npx -y @xchucx/agent-memory init

그 후에는, 여러분의 코딩 에이전트가 매번 아무 정보 없이 시작하는 대신 레포지토리에 메모리를 요청할 수 있습니다.

보상: 이식성

예상치 못한 보상은 바로 **이식성(portability)**입니다.

메모리가 특정 벤더의 클라우드나 특정 IDE의 개인 상태, 혹은 단일 채팅 세션이 아니라 레포지토리(repo) 내의 파일이기 때문에 — Claude Code, Cursor, Gemini, 그리고 MCP(Model Context Protocol)를 지원하는 로컬 에이전트(local agents) 모두가 동일한 프로젝트 메모리를 읽을 수 있습니다.

도구를 전환하더라도 더 이상 프로젝트 컨텍스트(context)가 초기화되지 않습니다.

컨텍스트가 코드와 함께 이동합니다.

에이전트 워크플로(agent workflows)가 특정 모델이나 특정 IDE에 덜 종속되게 될수록 이는 더욱 중요해집니다.

저는 제 레포지토리의 작업 메모리(working memory)가 이번 달에 우연히 가장 성능이 좋은 에이전트가 무엇인지에 따라 결정되는 것을 원하지 않습니다.

왜 그냥 AGENTS.md나 CLAUDE.md를 쓰지 않나요?

저도 그것들을 사용합니다.

하지만 저에게 AGENTS.md, CLAUDE.md, 그리고 .cursor/rules와 같은 파일들은 다음과 같은 정적 지침(static instructions)으로서 가장 효과적입니다:

  • 테스트를 실행하는 방법
  • 코딩 스타일 (coding style)
  • 레포지토리 규칙 (repo rules)
  • 선호하는 라이브러리 (preferred libraries)
  • 에이전트가 사용해야 하는 명령어
  • 에이전트가 피해야 하는 명령어
  • "항상 X를 수행할 것"
  • "절대 Y를 하지 말 것"

이것들은 에이전트에게 어떻게 행동해야 하는지를 알려줍니다.

제가 놓치고 있었던 것은 살아있는 프로젝트 메모리(living project memory)였습니다:

  • 이 브랜치(branch)가 현재 완료하려고 시도 중인 작업
  • 왜 다른 구현 대신 특정 구현을 선택했는지에 대한 이유
  • 어떤 모듈이 특정 흐름(flow)을 소유하고 있는지
  • 어제 발견한 버그(bug)나 실수하기 쉬운 부분(footgun)
  • 존재하지만 파일 하나만으로는 명확히 알 수 없는 컨벤션(convention)
  • 다음 에이전트가 레포지토리의 이 부분을 건드리기 전에 알아야 할 사항

그러한 종류의 정보는 더 자주 변경됩니다.

금방 오래된 정보(stale)가 될 수 있습니다.

검토(review)가 필요합니다.

검색(searchable)이 가능해야 합니다.

그리고 정적 지침 파일이 규칙, TODO, 오래된 세션 노트, 경고, 결정 사항, 무작위 알림 등이 뒤섞인 잡동사니 서랍(junk drawer)으로 변해서는 안 됩니다.

그래서 저는 이들을 서로 다른 계층(layers)으로 봅니다:

계층 (Layer)최적의 용도생명주기 (Lifecycle)
AGENTS.md / CLAUDE.md / .cursor/rules안정적인 지침 및 레포지토리 규칙드물게 수정됨, 정책으로 읽힘
...

목표는 지침 파일을 대체하는 것이 아닙니다.

목표는 지침 파일에 과부하를 주는 것을 멈추는 것입니다.

정적 지침과 살아있는 프로젝트 메모리는 서로 다른 생명주기를 가질 가치가 있습니다.

이를 형성한 세 가지 결정

1. 데이터베이스가 아닌 레포지토리 내의 마크다운 (Markdown)

저는 메모리가 지루하고 검사 가능한 형태이기를 원했습니다.

그래서 신뢰할 수 있는 원천(Source of truth)은 레포지토리 내부의 평범한 마크다운 (Markdown)입니다.

저는 이를 열어보고, 읽고, 편집하고, git diff로 확인할 수 있습니다.

SQLite 인덱스가 존재하지만, 이는 검색을 위한 로컬 섀도우 인덱스 (shadow index)일 뿐입니다. 이는 마크다운 파일로부터 완전히 다시 생성될 수 있습니다.

중요한 점은 메모리 자체가 불투명한 데이터베이스 안에 갇혀 있지 않다는 것입니다.

메모리는 코드 바로 옆에 존재합니다.

2. Human-in-the-loop 쓰기

저는 에이전트가 공유된 프로젝트 메모리를 조용히 다시 쓰는 것을 원하지 않았습니다.

그것은 잘못된 느낌이었습니다.

만약 코딩 에이전트가 지속적인 무언가, 즉 결정 사항, 컨벤션 (convention), 모듈에 관한 사실, 반복되는 실수 유발 요소 (footgun) 등을 학습했다면, 에이전트는 업데이트를 제안할 수 있어야 합니다.

하지만 그 업데이트가 자동으로 공유된 진실이 되어서는 안 됩니다.

따라서 지속적인 변경 사항은 검토를 위해 스테이징됩니다:

agent-memory review --diff
agent-memory apply

에이전트가 제안합니다.

제가 승인합니다.

이렇게 함으로써 메모리가 검토되지 않은 에이전트의 추측 더미로 변하지 않으면서도 유용하게 유지될 수 있습니다.

공유 메모리는 공유된 프로젝트 지식처럼 취급되어야 합니다.

3. 벡터 데이터베이스나 임베딩 (Embeddings) 미사용

AI 메모리 도구를 만들 때, 업계의 반사적인 반응은 벡터 데이터베이스 (vector databases)와 임베딩 (embedding) 모델을 찾는 것입니다.

그 이유는 이해합니다.

임베딩은 강력하며, 크고 모호한 지식 베이스의 경우에는 적절한 도구가 될 수 있습니다.

하지만 이 프로젝트에서 저는 더 단순한 것을 원했습니다.

프로젝트 메모리는 인터넷 전체가 아닙니다. 대개 사람이 작성한 수십 또는 수백 개의 작은 섹션들로 구성됩니다:

  • 결정 사항 (decisions)
  • 컨벤션 (conventions)
  • 함정 (pitfalls)
  • 모듈 노트 (module notes)
  • 작업 상태 (task state)
  • 로컬 프로젝트 사실 (local project facts)

이러한 규모에서는 벡터 데이터베이스가 잘못된 기본값처럼 느껴졌습니다.

임베딩은 API 키, 모델 선택, 재생성, 드리프트 (drift), 또는 더 무거운 로컬 설정과 같은 또 다른 가동 부품을 추가합니다.

더 중요한 것은, 벡터 데이터베이스를 쉽게 git commit 하여 프로젝트 지식으로서 팀과 공유할 수 없다는 점입니다.

그래서 저는 그 모든 것을 건너뛰었습니다.

agent-memory는 FTS5/BM25를 통한 표준 전문 검색 (full-text search) 기능을 갖춘 로컬 SQLite 섀도우 인덱스를 사용합니다.

신뢰할 수 있는 원천은 여전히 마크다운입니다.

이 인덱스는 마크다운 (Markdown) 파일로부터 완전히 재생성할 수 있습니다.

이를 통해 외부 서비스 없이도 빠르고 비용 효율적인 검색 (retrieval)이 가능합니다:

  • 임베딩 (embedding) API 없음
  • 벡터 데이터베이스 (vector database) 없음
  • 클라우드 의존성 없음
  • 모델 특정적 메모리 포맷 없음
  • 오프라인 작동 가능
  • 로컬 에이전트 (local agents)와 함께 작동 가능
  • 비행기 안에서도 작동 가능

현재 벤치마크 (benchmark)에서, 검색은 레이블이 지정된 쿼리 (queries)의 **98%**에 대해 올바른 섹션을 상위 5위 안에 포함시킵니다.

이는 recall@5 0.98을 의미합니다.

FTS5/BM25가 가능한 가장 화려한 검색 방법일까요?

아니요.

하지만 이는 지루하고, 검사 가능하며, 휴대 가능하고, 레포지토리 규모의 프로젝트 메모리에는 충분히 훌륭합니다.

그러한 트레이드오프 (tradeoff)가 바로 핵심입니다.

무엇이 메모리에 들어가야 하는가?

모든 것이 다 들어가는 것은 아닙니다.

agent-memory는 압축적이고 지속적인 프로젝트 지식에 가장 효과적입니다.

적합한 후보:

  • 아키텍처 결정 사항 (architecture decisions)
  • 프로젝트 컨벤션 (project conventions)
  • 알려진 함정 (known pitfalls)
  • 모듈 소유권 노트 (module ownership notes)
  • 현재 브랜치 또는 작업 상태 (current branch or task state)
  • 통합 시의 특이사항 (integration quirks)
  • 테스트 또는 빌드 시 주의사항 (test or build gotchas)
  • 미래의 에이전트가 다시 발견할 필요가 없는 것들

부적합한 후보:

  • 거대한 로그 (huge logs)
  • 임시 스크래치패드 노이즈 (temporary scratchpad noise)
  • 비밀 정보 (secrets)
  • 가공되지 않은 채팅 기록 (raw chat transcripts)
  • 레포지토리에 포함하고 싶지 않은 모든 것

저의 경험칙은 다음과 같습니다:

만약 메모리 항목이 코드 리뷰 (code review) 상황에서 의미가 없다면, 아마도 그곳에 있어서는 안 되는 것입니다.

어려운 부분: 실제로 작동하는가?

저는 단순히 과시용 수치를 내놓고 싶지 않았습니다.

그래서 이를 측정하려고 시도했습니다.

이 과정은 프로젝트에서 가장 흥미로운 부분이 되었습니다.

세 가지 서로 다른 질문이 있습니다:

  1. 검색이 올바른 메모리 섹션을 반환하는가?
  2. 한 세션에서 기록된 교훈이 다음 세션까지 유지되는가?
  3. 에이전트가 메모리 덕분에 실제로 다르게 행동하는가?

앞의 두 질문은 정직하게 측정하기가 비교적 쉽습니다.

세 번째 질문은 상황이 복잡해지는 지점입니다.

검색 (Retrieval)

검색은 결정론적 (deterministic)입니다.

레이블이 지정된 벤치마크에서, 올바른 섹션이 쿼리의 **98%**에 대해 상위 5위 안에 들어옵니다.

이는 recall@5 0.98입니다.

이는 CI (지속적 통합)에서 실행됩니다.

이것이 제품 전체가 작동한다는 것을 증명하는 것은 아니지만, 로컬 검색 계층 (local retrieval layer)이 단순히 느낌 (vibes)에 의존하는 것이 아님은 증명합니다.

연속성 (Continuity)

연속성 (Continuity)는 다른 질문을 던집니다:

세션 1에서 기록된 교훈이 세션 2까지 살아남는가?

실제 기록(record) → 영속화(persist) → 검색(retrieve) 루프를 통해:

  • 메모리가 있는 경우: 5/5 시나리오 성공
  • 메모리가 없는 경우: 0/5 시나리오 성공

또한 결정론적 (deterministic)입니다.

이것이 제가 원했던 핵심입니다. 다음 에이전트는 이전 에이전트가 이미 배운 것을 다시 발견할 필요가 없어야 합니다.

행동적 영향 (Behavioural impact)

가장 어려운 질문은 검색 (retrieval)이 아닙니다.

그것은 행동적 영향 (behavioural impact)입니다:

메모리가 존재하기 때문에 에이전트가 실제로 더 나은 결정을 내리는가?

저의 초기 사용 및 테스트 실행 결과는 긍정적입니다. 에이전트는 기억된 결정을 재사용하고, 반복되는 실수 (footguns)를 피하며, 메모리를 사용할 수 있을 때 프로젝트 컨텍스트 (project context)를 더 빠르게 복구합니다.

하지만 벤치마크 (benchmark)를 확실히 뒷받침할 수 있을 때까지 깔끔해 보이는 수치를 발표하고 싶지는 않습니다.

적절한 행동적 벤치마크를 분리해내는 것은 놀라울 정도로 어렵습니다:

  • 최신 코딩 에이전트들은 이미 많은 컨텍스트 소스를 사용합니다.
  • 일부 도구들은 자체적인 세션 간 메모리 (cross-session memory)를 가지고 있습니다.
  • 사용자 범위의 MCP 서버가 격리된 것으로 간주되는 실행 환경으로 유출될 수 있습니다.
  • 모델의 행동은 실행마다 달라집니다.
  • "성공"은 검색보다 점수를 매기기가 더 어렵습니다.

따라서 현재로서는 행동적 결과를 유망하지만 아직 완전히 벤치마크되지 않은 것으로 간주합니다.

이것이 아닌 것

agent-memory는 코드를 읽는 것을 대체하는 것이 아닙니다.

에이전트는 여전히 파일을 검사하고, 테스트를 실행하며, 실제 구현을 이해해야 합니다.

이것은 당신이 했던 모든 것을 위한 벡터 데이터베이스 (vector database)가 아닙니다.

이것은 가공되지 않은 트랜스크립트 저장소 (raw transcript store)가 아닙니다.

에이전트가 프로젝트의 진실을 조용히 재작성하도록 허용하려는 의도도 아닙니다.

목적은 더 좁습니다:

내구성이 있는 프로젝트 컨텍스트를 레포지토리 (repo) 근처에 유지하고, 에이전트가 검색할 수 있게 만들며, 변경 사항을 인간이 검토할 수 있게 만드는 것.

그게 전부입니다.

의도적으로 지루하게 만들었습니다.

실제 레포지토리에서 시도해보기

프로젝트는 여기에 있습니다:

github.com/xChuCx/agent-memory

최고의 테스트는 장난감 데모가 아닙니다.

최고의 테스트는 에이전트에게 동일한 컨텍스트를 계속해서 반복해서 설명해야 하는 짜증 나는 실제 프로젝트입니다.

그곳에서 직접 시도해 보세요.

특히 동일한 코드베이스에서 여러 개의 코딩 에이전트 (coding agents)를 사용하는 분들의 피드백에 관심이 많습니다:

  • 메모리 구조가 귀하의 워크플로 (workflow)에 맞습니까?
  • MCP 도구들이 적절한 기본 단위 (primitives)인가요?
  • 리뷰/적용 (review/apply) 흐름에서 무엇이 더 쉬워져야 할까요?
  • 어떤 클라이언트 (clients)가 더 잘 지원되어야 할까요?
  • 에이전트가 제안하도록 신뢰할 수 있는 메모리는 어떤 종류인가요?

GitHub에서 agent-memory 사용해 보기

이식 가능하고 리뷰 가능한 프로젝트 메모리가 코딩 에이전트의 일반적인 레이어 (layer)가 되기를 원하신다면, 스타 (star)를 눌러주는 것이 다른 사람들이 이 프로젝트를 찾는 데 큰 도움이 됩니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0