DEV Community

Cover image for AI 에이전트 영구 메모리 추가 방법: 어제 기억하기
Rihpig
Rihpig

Posted on • Originally published at apidog.com

AI 에이전트 영구 메모리 추가 방법: 어제 기억하기

요약

AI 에이전트에 영구 메모리를 4단계로 추가합니다:

(1) remember, recall, search, rollback 도구가 있는 MCP 메모리 서버를 설정하고,

(2) 에이전트 프롬프트에 메모리 지침을 추가하며,

(3) Claude Code의 경우 ~/.claude/settings.json을, Cursor의 경우 .cursor/mcp.json을 구성하고,

(4) 의사결정 로깅, 에이전트 인계, 세션 체크포인트에 메모리 패턴을 사용합니다.

에이전트는 세션 간에 컨텍스트를 유지하므로 더 이상 이전 대화를 복사-붙여넣기 할 필요가 없습니다.

Apidog을 지금 사용해 보세요

"어제 일을 기억하지 못하는 문제"를 해결하세요. MCP 프로토콜을 사용하여 AI 에이전트에 영구 메모리를 추가하면, 에이전트가 이전 세션의 결정, 결과물 및 컨텍스트를 기억할 것입니다.

익숙한 상황일 겁니다:

1일차: "사용자 인증 시스템을 구축하세요"
에이전트: [JWT 인증 구축, 사용자 테이블 생성, 새로 고침 토큰 구현]

2일차: "어제부터 계속 진행하세요"
에이전트: "이전 세션의 컨텍스트가 없습니다. 무엇을 했는지 붙여넣어 주시겠어요?"
Enter fullscreen mode Exit fullscreen mode

이전 대화를 복사-붙여넣기합니다. 에이전트가 2000줄의 컨텍스트를 읽습니다. 둘 다 속도를 다시 맞추는 데 15분을 낭비합니다.

영구 메모리가 이 문제를 해결합니다. MCP(모델 컨텍스트 프로토콜) 메모리를 사용하면 에이전트가 결정을 자동으로 저장하고 필요할 때 회상합니다. 복사-붙여넣기 없이, 재설명할 필요 없이.

이 튜토리얼에서는 AI 에이전트를 위한 MCP 메모리를 설정하는 방법을 안내합니다. 백엔드 아키텍트 세션의 결정을 저장하고, 데이터베이스 옵티마이저로 전환할 때 컨텍스트를 회상하며, 프론트엔드 개발자에게 결과물을 인계하는 방법을 모두 컨텍스트 손실 없이 구현할 수 있습니다. Apidog 통합으로 API를 구축하든, 여러 날에 걸친 개발 스프린트를 관리하든 동일한 메모리 패턴이 사용됩니다.

MCP 메모리란 무엇인가요?

MCP 메모리는 AI 에이전트가 세션 간에 정보를 저장하고 검색할 수 있도록 합니다. 에이전트가 쓰고 읽을 수 있는 공유 노트북이라고 생각하면 됩니다.

네 가지 도구가 MCP 메모리를 구동합니다:

도구 목적 예시
remember 태그와 함께 정보 저장 "UUID, bcrypt가 있는 사용자 테이블" 저장
recall 키워드 또는 태그로 검색 "인증 결정" 찾기
rollback 이전 상태로 복원 잘못된 스키마 변경 되돌리기
search 세션 전체에서 찾기 "백엔드 아키텍트가 무엇을 결정했는가?"
┌─────────────────┐         ┌──────────────────┐         ┌─────────────┐
│  AI 에이전트    │         │  MCP 메모리      │         │  저장소     │
│  (Claude Code)  │◄───────►│  서버            │◄───────►│  (SQLite)   │
└─────────────────┘   JSON  └──────────────────┘  I/O    └─────────────┘
Enter fullscreen mode Exit fullscreen mode

1단계: MCP 메모리 서버 설정

MCP 메모리 도구를 노출하는 서버가 필요합니다. 오픈 소스 구현체를 사용할 수 있습니다.

옵션 A: 호스팅된 메모리 서버 사용

npm install -g @example/mcp-memory-server
Enter fullscreen mode Exit fullscreen mode

옵션 B: 간단한 로컬 서버 실행

memory-server.js 파일을 만듭니다:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import fs from "fs/promises";
import path from "path";

const MEMORY_FILE = path.join(process.env.HOME, ".mcp-memory", "memories.json");

const server = new McpServer({
  name: "memory",
  version: "1.0.0"
});

// Ensure memory file exists
async function initMemory() {
  await fs.mkdir(path.dirname(MEMORY_FILE), { recursive: true });
  try {
    await fs.access(MEMORY_FILE);
  } catch {
    await fs.writeFile(MEMORY_FILE, JSON.stringify([]));
  }
}

