Featured Post

AI 바이브 코딩하다가 Graphiti로 코드 기억력 붙여준 이야기

Graphiti - 지식그래프 메모리 관련 이미지

왜 갑자기 지식 그래프 메모리에 꽂혔냐면요

요즘 제가 Cursor랑 Copilot을 거의 옆자리 동료처럼 두고 코딩하고 있거든요. 20년 넘게 개발 일을 해왔는데도, 가끔 AI가 뚝딱 만들어주는 코드를 보면 아직도 좀 신기해요. “아, 이거 예전 같으면 반나절은 잡아먹었겠다” 싶은 순간이 꽤 많습니다.

그런데 말이죠. 편하긴 정말 편한데, 묘하게 답답한 구석도 있어요. 특히 프로젝트가 조금만 커지면 AI가 앞에서 같이 작업했던 맥락을 은근히 잘 잊어버립니다. 방금 전까지 같이 만든 함수인데, 다음 파일로 넘어가면 모르는 사람처럼 굴 때가 있어요. 제가 “이거 아까 만든 validatePaymentStatus()랑 연결해야 되는 거 아니야?” 하고 물으면, AI는 대충 “네, 그렇게 할 수 있습니다”라고 답은 하는데, 느낌이 딱 와요. 얘가 정확히 기억해서 말하는 게 아니라 그냥 그럴듯하게 이어 붙이고 있구나.

그때부터 좀 찾아보기 시작했어요. AI 코딩 도구에 장기 기억을 붙이는 방법이 없을까 하고요. 그러다 눈에 들어온 게 지식 그래프 메모리였고, 그중에서도 Graphiti라는 도구가 꽤 실용적으로 보이더라고요. 이름만 들었을 땐 “또 뭔가 거창한 프레임워크인가?” 싶었는데, 막상 써보니 제 취향에는 꽤 잘 맞았습니다.

솔직히 처음엔 “메모리면 그냥 RAG 쓰면 되는 거 아닌가?” 싶었어요. 저도 처음엔 그렇게 생각했거든요. 그런데 RAG는 기본적으로 문서나 코드 조각을 검색해서 가져오는 방식이라, 관계를 깊게 이해하는 데는 한계가 있더라고요. 예를 들면 “이 함수가 어떤 클래스 안에 있고, 그 클래스가 어떤 모듈에 있고, 그 모듈이 어떤 API를 호출하고, 그 API 응답이 다시 어떤 DTO로 변환되는지” 같은 흐름은 단순 검색만으로는 좀 버겁습니다.

반면에 그래프는 노드와 엣지로 이어져 있으니까 구조가 훨씬 또렷해요. 함수, 클래스, 모듈, API, 설정값 같은 것들을 각각 노드로 만들고, 호출한다, 포함한다, 상속한다, 참조한다 같은 관계를 엣지로 연결해두는 식이죠. 그러면 AI에게 “대충 관련 있어 보이는 코드 찾아줘”가 아니라 “이 코드가 실제로 어디와 연결되어 있는지 보고 판단해줘”라고 말할 수 있게 됩니다. 이 차이가 생각보다 큽니다.

Graphiti로 코드베이스의 함수와 클래스 관계를 연결한 지식 그래프 구조

Graphiti를 써보니, AI에게 기억보다 구조를 주는 게 낫더라고요

제가 이해한 Graphiti는 이런 느낌이에요

Graphiti는 LLM과 함께 쓰기 좋은 동적 지식 그래프 도구라고 보면 됩니다. Python 기반으로 사용할 수 있고, 기존 데이터나 LLM이 만든 정보를 받아서 노드와 엣지를 만들고, 필요하면 계속 갱신해요.

여기서 제가 좋다고 느낀 포인트는 “동적”이라는 부분이었어요. 예전에 Neo4j 같은 걸로 지식 그래프를 만들 때는 스키마부터 고민하고, 어떤 관계를 어떻게 넣을지 미리 꽤 많이 정해야 했거든요. 물론 그 방식도 장점이 있어요. 그런데 AI 바이브 코딩처럼 코드가 계속 생기고 바뀌는 상황에서는 너무 딱딱하면 손이 잘 안 갑니다.

