devAlice
← Alice 활용법

3. 메모리 시스템 — 동적 컨텍스트를 어디에 어떻게 누적하는가

RAG도 벡터 DB도 아닌, 마크다운 파일 몇 개로 만든 메모리. 4가지 메모리 타입, 인덱스 전략, 자동 로드와 수동 호출의 분리, 그리고 가지치기까지.

이 글은 시리즈 Alice 활용법의 3편이다. 2편 페르소나 설계에서, 페르소나는 "이 협업자가 누구인가"이고 메모리는 "지금 알고 있는 사실"이라고 분리했다. 이 글은 그 메모리를 어떻게 설계했는지에 대한 기록이다.

처음에는 거창한 것을 떠올렸다. 벡터 DB, embeddings, RAG 파이프라인. 며칠 만에 다 접었다. 운영자가 매일 누적하는 양이 그 정도가 아니었다. 필요한 것은 휘발되지 않고, 사람이 직접 읽을 수 있고, 도구가 자동 로드할 수 있는 단순한 저장소였다. 답은 파일 시스템이었다.

0. 기억은 mind의 연장이다

페르소나가 "이 협업자는 누구인가"라면, 메모리는 "지금 이 협업자가 mind에 무엇을 들고 있는가"다.

휘발되는 컨텍스트는 운영자에게 매일 같은 mind를 다시 만들 비용을 강요한다. 어제 어떤 결정을 했는지, 왜 그렇게 했는지, 무엇이 작동하지 않았는지 — 이 모든 것이 다음 세션 시작점에서 사라져 있다면, 운영자는 매일 자기 mind의 어제 부분을 다시 짓고 있어야 한다.

메모리는 RAG도 벡터 DB도 아니다. 운영자의 결정·피드백·맥락을 다음 세션이 잃지 않게 잡아 두는 가장 단순한 장치다.

아래 §1부터는 그 장치를 어떻게 — 마크다운 파일 몇 개와 인덱스 하나로 — 만들었는지의 실제 구조를 다룬다.

1. 단순한 결정 — 마크다운 파일

운영자가 수렴한 구조는 다음과 같다.

memory/
├── MEMORY.md              # 인덱스 (자동 로드)
├── user_role.md           # user 타입
├── feedback_testing.md    # feedback 타입
├── project_q2_release.md  # project 타입
├── reference_grafana.md   # reference 타입
└── ...
  • 한 메모리 = 한 파일
  • 인덱스(MEMORY.md) 1개가 모든 파일에 대한 1줄 포인터
  • 모든 파일은 마크다운, 사람이 직접 읽을 수 있음
  • 도구가 세션 시작 시 인덱스의 앞 N줄을 자동 로드

이게 전부다. 복잡한 인프라가 없다. git으로 관리되고, 텍스트 에디터로 편집되고, grep으로 검색된다.

매일 누적하는 컨텍스트는 의외로 작다. 인프라보다 규율이 더 중요하다.

2. 네 가지 메모리 타입

모든 정보를 한 타입으로 다루지 않는다. 운영자는 네 가지 타입으로 분리했다.

2.1 user — 운영자에 대한 사실

운영자가 누구인지, 어떤 역할인지, 어떤 영역을 잘 아는지.

---
name: user-role-and-expertise
description: 운영자의 직무·전문 영역·플랫폼
metadata:
  type: user
---
 
운영자는 시니어 개발자, 주력 언어 X·Y·Z, 주로 다루는 플랫폼 A·B.
프론트엔드는 최근에 시작 — 프론트 설명은 백엔드 비유로 풀어줄 것.

이 타입의 가장 큰 효과는 — 응답이 운영자의 수준과 영역에 맞춰진다는 점이다. 학생 톤이 사라지고, 운영자가 이미 아는 것을 다시 설명하지 않는다.

2.2 feedback — 명시적 지침

운영자가 명시적으로 준 피드백. "이렇게 하지 마라" / "이렇게 계속 해라" 둘 다.

---
name: integration-test-policy
description: 통합 테스트는 실제 DB로, mock 금지
metadata:
  type: feedback
---
 
통합 테스트는 mocked DB가 아니라 실제 DB로 돌릴 것.
 
**Why:** 지난 분기 mock 테스트는 통과했는데 prod 마이그레이션이 깨진 사건.
**How to apply:** integration/ 경로 안의 모든 테스트. unit/는 mock 허용.

피드백 타입에는 항상 WhyHow to apply를 같이 적는다. 이유 없는 규칙은 엣지 케이스에서 작동하지 않는다. 이유가 있어야 새 상황에서도 같은 정신으로 판단할 수 있다.

