# AI 에이전트에게 기억력을 만들어준 삽질기

- 작성자: [조용한 작가](https://community.clauders.ai/member/imported-member-ny7O1ogWBr)
- 공간: [노트 / 글쓰기](https://community.clauders.ai/writing)
- 게시: 2026-03-06
- 연재: [노트 정리에서 검색과 에이전트 기억까지](https://community.clauders.ai/series/claude-knowledge-and-memory) 5/5편
- 주소: https://community.clauders.ai/writing/post/ai-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8%EC%97%90%EA%B2%8C-%EA%B8%B0%EC%96%B5%EB%A0%A5%EC%9D%84-%EB%A7%8C%EB%93%A4%EC%96%B4%EC%A4%80-%EC%82%BD%EC%A7%88%EA%B8%B0-fbac496b-ab0a-8152-b673-37cec41d361d

---

## Claude Code는 기억을 못 한다

Claude Code로 작업하다 보면 한 가지 불편함을 마주하게 된다. 터미널을 닫는 순간, 그 세션에서 나눈 모든 대화와 맥락이 사라진다는 것이다.

같은 프로젝트 폴더에서 터미널 두 개를 열어 작업하면, 각각의 Claude Code 인스턴스는 서로의 존재조차 모른다. CLAUDE.md에 프로젝트 규칙을 적어둔다 해도, 그건 규칙이지 "어제 내가 뭘 했는지"에 대한 기억이 아니다.

## 처음엔 수동으로 기억을 남기려 했다

Claude Code에게 기억을 시키기 위해 처음에는 커스텀 커맨드를 만들었다. ~/.claude/ 하위에 [MEMORY.md](http://memory.md/) 파일을 두면 다음 세션에서 자동으로 읽어온다. 그래서 세션을 끝내기 전에 "지금까지 한 거 MEMORY.md에 정리해"라고 말하면, 다음에 이어서 작업할 수 있다.

문제는 이걸 매번 기억하고 말해야 한다는 것이다. 까먹으면 그냥 날아간다. 그리고 터미널 여러 개를 동시에 쓸 때는 이 방법으로는 한계가 있다.

## 1단계: .zshrc의 preexec 훅으로 명령어 로깅

해결의 실마리는 의외로 Claude Code 바깥에 있었다. .zshrc는 zsh 셸이 시작될 때마다 실행되는 설정 파일인데, 여기에 훅 함수를 등록할 수 있다.

그중 preexec는 사용자가 명령어를 입력하고 Enter를 누른 직후, 명령이 실행되기 직전에 호출되는 함수다. 이걸 이용하면 터미널에서 실행하는 모든 명령어를 가로채서 로그로 남길 수 있다.

```
터미널에서 명령어 입력 → preexec 실행 → 로그 파일에 기록 → 원래 명령어 실행
```

핵심 아이디어는 이랬다. Claude Code가 스스로 기억하지 못하더라도, **터미널이 명령어 이력을 파일로 남겨두면**나중에 Claude Code가 그 파일을 읽어서 맥락을 복구할 수 있다. RAG(Retrieval-Augmented Generation)와 비슷한 발상이다. 기억을 내부에 저장하는 게 아니라, 외부 파일에서 필요할 때 꺼내 읽는 것이다.

.zshrc에 추가한 코드는 이게 전부다.

```
preexec() {
  local project_root
  project_root=$(git rev-parse --show-toplevel 2>/dev/null)
  if [[ -n "$project_root" && -d "$project_root/.claude" ]]; then
    local project_name=$(basename "$project_root")
    local local_log="$project_root/.logs"
    local global_log="$HOME/.claude/logs/$project_name"
    mkdir -p "$local_log" "$global_log"
    local entry="[$(date '+%H:%M:%S')] $1"
    echo "$entry" >> "$local_log/$(date '+%Y-%m-%d').log"
    echo "$entry" >> "$global_log/$(date '+%Y-%m-%d').log"
  fi
}
```

동작 방식을 정리하면 이렇다.

1. git rev-parse --show-toplevel로 현재 디렉토리가 속한 git 프로젝트의 루트를 찾는다
2. 그 루트에 .claude/ 폴더가 있는지 확인한다 — Claude Code를 사용하는 프로젝트라는 의미다
3. 조건이 맞으면 타임스탬프와 함께 명령어를 로그 파일에 기록한다

로그는 두 곳에 저장된다.

- 프로젝트 내부 (프로젝트/.logs/2026-02-27.log) — 해당 프로젝트 작업 이력을 바로 확인할 때
- 중앙 저장소 (~/.claude/logs/프로젝트명/2026-02-27.log) — 여러 프로젝트의 작업 이력을 한 곳에서 볼 때

.logs/ 폴더가 git에 올라가지 않도록 글로벌 gitignore도 설정했다.

```
git config --global core.excludesfile ~/.config/git/ignore
# ~/.config/git/ignore 내용: .logs/
```

근데 내가 기대한 바람과 다르게 실제 로그는 이렇게 나왔다.

```
[09:15:23] git status
[09:15:45] bun dev
[09:20:11] claude
[09:35:02] git add -A && git commit -m "feat: add login page"
[10:12:33] bun test
```

## 명령어만으론 부족했다

기록되는 건 git status, bun dev 같은 터미널 명령어뿐이었다. Claude Code와 나눈 대화 — "이 구조로 갈까?", "아니 그건 오버엔지니어링이야", "그러면 이렇게 바꿔" 같은 맥락은 전혀 남지 않았다.

명령어 로그만으로는 뭘 했는지는 알 수 있어도 왜 했는지는 알 수 없었다.

## transcript의 발견과 좌절

대화 내용을 기록할 방법을 찾다가, Claude Code가 이미 모든 세션의 대화를 저장하고 있다는 걸 발견했다.

```
~/.claude/transcripts/
├── ses_3f70b5850ffe.jsonl
├── ses_3f2c355eaffe.jsonl
├── ses_3f708da61ffe.jsonl
└── ...
```

세션마다 .jsonl 파일이 생기고, 안에는 사용자 입력과 도구 호출 기록이 담겨 있었다. "이미 있었잖아!" 싶었는데, 실제로 열어보니 두 가지 문제가 있었다.

첫째, 프로젝트별로 분류되어 있지 않다. 세션 ID 기반의 파일명만 있어서, 어떤 프로젝트에서의 대화인지 바로 알 수 없다.

둘째, 더 치명적인 문제가 있었다. transcript에는 user 타입(사용자 입력)과 tool_use/tool_result 타입(도구 호출)만 기록되어 있고, Claude의 텍스트 응답은 저장되지 않았다. 대화의 절반이 빠져 있는 셈이다.

```
# transcript 내 메시지 타입 분포
tool_use: 87
tool_result: 85
user: 6
assistant: 0  # Claude 응답이 없다
```

자동 추출 스크립트를 만들어서 해결하려 했지만, 애초에 데이터가 없으니 추출할 수도 없었다.

## 결국 Claude가 직접 쓰게 했다

결국 transcript에서 추출하는 대신, Claude Code 자신이 현재 세션의 대화를 회고해서 직접 기록하게 만들었다. 다시 돌고 돌아 Claude Code의 슬래시 커맨드 기능을 활용해서 /memory라는 커맨드를 만들었다.

```
현재 세션의 대화 내용을 프로젝트에 저장하는 커맨드.

## 작성 규칙

- 사용자 메시지는 원문 그대로 기록한다. 요약하지 않는다.
- Claude 응답은 사용자에게 보여진 텍스트만 기록한다.
  tool call, 시스템 메시지, 내부 사고 과정은 제외한다.
- 코드 블록이 포함된 응답은 코드 블록도 함께 기록한다.
- 대화 순서를 빠짐없이 유지한다. 건너뛰지 않는다.
```

세션이 끝날 때 /memory를 입력하면, Claude가 그 세션에서 나눈 모든 대화를 마크다운으로 정리해서 프로젝트의 .logs/transcripts/ 폴더에 저장한다.

```
프로젝트/.logs/transcripts/
├── 2026-02-27_09-30.md    ← 오늘 오전 세션
├── 2026-02-27_14-00.md    ← 오늘 오후 세션
└── 2026-02-28_10-15.md    ← 내일 세션
```

결과물은 이런 형태다.

```
# 대화 기록 — 2026-02-27 09:30

## 나
.zshrc가 무슨 역할이길래 클로드 초기 관련 세팅이 가능한 거야?

## Claude
Claude 전용 설정 파일이 아니야. zsh 셸이 시작될 때마다
자동으로 실행되는 스크립트야...

## 나
이거는 그럼 이 프로젝트에서만 작동하는 거야?

## Claude
.zshrc에 설정했으니까 모든 프로젝트에서 작동해...
```

내가 너무 깊이 생각을 하지 않고 작업한 걸 수도 있다. 분명히 다른 사람들도 각각의 터미널에서 병렬로 작업을 할 때에도 맥락이 이어지길 했으면 하는 생각은 한 번쯤은 했을 것이다. 레퍼런스를 좀 더 찾아보면 사실 이미 방법이 나와있을지도 모르겠다. 생각난 김에 오늘 방법을 다시 한 번 찾아봐야겠다.