Graphiti는 조금 더 유연한 느낌이에요. 예를 들어 Cursor가 새로운 클래스를 만들었고, 그 클래스 안에 함수 몇 개가 생겼다면, 그걸 다시 Graphiti 쪽에 넣어서 “이 클래스는 이 모듈에 포함됨”, “이 함수는 저 함수를 호출함” 같은 식으로 그래프를 업데이트할 수 있습니다. 그러면 다음 작업을 할 때 AI가 그 관계를 참고할 수 있어요.

처음에는 저도 약간 반신반의했어요. “이거 세팅하는 시간이 더 걸리는 거 아냐?” 하는 생각도 있었고요. 그런데 레거시 코드 리팩토링을 한 번 해보니까 생각이 바뀌었습니다. AI가 중복 함수를 덜 만들고, 이미 존재하는 책임 구조를 더 잘 따라오더라고요. 뭐랄까, 그냥 똑똑한 신입에게 일 시키는 느낌에서, 우리 프로젝트 구조를 어느 정도 아는 동료에게 부탁하는 느낌으로 바뀌었습니다.

일반 RAG랑 Graphiti를 써보며 느낀 차이

구분 일반 RAG Graphiti 지식 그래프 메모리
데이터를 보는 방식 텍스트 조각을 검색해서 가져옴 함수, 클래스, 모듈, API를 관계로 연결함
관계 표현 비슷한 문서나 코드 조각을 찾는 데 강함 호출, 포함, 상속, 참조 같은 실제 연결을 표현하기 좋음
업데이트 방식 변경이 많으면 재색인 부담이 생김 새 노드와 엣지를 점진적으로 추가하기 좋음
AI 코딩과의 궁합 컨텍스트 보강용으로 무난함 구조와 의존성을 이해시키는 데 훨씬 유리함
코드 리팩토링 관련 코드 검색은 잘하지만 구조 판단은 애매할 때가 있음 어디를 건드리면 어디에 영향이 가는지 보기 좋음

물론 RAG가 나쁘다는 얘기는 아니에요. 저도 문서 검색이나 API 스펙 검색에는 여전히 RAG를 씁니다. 다만 코드베이스 안에서 “관계”가 중요한 순간에는 Graphiti 같은 그래프 메모리가 훨씬 편했어요. 특히 오래된 프로젝트, 여러 사람이 만진 프로젝트, 도메인 규칙이 코드 곳곳에 흩어져 있는 프로젝트에서는 차이가 꽤 납니다.

실제로 리팩토링할 때 이렇게 써봤습니다

얼마 전에 사내 결제 모듈을 손볼 일이 있었어요. 이름만 들어도 살짝 피곤한 영역이죠. PaymentProcessor, InvoiceGenerator, TaxCalculator, RefundHandler 같은 클래스들이 여기저기 얽혀 있었고, 예전 개발자들이 급하게 넣어둔 예외 처리도 꽤 많았습니다. 이런 코드는 무작정 AI에게 “리팩토링해줘”라고 던지면 위험해요. 겉보기엔 깔끔해지는데, 숨은 정책이 깨지는 경우가 있거든요.

그래서 이번에는 먼저 코드 구조를 Graphiti에 넣어보기로 했습니다. 핵심은 단순했어요. Python 파일을 읽고, 클래스와 함수 정보를 뽑고, 호출 관계를 가능한 만큼 그래프로 만드는 거죠. 처음부터 완벽하게 하려는 욕심은 버렸습니다. 제가 이 나이 먹고 배운 게 하나 있다면, 개발 도구는 처음부터 완벽하게 만들려고 하면 대개 안 쓰게 된다는 겁니다. 일단 작게 만들어서 내 손에 붙는 게 더 중요해요.

처음에 만든 아주 단순한 코드 예시

from graphiti import Graph
import ast

graph = Graph()

