백엔드 개발자의 AI 비서 만들기 (3편) — "저번에 말한 거 기억해?"
TL;DR
- 문제: Claude는 대화가 끝나면 리셋. 어제 한 얘기를 오늘 또 처음부터 해야 했습니다
- 실패 1: 대화 전체를 시스템 프롬프트에 붙여넣기 → 4주 만에 토큰 폭발
- 실패 2: MEMORY.md 하나에 요약 누적 → 20KB 돌파 후 에이전트가 죽었습니다
- 성공: LanceDB 벡터 DB + 유사도 검색 → 관련 것만 꺼내서 주입
- 결과: 발표 전날 아침, Jarvis가 먼저 “내일 기술 공유 세미나가 있습니다”라고 말을 걸었습니다
발표 전날 아침에 벌어진 일
3월 어느 수요일 오전 8시, 아침 브리핑이 왔습니다.
Jarvis → #jarvis-morning
내일 팀 기술 공유 세미나가 있습니다 (오후 2시).
2월에 정리하셨던 Virtual Thread + HikariCP 데드락 건이 발표 주제로 적합해 보입니다:
- 재현 조건: JDK 21 Virtual Thread 100개 병렬 조회 + HikariCP 풀 10개
- 핵심 해결: 커넥션 풀 10→50 조정, 병렬 조회 제한, 타임아웃 설정
- 2월 중순에 정리해두신 코드 예시가 있습니다. 필요하시면 꺼내드리겠습니다
발표 자료 구성 도움이 필요하시면 말씀해주세요.
저는 이 메시지를 보내달라고 요청하지 않았습니다.
Jarvis가 스스로 오늘 날짜를 확인하고, RAG에서 “기술 공유 세미나 3월 XX일”이 담긴 청크를 꺼내고, 2월에 나눴던 데드락 분석 대화까지 합쳐서 정리해서 보낸 겁니다.
기억이 있어야 챙길 수 있다는 걸 이날 실감했습니다. 그 기억을 만드는 데 두 번 실패가 있었습니다.
기억이 없는 AI와 일한다는 것
Jarvis v0.1이 동작한 첫 날, 이런 대화를 했습니다.
나 → #jarvis-work
오늘 Spring Batch 리팩토링 마무리했어. Tasklet → Chunk 방식으로 바꿨고 트랜잭션 경계 정리 완료.
Jarvis
수고하셨습니다. 이후 성능 측정도 해보시면 좋겠습니다.
다음 날 다시 물었습니다.
나 → #jarvis-work
어제 Batch 리팩토링한 거 기억해? 성능 개선 수치 나왔는데 얘기하고 싶어.
Jarvis
죄송합니다, 이전 대화 내용에 접근할 수 없습니다. 어떤 Batch 작업을 리팩토링하셨는지 다시 설명해주시면 분석해드리겠습니다.
예상은 했지만, 막상 겪으니 불편함이 생각보다 컸습니다. 단순히 기억이 없다는 게 아니라, 매 대화마다 “저는 백엔드 9년차이고, SK D&D에서 IoT 플랫폼 개발 중이고, 현재 이직 준비 중이고…”를 처음부터 설명해야 한다는 게 문제였습니다. 그렇게 쓸 거면 Claude.ai 웹이랑 다를 게 없었습니다.
실패 1: 전체를 붙여넣기
가장 단순한 해결책부터 시도했습니다. 이전 대화를 텍스트 파일에 저장해두고, 다음 대화 시작 시 시스템 프롬프트에 전부 붙여넣는 방식.
“전부 넣으면 전부 기억하겠지.” 백엔드 9년을 하고도 이런 생각을 했습니다. DB에서 테이블 풀스캔 날리는 것과 같은 발상이었는데, 그때는 몰랐습니다.
처음 며칠은 잘 됐습니다.
문제는 쌓이면서 시작됐습니다.
| 경과 | 누적 대화량 | 응답 시간 | 현상 |
|---|---|---|---|
| 1주차 | ~8K tokens | 3~5초 | 정상 |
| 2주차 | ~25K tokens | 8~12초 | 체감 느림 |
| 3주차 | ~60K tokens | 20초+ | 종종 타임아웃 |
| 4주차 | ~90K tokens | 실패 | Max 컨텍스트 초과 |
한 달이 지나자 매 대화마다 90K 토큰짜리 과거 대화를 첨부하는 구조가 됐습니다. Claude Max 5시간 쿼터가 대화 몇 번 만에 다 닳고, 응답도 느렸습니다.
폐기했습니다.
실패 2: MEMORY.md가 죽는 날
다음 아이디어는 “전체를 붙이지 말고, 요약해서 붙이자”였습니다.
MEMORY.md 파일 하나를 만들고, 대화가 끝날 때마다 중요한 내용을 요약해서 추가했습니다. 시스템 프롬프트에는 전체 대화 대신 이 요약만 붙였습니다.
이 방식도 처음엔 잘 됐습니다. 요약이라 토큰이 훨씬 적었고, 핵심만 담겨있어서 오히려 대화 품질이 올라갔습니다.
그런데 두 달쯤 지나자 MEMORY.md가 20KB를 넘었습니다.
그날 이후로 응답이 이상해지기 시작했습니다. 실제로 벌어진 일을 보여드리면,
나 → #jarvis-work
저번에 정리하다 말았던 Virtual Thread 데드락 원인 분석, 어디까지 썼더라?
Jarvis (MEMORY.md 20KB 전부 읽은 후)
Virtual Thread 관련하여 정리해드리겠습니다. JANDI에서 진행하셨던 Spring Batch Tasklet→Chunk 전환 건에서 스레드 풀 설정을 다루셨던 내용이 관련이 있을 것 같습니다. 하몬소프트 SI 시절의 멀티스레드 환경 경험도 함께 참고해보시면 좋겠습니다.
Virtual Thread 데드락 분석을 물었는데, 전 직장 SI 경험이 나왔습니다. MEMORY.md에 하몬소프트 재직 기간 기록과 최근 SK D&D Virtual Thread 분석 기록이 뒤섞여 있었고, Jarvis가 “스레드”라는 키워드로 전부 꺼내오면서 시간 순서를 잃은 겁니다. 20KB짜리 파일에서 관련 내용만 뽑는 건 불가능했습니다.
더 황당한 건, 이 문제가 저만 겪은 게 아니었다는 겁니다. openclaw/memorybox 레포에 이슈를 올렸습니다. 제목은 그대로 “MEMORY.md 20KB가 에이전트를 죽였다”. 올린 지 이틀 만에 댓글이 10개 넘게 달렸습니다. “저도 이 문제로 일주일 날렸습니다”, “청크 분리 없이 파일 하나에 쌓으면 결국 이렇게 됩니다” 같은 반응이었습니다.
파일 하나에 모든 기억을 쌓는 방식 자체의 구조적 한계였습니다. 이 경험이 openclaw memorybox 오픈소스의 출발점이 됐습니다.
폐기했습니다.
세 번째: 필요한 것만 꺼내기
두 번 실패를 정리하면 이렇게 됩니다.
graph TD
subgraph F1["방법 1 — 전체 붙여넣기 ❌"]
direction LR
A1["대화 전체\n텍스트 파일"] -->|"매번 통째로"| A2["90K 토큰\n컨텍스트 초과"]
end
subgraph F2["방법 2 — MEMORY.md 요약 ❌"]
direction LR
B1["요약 파일\n점점 누적"] -->|"2개월 후 20KB"| B2["관련 없는\n기억이 혼재"]
end
subgraph OK["방법 3 — RAG ✅"]
direction LR
C1["대화 → 청크\n벡터DB 저장"] -->|"질문과\n유사도 검색"| C2["관련 청크 5개만\n프롬프트에 주입"]
end
F1 --> F2 --> OK두 가지 실패의 공통점은 “전부 넣는다”는 발상이었습니다.
“전부 넣는다”는 발상 자체가 문제였습니다.
사람도 대화할 때 모든 기억을 동시에 떠올리지 않습니다. 관련 있는 것만 떠올립니다. AI도 같은 방식으로 만들면 됩니다.
RAG(Retrieval-Augmented Generation)가 정확히 이 역할을 합니다.
graph LR
A["새 메시지\n'저번에 Batch 얘기했잖아'"] --> B["벡터 변환\n(임베딩)"]
B --> C["LanceDB\n유사도 검색"]
C --> D["관련 청크 TOP 5\n반환"]
D --> E["시스템 프롬프트에\n관련 내용만 주입"]
E --> F["Claude 응답"]동작 방식은 단순합니다.
- 대화가 끝나면 내용을 청크로 잘라서 벡터로 변환 후 LanceDB에 저장
- 새 대화가 오면 그 메시지와 유사한 청크를 검색
- 관련 청크 5~10개만 시스템 프롬프트에 추가해서 Claude에게 전달
단순해 보이지만, 설계 단계에서 예상보다 오래 걸린 결정이 하나 있었습니다.
청크 크기를 얼마로 잡을 것인가.
너무 크면 검색 단위가 굵어져 정확도가 떨어집니다. “Spring Batch 리팩토링 완료”와 “Virtual Thread 데드락 분석” 세 단락이 같은 청크에 묶이면, 검색이 엉뚱한 맥락을 꺼내옵니다. 너무 작으면 의미 단위가 쪼개져 청크 수만 폭발하고 맥락을 잃습니다.
처음엔 문장 단위로 잘랐다가 검색 결과가 너무 파편화됐고, 그다음엔 일정 토큰 수로 잘랐다가 대화 흐름이 끊겼습니다. 몇 번의 실험 끝에 대화 단위 — 질문 하나 + 답변 하나를 청크 한 개로 처리하는 방식으로 결론을 냈습니다. 지금 12만 청크는 이 기준으로 대화를 저장한 결과입니다.
RAG 전후: 실제 대화
같은 질문이 어떻게 달라지는지 봅니 다.
Before (RAG 없음)
나 → 저번에 정리하던 Redis 원자 연산 블로그 초안, 어디까지 썼더라?
Jarvis → 이전 대화 내용에 접근할 수 없습니다. 다시 말씀해주시면 이어서 도와드리겠습니다.
After (RAG 있음)
나 → 저번에 정리하던 Redis 원자 연산 블로그 초안, 어디까지 썼더라?
Jarvis (2월 중순 대화 청크 검색 후)
2월 17일 기준으로 정리해드리면:
- 글 주제: Redis INCR/DECR 동시성 문제 → SADD/SREM 전환 과정
- 서론 작성 완료, “왜 카운터가 틀렸나” 섹션까지 초안 있음
- 미완성 부분: 실제 운영 에러율 수치 섹션 — 수치 수집이 안 됐다고 메모해두셨습니다
이어서 작성하시겠습니까?
그리고 글 맨 앞에서 본 것처럼, 발표 전날 아침 크론이 스스로 RAG에서 관련 청크를 꺼내 챙겨줬습니다. 요청하지 않았는데.
현재 수치: 12만 청크
지금 LanceDB에 쌓인 청크는 12만 개 수준입니다.
| 청크 유형 | 비중 | 예시 |
|---|---|---|
| 대화 기록 | 65% | 기술 질문·답변, 코드 리뷰, 결정 근거 |
| 작업 히스토리 | 20% | 완료한 태스크, 리팩토링 결과, 배포 기록 |
| 참조 자료 | 10% | 읽은 블로그, 기술 아티클, 오픈소스 이슈 |
| 시스템 로그 | 5% | 장애 기록, 복구 히스토리 |
이 숫자가 어떻게 쌓였는지 타임라인으로 보면 이렇습니다.
| 시기 | 누적 청크 수 | 주요 변화 |
|---|---|---|
| RAG 도입 초기 | ~2,000 | 테스트 대화만 저장 |
| 2주 후 | ~70,000 | 크론 자동 저장 시작, Discord 채널 전체 커버 |
| 1개월 후 | ~120,000 | 시스템 로그·외부 아티클까지 포함 |
하루에 500개씩 새 청크가 생깁니다. 대화만이 아니라, 크론 실행 결과, 읽은 기술 아티클 요약, 장애 기록까지 전부 청크로 저장됩니다.
12만 청크가 많아 보이지만, 검색 시 실제로 주입되는 건 5~10개뿐입니다. 검색 응답 시간은 평균 **80~150ms**. 12만 청크를 뒤져서 관련 내용을 찾는 데 0.1초도 안 걸립니다.
솔직한 한계
한계 1: 임베딩 정확도
지금은 로컬 임베딩 모델(무료)을 씁니다. 정확도가 상용 임베딩보다 약간 떨어집니다. 아주 유사한 내용이 있는데 못 찾아오는 경우가 5~10% 정도 있습니다.
실제로 “SQS DLQ 관련 분석”을 물었을 때, 정작 SQS 분석 청크는 안 나오고 비슷한 시기에 나눈 “Kafka DLQ 설정” 대화가 나온 적이 있었습니다. 내용이 충분히 달랐는데도. 상용 임베딩으로 전환하면 정확도가 올라가겠지만, 그러면 비용이 발생합니다. 지금은 “5~10% 오차는 감수하고 쓴다”는 판단입니다.
한계 2: 오래된 기억의 노이즈
6개월 이상 된 청크는 현재 상황과 맞지 않는 경우가 있습니다. 가장 황당했던 케이스는 “지금 진행 중인 사이드 프로젝트 방향 어때?”라고 물었을 때, 6개월 전 아직 방향도 못 잡고 있던 시점의 청크가 올라온 것입니다. 그 이후로 청크에 날짜 가중치를 주는 방향으로 개선 중입니다. 최근 청크일수록 검색 우선순위를 높이는 방식입니다.
한계 3: 명시적 트리거가 필요하다
“저번에 말한”, “기억해?”, “아까 얘기한” 같은 트리거가 있을 때만 RAG를 실행합니다. 모든 대화에 RAG를 태우면 응답이 3~5초 늘어나기 때문에 의도적으로 제한했습니다.
부작용이 있습니다. 트리거 없이 물어보면 RAG가 실행되지 않아서, 관련 과거 기억이 있어도 꺼내오지 않습니다. “지금 진행 중인 리팩토링 어때?”라고 물으면, 정작 어제 정리해둔 리팩토링 맥락은 검색하지 않고 일반적인 답변이 나옵니다. “저번에 얘기한 리팩토링 어때?”라고 해야 RAG가 작동합니다. 습관이 되긴 했는데, 처음엔 자 꾸 잊었습니다.
마치며: 두 번 실패한 이유
첫 번째 실패(전체 붙여넣기)와 두 번째 실패(MEMORY.md) 모두 같은 이유였습니다.
기억을 저장하는 것과 필요할 때 꺼내는 것을 분리하지 않았다.
저장은 많이 해도 됩니다. 꺼낼 때 관련 있는 것만 꺼내면 됩니다. 이 원칙을 깨닫는 데 두 번의 실패가 필요했습니다.
다음 편에서는 Jarvis Board를 다룹니다.
크론이 93개가 됐을 때 생긴 문제는 단순했습니다. 어떤 크론이 실패했는지, 마지막으로 언제 실행됐는지, 지금 뭐가 돌아가고 있는지 — 이걸 확인하려면 맥미니에 SSH로 접속해서 터미널을 열어야 했습니다. 지하철에서, 핸드폰으로, 맥미니에.
그래서 대시보드를 만들었습니다. 어떻게 만들었고 어떤 문제를 해결했는지, 다음 편에 씁니다.
참고 :
https://lancedb.github.io/lancedb
https://github.com/ramsbaby/openclaw
