Claude Code에 장기 기억을 부여하는 방법: 상태 지속성(State Persistence)을 통해 얻은 5가지 교훈
요약
Claude Code 기반 자율 에이전트 시스템에 파일 기반의 장기 기억(State Persistence)을 부여하는 설계 방법론을 소개합니다. Markdown 파일을 활용해 프로젝트 명세, 상태 메모, 태스크, 결정 로그를 분리하여 관리함으로써 세션 간 컨텍스트 단절 문제를 해결합니다.
핵심 포인트
- 파일 기반의 장기 기억을 통해 세션 간 컨텍스트 유지 가능
- CLAUDE.md를 활용한 프로젝트 규칙 및 컨벤션 자동 로드
- 상태 메모와 태스크 파일을 통한 작업 연속성 확보
- 결정 로그(Decision Log)를 통한 의사결정 근거 기록 및 추적
요약 (TL;DR)
저는 Claude Code를 기반으로 구축하여 제 프로젝트들을 24시간 내내 수행하는 완전 자율 구현 시스템을 운영하고 있습니다. 제가 이 시스템에 가한 가장 큰 업그레이드는 더 똑똑한 프롬프트나 더 나은 모델을 도입한 것이 아니었습니다. 바로 **파일 기반의 장기 기억 (file-based long-term memory)**을 부여한 것이었습니다. 이 포스트에서는 제 에이전트를 금붕어에서 동료로 탈바꿈시킨 상태 지속성 (state persistence) 설계와 그 과정에서 배운 5가지 교훈을 다룹니다. 💡
문제점
모든 Claude Code 세션은 완전한 기억 상실 상태로 시작됩니다.
_당신_이 기억을 담당하고 있다면 괜찮습니다. 당신은 어제 Postgres 대신 SQLite를 사용하기로 결정했다는 사실을 기억하고, 불안정한 테스트가 이미 알려진 이슈라는 것을 기억하며, 그에 따라 방향을 잡을 수 있습니다.
하지만 인간을 루프에서 제외하는 순간 시스템은 무너집니다. 제 시스템은 아무도 지켜보지 않는 상태에서 정해진 스케줄에 따라 Claude Code 세션을 실행합니다. 초기에는 특정 밤의 트랜스크립트(transcript)가 다음과 같았습니다:
- 첫째 날 밤: 에이전트가 실패하는 빌드를 조사하여 근본 원인이 버전 불일치임을 파악하고 패치합니다. 좋습니다.
- 둘째 날 밤: 새로운 세션, 컨텍스트(context) 제로. 에이전트는 관련된 경고를 보고, 동일한 의존성 트리(dependency tree)를 처음부터 다시 조사하여 두 번째로
그 답은 당혹스러울 정도로 평범했습니다: 세션 시작 시 읽고, 결정 시점에 작성하는 Markdown 파일들.
세 개의 파일로 구성된 핵심 구조
저의 지속성 계층(persistence layer)은 프로젝트당 세 개의 파일을 중심으로 구축되었으며, 각 파일은 정확히 하나의 역할만을 수행합니다:
project/
├── CLAUDE.md # 작업 방식(HOW): 규칙, 컨벤션, 범위 (거의 변경되지 않음)
├── state/
...
형식보다 중요한 것은 분리 그 자체입니다:
- 프로젝트 명세 파일 (
CLAUDE.md): 코딩 컨벤션(coding conventions), 에이전트가 절대 건드려서는 안 되는 것, 결과 보고 방식 등 지속적인 규칙을 담습니다. Claude Code는 세션 시작 시 이 파일을 자동으로 로드하며, 이는 모든 세션에 영구적으로 적용되어야 하는 모든 사항을 위한 자연스러운 저장소가 됩니다. - 상태 메모 (state memo): 인수인계 노트 역할을 합니다. 이 파일은 단 하나의 질문에 답합니다: "만약 지금 당장 컨텍스트(context)가 전혀 없는 상태에서 새로운 세션이 시작된다면, 작업을 계속하기 위해 무엇을 알아야 하는가?" 현재 단계, 마지막으로 완료된 단계, 다음 단계, 그리고 현재 진행 중인 특이 사항 등을 기록합니다.
- 태스크 파일 (task file): 상태(
todo / doing / done / blocked)가 포함된 체크리스트입니다. 세션은 새로운 작업을 만들어내는 대신, 완료되지 않은 첫 번째 항목을 찾아 작업을 이어갑니다. - 결정 로그 (decision log): 추가만 가능한(append-only) 방식입니다. 모든 항목에는 ID(
D-001,D-002, …), 날짜, 결정 사항, 그리고 — 결정적으로 — 그 _이유(reason)_가 포함됩니다. 이전 항목을 절대 수정하지 않으며, 새로운 항목으로 기존 항목을 대체합니다.
작성 규칙
파일만으로는 아무것도 할 수 없습니다. 동작은 에이전트의 지침(instructions)에 내장된 두 가지 규칙에서 비롯됩니다:
규칙 1: 작업 전 읽기. 모든 세션은 코드를 건드리기 _전_에 명세 파일, 상태 메모, 태스크 파일을 해당 순서대로 읽는 것으로 시작합니다. 이는 타협할 수 없는 원칙입니다. 이 과정을 건너뛰는 세션은 지난주의 실수를 반복하는 세션이 됩니다.
규칙 2: 세션 종료 시점이 아닌, 결정이 내려진 시점에 기록하세요. 저의 첫 번째 버전은 "작업을 마치기 전에 상태 메모(state memo)를 업데이트하세요"라고 되어 있었습니다. 최악의 아이디어였습니다. 세션은 종료됩니다. 컨텍스트(context)가 가득 차거나, 프로세스가 종료되거나, 머신이 재부팅될 수 있습니다. "마지막에" 하려고 버퍼에 담아둔 모든 것은 정확히 유실되는 정보가 됩니다. 이제 규칙은 다음과 같습니다: 무언가가 결정되거나 발견되는 즉시 기록합니다. 상태 메모는 현실이 바뀔 때마다 세션 중간에 업데이트됩니다.
flowchart LR
A[Session start] --> B[Read spec + memo + tasks]
B --> C[Do work]
...
충돌 규칙 (The conflict rule)
여러 개의 파일이 생기면, 파일들은 반드시 서로 모순되는 내용을 담게 됩니다. 오래된 전략 노트에는 "우리는 X를 하고 있다"라고 적혀 있는데, 상태 메모에는 "우리는 Y로 피벗(pivot)했다"라고 적혀 있을 수 있습니다. 결정권(tie-breaker)이 없는 에이전트는 마지막으로 읽은 내용을 선택하거나, 더 나쁜 경우 두 가지를 모두 만족시키려 시도할 것입니다.
따라서 명세서(spec file)에는 다음과 같은 명시적인 우선순위 순서가 작성되어 있습니다:
상태 메모 (state memo) > 전략 문서 (strategy doc) > 프로젝트 명세 (project spec) > 결정 로그 (decision log) > 그 외 모든 것
가장 최근에 업데이트되었고 가장 운영 중심적인 파일인 상태 메모가 항상 우선합니다. 결정 로그(decision log)는 의도적으로 하단에 배치되었습니다. 그것은 _역사(history)_이며, 역사는 현재를 설명할 수는 있지만 현재를 덮어쓸 수는 없기 때문입니다. 에이전트가 경로를 변경할 때, 현재 상태 파일을 덮어쓰고 새로운 결정 항목을 추가합니다. 이렇게 하면 이전의 추론 과정이 현재의 계획으로 오인되지 않으면서도 그대로 보존됩니다.
실제 적용 사례
제 시스템에서 사용 중인 실제(약간 일반화된) 상태 메모의 모습입니다:
# State memo — updated 2026-07-04 23:10
## Current phase
...
이것을 읽는 새로운 세션은 고고학적 탐사를 할 필요가 전혀 없습니다. 현재 단계, 마지막으로 확인된 상태, 정확한 다음 행동, 그리고 추론 과정에 대한 포인터가 포함된 알려진 실패 모드(failure mode)를 즉시 파악합니다. 마지막 줄은 '금붕어에서 동료로(goldfish-to-coworker)' 진화하는 순간입니다. 에이전트는 타임아웃 문제를 처음부터 다시 진단하는 것이 아니라, 이미 발생했던 문제임을 인지합니다.
교훈 (Lessons Learned)
1. 세션 메모리 (Session memory)가 병목이지, 모델의 지능이 아닙니다. 저는 세션을 더 "똑똑하게" 만들기 위해 몇 주 동안 프롬프트(Prompt)를 튜닝하는 데 시간을 보냈지만, 실제 실패 원인은 각 세션이 매우 영리하면서도 동시에 "눈이 멀어(blind)" 있었다는 점이었습니다. Claude Code(저는 2026 릴리스를 사용 중입니다)는 세션 간에 컨텍스트(Context)가 유지될 때에만 수 주에 걸친 프로젝트를 충분히 수행할 수 있습니다. 프롬프트를 건드리기 전에 메모리부터 해결하세요.
2. 종료 시 기록(Write-on-exit)보다 결정 시 기록(Write-on-decision)이 언제나 승리합니다. 세션은 생각보다 훨씬 자주 갑작스럽게 종료됩니다: 컨텍스트 제한, 충돌(Crash), 기기 재부팅 등. 6개월간 24/7 운영해 본 결과, 세션 중간의 지속성(Persistence) 덕분에 유실된 작업을 수십 번 다시 수행해야 하는 상황을 피할 수 있었습니다. 만약 에이전트가 학습 내용을 최종 요약 단계까지 버퍼(Buffer)에 담아두고 있다면, 당신의 메모리 시스템은 가장 필요할 때 정확히 실패하는 구조를 가진 것입니다.
3. "어떻게(how)", "어디에(where)", "왜(why)"를 서로 다른 파일로 분리하세요. 저의 첫 번째 시도는 하나의 거대한 NOTES.md 파일이었습니다. 그것은 늪이 되었습니다. 700줄에 달하는 파일 안에 오래된 계획이 현재 상태 옆에 나란히 놓여 있었고, 에이전트는 무엇이 무엇인지 구분할 수 없었습니다. 변경 빈도(Rate of change)에 따라 분리하는 것(규칙은 드물게 변경, 상태는 매시간 변경, 결정은 추가만 수행)으로 이 문제를 해결했습니다. 각 파일은 하나의 역할과 하나의 업데이트 패턴을 가집니다.
4. 명시적인 우선순위 설정은 선택 사항이 아닙니다. 파일 간의 모순은 예외적인 상황이 아니라, 피벗(Pivot)을 거치는 모든 프로젝트의 상시적인 상태입니다. "상태 메모(State memo)가 항상 우선한다"라는 단 한 줄의 원칙이 제 설정에서 그 어떤 것보다 에이전트의 혼란을 더 많이 해결해 주었습니다. 이 원칙이 없다면, 에이전트는 당신의 옛 계획과 새 계획을 평균 내어 둘 다 아닌 무언가로 만들어 버릴 것입니다. ⚠️
5. 대화 기록(Transcript)이 아니라 결정(Decision)과 막다른 길(Dead end)을 지속시키세요. 저의 본능은 모든 것을 보관하는 것이었습니다. 틀렸습니다. 전체 세션 로그는 쓰기 전용 쓰레기(Write-only garbage)에 불과합니다. 미래의 어떤 세션도 4만 토큰에 달하는 과거 이력을 읽지 않을 것입니다. 금값만큼 가치 있는 것은 아주 작습니다: 우리가 무엇을 결정했는지, 왜 그랬는지, 그리고 시도했지만 실패했던 것은 무엇인지. "명백한 리팩터링(Refactor)이 릴리스 스크립트를 깨뜨림 — D-014 참조"라는 한 줄은 밤샘 작업을 통째로 아껴줍니다. 압축(Compression)은 타협이 아니라 기능(Feature)입니다.
다음 단계 (What's Next)
현재의 설계는 의도적으로 로우테크(low-tech)를 지향하고 있으며, 저는 이 상태를 유지할 계획입니다. 하지만 두 가지 업그레이드 사항을 고려하고 있습니다:
- 메모리 검토 단계 (Memory review passes): 상태 파일(state files)을 정리하는 것만을 목적으로 하는 주기적인 세션입니다. 중복된 내용을 병합하고, 오래된 항목을 삭제하며, 문제가 발생하기 전에 모순되는 부분을 표시(flagging)합니다.
- 교차 프로젝트 메모리 (Cross-project memory): 저의 에이전트들은 여러 코드베이스(codebases)에 걸쳐 작업하지만, 한 곳에서 배운 교훈(
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기