def add_code_entity(source_code, filepath):
    tree = ast.parse(source_code)

    for node in ast.walk(tree):
        if isinstance(node, ast.ClassDef):
            graph.add_node(
                name=node.name,
                node_type="class",
                properties={
                    "file": filepath,
                    "start_line": node.lineno
                }
            )

        if isinstance(node, ast.FunctionDef):
            graph.add_node(
                name=node.name,
                node_type="function",
                properties={
                    "file": filepath,
                    "start_line": node.lineno
                }
            )

for path in project_python_files:
    with open(path, "r", encoding="utf-8") as f:
        add_code_entity(f.read(), path)

graph.persist("knowledge_graph.json")

이 코드는 사실 굉장히 단순합니다. 함수 호출 관계까지 제대로 잡으려면 더 손봐야 해요. 그래도 처음에는 이 정도만으로도 도움이 됐습니다. 적어도 “어떤 파일에 어떤 클래스와 함수가 있는지”를 AI에게 매번 새로 설명하지 않아도 됐거든요.

그다음에는 조금씩 관계를 추가했습니다. 예를 들면 PaymentProcessor가 InvoiceGenerator를 호출하면 이런 식으로 엣지를 넣는 거죠.

graph.add_edge(
    from_node="PaymentProcessor",
    to_node="InvoiceGenerator",
    relationship="USES"
)

graph.add_edge(
    from_node="InvoiceGenerator",
    to_node="TaxCalculator",
    relationship="CALLS"
)

이렇게 해두고 Cursor에서 작업할 때, Graphiti에서 뽑은 내용을 프롬프트 앞쪽에 넣어줬어요. 아주 거창한 형태는 아니고, 그냥 사람이 읽기 편한 문장으로 바꿨습니다.

현재 코드베이스 지식 그래프 정보:

  • PaymentProcessor는 InvoiceGenerator를 사용합니다.
  • InvoiceGenerator는 TaxCalculator를 호출합니다.
  • TaxCalculator는 tax_policy.yaml 설정을 참조합니다.
  • RefundHandler는 PaymentProcessor의 결제 상태 값을 재사용합니다.
새 코드를 만들 때 위 관계를 깨지 않도록 기존 구조를 우선 확인하세요. 중복 클래스를 만들지 말고, 이미 존재하는 책임을 재사용하세요.

이렇게만 해도 AI 응답이 꽤 달라졌어요. 예전에는 비슷한 역할의 TaxService 같은 클래스를 새로 만들려고 했다면, 이제는 “기존 TaxCalculator를 확장하는 편이 좋겠습니다”라고 제안하더라고요. 이런 순간이 은근히 기분 좋습니다. 내가 원하는 방향을 AI가 조금씩 알아듣는 느낌이랄까요.

쓰면서 겪은 삽질도 좀 있었어요

좋은 얘기만 하면 재미없죠. 실제로는 삽질도 꽤 했습니다. 특히 초반에 그래프를 너무 자세하게 만들려고 했다가 한 번 망했어요. 변수 하나하나, 내부 함수 호출 하나하나까지 다 넣으려고 하니까 그래프가 너무 지저분해지더라고요. 사람이 보기에도 정신없고, AI에게 넣어줘도 별 도움이 안 됐습니다.

그래서 기준을 바꿨어요. “AI가 설계 판단을 할 때 필요한 관계만 넣자.” 이게 훨씬 낫더라고요. 예를 들면 로컬 변수나 아주 작은 유틸 함수까지는 굳이 넣지 않았고, 도메인 책임이 있는 클래스, 외부 API 호출, 설정 파일 참조, DB 테이블 접근 정도만 우선 넣었습니다.

제가 정한 그래프에 넣을 정보 기준

