새 세션을 열 때마다 어제까지 쌓아온 맥락이 통째로 사라져 있었습니다. 주식 앱을 만들던 때였어요. 전날 정한 아키텍처 방향이나 포기한 대안들을 새 세션은 전혀 모르는 채로 시작했죠. 처음에는 붙여넣기로 대응했습니다. “이 프로젝트는 이러저러하고, 지금까지 이렇게 해왔고, 어제 이런 결정을 했어요.” 이게 반복되면서 배경 설명이 세금처럼 쌓이기 시작했습니다.
코딩 프로젝트만의 문제도 아니었어요. GPT에서 프로젝트까지 분리하고 세션까지 나눴는데 다른 프로젝트 내용을 가져와서 생각하질 않나, 새 채팅을 열 때마다 배경을 다시 설명해야 하질 않나. 결국 GPT 프로젝트는 안 쓰게 됐습니다.
이 글은 그 문제를 따라가다 도착한 컨텍스트 엔지니어링 이야기입니다. 거창한 말 같지만 질문은 하나예요. 기억이 없는 LLM에게 매번 무엇을 실어 보낼 것인가.
LLM은 기억을 가지지 못한다
출발점은 LLM의 근본 속성입니다. LLM은 세션이 끝나면 기억을 갖지 않습니다. 어제의 대화는 모델 어디에도 저장되지 않아요. 다음 세션의 Claude는 이전 Claude가 아닙니다. 완전히 백지에서 시작하는 거죠.
처음 배울 때 “기억이 없다”는 말을 듣긴 했습니다. 그때는 “아, 그렇구나” 하고 흘렸는데, 위의 장면들을 겪고 나서야 그 말이 뭘 의미하는지 몸으로 알게 됐어요.
그럼 채팅은 어떻게 어제 대화를 기억하나
여기서 이상한 점이 하나 있습니다. 기억이 없다면서, 같은 채팅방 안에서는 아까 한 말을 잘 기억하잖아요?
트릭은 단순합니다. 기억하는 게 아니라, 매번 대화 전체를 처음부터 다시 보냅니다. 내가 메시지를 하나 보낼 때마다 채팅앱이 지금까지의 대화 이력 전부를 모델에게 통째로 다시 전달하고, 모델은 그걸 매번 처음 읽는 거예요. 기억하는 것처럼 보이는 것뿐입니다.
이 구조를 알면 두 가지가 바로 따라옵니다. 대화가 길어질수록 매번 보내는 양이 늘어난다는 것, 그리고 그게 전부 토큰 비용이라는 것. 실제로 Claude Code에서 한 주제로 길게 판 세션에 새 주제를 이어가려 했더니, 앞선 검색 결과들이 새 주제 매 메시지마다 따라붙어서 토큰도 새고 attention도 희석된다는 걸 알게 됐습니다. 그래서 새 주제는 새 챗으로 옮기는 게 낫다는 거였고요.
대화 말고 프로젝트 정보는 어떻게 전달하나
대화 이력은 채팅앱이 알아서 다시 보내줍니다. 그런데 프로젝트 정보는요? “이 프로젝트가 뭐고, 어떤 구조고, 어떤 결정을 해왔는지”는 대화 이력 어디에도 없습니다. 새 세션마다 내가 손으로 다시 깔아줘야 했던 게 바로 이 부분이었어요.
Claude Code에는 이걸 위한 자리가 있습니다. 프로젝트 루트에 CLAUDE.md 파일을 두면 세션이 시작할 때 자동으로 읽혀요. 매 대화에 실려 들어가는 상시 컨텍스트인 거죠. 여기에 프로젝트 정보를 넣어두면 새 세션의 LLM도 그걸 읽고 시작합니다.
처음에는 코딩 컨벤션 같은 규칙만 넣었습니다. 그러다 다른 것들도 넣기 시작했어요. “지금 이 프로젝트는 이런 상태다”, “이 결정을 이런 이유로 했다”, “이걸 시도했다가 이래서 포기했다.” 그랬더니 LLM이 갑자기 아는 사람이 됐습니다. 새 세션을 열어도 배경 설명 없이 바로 이어갈 수 있었어요.
다 넣었더니 무시당했다
그럼 프로젝트 정보를 전부 CLAUDE.md에 넣으면 되겠네? 저도 그렇게 갔습니다. 넣을수록 좋을 줄 알고 계속 길어졌는데, 어느 순간부터 컨벤션 누락이 생기기 시작했어요. 분명히 파일에 적혀 있는 규칙인데 안 지키는 겁니다. “있긴 한데 무시됨” 상태.
원인을 파보니 CLAUDE.md 과적재였습니다. 두 가지 문제가 겹쳐요.
하나는 비용. CLAUDE.md는 매 메시지마다 다시 읽힙니다. 거기 쓴 모든 글자가 세션 내내 매 요청 비용에 얹혀요. 파일이 길수록 그 세금이 대화 내내 반복해서 나갑니다.
다른 하나는 준수율. 컨텍스트가 길어지면 모델의 attention이 전체에 퍼지면서, 멀리 떨어진 상시 블록에 박힌 특정 규칙이 우선순위에서 밀려납니다. CLAUDE.md가 길수록 개별 규칙 준수율은 더 떨어져요. 항상 떠 있다고 항상 따르는 게 아니었습니다. 컨텍스트가 너무 많으면 멍청해진다는 얘기가 그냥 하는 말이 아니었던 거죠.
그래서 목차만 들고 다닌다
여기서 나온 판단 기준이 하나 있습니다. “이걸 매 메시지마다 들고 다닐 가치가 있나?”
있으면 CLAUDE.md에 남기고, 아니면 밖으로 뺍니다. 프로젝트 정보 본문은 docs/ 아래 문서로 빼고, CLAUDE.md에는 그 문서들의 목차만 둬요. “P&L 작업할 땐 docs/pnl.md 먼저 읽어” 같은 한 줄 포인터. 그러면 무거운 내용은 평소엔 컨텍스트에 안 실리고, 필요한 작업이 왔을 때만 LLM이 해당 파일을 읽어서 그 세션에 가져갑니다.
제 프로젝트에서 살아남은 최소 골격은 세 파일입니다. index.md(지금 어떤 상태인가), todo.md(뭘 할 건가), log.md(뭘 했나). 그중에서도 index.md가 핵심이에요. 새 세션의 LLM에게 주는 인수인계서거든요. 1분 안에 이 프로젝트가 어디에 있는지 파악할 수 있게.
단, 이 구조에는 조건이 하나 붙습니다. 목차가 가리키는 문서가 현행이어야 해요. index.md가 두 달 전 상태에 멈춰 있으면 LLM은 두 달 전 지도를 들고 일합니다. 그래서 코드가 바뀔 때 문서도 같이 갱신하는 습관이 이 구조의 절반입니다. 문서는 분량이 아니라 밀도고, LLM이 지금 읽을 수 있는 것만 가치가 있어요.
내 CLAUDE.md는 이렇게 생겼다
말로만 하면 감이 안 오니까 골격 예시를 하나 두겠습니다. 실제로 쓰는 구조를 단순화한 거예요.
# trading_mvp
주식 포트폴리오 앱. 작업 브랜치는 dev.
## 상시 규칙 (짧고 보편적인 것만)
- 커밋 전 테스트를 돌린다.
- 문서는 한글로 쓴다.
## 문서 목차 (필요할 때 읽어라)
- 지금 상태 → docs/index.md
- 할 일 → docs/todo.md
- 한 일·결정 이력 → docs/log.md
- P&L 로직 작업할 땐 → docs/pnl.md 먼저 읽기
포인트는 두 개입니다. 상시 규칙은 모든 작업에 적용되는 것만 짧게. 나머지는 전부 포인터로. 이 파일이 한 화면을 넘어가기 시작하면 뭔가를 밖으로 뺄 때가 된 겁니다.
시작은 index.md 한 단락이면 됩니다. “지금 이 프로젝트는 어떤 상태인가” 3~5줄. 다음에 이 프로젝트를 열 때 LLM에게 주는 첫 브리핑이 됩니다.