피드백은 두 가지에서 모두 기록한다 — 실패 시 교정성공 시 검증. 후자는 자주 빠뜨리는데, 누락하면 다음 세션에서 같은 좋은 결정을 다시 협의해야 한다.

2.3 project — 진행 중인 일의 맥락

지금 진행 중인 일에 대한 사실. 가장 변동이 큰 타입.

---
name: q2-merge-freeze
description: Q2 모바일 릴리스 컷에 따른 머지 동결 일정
metadata:
  type: project
---
 
2026-03-05부터 머지 동결 (모바일 릴리스 브랜치 컷).
 
**Why:** 모바일 팀이 릴리스 브랜치를 자르는 기간.
**How to apply:** 그 날짜 이후 비-크리티컬 PR 작업 일정은 사용자에게 미리 알릴 것.

핵심은 상대 시간을 절대 시간으로 변환하는 것이다. "다음 주", "목요일" 같은 표현은 메모리에 들어가면 며칠 후엔 의미가 사라진다. "2026-03-05" 같은 절대 날짜로 변환해서 기록한다.

project 타입은 가장 빠르게 stale 되는 타입이라 가지치기 대상도 가장 잦다(§5 참조).

2.4 reference — 외부 시스템 포인터

정보가 어디 있는지에 대한 포인터. 정보 자체가 아니라.

---
name: oncall-latency-dashboard
description: Grafana 보드 grafana.internal/d/api-latency — 오너 oncall이 보는 지연 대시보드
metadata:
  type: reference
---
 
요청 처리 코드를 건드릴 때 확인할 대시보드.
오너 oncall이 이걸 보고 페이저가 울리니, 변경 전후 비교 필요.

reference는 외부 시스템(Linear, Grafana, Notion, Slack 등)의 어떤 위치가 어떤 목적인지를 기록한다. 정보 자체는 변하지만 위치는 안정적이다. 위치를 기억하면 매번 찾아 헤매지 않는다.

3. 인덱스 — MEMORY.md 단 하나

모든 메모리 파일은 인덱스 한 개를 통해 발견된다.

# Memory
 
> 첫 200줄이 자동 로드. 핵심 규칙은 페르소나에, 여기는 포인터만.
 
## 메모리 파일 인덱스
 
### 규칙/절차
- [work-process.md](rules/work-process.md) — 작업 착수 절차
- [multi-agent.md](rules/multi-agent.md) — 서브에이전트 위임, Quality Gate
- ...
 
### 피드백/참조
- [feedback/](feedback/) — testing / model_policy / env_minimalism / ...
- [reference/dashboards.md](reference/dashboards.md) — oncall 대시보드 인덱스

인덱스는 메모리가 아니다. 메모리들의 한 줄 hook 모음이다. 인덱스 자체에 내용을 쓰지 않는다 — 항상 포인터.

이 분리 덕에 인덱스는 짧게 유지된다. 도구가 세션 시작 시 앞 N줄만 자동 로드하더라도, 그 안에 모든 메모리의 hook이 들어가 있다. 어떤 메모리가 필요한 순간이 오면 그 시점에 그 파일만 Read한다.

메모리는 RAG보다 단순한 인덱스 + 트리거-기반 Read로 대부분 충분하다.

4. 자동 로드 vs 수동 호출 — 토큰 경제

이 시스템은 두 가지 로드 방식을 결합한다.

방식무엇이 들어오는가언제
자동 로드MEMORY.md 앞 N줄매 세션 시작
수동 Read인덱스가 가리키는 특정 파일그 파일이 관련된 작업 진입 시

이 분리는 토큰 경제의 핵심이다. 매 세션 모든 메모리 파일을 다 읽으면 토큰이 폭발한다. 인덱스만 자동 로드하고, 본문은 필요할 때만 Read하면 거의 모든 세션이 매우 가볍게 시작된다.

본문 Read를 결정하는 트리거는 두 가지다.

  1. 운영자가 명시적으로 언급 — "그 통합 테스트 정책 좀 봐"
  2. 작업 자체가 관련 영역에 진입 — DB 관련 PR을 작업하기 시작하면 DB 관련 메모리를 Read

후자가 더 중요하다. 운영자가 매번 "이 정책 봐라" 하지 않아도 협업자가 알아서 관련 메모리를 가져온다. 인덱스에서 해당 항목의 description 한 줄이 그 결정의 근거가 된다.

