Cursor에서 "이 함수 어디서 호출돼?"라고 물었을 때 AI가 정확한 답을 내놓는다면, 그건 AI가 코드를 '이해'한 게 아닐 수 있다. 그 이면에는 프로토콜 간 협력, 검색 전략의 선택, 그리고 메모리 아키텍처의 한계가 동시에 작동하고 있다. AI 도구가 코드베이스를 실제로 어떻게 읽는지—그 구조를 지금부터 한 겹씩 벗겨보자.
프로토콜의 수렴: LSP가 깔아둔 길을 MCP가 걷는다
2016년 Microsoft가 내놓은 LSP(Language Server Protocol)는 에디터와 언어 분석 엔진 사이의 통신을 JSON-RPC로 표준화했다. VS Code, Neovim, IntelliJ가 동일한 언어 서버를 공유할 수 있게 된 건 이 덕분이다. LSP는 "코드와 인텔리전스 사이의 인터페이스를 표준화하는 것이 단일 구현보다 더 가치 있다"는 사실을 증명했다.
Anthropics가 2024년 중반 출시한 MCP(Model Context Protocol)는 같은 문제를 AI 모델 레이어에서 다시 푼다. 이전까지 LLM을 외부 데이터 소스에 연결하려면 인증, 에러 처리, 데이터 포매팅을 각각 손으로 짜야 했다. MCP는 이를 Host–Client–Server의 3단 구조로 표준화한다. Claude Desktop 같은 호스트가 요청하면, 클라이언트가 중계하고, MCP 서버가 데이터와 툴을 모델에 노출하는 방식이다. dev.to의 기술 분석에 따르면, 이 구조는 LSP 모델을 의도적으로 닮아있으면서도 LLM의 확률적 특성과 컨텍스트 윈도우 제약에 맞게 재설계됐다.
두 프로토콜이 수렴할 때 진짜 힘이 나온다. LSP가 심볼, 타입, 레퍼런스, 정의 같은 코드의 구조적 정보를 제공하면, MCP는 이 데이터를 AI 모델에 리소스나 툴로 노출할 수 있다. 예를 들어 find_references라는 MCP 툴이 LSP 서버에 쿼리를 날려 특정 심볼의 모든 사용처를 찾아오면, AI는 그 결과를 바탕으로 자연어로 분석을 이어간다. "인증 관련 버그 찾아줘"가 아니라, "@auth 데코레이터가 붙은 함수를 LSP로 먼저 조회하고, 그 로직을 LLM이 분석하는" 구조다.
벡터냐 그래프냐: AI가 코드를 찾는 두 가지 방식
AI 코딩 도구가 코드베이스를 표현하는 방식은 크게 두 가지로 나뉜다. 벡터 임베딩과 의존성 그래프. 두 진영은 서로 다른 질문에 옳다.
벡터 임베딩은 의미론적 유사성을 잡는다. "JWT 토큰 파싱"이라는 쿼리가 verifyToken()이 아닌 decodeAuthPayload()라는 이름의 함수를 찾아낼 수 있는 건 임베딩 공간에서 두 개념이 가깝기 때문이다. 낯선 코드베이스에서 "이런 기능을 하는 함수"를 찾거나, 중복 로직을 감지하거나, 스타일이 다른 코드 사이에서 자동완성을 제안할 때 탁월하다. 단, 벡터는 구조를 모른다. 의미적으로 유사한 두 함수 중 하나가 deprecated된 코드라도 임베딩은 그 차이를 알지 못한다.
의존성 그래프는 구조적 사실을 다룬다. auth/middleware.js를 호출하는 모듈이 23개라는 건 그래프 순회로만 알 수 있다. 함수 하나를 바꿨을 때 무엇이 깨지는지, 모듈 간 경계는 어디인지, 팀의 소유권 구분은 어디서 나뉘는지—이건 그래프의 영역이다. 반면 그래프는 관련성 순위를 매기지 못한다. 구조적으로 연결된 파일 40개를 반환하면서 그 중 어떤 3개가 실제로 중요한지는 말해주지 않는다.
성숙한 코드 인텔리전스 도구들이 수렴하는 패턴은 이 둘을 체이닝하는 것이다. 먼저 그래프로 구조적으로 연관된 파일 집합을 좁히고, 그 안에서 벡터 검색으로 의미 기반 재순위를 매긴다. Spiderbrain이 채택한 방식이 정확히 이 패턴이다. 전역 임베딩 검색은 deprecated 코드나 죽은 코드를 되살려올 위험이 있고, 그래프 순회만으로는 "어떤 것이 진짜 중요한가"를 판단하지 못한다. 둘을 연결해야 비로소 구조적으로 유효하고 의미적으로 관련 있는 결과가 나온다.
메모리의 착각: "기억하는 것"과 "집계하는 것"은 다른 기계다
AI 에이전트의 메모리를 설계할 때 가장 흔한 실수는 벡터 스토어를 모든 기억의 저장소로 쓰는 것이다. 사실 검색과 유사도 기반 리콜에는 완벽하게 작동한다. "이 사용자가 Berlin 마이그레이션에 대해 뭐라고 했어?"—이건 벡터 스토어가 잘 한다.
그런데 "이번 달 가격 관련 질문이 몇 번이었어?"라고 물으면 무너진다. 벡터 스토어의 기본 연산은 최근접 이웃 검색이다. COUNT, GROUP BY, HAVING이 없다. 4,000개의 메모리 중 top-k로 20개를 뽑아 LLM이 그 20개를 보고 숫자를 추산하면, 전체 데이터의 0.5%만 본 채로 내린 결론이 된다. 답은 유창하고 그럴듯하지만, 틀렸다. nlqdb의 분석처럼, 이건 오작동이 아니라 두 질문이 원래 다른 기계를 필요로 하기 때문이다.
올바른 구조는 벡터 스토어와 관계형 DB를 레이어로 분리하는 것이다. "이것과 비슷한 것 찾기"는 벡터 스토어, "저장한 것을 세거나 묶거나 순위 매기기"는 실제 쿼리 플래너를 가진 관계형 스토어. 둘은 대체 관계가 아니라 보완 관계다.
시사점: AI 도구를 쓰는 개발자가 알아야 할 것
이 세 가지 구조—프로토콜 수렴, 벡터-그래프 선택, 메모리 아키텍처—는 결국 하나의 질문으로 모인다. "AI 도구가 지금 어떤 방식으로 코드를 보고 있는가?" Cursor나 Claude에서 AI가 엉뚱한 결과를 내놓을 때, 그건 모델이 멍청한 게 아니라 잘못된 검색 전략이나 잘못된 메모리 레이어를 쓰고 있을 가능성이 높다.
프론트엔드 개발자 입장에서 실용적 함의는 명확하다. 첫째, AI 도구에 "이 컴포넌트 어디서 쓰여?" 같은 구조 질문을 잘 하려면 LSP 기반 툴링이 제대로 붙어있어야 한다. 둘째, 코드베이스가 커질수록 순수 벡터 검색은 노이즈가 늘어난다—그래프 기반 컨텍스트 제약을 먼저 걸어야 한다. 셋째, 에이전트에게 집계형 질문을 시키려면 벡터 스토어만으로는 구조적으로 불가능하다는 걸 전제로 설계해야 한다.
MCP의 확산과 LSP의 진화가 가속화될수록, AI가 코드를 이해하는 방식은 점점 더 구조화된 프로토콜 위에서 작동하게 된다. 앞으로 좋은 AI 코딩 도구의 기준은 "얼마나 영리한 모델을 쓰느냐"보다 "어떤 검색 전략과 메모리 아키텍처 위에 서 있느냐"가 될 것이다. 그 구조를 이해하는 개발자가 도구를 더 잘 쓰고, 더 잘 설계한다.