Skip to content

백엔드 개발자의 AI 비서 만들기 (3편) - MEMORY.md 20KB가 에이전트를 죽였다

📚 시리즈: 백엔드 개발자의 AI 비서 만들기

3 / 5
  1. 1당근마켓에서 Mac mini를 주워온 날
  2. 248개의 크론잡으로 살아나다
  3. 3MEMORY.md 20KB가 에이전트를 죽였다지금 읽는 중
  4. 4오픈소스 12 Stars의 현실
  5. 5Discord 7개 채널로 생활을 자동화하기
백엔드 개발자의 AI 비서 만들기 (3편) - MEMORY.md 20KB가 에이전트를 죽였다

시리즈 안내

이 시리즈에서 다룬 내용:


TL;DR

  • 상황: OpenClaw 에이전트를 24/7 운영하다가 MEMORY.md가 20KB+로 팽창
  • 원인: 매 세션마다 MEMORY.md를 전부 로딩 → 컨텍스트 압박 → 게이트웨이 크래시
  • 해결 1: openclaw-self-healing — 4단계 자동 복구 (~30초)
  • 해결 2: openclaw-memorybox — 3-tier 메모리 관리 CLI (20KB → 3.5KB)
  • 한계: 토큰 절감은 전체 세션의 5-15%. 진짜 가치는 컨텍스트 오버플로 방지


서론

OpenClaw 에이전트를 24/7로 운영하고 있습니다.

Discord 7개 채널, 크론 잡 48개. 주식 모니터링, 일정 관리, 가족 알림까지 — 꽤 잘 돌아가고 있었습니다.

Discord 채널 구조

그러다 어느 날 새벽에 에이전트가 완전히 죽었습니다. 크론 전부 중단, 알림 없음, 침묵. 아침에 일어나서 Discord를 열었는데 아무 메시지도 없길래 뭔가 이상하다 싶었죠.

원인을 추적해보니 MEMORY.md라는 파일 하나가 문제였습니다.

“파일 하나가 뭔 문제야?” 하실 수 있는데, 이게 생각보다 심각한 구조적 문제였습니다.



MEMORY.md가 뭔가요?

OpenClaw를 안 써보신 분들을 위해 간단히 설명하면, MEMORY.md는 에이전트의 장기 기억 저장소입니다. 마크다운 파일 하나에 에이전트가 기억해야 할 모든 것을 저장합니다.

# MEMORY.md 예시

## 카카오 캘린더 API
- Access Token: 6시간 유효
- 갱신 스크립트: ~/scripts/kakao-oauth-refresh.js

## 채널별 모델 설정
- #jarvis → sonnet-4-5
- #jarvis-dev → opus-4-6

문제는, 이 파일이 매 세션마다 전부 로딩된다는 겁니다. 모든 채널, 모든 크론, 모든 하트비트에서요.



뭐가 문제였나?

에이전트가 학습할수록 MEMORY.md가 커집니다. 결정사항, 사용자 선호도, API 설정, 프로젝트 기록… 계속 쌓이다 보니 어느새 20KB를 넘었습니다.

OpenClaw의 컨텍스트 윈도우는 200K 토큰입니다. MEMORY.md 20KB면 그 중 약 10%를 메모리 파일 하나가 차지하는 겁니다. 거기에 AGENTS.md, TOOLS.md, HEARTBEAT.md 같은 워크스페이스 파일까지 합치면 시스템 프롬프트만으로 컨텍스트의 상당 부분이 사라집니다.

대화가 길어지면 컨텍스트가 100%를 찍고, compaction이 시작되면서 상태가 꼬이고… 결국 게이트웨이가 크래시합니다.

급하게 설정을 고치려다가 오히려 상황을 더 악화시켰습니다. 전형적인 “고치려다 더 망가뜨린” 케이스였죠.



크래시 타임라인

실제로 겪었던 상황을 시간순으로 정리하면 이렇습니다.