정보 종류 그래프에 넣었는지 이유
도메인 클래스 넣음 비즈니스 책임을 이해하는 데 중요해서요
공개 메서드 넣음 다른 모듈에서 호출될 가능성이 높습니다
private 성격의 작은 함수 상황 봐서 넣음 너무 많이 넣으면 그래프가 지저분해졌어요
로컬 변수 대부분 제외 AI 설계 판단에는 큰 도움이 안 됐습니다
외부 API 호출 넣음 장애 영향 범위 파악에 꽤 중요합니다
설정 파일 참조 넣음 운영 환경에서 의외로 사고가 자주 나는 지점이라서요

그리고 한 가지 더. 그래프 업데이트를 AI에게 완전히 맡기면 가끔 이상한 노드를 만듭니다. 예를 들어 실제 코드에는 없는 “PaymentValidationManager” 같은 이름을 갑자기 만들어서 관계에 넣는 식이에요. AI 입장에서는 그럴듯한 이름이지만, 우리 코드베이스에는 없는 존재죠. 그래서 저는 그래프 업데이트 전에 검증 단계를 하나 넣었습니다.

def safe_add_node(graph, name, node_type, properties):
    if not properties.get("file"):
        raise ValueError(f"file 정보가 없는 노드는 추가하지 않습니다: {name}")

    if node_type in ["class", "function"] and not exists_in_codebase(name):
        raise ValueError(f"코드베이스에서 찾을 수 없는 노드입니다: {name}")

    graph.add_node(
        name=name,
        node_type=node_type,
        properties=properties
    )

이런 작은 방어 코드가 생각보다 중요합니다. AI랑 같이 개발하다 보면 생산성은 확 올라가는데, 그만큼 그럴듯한 실수도 빨리 만들어지거든요. 저는 이걸 “속도에 브레이크를 달아주는 장치”라고 생각합니다. 속도는 좋지만, 낭떠러지로 빨리 가면 곤란하잖아요.

Cursor Rules에 넣어두니 훨씬 편했습니다

제가 실제로 효과를 봤던 건 Cursor Rules에 Graphiti 관련 규칙을 넣어둔 거였어요. 매번 프롬프트에 길게 쓰기 귀찮잖아요. 저도 귀찮은 건 못 참는 편이라, 반복되는 지시는 Rules로 빼버렸습니다.

When creating or modifying a function/class:

  • Check the existing knowledge graph context first.
  • Do not create duplicate domain services if a similar class already exists.
  • If you add a new public function, describe its relationship with existing classes.
  • If the function calls another module, update the knowledge graph edge.
  • Prefer extending existing domain responsibilities over inventing new structures.

이 규칙을 넣고 나서부터는 AI가 새 클래스를 막 만드는 빈도가 줄었습니다. 물론 완벽하진 않아요. 그래도 “기존 구조 먼저 확인해”라는 습관을 계속 주입하는 효과가 있었습니다. 사람도 코드 리뷰 때 같은 말을 계속 들으면 언젠가는 조심하잖아요. AI도 약간 그런 느낌입니다.

제가 실제로 쓰는 작업 흐름

    • 핵심 모듈만 먼저 그래프화합니다. 전체 프로젝트를 한 번에 넣으려고 하면 금방 지칩니다. 저는 결제, 주문, 정산처럼 사고 나면 큰일 나는 모듈부터 시작했어요.
    • Graphiti 쿼리 결과를 짧은 문장으로 바꿔서 AI에게 줍니다. 그래프 데이터를 그대로 던지는 것보다 사람이 읽는 설명처럼 넣는 게 더 안정적이었습니다.
    • 새 코드가 생기면 관계를 같이 기록하게 합니다. 함수만 만들고 끝내는 게 아니라, 이 함수가 누구를 호출하고 어디에서 쓰일지까지 남기게 하는 거죠.
    • 그래프 변경은 검증 스크립트를 거칩니다. 없는 클래스, 없는 파일, 애매한 관계가 들어오면 바로 막습니다.
    • 시각화는 꼭 붙입니다. Graphviz나 Neo4j Browser 같은 걸로 한 번 보면, 텍스트로 볼 때 놓치던 의존성이 바로 보입니다.

