레슨 3: 외부 메모리: 파일과 검색
학습 목표:
- 세션을 넘어 살아남아야 하는 메모리가 윈도우 밖, 파일에 기록되어야 하는 이유 설명하기
- CLAUDE.md 같은 사람이 쓰는 메모리 파일과 Auto memory 같은 모델이 쓰는 메모리 파일 구분하기
- '필요할 때 검색'과 '미리 전부 로드' 사이의 트레이드오프 말하기
- 메모리 파일을 읽고 쓰는 도구에 안전한 경로 경계 검사 추가하기
전제: 레슨 2를 마치고 컴팩션과 도구 결과 비우기의 차이를 이해 | 이전: 레슨 2 << | 다음: 레슨 4 >>
세션이 끝나면 윈도우 안의 모든 것이 사라진다
레슨 2는 풀리지 않은 문제로 끝났습니다: 사용자가 지난주 '저는 매운 음식 안 먹어요'라고 말한 일정 보조 에이전트가, 이번 주에 빈 윈도우로 새 대화를 열면 — 그 문장이 말해진 적 있다는 것을 전혀 모릅니다.
잘라내기, 컴팩션, 도구 결과 비우기로는 이것을 고칠 수 없습니다. 이 셋은 모두 '이 하나의 대화 안에서 자리가 부족해졌다'를 다룹니다. 여기 문제는 다릅니다: 이 대화에는 첫 턴부터 지난주의 내용이 전혀 들어 있지 않았습니다. 공식 쿡북은 선을 직설적으로 긋습니다 — 비우기와 컴팩션은 둘 다 현재 컨텍스트를 대상으로 하며, 새 세션이 시작되고 윈도우가 비어 있을 때는 어느 쪽도 도움이 되지 않습니다. 메모리가 그 문제를 풉니다1. 윈도우는 컨테이너로서 이 하나의 세션만큼만 삽니다. 세션을 닫으면, 윈도우 안에서 다른 곳으로 옮겨지지 않은 것은 진짜로 사라집니다.
정보를 이 세션 너머로 살려 두는 유일한 방법은, 세션이 끝나기 전에 그것을 윈도우 밖 어딘가에 기록하는 것입니다 — 외부 메모리로: 이 대화의 생애 주기에 묶이지 않은 저장소, 대개는 그냥 디스크의 파일입니다. 다음 세션이 시작되면 그 파일을 다시 읽어 그 내용을 새 컨텍스트 윈도우에 로드합니다.
CLAUDE.md: 사람이 쓰고, 매번 전부 로드된다
외부 메모리의 가장 직접적인 패턴은 사람이 메모리 파일을 관리하며, 프로젝트에 두고 매 세션 시작 때 전부 읽는 것입니다. Claude Code의 CLAUDE.md가 대표적 사례입니다: 공식 문서에 따르면 CLAUDE.md 파일은 매 세션 시작 때 컨텍스트 윈도우에 로드되어 대화 자체와 나란히 토큰을 쓰며, 권장 크기 목표는 각 파일을 200줄 아래로 유지하는 것입니다 — 파일이 길수록 더 많은 컨텍스트를 소비하고 에이전트의 지시 준수도가 낮아집니다.2 200줄은 완화된 권장치라는 점에 유의하세요. 진짜 딱딱한 한계는 4 MiB입니다: 그보다 큰 CLAUDE.md는 통째로 건너뜁니다.2
이 파일에서 눈여겨볼 만한 한 가지 세부가 있습니다: 공식 문서는 CLAUDE.md 안의 블록 수준 HTML 주석이 내용이 에이전트 컨텍스트에 주입되기 전에 제거된다고 설명합니다.2 다시 말해, <!-- --> 안에 무엇을 쓰든 사람이 파일을 열면 보이지만, 에이전트가 읽는 버전에는 그 주석이 포함되지 않습니다 — 이는 사람에게 '에이전트의 토큰 예산을 쓰지 않으면서 나에게 메모를 남기는' 방법을 줍니다.
CLAUDE.md에는 레슨 2의 컴팩션과 직접 이어지는 또 다른 성질이 있습니다: 문서는 프로젝트 루트 CLAUDE.md가 컴팩션을 견뎌 낸다고 언급합니다 — /compact 후 Claude는 그것을 디스크에서 다시 읽어 세션에 재주입합니다.2 달리 말하면, 이런 파일은 컴팩션에 의해 '우연히 보존'되는 것이 아니라, 따로 다시 읽혀 재주입됩니다 — 그 컴팩션이 그 내용을 요약에 남겼는지 여부에 전혀 의존하지 않습니다.
Auto memory: 모델이 쓰고, 필요할 때 검색된다
CLAUDE.md는 사람이 쓰고 매번 전부 로드됩니다. 이를 보완하는 패턴이 있습니다: 대화가 진행됨에 따라 기억할 가치가 있는 것을 모델이 직접 적어, 자신의 메모리 파일에 저장하게 하는 것 — Claude Code는 이 메커니즘을 Auto memory라 부릅니다. CLAUDE.md와의 분업은 상호 보완적이며, 비교표가 그 차이를 분명히 보여 줍니다: CLAUDE.md는 당신이 쓰고, Auto memory는 Claude가 씁니다.2
모델이 스스로 쓰는 메모리는 대개 두 층으로 갈립니다: 인덱스 파일(이를테면 MEMORY.md)과 주제별로 나뉜 구체적 메모리 파일 더미입니다. 인덱스 파일도 무한정 로드되지 않습니다 — 문서가 주는 규칙은 이렇습니다: 매 대화 시작 때 MEMORY.md의 첫 200줄, 또는 첫 25KB 중 먼저 도달하는 지점까지만 로드되고, 그 임계값을 넘는 내용은 세션 시작 때 로드되지 않습니다.2
그것이 **필요할 때 검색(retrieval on demand)**입니다: 세션 시작 때 에이전트는 인덱스 항목의 요약만 보고('이 주제에 대한 상세 메모는 어떤 파일에 있다' 같은 것), 각 구체적 메모리 파일의 전체 내용은 보지 않습니다. 문서는 이에 대해 직접적입니다: 주제 파일은 시작 때 로드되지 않으며, Claude는 그 정보가 필요할 때 표준 파일 도구로 필요할 때 로드한다2. 현재 작업이 실제로 특정 주제를 요구할 때에만, 그 구체적 메모리 파일의 내용이 이번 회차의 컨텍스트 윈도우로 끌려 들어옵니다.
나란히 놓고 보면, CLAUDE.md와 Auto memory는 메모리의 서로 다른 두 차원을 다룹니다:
- CLAUDE.md — 사람이 선별하고 크기를 절제한, 매번 적용되는 규칙과 관례. '이 프로젝트는 원래 이렇게 돌아가기로 되어 있다'류의 안정된 정보에 맞고, 미리 전부 로드됩니다.
- Auto memory — 수가 많을 수 있고 특정 작업에서만 중요한 구체적 세부. 필요할 때 검색에 맞아, 이번 작업이 필요로 하지 않는 메모리에 윈도우 예산을 낭비하지 않습니다.
둘 다 외부 메모리입니다. 차이는 '누가 쓰는가'와 '언제 로드되는가'뿐이며 — 이는 레슨 2의 사고 모델을 되울립니다: 메모리의 요점은 정보를 윈도우 밖으로 옮겨 세션을 넘어 살아남게 하는 것이고, 그 정보가 미리 전부 로드되는지 필요할 때 검색되는지는 그것이 얼마나 안정적이고 얼마나 자주 쓰이는지에 달려 있습니다.
메모리 파일 읽기·쓰기에 안전한 경계 더하기
CLAUDE.md 같은 사람이 쓴 파일이든 Auto memory 같은 모델이 쓴 파일이든, 에이전트가 메모리 파일을 읽고 쓰는 도구를 갖게 되면 마주해야 할 구체적인 엔지니어링 질문이 있습니다: 그 도구가 프로젝트 디렉터리 밖의 파일을 읽거나 쓰도록 구슬려질 수 있는가?
문자열 접두사 매치만 하는 경로 검사는 '메모리 디렉터리를 탈출하라'는 요청을 막는 것처럼 보이지만, 고전적인 구멍이 있습니다. 메모리 루트가 /project/memory라면, 순진한 startsWith("/project/memory") 검사는 /project/memory-evil 같은 경로도 통과시킵니다. 그 문자열로 시작하기는 하니까요 — 비록 그것이 메모리 루트 밖에 있는 완전히 다른 디렉터리이더라도 말입니다. 안전한 방법은 경로가 루트와 정확히 같거나, '루트 뒤에 경로 구분자가 붙은 것'으로 시작하도록 요구하는 것입니다:
abs === MEMORY_ROOT || abs.startsWith(MEMORY_ROOT + path.sep) 조합이 '루트 자체와 같은' 경로나 '루트 뒤에 구분자가 붙은 것으로 시작하는' 경로만 통과되도록 실제로 보장하는 것입니다 — /project/memory-evil은 어느 조건도 만족하지 않으므로 /project/memory 안의 경로로 오인되지 않습니다. 이 패턴은 레슨 6에서 지속 메모리 계층의 읽기·쓰기 도구를 만들 때 그대로 다시 쓰이며, 이 경계 검사가 무력할 때 메모리 파일이 어떤 종류의 공격 표적이 되는지는 레슨 5에서 분명히 밝힐 것입니다.
정리
- 세션이 끝나면 윈도우 안에서 밖으로 옮겨지지 않은 것은 영영 사라진다. 정보를 세션을 넘어 지키려면 세션이 끝나기 전에 윈도우 밖 외부 메모리에 기록해야 한다
- CLAUDE.md는 사람이 쓰고 매 세션 컨텍스트에 전부 로드되며, 공식 크기 목표는 200줄이다(딱딱한 한계 4 MiB, 그보다 크면 통째로 건너뛴다). 블록 수준 HTML 주석은 주입 전에 제거되고, 프로젝트 루트 CLAUDE.md는
/compact 후 다시 읽혀 재주입된다2
- Auto memory는 모델이 쓰고, 인덱스 파일과 구체적 주제 파일로 나뉜다. 인덱스는 첫 200줄 또는 25KB만 로드되고, 주제 파일은 시작 때 로드되지 않고 필요할 때 로드한다2. 그래서 쓰이지 않을 메모리에 윈도우 예산을 낭비하지 않는다
- 둘은 상호 보완적이다: CLAUDE.md는 매번 쓸모 있는 안정된 규칙에, Auto memory는 특정 작업에만 필요한 대량의 세부에 맞는다
- 메모리 파일의 읽기·쓰기 도구는 안전한 경로 경계 검사를 해야 한다.
abs === ROOT || abs.startsWith(ROOT + path.sep) 조합은 양쪽이 다 필요한데, startsWith 하나만으로는 같은 접두사 우회 구멍이 있기 때문이다
>> 레슨 4: 구조화된 상태: 에이전트가 작업이 어디까지 왔는지 기억하는 법