5. 가지치기 — 메모리가 자라기만 하면 무너진다

메모리 시스템의 가장 큰 함정은 누적만 하고 정리하지 않는 것이다. 누적만 하면 두 가지 문제가 생긴다.

  1. stale 정보가 살아 있다 — 한 달 전에 끝난 프로젝트의 일정이 여전히 메모리에 남아 있어 잘못된 판단을 유도
  2. 인덱스가 비대해진다 — 200줄 자동 로드 범위를 넘어가는 항목이 생기고, 자동 로드가 의미를 잃음

운영자는 다음 가지치기 패턴을 운영한다.

타입가지치기 주기기준
user거의 안 함사람의 정체성은 천천히 변함
feedback분기 1회더 이상 유효하지 않은 피드백 제거
project월 1회종료된 프로젝트는 아카이브 디렉토리로 이동
reference반기 1회끊어진 링크·사라진 시스템 제거

아카이브는 삭제가 아니다. memory/archived/ 같은 별도 디렉토리에 옮긴다. 인덱스에서는 빠지지만 git 히스토리와 파일은 남아 있어서 필요하면 다시 가져올 수 있다.

또 한 가지 패턴 — 자동 감사 도구가 있다. 메모리 디렉토리를 주기적으로 스캔해서 "이 항목은 60일 이상 참조되지 않았다" 같은 보고를 자동으로 만든다. 운영자는 그 보고를 보고 가지치기 결정을 빠르게 내린다.

6. 실패 패턴 — 운영자가 한 번씩 다 해본 것

이 시스템에 도달하기 전에 운영자가 시도하다 실패한 패턴들.

6.1 단일 거대 파일

처음에는 모든 메모리를 한 파일에 넣었다. notes.md 한 개. 며칠 만에 1,000줄을 넘었고, 도구가 그걸 매번 읽으면서 거의 모든 토큰을 소비했다.

교훈: 메모리는 분할되어야 한다. 인덱스 + 본문 파일 분리.

6.2 너무 잘게 쪼개기

반대로 너무 잘게 쪼개기도 해봤다. 한 결정마다 한 파일. 50개 파일이 생겼고, 인덱스가 너무 커져서 자동 로드 범위를 다 잡아먹었다.

교훈: 한 파일 = 한 "주제"지, 한 "결정"이 아니다. 같은 주제의 결정 여러 개는 한 파일에 모은다.

6.3 무차별 누적

세션이 끝날 때마다 "오늘 한 일 정리해서 메모리에 남겨" 식의 자동화를 시도했다. 메모리가 매일 자라기만 하고, 중요한 결정이 잡음에 묻혔다.

교훈: 메모리에 들어가는 것은 다음 세션에도 유효한 결정·사실·포인터다. 오늘만의 진행 상황은 메모리가 아니라 task board나 history 로그에 들어간다.

6.4 메타데이터 없는 평문

처음에는 frontmatter 없이 그냥 마크다운만 썼다. 점점 늘어나면서 "이게 어떤 타입인지" "왜 만들었는지" 추적이 불가능해졌다.

교훈: name, description, type 최소 3종은 frontmatter로 강제. 추가로 본문에 Why / How to apply 두 줄.

7. 실제 효과

이 시스템 도입 후 운영자가 체감한 변화.

항목
세션 시작 시 컨텍스트 복원 시간5-10분즉시 (인덱스 자동 로드)
같은 결정 재협의자주거의 없음
어제 끝난 일 다시 설명매번안 함 (메모리에 있음)
stale 정보로 인한 오판가끔거의 없음 (가지치기)
토큰 소비 패턴평탄하게 항상 높음작업에 비례 (얕은 작업은 가볍게)

가장 큰 변화는 마지막 항목이다. 토큰 소비가 작업의 깊이에 비례해서 달라진다는 점. 얕은 작업은 거의 비용 없이 끝나고, 깊은 작업에만 자원을 쏟는다.

8. 한 가지 원칙으로 압축

메모리 시스템 설계의 핵심은 한 원칙으로 줄어든다.

"인덱스에는 항상 있는다. 본문은 필요할 때만 들어온다. 더 이상 유효하지 않은 것은 인덱스에서 빠진다."

이 세 가지가 다 작동하면, 메모리는 자라기만 하는 부담이 아니라 작업을 가속하는 자산이 된다.

다음 글에서는 메모리·페르소나가 가리키는 다음 레이어 — 스킬과 슬래시 커맨드, 즉 어떤 반복을 한 줄 호출로 만들 가치가 있는지에 대해 다룬다.


댓글