개인적으로는 시각화가 꽤 컸어요. 코드만 볼 때는 “이 정도면 괜찮겠지” 싶었는데, 그래프로 보면 특정 클래스에 의존성이 너무 몰려 있는 게 한눈에 보입니다. 예전에 여행 가서 지도를 펼쳐놓고 동선을 보면 괜히 마음이 정리되는 느낌 있잖아요. 코드도 비슷하더라고요. 눈으로 구조가 보이면 불안감이 확 줄어듭니다.

Graphiti를 쓰면서 제일 좋았던 순간

가장 좋았던 건 리팩토링할 때 영향 범위를 훨씬 빨리 잡을 수 있었다는 점이에요. 예를 들어 TaxCalculator 쪽 로직을 바꿔야 하는 상황이 있었는데, 예전 같으면 grep 돌리고, IDE에서 Find Usage 보고, 테스트 돌리고, 그래도 뭔가 찜찜한 상태로 PR을 올렸을 겁니다.

이번에는 Graphiti에 물어봤어요. “TaxCalculator와 연결된 주요 노드를 보여줘.” 그러니까 InvoiceGenerator, PaymentProcessor, SettlementReportBuilder, tax_policy.yaml 같은 관계가 바로 나왔습니다. 그걸 보고 테스트 범위를 정하니까 훨씬 마음이 편하더라고요.

TaxCalculator
 ├─ CALLED_BY: InvoiceGenerator
 ├─ CALLED_BY: SettlementReportBuilder
 ├─ USES_CONFIG: tax_policy.yaml
 └─ AFFECTS: PaymentProcessor.calculate_final_amount

이런 정보가 있으면 AI에게도 더 구체적으로 지시할 수 있어요.

TaxCalculator의 rounding rule을 변경하려고 합니다.
현재 그래프상 InvoiceGenerator와 SettlementReportBuilder가 이 클래스를 호출합니다.
두 호출부의 테스트 케이스를 함께 수정하고,
PaymentProcessor.calculate_final_amount의 결과가 달라질 수 있는지도 확인해주세요.

이렇게 말하면 AI가 훨씬 덜 헤맵니다. 그냥 “세금 계산 로직 바꿔줘”라고 하는 것과는 결과물이 달라요. 맥락을 같이 주니까, AI도 불필요한 상상을 덜 하게 됩니다. 저는 AI 코딩에서 제일 중요한 게 이거라고 봐요. AI를 천재처럼 대하는 게 아니라, 좋은 맥락을 주고 좋은 질문을 하는 것. 결국 일 잘하는 사람한테도 똑같잖아요.

제가 느낀 Graphiti 활용 꿀팁

며칠 써보고, 또 실제 업무 코드에 붙여보면서 느낀 팁들을 조금 정리해볼게요. 거창한 방법론이라기보다는, 제가 삽질하면서 몸으로 익힌 것들입니다.

    • 처음 그래프는 작고 정확하게 만드는 게 좋습니다. 처음부터 전사 코드베이스를 다 넣으려고 하면 금방 포기하게 됩니다. 핵심 도메인 모듈 3~4개 정도가 딱 좋았어요.
    • 관계 이름을 너무 다양하게 만들지 마세요. CALLS, CONTAINS, USES, EXTENDS, READS_CONFIG 정도만 있어도 초반에는 충분했습니다. 관계 이름이 많아지면 관리가 더 힘들어집니다.
    • AI가 만든 그래프 변경은 바로 믿지 않는 게 좋습니다. 그럴듯한 이름을 새로 만들어내는 경우가 있어서, 실제 파일과 심볼 존재 여부를 확인해야 합니다.
    • 프롬프트에는 그래프 전체가 아니라 필요한 부분만 넣으세요. 너무 많이 넣으면 AI도 산만해집니다. 사람도 회의 전에 문서 100장을 주면 싫잖아요.
    • PR 리뷰 때 그래프 변경 내용을 같이 보면 좋습니다. 코드 변경만 보는 것보다 “이번 작업으로 의존성이 어떻게 바뀌었는지” 확인할 수 있어서 리뷰 품질이 올라갑니다.

