
AI 코딩 에이전트를 위한 LSP: 당신의 에이전트가 아직 사용하지 않고 있는 프로토콜
요약
본 글은 Language Server Protocol (LSP)의 개념과 중요성을 설명하며, 에이전트가 코드를 작성할 때 LSP를 활용하면 추측 대신 정확한 코드 인텔리전스를 제공한다고 강조합니다. 실제 적용 사례에서 LSP 사용 시 토큰 사용량 감소 및 실행 시간 단축 효과를 입증했습니다.
핵심 포인트
- LSP는 코드 에디터와 소스 분석 간의 표준 통신 프로토콜입니다.
- LLM이 코드를 작성할 때, LSP는 정확한 코드 인텔리전스를 제공합니다.
- LSP 도입으로 에이전트 토큰 사용량이 16-22% 감소했습니다.
- 개발자에게 메모장 환경을 벗어나 전문 IDE 기능을 제공합니다.
서론 (Introduction)
당신의 에이전트는 추측합니다. 에이전트는 이름을 텍스트로 매칭하며 자신이 읽은 내용 중에 올바른 줄이 포함되어 있기를 바랍니다. 언어 서버 (Language Server)는 코드를 파싱하여 알고 있기 때문에, 이러한 추측을 코드 인텔리전스 (Code Intelligence)로 대체합니다.
거의 1년 동안 저는 제품 기업에서 AI 인에이블먼트 리드 (AI Enablement Lead)로 근무하며 소프트웨어 개발자와 QA 사이의 연구 및 AI 도입을 담당해 왔습니다. 저희 코드베이스에서 LSP는 에이전트의 토큰 사용량을 16-22% 줄였고, 실행 시간은 30-40% 단축했습니다. 아래의 모든 내용은 저의 자체 테스트와 CircleCI의 독립적인 벤치마크를 바탕으로 합니다.
LSP란 무엇인가 (What LSP is)
LSP (Language Server Protocol)는 코드 에디터와 소스 코드를 분석하여 관련 정보를 제공하는 서버 간의 통신을 위한 표준화된 프로토콜입니다.
2016년에 등장하기 전에는, 모든 스마트 에디터 기능은 언어마다 매번 다시 작성되어야 했습니다. VS Code에서 제대로 된 Python을 사용하고 싶다면 누군가 앉아서 플러그인을 작성해야 합니다. Vim에서 동일한 기능을 원한다면 API가 다르기 때문에 모든 것을 처음부터 다시 시작해야 합니다. Emacs에서도 다시 처음부터 시작해야 합니다. 20개의 언어를 위해 10개의 에디터가 있다면, 200개의 별도 플러그인이 필요하며 그 품질은 매우 제각각입니다.
LSP는 단 하나의 합의를 통해 이러한 불합리함을 제거했습니다. 언어 팀은 하나의 프로그램(Python을 위한 pyright, Go를 위한 gopls, Rust를 위한 rust-analyzer)을 작성하고, 에디터 팀은 하나의 래퍼 (Wrapper)를 작성하면, 그 이후로는 모든 것이 모든 것과 호환됩니다.
LSP가 어떻게 LLM 세계로 들어왔는가 (How LSP got into the LLM world)
LSP와 LLM의 교차점에서 나타난 첫 번째 잘 알려진 논문은 2023년 6월 Microsoft Research에서 발표되었습니다 (arXiv 2306.10763).
당시 AI는 소프트웨어 개발 능력이 여전히 부족했으며, 한 가지 핵심적인 문제가 있었습니다. LLM이 코드를 작성하고 객체(예: 클래스의 인스턴스)에 접근할 때, 만약 그 정의가 인접한 파일에 있다면 이 객체가 실제로 어떤 메서드(method)를 가지고 있는지 알아낼 방법이 전혀 없었습니다. 찾아볼 곳이 없었던 것입니다. 당시의 모델은 도구도 없고, 무언가를 찾아 열 수 있는 방법도 없이 단순히 왼쪽에서 오른쪽으로 텍스트를 입력할 뿐이었습니다. 함수 및 도구 호출(Function calling and tool calls)은 이 논문이 발표된 것과 정확히 같은 달인 2023년 6월 13일에 API에 등장했으며, 제대로 된 첫 번째 에이전트들이 등장하기까지는 아직 거의 1년의 시간이 더 남아 있었습니다.
이 지점이 바로 모델이 환각(hallucination)을 일으키며 진실처럼 보이는 무언가를 지어내기 시작하는 바로 그 지점입니다. 개발자에게 IntelliSense 기능이 있는 IDE를 빼앗고 메모장(Notepad)에 앉혀둔 뒤, 나머지 프로젝트 파일들은 모두 잠가버린 상황을 상상해 보십시오. 그리고 당신은 user.을 입력하며 이것이 무엇이었는지 기억해내려 애쓰고 있습니다. username이었나? email이었나? 아니면 결국 name이었나?
연구진은 이 문제를 어떻게 해결할지 고민하기 시작했고, 다음과 같은 아이디어를 떠올렸습니다.
모델은 단어가 아닌 토큰(token) 단위로 코드를 작성하며, 매 단계마다 후보 목록에서 다음 토큰을 선택합니다. 객체 뒤에 마침표(.)를 찍는 순간, 생성 프로세스가 일시 중지되고 언어 서버(language server)에 이 객체가 실제로 어떤 멤버(member)를 가지고 있는지 묻습니다. 이때 반환된 목록은 모델로 들어가는 것이 아니라 모델을 감싸고 있는 래퍼(wrapper)로 전달되며, 래퍼는 프로젝트에 존재하지 않는 모든 이름을 후보 목록에서 단순히 지워버립니다. 그러면 모델은 남은 것들 중에서 선택하게 되므로, 물리적으로 존재하지 않는 메서드를 입력하는 것이 불가능해집니다.
이러한 접근 방식은 Monitor-Guided Decoding이라고 불렸습니다. Java에서 컴파일 속도가 19-25% 향상되었으며, 이를 위해 모델을 재학습시킬 필요도 없었습니다.
이 논문은 언어 서버(Language Server)가 단순히 모델에 컨텍스트(Context)를 제공하는 것을 넘어, 모델을 제한(limit)하는 역할을 수행하는 최초의 잘 알려진 논문입니다. 그리고 그 부산물이 실험 자체보다 더 중요하게 작용했습니다. 저자들은 Python에서 서버를 호출하기 위해 multilspy 라이브러리를 작성했습니다. 이것이 바로 이후 Serena 내부에서 solid-lsp로 발전하게 된 토대가 되었으며, Serena는 에이전트를 위한 가장 인기 있는 LSP 도구입니다 (이 글을 쓰는 시점 기준 별 2만 7천 개).
따라서 이 흐름은 직접적입니다: 2023년의 연구가 라이브러리를 탄생시켰고, 그 라이브러리가 2025-2026년의 에이전트 툴링(Tooling)을 만들어냈습니다.
작동 원리
기본적으로 LLM(Large Language Model)은 코드를 일반 텍스트(Plain text)로 처리하며, 기본적인 "정의로 이동(go to definition)"조차 수행할 수 없습니다. 에이전트가 객체의 정의를 찾으려면 일반 텍스트 검색을 사용해야 하며, grep 도구를 통해 필요한 정보를 수집해야 합니다.
최신 모델들은 이미 이 작업을 충분히 잘 수행하며 컨텍스트(Context)를 과도하게 점유하지도 않습니다. 예를 들어, Opus 5는 특정 객체의 정의를 찾을 때 파일 전체를 한 번에 읽지 않고 필요한 부분만 찾아냅니다. 하지만 프로젝트 규모가 크고 의존성(Dependencies)이 복잡할 경우, 이는 더 많은 토큰과 시간을 소모하며 어떤 상황에서는 오류를 유발할 수도 있습니다.
반면, LSP는 에이전트에게 언어 서버(Language Server)에 대한 접근 권한을 부여합니다. 해당 서버의 이면에는 프로젝트의 의미론적 모델(Semantic model)이 존재하며, 이를 통해 LLM은 코드 내에서 더욱 유연하고 정밀한 탐색을 할 수 있는 수많은 도구를 얻게 됩니다. 이는 성능이 낮은 모델에서 특히 두드러지게 나타납니다 (아래 연구에서 이를 확인할 수 있습니다).
LSP는 grep을 대체하는 것이 아니라, LLM이 할 수 있는 능력을 확장할 뿐입니다.
왜 LSP에 대해 말하는 사람이 적을까
LLM과 함께 LSP가 처음 사용된 것은 2023년이었지만, 대형 에이전트(agents)에서 네이티브 LSP가 존재하기 시작한 것은 2025년 12월(Claude Code 및 Kiro CLI)부터입니다. 그로부터 불과 반년밖에 지나지 않았기 때문에, 아직 대규모의 독립적인 연구나 벤치마크(benchmarks)가 존재하지 않습니다. 이 접근 방식은 여전히 새로우며, 모든 관심은 에이전틱 개발(agentic development)이나 컨텍스트 엔지니어링(context engineering)과 같은 다른 유행하는 방향으로 쏠려 있습니다.
제 생각에 LSP는 이미 큰 잠재력을 가지고 있으며, 향후 몇 년 안에 에이전틱 개발(agentic development)의 주요 기둥 중 하나가 되는 것을 보게 될 것입니다.
흥미로운 사실: 이미 어디에서나 사용되고 있는 MCP는 개발 과정에서 LSP로부터 영감을 받았습니다.
MCP는 개발 도구의 전체 생태계에 걸쳐 프로그래밍 언어 지원을 추가하는 방식을 표준화하는 언어 서버 프로토콜 (Language Server Protocol, LSP)에서 일부 영감을 얻었습니다. 이와 유사하게, MCP는 AI 애플리케이션 생태계에 추가적인 컨텍스트와 도구를 통합하는 방식을 표준화합니다. 출처
장점
위에서 언급한 "정의로 이동 (go to definition)" 외에도, LSP는 다음과 같은 도구들을 제공합니다:
진단 (Diagnostics) - 언어 서버(language server) 자체가 매 편집 후에 타입(types), 누락된 임포트(missing imports), 구문(syntax) 등의 오류와 경고를 반환합니다. 컴파일러(compiler)나 린터(linter)를 실행할 필요가 없습니다. 에이전트에게 이는 매우 중요합니다. 에이전트가 스스로 실수를 저질렀을 경우, 사용자나 CI를 기다리지 않고 동일한 턴(turn) 내에서 실수를 확인하고 수정할 수 있습니다. 빌드(build)에 약 10~15분이 소요될 수 있는 대규모 프로젝트와 복잡한 작업의 경우, 이는 생산성에 큰 향상을 가져다줍니다.
모든 참조 찾기 (Find all references) - 심볼(symbol)의 모든 사용처를 찾습니다. textDocument/references. 이름으로 grep을 수행하면 주석, 문자열, 다른 클래스 내의 동일한 이름의 필드 등에서 일치하는 항목을 찾아내지만, 재내보내기(re-exports)나 별칭(aliases)은 놓치게 됩니다. LSP는 컴파일러가 해당 심볼의 참조로 간주하는 정확한 위치를 반환합니다. 이는 "이것을 변경하면 무엇이 깨질 것인가"를 파악하기 위한 핵심 작업입니다.
문서 심볼 (Document symbols) - 파일의 목차 역할을 합니다: 클래스, 메서드, 필드, 그리고 이들의 중첩 구조와 경계 등을 나타냅니다. textDocument/documentSymbol. 파일 전체를 읽는 것을 대체할 수 있는 저렴한 방법입니다.
호출 계층 구조 (Call hierarchy) - 양방향 호출 체인을 제공합니다: 이 함수를 호출하는 곳(incoming)과 이 함수가 호출하는 곳(outgoing). callHierarchy/incomingCalls / outgoingCalls. 이는 단일 호출에 대해 여러 단계 깊이의 추적을 수행합니다. 에이전트가 수동으로 이를 수행하려면 수십 번의 grep을 실행해야 하며, 이 과정에서 콜백(callbacks)이나 가상 디스패치(virtual dispatches)에 있는 분기들을 놓치게 됩니다.
위치에서의 호버 / 타입 정보 (Hover / type info at a position) - 특정 지점에서의 표현식(expression)의 실제 타입입니다. textDocument/hover. 타입이 명시되지 않고 추론되는 경우(예: var, 제네릭(generics), LINQ 체인, 팩토리의 반환값 등)에는 대체 불가능한 기능입니다. grep으로는 물리적으로 이 질문에 답할 수 없습니다.
만약 당신이 모바일 개발 분야에서 일해 보았고 프로젝트 빌드(스타일 및 뷰 컴파일, 바인딩, 번역 리소스 등)에 얼마나 오랜 시간이 걸리는지 알고 있다면, 왜 진단(diagnostics) 기능이 제가 Language Server Protocol을 좋아하는 이유 중 하나인지 이해할 것입니다. 이를 통해 에이전트는 다음 빌드를 기다리는 대신 즉시 오류를 찾아낼 수 있습니다.
이것이 기능의 전체 목록은 아닙니다. 다만, 사용 가능한 기능은 언어와 환경에 따라 다를 수 있습니다. 따라서 사용하기 전에 항상 문서를 확인하십시오.
단점
물론 LSP가 모든 것을 해결해주지는 않으며, 모든 문제에 대한 정답도 아닙니다. 사용 시 몇 가지 단점도 존재합니다. 이를 더 자세히 살펴보겠습니다.
당신이 알아야 할 주요 단점은 어떤 상황에서는 검색이 불완전할 수 있다는 점입니다. LSP는 단 하나의 언어와 하나의 프로젝트 내부에서 컴파일러가 보는 것에 대해서만 책임을 집니다. 따라서 클래스나 메서드의 이름이 문자열(string)로 존재하는 모든 것들은 LSP를 그냥 지나칩니다. 예를 들어 리플렉션(reflection)을 통해 이름으로 타입을 생성하는 경우, 설정 파일이나 로거(logger) 설정 내의 클래스 이름, 원시 SQL(raw SQL) 및 저장 프로시저(stored procedures) 내의 테이블과 컬럼, 프론트엔드가 호출하는 문자열 라우트(string routes) 등이 이에 해당합니다. 이 모든 것들은 코드 옆에 존재하지만, 심볼(symbol)에 대한 직접적인 참조는 아닙니다.
이러한 경우 에이전트는 LSP에 요청하여 12곳을 찾아낼 수는 있지만, grep이 찾아냈을 나머지 5곳은 찾지 못할 수 있습니다. 동시에 에이전트는 무언가를 놓쳤다는 사실조차 알지 못하는데, 왜냐하면 답변이 깔끔하게 돌아왔기 때문입니다 (이런 상황에서는 grep을 사용하여 불확실성을 줄이는 방식이 효과적이지 않을 수 있습니다).
grep을 사용할 때의 상황은 다음과 같습니다: 노이즈를 포함한 40개의 일치 항목이 나오고, 에이전트는 이를 하나씩 살펴보고 결정합니다. 더 느리고 비용이 많이 들지만, 정확합니다.
이 시점에서 당신은 "조금 더 기다리고 비용을 더 지불하더라도, grep이 모든 것을 처리하게 하여 더 나은 결과를 얻겠다"라고 생각할 수도 있습니다. 하지만 grep은 더 자주 틀리며, 심지어 무작위로 틀립니다.
grep을 사용하면 에이전트가 800개의 일치 항목을 찾을 수 있지만, 컨텍스트 제한(context limit) 때문에 의미 때문이 아니라 단순히 제한에 걸려 30개만 읽고 나머지는 버리게 될 수도 있습니다.
제 테스트 결과, LSP와 grep은 동일한 결과 정확도를 보여주었으며, 즉 주요 우려 사항이 실제로 발생하지는 않았습니다. 실제로 CircleCI의 독립적인 연구에 따르면, LSP가 grep보다 결과의 정확도가 더 높았습니다 (자세한 내용은 아래 참조).
Language Server Protocol(LSP) 사용 경험
저는 공식 지원이 나온 지 한 달 뒤인 2026년 1월에 처음으로 LSP를 Claude Code에 연결해 보았습니다. 당시에는 작동 속도가 느리고 효율적이지 않았으며, 때때로 멈추기도 했습니다. 보통 프로젝트의 첫 인덱싱(indexing) 단계에서 멈추곤 했는데, 프로젝트 규모가 크고 의존성(dependencies)이 많았기 때문입니다. 그로부터 반년이 지났고, 수많은 버그가 수정되었기에 저는 다시 한번 시도해 보기로 했습니다.
LSP가 얼마나 효과적인지는 모델, 모델의 노력(effort), 프로그래밍 언어, 작업의 컨텍스트(context), 프로젝트의 규모 등 많은 요소에 달려 있다는 점을 미리 경고하고 싶습니다. 또한, 이 프로토콜(protocol)에 대한 지원은 여전히 활발히 개발 중이며 상황은 변할 수 있습니다.
저는 LSP가 좋은 결과를 보여준 작은 연구를 수행했습니다.
설정 (Setup):
- 제품: 거대한 코드베이스 (약 20개의 솔루션 및 100개의 프로젝트)
- 외부 의존성 (External dependencies): NuGet 패키지, C++로 작성된 .dll 라이브러리, API 사양
- 언어: C# / .NET 10
- 모델: Opus 5 / 노력 (Effort): Max
- 방법론 (Methodology): 연구 과제 및 참조 질문, 50회의 독립적인 에이전트 실행
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
