Back to all articles·

LSP는 에디터와 언어의 M×N 문제를 어떻게 줄였나

Language Server Protocol이 왜 등장했는지, 에디터와 언어 서버가 어떤 방식으로 통신하는지, 개발 도구와 AI 도구에서 왜 중요한지 정리해요.

LSP는 에디터와 언어의 M×N 문제를 어떻게 줄였나

LSP는 에디터와 언어의 M×N 문제를 어떻게 줄였나 thumbnail 자동완성, 정의로 이동, 참조 찾기, 진단 메시지는 이제 당연한 개발 환경처럼 느껴집니다. 하지만 이 기능을 모든 에디터와 모든 언어에 각각 구현해야 한다면 이야기가 달라집니다.

예를 들어 에디터가 10개이고 언어가 20개라면 조합은 200개예요. 각 에디터마다 Go, Rust, TypeScript, Python 지원을 따로 만들고 유지해야 해요. 이것이 LSP 이전 개발 도구 생태계의 전형적인 문제였어요.

Language Server Protocol, 즉 LSP는 이 문제를 에디터와 언어 사이의 표준 프로토콜로 풀었어요.

LSP가 해결한 문제

LSP의 핵심은 단순해요. 에디터는 LSP 클라이언트가 되고, 언어별 분석 도구는 Language Server가 됩니다. 둘은 JSON-RPC 기반 프로토콜로 대화해요.

이 구조가 생기면 각 에디터는 모든 언어를 직접 알 필요가 없어요. LSP 클라이언트만 구현하면 돼요. 각 언어도 모든 에디터를 직접 알 필요가 없어요. Language Server 하나를 만들면 여러 에디터에서 같은 언어 기능을 쓸 수 있어요.

즉, M개의 에디터와 N개의 언어가 있을 때 M×N개의 구현이 아니라 M+N개의 구현으로 줄어듭니다.

클라이언트와 서버의 역할

LSP에서 클라이언트는 보통 VS Code, Neovim, Emacs, Zed 같은 에디터예요. 서버는 rust-analyzer, gopls, pyright, tsserver 같은 언어 지능 도구예요.

클라이언트는 사용자가 파일을 열고 수정하고 저장하는 이벤트를 서버에 전달해요. 서버는 해당 언어의 문법, 타입, 프로젝트 구조를 분석해서 필요한 응답을 돌려줘요.

대표적인 흐름은 다음과 같아요.

  1. 에디터가 서버 프로세스를 시작해요.
  2. initialize 요청으로 클라이언트와 서버의 기능을 협상해요.
  3. 파일이 열리면 textDocument/didOpen 알림을 보냅니다.
  4. 파일이 바뀌면 textDocument/didChange 알림을 보냅니다.
  5. 서버는 오류와 경고를 textDocument/publishDiagnostics로 보냅니다.
  6. 사용자가 자동완성이나 정의 이동을 요청하면 클라이언트가 별도 요청을 보냅니다.

이 구조 덕분에 언어 서버는 에디터 UI와 분리된 독립 프로세스로 동작해요. 무거운 분석 작업을 별도 프로세스에서 처리할 수 있고, 같은 서버를 여러 편집 환경에서 재사용할 수 있어요.

LSP는 기능 목록이 아니라 계약입니다

LSP를 단순히 자동완성 프로토콜로 이해하면 부족해요. LSP는 에디터와 언어 서버가 서로 무엇을 지원하는지 협상하는 계약이예요.

어떤 서버는 코드 액션을 지원하고, 어떤 서버는 시맨틱 토큰을 지원해요. 어떤 클라이언트는 워크스페이스 폴더 변경을 보낼 수 있고, 어떤 클라이언트는 일부 기능만 구현해요. 초기화 단계에서 이런 기능을 capability로 주고받기 때문에 서로 다른 도구가 같은 프로토콜 안에서 동작할 수 있어요.

이 점이 중요해요. LSP는 모든 언어 기능을 완전히 동일하게 만드는 표준이 아니예요. 서로 다른 도구가 최소한의 공통 언어로 통신할 수 있게 하는 표준이예요.

AI 코딩 도구에도 LSP가 중요한 이유

AI 코딩 도구가 코드베이스를 이해하려면 단순 문자열 검색만으로는 부족해요. 함수 정의가 어디 있는지, 타입이 무엇인지, 어떤 진단이 발생했는지, 심볼이 어디에서 참조되는지 알아야 해요.

이 정보는 이미 Language Server가 잘 만들고 있어요. 그래서 AI 도구도 LSP 정보를 활용하면 단순한 텍스트 생성에서 벗어나 더 의미론적인 작업을 할 수 있어요.

물론 LSP가 AI의 모든 문제를 해결하지는 않아요. LSP는 코드의 구조와 언어 지능을 제공해요. 제품 요구사항, 런타임 데이터, 배포 환경, 사용자 의도는 여전히 별도의 맥락으로 제공해야 해요.

피해야 할 오해

LSP를 도입하면 개발자 경험이 자동으로 좋아진다고 생각하기 쉬워요. 하지만 실제로는 서버 품질과 프로젝트 설정이 중요해요.

예를 들어 TypeScript 프로젝트에서 tsconfig.json이 잘못되어 있으면 서버는 잘못된 프로젝트 경계를 분석해요. Go 프로젝트에서 모듈 경로가 꼬이면 gopls도 정확한 정보를 주기 어려워요. Rust 프로젝트에서 매크로나 빌드 스크립트가 무거우면 rust-analyzer의 응답성이 떨어질 수 있어요.

LSP는 표준 통신 방식이지, 프로젝트 설정 오류까지 대신 고쳐주는 도구는 아니예요.

정리

LSP의 가치는 자동완성 하나에 있지 않아요. 에디터와 언어 도구 사이의 결합을 줄이고, 언어 지능을 재사용 가능한 서버로 분리했다는 점에 있어요.

개발 도구를 고를 때도 이 관점이 유용해요. 어떤 에디터가 마음에 드는지와 별개로, 내가 쓰는 언어의 Language Server가 성숙한지, 프로젝트 설정을 제대로 읽는지, 진단과 코드 액션이 안정적인지를 봐야 해요.

LSP는 개발 환경의 보이지 않는 인프라예요. 잘 동작할 때는 티가 나지 않지만, 무너지면 생산성 전체가 흔들려요.

참고