특히 프롬프트에 넣는 그래프 정보는 짧고 선명한 게 좋았습니다. 저는 보통 이런 식으로 넣어요.

관련 지식 그래프 요약:

  • PaymentProcessor는 결제 승인 흐름의 중심 클래스입니다.
  • PaymentProcessor는 InvoiceGenerator와 RefundHandler를 사용합니다.
  • InvoiceGenerator는 TaxCalculator를 호출합니다.
  • TaxCalculator는 tax_policy.yaml 설정을 읽습니다.
작업 지시: 기존 책임 구조를 유지하면서 쿠폰 할인 계산을 추가해주세요. 새로운 도메인 서비스를 만들기 전에 기존 클래스 확장 가능성을 먼저 검토해주세요.

이 정도면 충분합니다. 더 길게 넣으면 오히려 초점이 흐려질 때가 있었어요. AI에게도 적당한 양의 맥락이 필요합니다. 너무 적으면 헛소리하고, 너무 많으면 중요한 걸 놓쳐요. 사람하고 비슷합니다.

이런 분들은 Graphiti를 한 번 써보셔도 좋겠습니다

제가 써본 느낌으로는, Graphiti 지식 그래프 메모리가 모든 프로젝트에 꼭 필요한 건 아닙니다. 작은 토이 프로젝트나 단발성 스크립트라면 오히려 과할 수 있어요. 그냥 Cursor나 Copilot에 파일 몇 개 열어두고 작업하는 게 더 빠릅니다.

그런데 아래 상황이라면 이야기가 좀 달라집니다.

    • 레거시 코드가 많고, 특정 기능이 어디에 숨어 있는지 매번 찾아야 하는 팀
    • AI 바이브 코딩을 쓰긴 쓰는데, AI가 자꾸 중복 클래스를 만들어서 피곤한 개발자
    • 마이크로서비스 구조에서 서비스 간 의존성을 AI에게 설명하기 힘든 상황
    • 리팩토링할 때 영향 범위를 더 정확히 잡고 싶은 백엔드 개발자
    • 팀원마다 다른 AI 도구를 쓰면서 코드 구조 일관성이 흔들리는 팀
    • 코드에서 다이어그램이나 문서를 자동 생성하고 싶은 개발 조직

초기 세팅은 조금 귀찮습니다. 저도 스키마 비슷한 기준 잡고, 어떤 노드를 넣고 뺄지 정하느라 이틀 정도는 삽질했어요. 그런데 한 번 방향이 잡히고 나니까, 그다음부터는 확실히 편했습니다. 특히 “AI가 내 프로젝트를 조금은 기억하고 있구나”라는 느낌이 생기는 게 좋았어요.

물론 Graphiti가 마법은 아닙니다. AI가 갑자기 완벽한 시니어 개발자가 되는 것도 아니고요. 그래도 적어도 프로젝트의 뼈대와 관계를 계속 공유할 수 있다는 점에서, AI 코딩의 품질을 꽤 안정적으로 끌어올려 줍니다. 저는 이 부분이 마음에 들었습니다. 빠르게 코딩하는 것도 중요하지만, 방향을 잃지 않는 게 더 중요하니까요.

혹시 요즘 AI 바이브 코딩을 하면서 “왜 얘는 방금 말한 것도 까먹지?”, “왜 자꾸 비슷한 클래스를 새로 만들지?”, “레거시 구조를 이해시키는 데 시간이 더 걸리네” 이런 답답함을 느끼고 있다면, Graphiti 같은 지식 그래프 메모리를 한 번 붙여보셔도 좋겠습니다.

특히 어느 정도 규모가 있는 코드베이스를 다루는 개발자라면 꽤 재미있게 써볼 만해요. 처음엔 살짝 낯설지만, 익숙해지고 나면 AI에게 그냥 일을 시키는 게 아니라, 같이 프로젝트 지도를 보면서 일하는 느낌이 납니다. 저는 그게 꽤 든든하더라고요.

댓글