2026-02-06 21:15  게이트웨이 응답 없음 (Health Check 실패)
2026-02-06 21:20  Level 3 Emergency Recovery 자동 시작
                  → Claude Code PTY 세션으로 로그 분석
2026-02-06 21:50  Claude가 원인 진단 + 설정 수정 → 복구 성공
2026-02-06 21:50  Discord 알림 시도 → webhook URL 오류로 실패

이건 실제 로그입니다:

[2026-02-06 21:20:36] === Emergency Recovery Started (PID: 86715) ===
[2026-02-06 21:20:36] Dependencies check passed
[2026-02-06 21:20:36] Starting Claude Code session in tmux...
[2026-02-06 21:50:53] Claude successfully recovered the gateway! (HTTP 200)
[2026-02-06 21:50:53] === Emergency Recovery Completed (1817s) ===

30분 걸렸습니다. Claude가 로그를 읽고, 원인을 파악하고, 설정을 직접 수정한 시간입니다. 사람이 새벽에 깨서 했으면 훨씬 오래 걸렸을 겁니다.



1차 대응: 일단 살려야 한다 (Self-Healing)

근본 원인 분석은 나중이고, 우선 “에이전트가 죽으면 자동으로 살아나게” 만들어야 했습니다.

4단계 자동 복구 시스템을 설계했습니다.

graph TB
    L1["Level 1: Watchdog<br/>프로세스 감시 → 즉시 재시작<br/>~10초"]
    L2["Level 2: Health Check<br/>HTTP 상태 체크 → 강제 재시작<br/>1분 주기"]
    L3["Level 3: Emergency Recovery<br/>Claude Code → 로그 분석 → 자동 수정<br/>최대 30분"]
    L4["Level 4: Discord Alert<br/>모든 자동 복구 실패 → 사람에게 알림"]
    
    L1 -->|실패| L2
    L2 -->|5분간 반복 실패| L3
    L3 -->|실패| L4
    
    style L1 fill:#e8f5e9
    style L2 fill:#fff3e0
    style L3 fill:#ffebee
    style L4 fill:#fce4ec

Level 1~2는 심플합니다. macOS에서는 LaunchAgent, Linux에서는 systemd user-level 유닛으로 프로세스를 감시하고, 1분마다 HTTP 상태를 체크합니다. 대부분의 크래시는 이 단계에서 30초 이내에 복구됩니다.

Level 3가 좀 재미있는 부분인데요, Claude Code CLI를 tmux 세션으로 띄워서 에러 로그를 읽히고 수정 방법을 AI가 직접 판단합니다. “AI가 AI를 고치는” 구조입니다.


Level 2: Health Check 핵심 로직

실제 코드에서 발췌한 Health Check의 핵심 흐름입니다:

# gateway-healthcheck.sh (간소화)
GATEWAY_URL="${OPENCLAW_GATEWAY_URL:-http://localhost:18789/}"
MAX_RETRIES="${HEALTH_CHECK_MAX_RETRIES:-3}"
ESCALATION_WAIT="${HEALTH_CHECK_ESCALATION_WAIT:-300}"

# HTTP 상태 체크
http_code=$(curl -s -o /dev/null -w "%{http_code}" \
  --max-time "$HTTP_TIMEOUT" "$GATEWAY_URL" 2>/dev/null || echo "000")

if [[ "$http_code" != "200" ]]; then
  # 재시작 시도
  openclaw gateway restart
  
  # 5분 대기 후 재검증
  sleep "$ESCALATION_WAIT"
  
  # 여전히 안 되면 Level 3로 에스컬레이션
  if ! check_gateway; then
    log "Still unhealthy after ${ESCALATION_WAIT}s, triggering emergency recovery..."
    bash "$HOME/openclaw/scripts/emergency-recovery.sh"
  fi
fi