// Tool: remember
server.tool(
  "remember",
  {
    content: z.string().describe("정보 저장"),
    tags: z.array(z.string()).describe("검색을 위한 태그 (예: ['backend', 'auth'])"),
    agent: z.string().optional().describe("태그 지정을 위한 에이전트 이름")
  },
  async ({ content, tags, agent }) => {
    await initMemory();
    const memories = JSON.parse(await fs.readFile(MEMORY_FILE, "utf-8"));
    const memory = {
      id: Date.now().toString(),
      content,
      tags,
      agent,
      timestamp: new Date().toISOString()
    };
    memories.push(memory);
    await fs.writeFile(MEMORY_FILE, JSON.stringify(memories, null, 2));
    return { content: [{ type: "text", text: `태그와 함께 메모리 저장: ${tags.join(", ")}` }] };
  }
);

// Tool: recall
server.tool(
  "recall",
  {
    query: z.string().describe("검색 쿼리 또는 찾을 태그"),
    agent: z.string().optional().describe("에이전트 이름으로 필터링")
  },
  async ({ query, agent }) => {
    await initMemory();
    const memories = JSON.parse(await fs.readFile(MEMORY_FILE, "utf-8"));
    const results = memories.filter(m => {
      const matchesQuery = m.content.toLowerCase().includes(query.toLowerCase()) ||
                          m.tags.some(t => t.toLowerCase().includes(query.toLowerCase()));
      const matchesAgent = !agent || m.agent === agent;
      return matchesQuery && matchesAgent;
    });
    return {
      content: [{
        type: "text",
        text: results.length === 0
          ? "메모리가 없습니다."
          : results.map(m => `[${m.timestamp}] ${m.content}`).join("\n\n")
      }]
    };
  }
);

// Tool: search
server.tool(
  "search",
  {
    tags: z.array(z.string()).describe("검색할 태그"),
    limit: z.number().optional().default(10)
  },
  async ({ tags, limit }) => {
    await initMemory();
    const memories = JSON.parse(await fs.readFile(MEMORY_FILE, "utf-8"));
    const results = memories
      .filter(m => tags.some(t => m.tags.includes(t)))
      .slice(0, limit);
    return {
      content: [{
        type: "text",
        text: results.map(m => `[${m.agent || "알 수 없음"}] ${m.content}`).join("\n\n")
      }]
    };
  }
);

// Tool: rollback
server.tool(
  "rollback",
  {
    agent: z.string().describe("롤백할 에이전트 이름"),
    timestamp: z.string().describe("이 타임스탬프로 롤백")
  },
  async ({ agent, timestamp }) => {
    await initMemory();
    const memories = JSON.parse(await fs.readFile(MEMORY_FILE, "utf-8"));
    const rolledBack = memories.filter(m =>
      m.agent !== agent || new Date(m.timestamp) <= new Date(timestamp)
    );
    await fs.writeFile(MEMORY_FILE, JSON.stringify(rolledBack, null, 2));
    return {
      content: [{
        type: "text",
        text: `${agent}${timestamp}로 롤백했습니다.`
      }]
    };
  }
);

const transport = new StdioServerTransport();
await server.connect(transport);
Enter fullscreen mode Exit fullscreen mode

서버를 실행하세요:

node memory-server.js
Enter fullscreen mode Exit fullscreen mode

2단계: 모든 에이전트에 메모리 지침 추가

에이전트 코드를 직접 수정할 필요 없이 프롬프트에 다음과 같이 메모리 지침을 추가하세요:

MCP 메모리 도구(remember, recall, search, rollback)를 사용할 수 있습니다.

다음 메모리 프로토콜을 따르세요:

**세션을 시작할 때:**
1. 컨텍스트 회상: recall(query="ecommerce-api", agent="Backend Architect")
2. 지난 세션의 보류 중인 항목 검토

**작업을 완료할 때:**
1. 태그와 함께 결과물 기억:
   - remember(content="UUID 기본 키, bcrypt 비밀번호 해싱, 새로 고침 토큰을 사용한 JWT 인증으로 사용자 테이블 생성", tags=["ecommerce-api", "database", "auth"], agent="Backend Architect")
2. 결정 사항과 보류 중인 항목 포함

**다른 에이전트에 인계할 때:**
1. 인계받을 에이전트를 위한 컨텍스트 남기기:
   - remember(content="API 엔드포인트: /auth/login, /auth/register, /products, /orders. 인증 흐름: JWT 액세스 토큰 (15분) + 새로 고침 토큰 (7일). 알려진 문제: 속도 제한이 아직 구현되지 않음", tags=["ecommerce-api", "handoff"], agent="Backend Architect", for="Frontend Developer")

