작동 원리 심층 분석 (Under the Hood)
이 시스템이 내부적으로 어떻게 움직이는지 단계별로 깊이 파헤쳐 보겠습니다.
1단계: Tree-sitter를 통한 구문 분석과 다국어 지원
그래프를 만들기 위한 첫 단추는 코드를 컴퓨터가 이해할 수 있는 트리 형태로 변환하는 것입니다. 이를 위해 Code Graph RAG는 Tree-sitter라는 강력한 구문 분석(Parsing) 엔진을 사용합니다. Tree-sitter는 최신 텍스트 에디터들이 실시간 구문 강조(Syntax Highlighting)를 위해 사용하는 도구로, 문법 오류가 있거나 코드를 작성하는 도중에도 멈추지 않고 유효한 추상 구문 트리(AST)를 만들어내는 데 탁월합니다.
특히 대규모 모노레포는 단일 언어로만 이루어지지 않습니다. 백엔드는 Java나 Go, 프론트엔드는 TypeScript, 데이터 파이프라인은 Python으로 작성된 경우가 흔하죠. Code Graph RAG는 11개 이상의 주요 언어(Python, Java, C, C#, Go 등)를 기본적으로 지원합니다.
놀라운 점은 서로 다른 언어의 문법 구조를 단일하고 통일된 그래프 스키마(Schema)로 정규화한다는 것입니다. 언어가 달라도 그래프 내부에서는 모두 동일한 ‘클래스’, ‘함수’, ‘임포트’ 노드로 취급됩니다. 최근에는 플러그형 ast-grep 계층이 추가되어, 복잡하게 파서 코드를 새로 작성할 필요 없이 단일 YAML 패턴 파일만으로 Ruby와 같은 새로운 언어의 문법을 쉽게 그래프에 편입시킬 수 있게 되었습니다.
%%{init: {"theme":"base","themeVariables":{"primaryColor":"#F0EEE9","primaryBorderColor":"#2a78d6","primaryTextColor":"#2b2926","secondaryColor":"#e8f0fb","secondaryBorderColor":"#4a3aa7","secondaryTextColor":"#2b2926","tertiaryColor":"#eafaf3","tertiaryBorderColor":"#1baf7a","tertiaryTextColor":"#2b2926","lineColor":"#8a8578","textColor":"#2b2926","edgeLabelBackground":"#F0EEE9","noteBkgColor":"#F0EEE9","noteTextColor":"#2b2926","noteBorderColor":"#8a8578","clusterBkg":"#faf9f6","clusterBorder":"#d8d4c8","fontFamily":"Pretendard, sans-serif"}}}%%
stateDiagram-v2
[*] --> 초기화 단계
초기화 단계 --> 저장소 파일 스캔
저장소 파일 스캔 --> 다국어 구문 분석
다국어 구문 분석 --> 노드와 엣지 변환
노드와 엣지 변환 --> 그래프 데이터베이스 적재
그래프 데이터베이스 적재 --> 사용자 질의 대기
사용자 질의 대기 --> [*]
2단계: Memgraph를 활용한 초고속 인메모리 지식 그래프 구축
추출된 방대한 노드와 엣지 데이터는 어디로 갈까요? 이 프로젝트는 데이터 저장소로 Memgraph를 채택했습니다. Memgraph는 완전한 인메모리(In-Memory) 환경에서 동작하는 C++ 기반의 초고속 그래프 데이터베이스입니다.
일반적인 관계형 데이터베이스(RDBMS)에서는 복잡한 관계를 조회하기 위해 비용이 매우 비싼 조인(JOIN) 연산을 거쳐야 하지만, 그래프 데이터베이스는 노드 간의 포인터를 직접 따라가므로 깊이가 수십 단계에 이르는 상속 체인이나 호출 스택도 밀리초(ms) 단위로 즉시 탐색해 냅니다.
그래프 내부의 데이터 구조는 다음과 같은 형태로 연결됩니다.
%%{init: {"theme":"base","themeVariables":{"primaryColor":"#F0EEE9","primaryBorderColor":"#2a78d6","primaryTextColor":"#2b2926","secondaryColor":"#e8f0fb","secondaryBorderColor":"#4a3aa7","secondaryTextColor":"#2b2926","tertiaryColor":"#eafaf3","tertiaryBorderColor":"#1baf7a","tertiaryTextColor":"#2b2926","lineColor":"#8a8578","textColor":"#2b2926","edgeLabelBackground":"#F0EEE9","noteBkgColor":"#F0EEE9","noteTextColor":"#2b2926","noteBorderColor":"#8a8578","clusterBkg":"#faf9f6","clusterBorder":"#d8d4c8","fontFamily":"Pretendard, sans-serif"}}}%%
erDiagram
CODE_FILE {
string file_path
string language_type
}
CODE_CLASS {
string class_name
int start_line_number
}
CODE_METHOD {
string method_name
string return_type
}
CODE_FILE ||--o{ CODE_CLASS : "CONTAINS_CLASS"
CODE_CLASS ||--o{ CODE_METHOD : "OWNS_METHOD"
CODE_METHOD }o--o{ CODE_METHOD : "CALLS_METHOD"
이러한 모델링을 통해 “A 클래스가 B 메서드를 가지고 있고, B 메서드는 C 메서드를 호출한다”는 구조적 사실이 명확하게 기록됩니다.
3단계: 자연어를 Cypher 쿼리로 변환하는 AI 계층
데이터베이스가 준비되었으니 이제 질문을 던질 차례입니다. 하지만 개발자나 AI 에이전트가 매번 복잡한 데이터베이스 질의어를 직접 작성할 수는 없겠죠. Code Graph RAG 내부에는 Cypher 쿼리 생성 레이어가 존재합니다.
사용자가 “데이터베이스 연결을 초기화하는 클래스를 상속받는 모든 자식 클래스를 찾아줘”라고 자연어로 질문하면, 중간에 개입하는 경량화된 AI 모델이 이 문장을 분석해 다음과 같은 Cypher(그래프 DB 표준 질의어) 코드로 변환합니다.
1
2
| MATCH (child:Class)-[:INHERITS]->(parent:Class {name: 'DatabaseConnection'})
RETURN child.name, child.file_path
|
이 쿼리가 Memgraph에서 실행된 후 정확한 결과 노드들이 반환되면, 그제야 최종 문맥이 정리되어 메인 AI 에이전트에게 전달됩니다. 쿼리 생성 모델로는 OpenAI나 Gemini 같은 외부 API를 사용할 수도 있지만, Ollama를 연결해 완전한 로컬(Local) 오프라인 환경에서 비용 없이 구동할 수도 있습니다.
%%{init: {"theme":"base","themeVariables":{"primaryColor":"#F0EEE9","primaryBorderColor":"#2a78d6","primaryTextColor":"#2b2926","secondaryColor":"#e8f0fb","secondaryBorderColor":"#4a3aa7","secondaryTextColor":"#2b2926","tertiaryColor":"#eafaf3","tertiaryBorderColor":"#1baf7a","tertiaryTextColor":"#2b2926","lineColor":"#8a8578","textColor":"#2b2926","edgeLabelBackground":"#F0EEE9","noteBkgColor":"#F0EEE9","noteTextColor":"#2b2926","noteBorderColor":"#8a8578","clusterBkg":"#faf9f6","clusterBorder":"#d8d4c8","fontFamily":"Pretendard, sans-serif"}}}%%
sequenceDiagram
participant DEVELOPER as "개발자"
participant AGENT as "AI 에이전트"
participant MCP as "MCP 서버"
participant DB as "Memgraph DB"
DEVELOPER->>AGENT: "인증 모듈을 호출하는 모든 위치를 알려줘"
AGENT->>MCP: "도구 호출 구조적 그래프 검색"
MCP->>MCP: "자연어를 Cypher 쿼리로 변환"
MCP->>DB: "그래프 순회 및 데이터 조회"
DB-->>MCP: "노드와 엣지 결과 반환"
MCP-->>AGENT: "구조화된 코드 문맥 전달"
AGENT-->>DEVELOPER: "정확한 호출 경로 및 코드 응답"
4단계: FLOWS_TO 데이터 흐름 추적과 테인트 분석
최근 업데이트에서 가장 주목받는 기능 중 하나는 데이터 흐름(Data-Flow) 추적입니다. 단순한 호출 관계를 넘어, 특정 변수나 데이터가 여러 함수와 객체를 거쳐 어떻게 흘러가는지를 FLOWS_TO라는 특별한 엣지로 연결합니다.
이는 보안 분야에서 주로 사용하는 테인트 분석(Taint Analysis)과 유사한 개념입니다. 사용자의 입력을 받는 API 엔드포인트(Source)부터, 그 데이터가 수많은 래퍼 클래스를 거쳐 최종적으로 데이터베이스나 로그에 기록되는 지점(Sink)까지의 긴 여정을 그래프 탐색 한 번으로 추적할 수 있습니다. 현재 C#, Java, C, Go 언어 환경에서 이 강력한 데이터 흐름 엣지를 지원합니다.
5단계: ast-grep 기반의 구조적 검색 및 치환
그래프가 코드의 위치를 정확히 짚어냈다면, 이제 코드를 수정할 차례입니다. 일반적인 텍스트 치환(정규 표현식 등)은 괄호의 중첩이나 줄바꿈을 제대로 처리하지 못해 코드를 망가뜨리기 일쑤입니다.
이 시스템은 ast-grep을 에이전트 도구로 결합했습니다. 에이전트는 “모든 try-catch 블록 중 catch 구문이 비어있는 곳을 찾아서 로그를 남기는 코드로 바꿔라” 같은 복잡한 구조적 리팩토링을 안전하게 수행할 수 있습니다. AST 레벨에서 일치하는 패턴을 찾기 때문에 띄어쓰기나 주석의 위치에 영향을 받지 않으며, 실제 코드를 변경하기 전에 정밀한 Diff(차이점)를 미리 확인하게 해줍니다.
%%{init: {"theme":"base","themeVariables":{"primaryColor":"#F0EEE9","primaryBorderColor":"#2a78d6","primaryTextColor":"#2b2926","secondaryColor":"#e8f0fb","secondaryBorderColor":"#4a3aa7","secondaryTextColor":"#2b2926","tertiaryColor":"#eafaf3","tertiaryBorderColor":"#1baf7a","tertiaryTextColor":"#2b2926","lineColor":"#8a8578","textColor":"#2b2926","edgeLabelBackground":"#F0EEE9","noteBkgColor":"#F0EEE9","noteTextColor":"#2b2926","noteBorderColor":"#8a8578","clusterBkg":"#faf9f6","clusterBorder":"#d8d4c8","fontFamily":"Pretendard, sans-serif"}}}%%
flowchart TD
A["구조적 검색 패턴 정의"]
B["전체 소스 트리 매칭"]
C["대상 코드 블록 식별"]
D["코드 치환 규칙 적용"]
E["외과적 코드 패치 및 변경 사항 검토"]
A --> B
B --> C
C --> D
D --> E