주목할 점은 에스컬레이션 구조입니다. 바로 Level 3을 호출하지 않고 5분간 자가 복구를 기다립니다. 대부분의 일시적 장애는 단순 재시작으로 해결되니까요.


Level 3: Emergency Recovery 핵심 로직

Level 3는 Claude Code를 tmux 세션에 띄워서 자동 진단을 시킵니다. 실제 코드에서 재미있는 부분만 뽑으면 이렇습니다:

# emergency-recovery.sh (핵심 부분만 발췌)
TMUX_SESSION="emergency_recovery_$TIMESTAMP"

# 1. tmux에서 Claude Code 세션 시작
tmux new-session -d -s "$TMUX_SESSION" "claude"
sleep "$CLAUDE_STARTUP_WAIT"

# 2. 워크스페이스 신뢰 프롬프트 감지 (독특한 부분)
#    Claude Code는 처음 실행 시 "trust this workspace?" 프롬프트를 띄움
if wait_for_claude_prompt "$TMUX_SESSION" "$CLAUDE_WORKSPACE_TRUST_TIMEOUT"; then
  tmux send-keys -t "$TMUX_SESSION" "" C-m
fi

# 3. 복구 명령 전송 — 구체적인 작업 순서를 지시
recovery_command="OpenClaw 게이트웨이가 5분간 재시작했으나 복구되지 않았습니다.
작업 순서:
1. openclaw status 체크
2. 로그 분석 (~/.openclaw/logs/*.log)
3. 설정 검증 (~/.openclaw/openclaw.json)
4. 포트 충돌 체크 (lsof -i :18789)
5. 복구 시도 (설정 수정, 프로세스 재시작)"

tmux send-keys -t "$TMUX_SESSION" "$recovery_command" C-m

# 4. 폴링으로 복구 완료 감지 (30분 타임아웃)
while [ $elapsed -lt "$RECOVERY_TIMEOUT" ]; do
  sleep 30
  current_output=$(tmux capture-pane -t "$TMUX_SESSION" -p | tail -20)
  
  # 완료 시그널 체크
  if echo "$current_output" | grep -qiE \
    "(recovery completed|gateway.*restored|http 200)"; then
    log "Claude appears to have completed"
    break
  fi
  
  # 3분간 출력이 없으면 idle → 완료로 간주
  if [ "$current_output" = "$last_output" ]; then
    idle_count=$((idle_count + 1))
    [ $idle_count -ge 6 ] && break
  fi
done

여기서 wait_for_claude_prompt 함수가 재미있습니다. Claude Code CLI는 새 워크스페이스에서 처음 실행하면 “trust this workspace?” 프롬프트를 보여주는데, 이걸 tmux pane 출력에서 grep으로 감지하고 자동으로 승인합니다. 자동화 스크립트에서 이런 인터랙티브 프롬프트 처리가 은근히 까다롭거든요.

그리고 복구 완료 감지도 단순하지 않습니다. Claude가 정확히 “완료됐습니다”라고 말해주지 않을 수도 있으니까, 3분간 출력이 없으면 idle로 판단해서 빠져나옵니다. 이런 방어적 프로그래밍이 프로덕션에서는 중요합니다.


동시 실행 방지

복구 스크립트가 동시에 두 번 실행되면 상황이 더 꼬입니다. 파일 기반 lock으로 방지합니다:

# 민감한 경로에 lock 파일 생성 (/tmp 아닌 memory 디렉토리)
LOCKFILE="$LOG_DIR/.emergency-recovery.lock"

cleanup() {
    rm -f "${LOCKFILE:-$HOME/openclaw/memory/.emergency-recovery.lock}"
}
trap cleanup EXIT INT TERM

그리고 중요한 건, 이 코드가 set -euo pipefail로 시작한다는 점입니다. 복구 스크립트 자체가 에러로 죽으면 본말전도니까요. ShellCheck CI도 매 커밋마다 돌리고 있습니다.

전체 스크립트는 384줄입니다. Discord 알림, 메트릭 수집, 로그 로테이션, Claude API 쿼터 체크까지 포함하면 이 정도 분량이 됩니다.

Self-Healing 자동 복구 데모

이걸 정리해서 오픈소스로 공개했습니다: openclaw-self-healing

순수 Bash, 의존성 제로, macOS와 Linux 모두 지원합니다.



잠깐, 근본 원인은?

Self-healing으로 “죽으면 살린다”는 해결했습니다. 근데 왜 죽었는지는 여전히 안 고친 상태였습니다.

Self-healing만 있으면 같은 이유로 죽고 → 살고 → 또 죽고를 반복합니다. 밴드에이드는 붙였는데 상처 원인은 그대로인 거죠.

실제로 memory/ 디렉토리에 쌓인 로그를 보면 이 패턴이 보입니다:

emergency-recovery-2026-02-06-2120.log
emergency-recovery-2026-02-08-1104.log
emergency-recovery-2026-02-08-1138.log  ← 같은 날 2번
emergency-recovery-2026-02-09-1524.log
emergency-recovery-2026-02-09-1943.log
emergency-recovery-2026-02-09-2007.log
emergency-recovery-2026-02-09-2017.log
emergency-recovery-2026-02-09-2033.log  ← 같은 날 5번!

2월 9일에 5번 죽었습니다. Self-healing 덕분에 매번 살아나긴 했는데, 이건 정상이 아니었습니다.

원인을 다시 정리하면 이렇습니다:

  1. MEMORY.md가 20KB+로 성장
  2. 55개 세션(크론 + 채널 + 하트비트)이 매번 전부 로딩
  3. 컨텍스트 윈도우의 약 10%를 메모리가 차지
  4. 대화가 길어지면 → 100% → compaction → 상태 꼬임 → 크래시

진짜 필요한 건 MEMORY.md를 안 터지게 관리하는 것이었습니다.



2차 대응: 근본 원인 해결 (MemoryBox)

Letta/MemGPT의 계층적 메모리 패턴에서 영감을 받았습니다. 핵심 아이디어는 심플합니다:

모든 걸 하나의 파일에 넣지 말고, 중요도별로 나누자.

graph TB
    subgraph "Tier 1 — 매 세션 자동 로딩"
        M["MEMORY.md<br/>핵심 사실만 ≤10KB"]
        D["memory/YYYY-MM-DD.md<br/>오늘/어제 로그"]
    end
    
    subgraph "Tier 2 — 필요할 때만 검색"
        R1["memory/domains/persona.md"]
        R2["memory/domains/decisions.md"]
        R3["memory/domains/milestones.md"]
    end
    
    subgraph "Tier 3 — 수동 조회"
        A["memory/archive/<br/>14일 지난 로그"]
    end
    
    M -.->|memory_search| R1
    M -.->|memory_search| R2
    D -.->|14일 후 이동| A
    
    style M fill:#e8f5e9
    style D fill:#e8f5e9
    style R1 fill:#fff3e0
    style R2 fill:#fff3e0
    style R3 fill:#fff3e0
    style A fill:#f5f5f5

여기서 핵심 포인트가 하나 있습니다. OpenClaw의 memory_searchmemory/**/*.md를 재귀적으로 인덱싱합니다. 그래서 Tier 2 파일들도 검색은 가능한데, 매 세션마다 로딩되진 않습니다. 설정 변경 없이 파일 구조만 바꾸면 되는 거죠.

이걸 CLI 도구로 자동화했습니다. 10개의 서브커맨드(analyze, split, archive, report, health, dedupe, stale, suggest, init, doctor)를 제공합니다. 실제로 제가 가장 많이 쓰는 건 doctor입니다:

# 전체 진단 (여기서 시작하세요)
memorybox doctor ~/openclaw

실행하면 5단계 진단을 돌립니다:

  MemoryBox Doctor — Full Diagnostic

[1/5] Health Check
  MEMORY.md: 3,460 bytes (34%)
  domains/: 5 files
  Daily logs up to date
  Health Score: 100/100

[2/5] Size Analysis
  ...

[3/5] Duplicate Detection
  ...

[4/5] Stale Content Check
  ...

[5/5] Smart Suggestions
  ...

20KB짜리 MEMORY.md를 넣으면 Health Score가 30점 정도 나오면서, 어떤 섹션을 domains/로 분리하면 좋을지 제안해줍니다. split 명령어로 인터랙티브하게 진행하면 5분이면 끝납니다.

실제 코드의 사이즈 판정 로직을 보면, 10KB 기준으로 단순 pass/fail이 아니라 초과 비율에 따라 감점 폭이 달라집니다:

# memorybox health 판정 로직 (실제 코드에서 발췌)
local pct=$((size * 100 / MAX_MEMORY_BYTES))
if [[ $pct -gt 400 ]]; then
  score=$((score - 70))   # 4배 초과: -70점
elif [[ $pct -gt 200 ]]; then
  score=$((score - 50))   # 2배 초과: -50점
else
  score=$((score - 30))   # 1배 초과: -30점
fi

오픈소스: openclaw-memorybox

역시 순수 Bash, 의존성 제로입니다.



결과

현재 제 워크스페이스의 실제 수치입니다.

적용 전에는 MEMORY.md가 20,542 bytes였습니다. 3-Tier로 분리한 후에는 3,605 bytes. 약 82% 감소했습니다.

컨텍스트 사용률은 체감이 확실했습니다. 이전에는 크론 몇 개만 돌려도 90% 이상을 찍으면서 상시 compaction이 걸렸는데, 지금은 여유 있게 동작합니다.

무엇보다 healthcheck 로그를 보면 변화가 명확합니다. 2월 10일 이후로 emergency recovery가 한 번도 트리거되지 않았습니다:

$ ls memory/emergency-recovery-*.log | tail -5
emergency-recovery-2026-02-09-1524.log
emergency-recovery-2026-02-09-1943.log
emergency-recovery-2026-02-09-2007.log
emergency-recovery-2026-02-09-2017.log
emergency-recovery-2026-02-09-2033.log
  ← 2/10 이후 없음

하루 5번 죽던 게 0번이 된 겁니다.

블로그 트래픽 통계

솔직한 한계

과대포장은 하고 싶지 않습니다.

토큰 절감 “82%“의 실체:

82%는 MEMORY.md 로딩 비용만 계산한 수치입니다. 전체 세션 토큰으로 보면 5-15% 수준이에요. 시스템 프롬프트에는 MEMORY.md 외에도 AGENTS.md, TOOLS.md, HEARTBEAT.md 같은 파일들이 있고, 실제 대화 토큰이 대부분을 차지하니까요. 48개 크론이 하루 수백 번 돌면 누적으로 의미는 있지만, 마법의 숫자는 아닙니다.

MemoryBox는 메모리 “엔진”이 아닙니다:

Mem0, Supermemory, QMD 같은 도구는 “뭘 기억할지”를 AI가 자동 판단합니다. MemoryBox는 그런 게 아니에요. “기억한 것들이 매 세션마다 과도하게 로딩되는 걸” 방지하는 유지보수 도구입니다. 경쟁이 아니라 보완 관계라서, Mem0 쓰면서 MemoryBox도 같이 쓸 수 있습니다.

진짜 가치는 뭐냐면:

토큰 절감보다 컨텍스트 오버플로 방지가 핵심입니다. 20KB MEMORY.md가 200K 컨텍스트의 10%를 차지하면, 에이전트가 실제로 생각할 수 있는 공간이 그만큼 줄어듭니다. 이건 느려지는 게 아니라 죽는 겁니다.



교훈 3가지

예방과 복구, 둘 다 필요합니다

백엔드 개발을 하면서 Circuit Breaker와 Rate Limiter를 같이 쓰는 것처럼, Self-healing(복구)과 MemoryBox(예방)도 둘 다 필요했습니다.

Self-healing만 있으면 같은 이유로 계속 죽고 살고를 반복합니다. 2월 9일에 하루 5번 죽은 게 그 증거였습니다. MemoryBox만 있으면 다른 이유로 죽었을 때 무방비고요.

graph LR
    P["예방 레이어<br/>MemoryBox"] -->|메모리 관리| S["안정 운영"]
    R["복구 레이어<br/>Self-Healing"] -->|장애 복구| S
    S -->|모니터링| P
    
    style P fill:#e8f5e9
    style R fill:#ffebee
    style S fill:#e3f2fd

프로덕션 환경에서는 양쪽 다 필요하다는 걸 뼈저리게 느꼈습니다.


Zero Dependency는 전략입니다

Mem0는 VC 펀딩 받은 서비스, Supermemory는 클라우드 구독, QMD는 OpenAI 키가 필요합니다. 다 좋은 도구인데, 의존성은 곧 장애점이에요.

제가 겪은 크래시의 직접 원인이 “설정 하나 잘못 건드림”이었거든요. 복잡한 솔루션을 설치할수록 장애점이 늘어납니다. 순수 Bash + 파일 조작만으로 해결할 수 있다면, 그게 프로덕션에서 가장 안전합니다.

실제로 Self-Healing의 핵심 스크립트들은 shellcheck을 전부 통과합니다. CI에서 매 커밋마다 검증하고 있습니다. 복구 스크립트에 버그가 있으면 본말전도니까요.


메트릭은 정직해야 합니다

“82% 절감”이라고 쓰면 클릭은 되겠지만, 실제론 전체의 5-15%입니다. README에도 이 사실을 명시해뒀습니다. 오픈소스를 과대포장하면 설치한 사람이 실망하고, 결국 신뢰를 잃게 됩니다.

GitHub 트래픽을 보면 재미있는 수치가 하나 있습니다. Self-Healing 리포지토리의 Clone 수(212 unique)가 View 수(195 unique)보다 많습니다. README를 보고 바로 설치하는 사람이 그만큼 많다는 뜻인데, 이 사람들이 실망하지 않게 하는 게 중요하다고 생각합니다.



GitHub 리포지토리

시도해보기

둘 다 5분이면 설치 가능합니다.

Self-Healing (크래시 자동 복구):

git clone https://github.com/Ramsbaby/openclaw-self-healing.git
cd openclaw-self-healing && bash install.sh

macOS(LaunchAgent)와 Linux(systemd user-level) 모두 지원합니다. sudo 없이 설치됩니다.

MemoryBox (메모리 관리):

git clone https://github.com/Ramsbaby/openclaw-memorybox.git
cd openclaw-memorybox && chmod +x bin/memorybox
sudo ln -sf "$(pwd)/bin/memorybox" /usr/local/bin/memorybox
memorybox doctor ~/openclaw

둘 다 MIT 라이선스, 제로 의존성입니다. 같은 프로덕션 인스턴스에서 24/7로 돌리면서 검증하고 있습니다.



다음 편 예고

내 문제를 풀기 위해 만든 이 도구들을, GitHub에 공개하기로 했습니다. 첫 Star, HackerNews 1 point의 씁쓸함, 그리고 12 Stars까지의 현실적인 이야기.

4편에서 계속




참고 :

https://github.com/Ramsbaby/openclaw-self-healing
https://github.com/Ramsbaby/openclaw-memorybox
https://github.com/letta-ai/letta
https://docs.openclaw.ai




읽어주셔서 감사합니다.🖐


Ramsbaby
Written byRamsbaby
이 블로그는 직접 개발/운영하는 블로그이므로 당신을 불쾌하게 만드는 불필요한 광고가 없습니다.

#My Github#소개 페이지#Blog OpenSource Github#Blog OpenSource Demo Site