**문제가 발생할 때:**
1. 마지막으로 알려진 정상 상태 검색
2. 롤백을 사용하여 복원
Enter fullscreen mode Exit fullscreen mode

실행 예시:

당신: "백엔드 아키텍트 모드를 활성화하세요. 우리 전자상거래 API를 위한 사용자 시스템을 설계하세요."

[에이전트가 시스템을 설계하고, 스키마를 생성하고, 인증을 구현합니다]

에이전트: "사용자 시스템 완료. 메모리 저장 중..."
→ 호출: remember("UUID, bcrypt, JWT + 새로 고침 토큰이 있는 사용자 테이블", tags: ["ecommerce-api", "auth", "database"])

[세션 종료]

[다음 날]

당신: "어제부터 계속 진행하세요"

에이전트: "컨텍스트 회상 중..."
→ 호출: recall(query="ecommerce-api")
→ 반환: "UUID, bcrypt, JWT + 새로 고침 토큰이 있는 사용자 테이블"

에이전트: "어제 UUID 기본 키로 사용자 테이블을 설계했고, bcrypt 비밀번호 해싱을 구현했으며, 새로 고침 토큰으로 JWT 인증을 설정했습니다. 다음 단계: 제품 카탈로그 스키마. 계속 진행할 준비가 되었습니다."
Enter fullscreen mode Exit fullscreen mode

3단계: Claude Code 구성

MCP 메모리 서버를 Claude Code에 연결하려면 ~/.claude/settings.json 파일을 편집하세요:

{
  "mcpServers": {
    "memory": {
      "command": "node",
      "args": ["/absolute/path/to/memory-server.js"],
      "env": {
        "HOME": "/Users/your-username"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Claude Code를 재시작하면 메모리 도구를 사용할 수 있습니다.

테스트:

remember 도구를 사용하여 다음을 저장하세요: "전자상거래 프로젝트를 위한 테스트 메모리"
태그: ["test", "ecommerce-api"]
Enter fullscreen mode Exit fullscreen mode
recall 도구를 사용하여 "test"에 대한 메모리를 찾으세요.
Enter fullscreen mode Exit fullscreen mode

4단계: Cursor 구성

프로젝트 디렉토리에 .cursor/mcp.json 파일 생성:

{
  "mcpServers": {
    "memory": {
      "command": "node",
      "args": ["/absolute/path/to/memory-server.js"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

테스트:

@memory remember "PostgreSQL로 전자상거래 API 프로젝트 시작"
태그: ["ecommerce-api", "setup"]
Enter fullscreen mode Exit fullscreen mode
@memory recall query="ecommerce"
Enter fullscreen mode Exit fullscreen mode

실제 워크플로우를 위한 메모리 패턴

패턴 1: 의사결정 로깅

기술적 결정을 내릴 때마다 기록하세요:

remember({
  content: "PostgreSQL을 MySQL 대신 선택한 이유: (1) 유연한 제품 속성을 위한 JSONB 지원, (2) 더 나은 전체 텍스트 검색, (3) UUID 네이티브 지원",
  tags: ["ecommerce-api", "database", "decision"],
  agent: "Backend Architect"
})
Enter fullscreen mode Exit fullscreen mode

나중에 누군가 "왜 PostgreSQL인가요?"라고 물으면:

recall(query="PostgreSQL MySQL decision")
Enter fullscreen mode Exit fullscreen mode

패턴 2: 에이전트 인계

에이전트를 전환할 때, 인계 노트를 남기세요:

remember({
  content: "백엔드 완료. 엔드포인트: POST /auth/login, POST /auth/register, GET /products, POST /orders. 인증: JWT 15분 액세스 + 7일 새로 고침. 보류 중: 속도 제한, 이메일 확인. 프론트엔드 필요: 로그인 폼, 제품 목록, 장바구니, 결제.",
  tags: ["ecommerce-api", "handoff", "backend-complete"],
  agent: "Backend Architect",
  for: "Frontend Developer"
})
Enter fullscreen mode Exit fullscreen mode

프론트엔드 개발자는 다음으로 시작합니다:

recall(query="handoff", agent="Backend Architect")
Enter fullscreen mode Exit fullscreen mode

패턴 3: 세션 체크포인트

각 작업 세션이 끝날 때:

remember({
  content: "세션 완료. 완료: 사용자 테이블, 인증 엔드포인트, 제품 스키마. 다음 세션: 주문 시스템, 결제 웹훅. 블로커: Stripe API 키 대기 중.",
  tags: ["ecommerce-api", "checkpoint", "session-1"],
  agent: "Backend Architect"
})
Enter fullscreen mode Exit fullscreen mode

다음 세션 재개:

recall(query="checkpoint session-1")
Enter fullscreen mode Exit fullscreen mode

패턴 4: 버그 추적

버그를 발견할 때:

remember({
  content: "버그: 로그아웃 후 새로 고침 토큰이 만료되지 않음. 토큰이 메모리에 저장되어 있고, 영구 저장되지 않음. 수정: TTL을 사용하여 Redis로 이동.",
  tags: ["ecommerce-api", "bug", "auth"],
  agent: "Code Reviewer",
  severity: "high"
})
Enter fullscreen mode Exit fullscreen mode

나중에 버그 검색:

search(tags=["bug", "ecommerce-api"])
Enter fullscreen mode Exit fullscreen mode

문제 해결

메모리가 유지되지 않음:

  • 메모리 파일 경로 확인 (~/.mcp-memory/memories.json)
  • MCP 서버가 실행 중인지 확인
  • Claude Code/Cursor에 MCP 구성이 있는지 확인

회상 시 결과가 너무 많음:

  • 더 구체적인 태그 추가
  • 에이전트 이름으로 필터링
  • 따옴표로 정확한 구문 사용

메모리 파일이 너무 커짐:

  • 오래된 메모리 주기적으로 보관
  • 완료된 프로젝트 정리를 위해 rollback 사용
  • 메모리 스키마에 만료 날짜 추가

구축한 것

구성 요소 목적
MCP 메모리 서버 세션 간에 정보 저장/검색
remember 도구 결정, 결과물, 인계 사항 기록
recall 도구 이전 세션의 컨텍스트 찾기
search 도구 모든 메모리에서 태그별로 쿼리
rollback 도구 필요할 때 이전 상태로 복원
메모리 패턴 의사결정 로깅, 인계, 체크포인트, 버그 추적

다음 단계

메모리 서버 확장:

  • 임베딩을 사용한 의미론적 검색 추가
  • 메모리 만료 구현 (30일 후 자동 보관)
  • 메모리 요약 추가 (긴 세션 요약)

팀 메모리 구축:

  • 팀 전체에 중앙 메모리 서버 공유
  • 프로젝트 및 개발자별로 메모리 태그 지정
  • 신규 팀원을 위한 온보딩 흐름 생성

도구와 통합:

  • Git 커밋을 메모리로 자동 로깅
  • 프로젝트 관리 도구(Jira, Linear)와 동기화
  • 메모리를 문서로 내보내기

일반적인 문제 해결

세션 간에 메모리가 유지되지 않음:

  • Claude Code를 시작하기 전에 MCP 서버가 실행 중인지 확인
  • 메모리 파일 경로가 존재하는지 확인: ls -la ~/.mcp-memory/memories.json
  • 파일 권한이 읽기/쓰기를 허용하는지 확인: chmod 644 ~/.mcp-memory/memories.json
  • ~/.claude/settings.json의 서버 구성이 올바른 경로를 가리키는지 확인

회상 시 빈 결과가 반환됨:

  • 쿼리가 저장된 태그와 일치하는지 확인 (대소문자 구분)
  • 더 넓은 검색어를 시도하거나 특정 태그와 함께 search 사용
  • 메모리가 실제로 저장되었는지 확인: cat ~/.mcp-memory/memories.json
  • 에이전트 이름 필터가 일치하는지 확인 (agent 매개변수 사용 시)

메모리 파일이 너무 커짐:

  • 30일 이상 된 메모리에 대한 자동 보관 구현
  • 날짜 범위로 메모리를 삭제하는 prune 도구 추가
  • 프로젝트 또는 날짜별로 메모리를 별도의 파일로 분할
  • 대규모 사용을 위해 JSON 대신 데이터베이스 백엔드(SQLite) 사용

서버 시작 실패:

  • Node.js 버전 확인: node --version (18 이상이어야 함)
  • 누락된 종속성 설치: npm install @modelcontextprotocol/sdk zod
  • 서버 코드에서 구문 오류 찾기
  • 오류를 직접 확인하려면 실행: node memory-server.js

여러 에이전트가 서로의 메모리를 덮어씀:

  • remember를 호출할 때 항상 agent 필드 포함
  • 프로젝트별로 고유한 태그 사용: ["project-x", "backend", "auth"]
  • 컨텍스트를 검색할 때 에이전트 이름으로 회상 필터링
  • 프로젝트별로 별도의 메모리 파일 고려

메모리 서버 보안 고려 사항

API 키 저장: 민감한 데이터(API 키, 비밀번호)는 암호화로 보호하세요.

import crypto from 'crypto';

const ENCRYPTION_KEY = process.env.MEMORY_ENCRYPTION_KEY;
const ALGORITHM = 'aes-256-gcm';

function encrypt(text) {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv(ALGORITHM, Buffer.from(ENCRYPTION_KEY), iv);
  const encrypted = cipher.update(text, 'utf8', 'hex');
  return {
    encryptedData: encrypted + cipher.final('hex'),
    iv: iv.toString('hex'),
    authTag: cipher.getAuthTag().toString('hex')
  };
}

function decrypt(encrypted) {
  const decipher = crypto.createDecipheriv(
    ALGORITHM,
    Buffer.from(ENCRYPTION_KEY),
    Buffer.from(encrypted.iv, 'hex')
  );
  decipher.setAuthTag(Buffer.from(encrypted.authTag, 'hex'));
  return decipher.update(encrypted.encryptedData, 'hex', 'utf8') + decipher.final('utf8');
}
Enter fullscreen mode Exit fullscreen mode

접근 제어: 팀 메모리 서버의 경우 인증 기능을 추가하세요.

  • 메모리 도구 호출 시 API 키 요구
  • 사용자별 메모리 네임스페이스 구현
  • 감사 추적을 위해 모든 메모리 작업 로깅
  • 사용자별 요청 속도 제한

이제 AI 에이전트가 영구 메모리를 갖게 됩니다. 어제 일을 기억하고, 결정을 회상하며, 복사-붙여넣기 없이 컨텍스트를 인계합니다.

더 이상 "이전 세션의 컨텍스트가 없습니다"라는 말은 없습니다. 더 이상 재설명 필요도, 시간 낭비도 없습니다.

이것이 바로 MCP 메모리의 힘입니다. 에이전트에게 공유 노트북을 제공하고, 여러 날에 걸친 프로젝트에서 에이전트가 실제로 유용해지는 것을 경험하세요.

자주 묻는 질문

MCP 메모리란 무엇인가요?

MCP 메모리는 AI 에이전트가 세션 간에 정보를 저장하고 검색할 수 있도록 하는 프로토콜 구현입니다. 에이전트가 쓰고 읽을 수 있는 공유 노트북으로, 단일 대화를 넘어 컨텍스트를 유지한다고 생각하면 됩니다.

Claude Code에 영구 메모리를 어떻게 설정하나요?

MCP 메모리 서버를 설치한 다음, 서버 명령과 경로를 사용하여 ~/.claude/settings.json에 추가하세요. Claude Code를 다시 시작하면 메모리 도구(remember, recall, search, rollback)를 사용할 수 있습니다.

어떤 AI 에이전트가 MCP 메모리를 지원하나요?

MCP 호환 클라이언트(Claude Code, Cursor, Windsurf)에서 실행되는 모든 에이전트는 메모리 도구를 사용할 수 있습니다. 에이전트 파일을 수정할 필요 없이 프롬프트에 메모리 지침만 추가하면 됩니다.

에이전트 인계를 위한 가장 좋은 메모리 패턴은 무엇인가요?

["handoff", "project-name"]과 같은 태그와 함께 remember를 사용하여 다음 에이전트를 위한 컨텍스트를 남기세요. 완료된 작업, 보류 중인 항목 및 알려진 문제를 포함하세요. 인계받는 에이전트는 recall(query="handoff")를 호출하여 이를 검색합니다.

MCP 서버는 얼마나 많은 메모리를 저장할 수 있나요?

구현에 따라 다릅니다. 참조 서버는 무한히 커지는 JSON 파일을 사용합니다. 프로덕션 서버는 대규모 사용을 위해 만료 정책, 자동 보관 또는 데이터베이스 백엔드를 추가해야 합니다.

팀이 중앙 메모리 서버를 공유할 수 있나요?

예. 공유 머신 또는 클라우드 인스턴스에서 메모리 서버를 실행하고, 모든 팀원의 클라이언트를 서버에 연결하도록 구성하며, 체계적인 검색을 위해 프로젝트 및 개발자별로 메모리에 태그를 지정하세요.

메모리 회상 시 결과가 너무 많이 반환되면 어떻게 해야 하나요?

메모리를 저장할 때 더 구체적인 태그를 추가하세요. 회상 쿼리에서 에이전트 이름으로 필터링하세요. 따옴표로 정확한 구문을 사용하세요. 더 스마트한 검색을 위해 임베딩을 사용한 의미론적 검색 구현을 고려하세요.

Top comments (0)