🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

LSP Language Server Protocol 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 5. · 15 Views

LSP Language Server Protocol 완벽 가이드

코드 에디터의 자동완성, 오류 표시, 호버 정보가 어떻게 작동하는지 궁금하셨나요? LSP의 구조와 동작 원리를 실무 예제와 함께 쉽게 알아봅니다.


목차

  1. LSP란_무엇인가
  2. lsp_디렉토리_구조
  3. LSP_서버_자동_감지와_시작
  4. lsp-diagnostics.ts_코드_진단
  5. lsp-hover.ts_호버_정보
  6. 커스텀_LSP_서버_설정

1. LSP란 무엇인가

김개발 씨는 VS Code에서 코드를 작성하다가 문득 궁금해졌습니다. 타이핑을 하면 자동으로 함수 이름이 추천되고, 마우스를 올리면 타입 정보가 나타나고, 오류가 있으면 빨간 밑줄이 그어집니다.

도대체 에디터는 어떻게 이런 똑똑한 기능을 제공하는 걸까요?

**LSP(Language Server Protocol)**는 코드 에디터와 언어 분석 서버 사이의 통신 규약입니다. 마치 통역사가 서로 다른 언어를 사용하는 두 사람 사이에서 소통을 돕듯이, LSP는 에디터와 언어 분석 도구가 서로 대화할 수 있게 해줍니다.

이 프로토콜 덕분에 하나의 언어 서버를 만들면 VS Code, Vim, Emacs 등 어떤 에디터에서든 동일한 기능을 사용할 수 있습니다.

다음 코드를 살펴봅시다.

// LSP 클라이언트와 서버 간의 기본 통신 구조
interface LSPMessage {
  jsonrpc: "2.0";           // JSON-RPC 2.0 프로토콜 사용
  id?: number;              // 요청/응답 매칭용 ID
  method?: string;          // 호출할 메서드 (예: "textDocument/hover")
  params?: object;          // 메서드에 전달할 파라미터
  result?: object;          // 응답 결과
}

// 에디터가 서버에 호버 정보를 요청하는 예시
const hoverRequest: LSPMessage = {
  jsonrpc: "2.0",
  id: 1,
  method: "textDocument/hover",
  params: { textDocument: { uri: "file:///app.ts" }, position: { line: 10, character: 5 } }
};

김개발 씨는 입사 첫날부터 VS Code의 편리함에 감탄했습니다. 함수 이름을 몇 글자만 입력해도 자동완성이 되고, 오타를 치면 즉시 빨간 밑줄이 나타났습니다.

하지만 이 모든 기능이 어떻게 작동하는지는 전혀 몰랐습니다. 어느 날 점심시간, 호기심 많은 김개발 씨가 선배 박시니어 씨에게 물었습니다.

"선배님, VS Code가 어떻게 TypeScript 문법을 이해하는 거예요? 에디터 안에 TypeScript 컴파일러가 들어있는 건가요?" 박시니어 씨가 웃으며 답했습니다.

"좋은 질문이에요. 사실 에디터 자체는 프로그래밍 언어를 이해하지 못해요.

대신 Language Server라는 별도의 프로그램이 그 역할을 해주죠." 그렇다면 **LSP(Language Server Protocol)**란 정확히 무엇일까요? 쉽게 비유하자면, LSP는 마치 UN 회의장의 동시통역 시스템과 같습니다.

각 나라 대표들은 자국어로 말하지만, 통역 시스템 덕분에 모두가 서로를 이해할 수 있습니다. LSP도 마찬가지입니다.

에디터(VS Code, Vim, Emacs 등)와 언어 분석 서버(TypeScript, Python, Go 등) 사이에서 표준화된 방식으로 정보를 주고받게 해줍니다. LSP가 등장하기 전에는 어땠을까요?

각 에디터마다 언어 지원 플러그인을 따로 만들어야 했습니다. VS Code용 TypeScript 플러그인, Vim용 TypeScript 플러그인, Emacs용 TypeScript 플러그인...

에디터가 10개이고 언어가 20개라면 무려 200개의 플러그인이 필요했습니다. 개발자들의 시간과 노력이 엄청나게 낭비되었습니다.

마이크로소프트가 이 문제를 해결하기 위해 LSP를 만들었습니다. LSP를 사용하면 언어당 하나의 서버만 만들면 됩니다.

그 서버가 LSP 규격만 따르면, 모든 LSP 지원 에디터에서 자동으로 작동합니다. 200개가 필요했던 플러그인이 30개(에디터 10개 + 언어 20개)로 줄어든 것입니다.

위의 코드를 살펴보면, LSP는 JSON-RPC 2.0 프로토콜을 기반으로 통신합니다. 에디터가 "이 위치의 타입 정보를 알려줘"라고 요청하면, 서버가 분석 결과를 JSON 형태로 응답합니다.

모든 메시지에는 method 필드가 있어서 어떤 종류의 요청인지 구분할 수 있습니다. 실제 현업에서 LSP는 어디에나 있습니다.

VS Code에서 TypeScript를 사용할 때 자동완성이 되는 것도, PyCharm에서 Python 함수의 docstring이 보이는 것도, Neovim에서 Go 코드의 오류가 표시되는 것도 모두 LSP 덕분입니다. 심지어 GitHub Copilot 같은 AI 코딩 도구도 LSP 프로토콜을 활용합니다.

다시 김개발 씨의 이야기로 돌아가봅시다. 박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다.

"아, 그래서 VS Code가 그렇게 많은 언어를 지원할 수 있는 거군요!" LSP를 이해하면 에디터의 마법 같은 기능들이 더 이상 마법이 아니게 됩니다. 어떻게 작동하는지 알면, 필요할 때 직접 커스터마이징하거나 문제를 해결할 수 있습니다.

실전 팁

💡 - LSP는 에디터에 독립적이므로, 한 언어 서버를 만들면 모든 에디터에서 사용 가능합니다

  • JSON-RPC 기반이라 디버깅할 때 메시지를 쉽게 읽고 분석할 수 있습니다
  • VS Code의 출력 패널에서 "Language Server" 로그를 확인하면 LSP 통신 내용을 볼 수 있습니다

2. lsp 디렉토리 구조

김개발 씨가 새로운 프로젝트에 LSP 지원을 추가하려고 코드베이스를 열어보았습니다. lsp/ 폴더 안에 여러 파일들이 있는데, 어디서부터 봐야 할지 막막합니다.

체계적인 디렉토리 구조를 이해하면 전체 그림이 보이기 시작합니다.

LSP 구현체는 보통 모듈별로 파일을 분리하여 관리합니다. 서버 시작과 생명주기를 담당하는 파일, 코드 진단을 처리하는 파일, 호버 정보를 제공하는 파일 등으로 나눕니다.

이렇게 분리하면 각 기능을 독립적으로 개발하고 테스트할 수 있어 유지보수가 훨씬 쉬워집니다.

다음 코드를 살펴봅시다.

lsp/
├── index.ts              // LSP 모듈의 진입점, 모든 기능을 export
├── server.ts             // LSP 서버 시작/종료 로직
├── lsp-manager.ts        // 여러 언어 서버 인스턴스 관리
├── lsp-diagnostics.ts    // 코드 진단 (오류, 경고) 처리
├── lsp-hover.ts          // 호버 시 타입/문서 정보 제공
├── lsp-completion.ts     // 자동완성 기능
├── lsp-definition.ts     // "정의로 이동" 기능
├── lsp-references.ts     // "참조 찾기" 기능
├── types.ts              // LSP 관련 타입 정의
└── utils.ts              // 유틸리티 함수들

프로젝트의 규모가 커지면 모든 코드를 하나의 파일에 담는 것은 불가능합니다. 김개발 씨도 처음에는 lsp.ts 하나에 모든 기능을 넣었다가, 파일이 2000줄을 넘어가자 수정할 때마다 원하는 부분을 찾기가 힘들어졌습니다.

박시니어 씨가 코드 리뷰를 하다가 말했습니다. "이 파일 좀 분리하는 게 어때요?

기능별로 나누면 훨씬 관리하기 쉬워질 거예요." 체계적인 디렉토리 구조는 마치 잘 정리된 서랍장과 같습니다. 양말은 양말 서랍에, 셔츠는 셔츠 서랍에 넣어두면 아침에 옷을 찾기가 훨씬 쉽습니다.

LSP 코드도 마찬가지입니다. 진단 관련 코드는 lsp-diagnostics.ts에, 호버 관련 코드는 lsp-hover.ts에 모아두면 됩니다.

가장 먼저 index.ts를 살펴봅시다. 이 파일은 LSP 모듈의 얼굴입니다.

외부에서 LSP 기능을 사용하려면 이 파일을 통해 접근합니다. 보통 다른 파일들의 기능을 한데 모아서 re-export하는 역할을 합니다.

"여기가 LSP 백화점 1층 안내데스크입니다. 어떤 기능을 찾으시나요?" 다음은 server.ts입니다.

LSP 서버의 시작과 종료를 담당합니다. 어떤 포트에서 연결을 받을지, 초기화할 때 어떤 설정을 사용할지, 종료할 때 리소스를 어떻게 정리할지 등을 정의합니다.

자동차의 시동 버튼과 같은 역할이라고 생각하면 됩니다. lsp-manager.ts는 여러 언어 서버를 관리합니다.

하나의 프로젝트에서 TypeScript, CSS, HTML을 동시에 사용한다면, 각 언어별로 별도의 LSP 서버가 필요할 수 있습니다. 매니저는 이 서버들의 생성, 상태 확인, 재시작 등을 중앙에서 관리합니다.

기능별 파일들도 중요합니다. lsp-diagnostics.ts는 코드의 오류와 경고를 분석합니다.

lsp-hover.ts는 마우스를 올렸을 때 보여줄 정보를 생성합니다. lsp-completion.ts는 자동완성 후보를 제공합니다.

lsp-definition.tslsp-references.ts는 각각 "정의로 이동"과 "모든 참조 찾기" 기능을 담당합니다. 마지막으로 types.tsutils.ts가 있습니다.

types.ts에는 LSP 관련 TypeScript 인터페이스와 타입이 정의되어 있습니다. utils.ts에는 여러 파일에서 공통으로 사용하는 헬퍼 함수들이 모여있습니다.

이렇게 분리해두면 코드 중복을 피할 수 있습니다. 김개발 씨가 박시니어 씨의 조언대로 파일을 분리하고 나니, 코드를 수정할 때 해당 파일만 열면 되었습니다.

"호버 기능에 버그가 있다"는 리포트가 오면 바로 lsp-hover.ts를 열면 됩니다. 더 이상 2000줄짜리 파일에서 Ctrl+F로 헤맬 필요가 없어졌습니다.

실전 팁

💡 - 파일당 500줄을 넘지 않도록 관리하면 가독성이 좋습니다

  • 새로운 LSP 기능을 추가할 때는 기존 파일 구조를 참고하여 일관성을 유지하세요
  • index.ts에서 필요한 것만 export하여 모듈의 공개 API를 명확히 하세요

3. LSP 서버 자동 감지와 시작

김개발 씨가 프로젝트 폴더를 열었더니 TypeScript 언어 서버가 자동으로 시작되었습니다. 분명히 수동으로 시작한 적이 없는데 어떻게 된 걸까요?

에디터는 프로젝트의 언어를 어떻게 감지하고, 적절한 LSP 서버를 어떻게 찾아서 실행하는 걸까요?

LSP 서버 자동 감지는 파일 확장자, 설정 파일, 언어 ID를 기반으로 동작합니다. 에디터가 .ts 파일을 열면 TypeScript 서버가 필요하다고 판단하고, tsconfig.json이 있는지 확인한 뒤, 설치된 TypeScript 언어 서버를 자동으로 시작합니다.

이 과정이 매끄럽게 이루어지기 때문에 사용자는 별다른 설정 없이도 언어 지원 기능을 바로 사용할 수 있습니다.

다음 코드를 살펴봅시다.

// LSP 서버 자동 감지 및 시작 로직
interface LanguageServerConfig {
  languageId: string;           // 언어 식별자 (예: "typescript")
  fileExtensions: string[];     // 지원하는 확장자
  configFiles: string[];        // 프로젝트 설정 파일
  serverCommand: string;        // 서버 실행 명령어
}

const serverConfigs: LanguageServerConfig[] = [
  {
    languageId: "typescript",
    fileExtensions: [".ts", ".tsx"],
    configFiles: ["tsconfig.json"],
    serverCommand: "typescript-language-server --stdio"
  }
];

// 파일 열 때 적절한 서버 찾기
function findServerForFile(filePath: string): LanguageServerConfig | undefined {
  const ext = path.extname(filePath);
  return serverConfigs.find(config => config.fileExtensions.includes(ext));
}

많은 개발자들이 당연하게 여기는 기능이 있습니다. VS Code에서 TypeScript 파일을 열면 자동완성이 바로 작동합니다.

Python 파일을 열면 Python 언어 지원이 활성화됩니다. 하지만 이 "당연한" 기능 뒤에는 정교한 자동 감지 로직이 숨어있습니다.

김개발 씨가 새 프로젝트를 클론받아 열었습니다. 폴더 안에는 TypeScript 파일, CSS 파일, HTML 파일이 섞여 있었습니다.

그런데 각 파일 타입에 맞는 언어 지원이 알아서 활성화되었습니다. "에디터가 파일 타입을 어떻게 구분하는 거죠?" 김개발 씨의 질문에 박시니어 씨가 설명을 시작했습니다.

첫 번째 단서는 파일 확장자입니다. .ts로 끝나면 TypeScript, .py면 Python, .go면 Go입니다.

가장 직관적이고 빠른 판별 방법입니다. 마치 책의 표지만 봐도 어떤 종류의 책인지 대략 알 수 있는 것과 같습니다.

두 번째 단서는 설정 파일입니다. 폴더에 tsconfig.json이 있으면 TypeScript 프로젝트일 가능성이 높습니다.

package.json에 TypeScript가 의존성으로 있다면 더욱 확실해집니다. 이런 설정 파일들은 프로젝트의 신분증과 같습니다.

세 번째로 파일 내용을 분석하기도 합니다. 확장자가 없거나 애매한 경우, 파일의 첫 몇 줄을 읽어서 언어를 추측합니다.

쉘 스크립트의 #!/bin/bash 같은 shebang이 대표적인 예입니다. 이렇게 언어가 감지되면, 다음 단계는 적절한 서버를 찾는 것입니다.

에디터는 설정된 서버 목록에서 해당 언어를 지원하는 서버를 찾습니다. 위의 코드에서 serverConfigs 배열이 바로 그 목록입니다.

각 설정에는 언어 ID, 지원 확장자, 서버 실행 명령어가 포함되어 있습니다. 서버를 찾았으면 프로세스를 시작합니다.

serverCommand에 정의된 명령어를 실행하여 새 프로세스를 띄웁니다. 이 프로세스는 백그라운드에서 실행되며, 에디터와 표준 입출력(stdio) 또는 소켓을 통해 통신합니다.

시작된 서버는 초기화 핸드셰이크를 거칩니다. 에디터가 initialize 요청을 보내고, 서버가 자신이 지원하는 기능 목록을 응답합니다.

"저는 자동완성, 호버, 정의로 이동을 지원합니다"라고 자기소개를 하는 것입니다. 김개발 씨는 감탄했습니다.

"그냥 파일 열기만 했는데 이렇게 복잡한 과정이 뒤에서 일어나고 있었군요!" 이 자동화 덕분에 개발자는 언어 서버 설정에 시간을 쏟지 않고 바로 코딩에 집중할 수 있습니다. 물론 필요하다면 수동으로 서버를 설정하거나 교체할 수도 있습니다.

실전 팁

💡 - 언어 서버가 자동으로 시작되지 않으면 해당 언어의 LSP 서버가 설치되어 있는지 확인하세요

  • 여러 버전의 언어 서버가 충돌할 수 있으니, 프로젝트별로 버전을 고정하는 것이 좋습니다
  • 에디터의 출력 패널에서 서버 시작 로그를 확인할 수 있습니다

4. lsp-diagnostics.ts 코드 진단

김개발 씨가 코드를 작성하던 중 오타를 쳤습니다. const user = getUesr(); 순간 빨간 밑줄이 그어지며 "Cannot find name 'getUesr'.

Did you mean 'getUser'?"라는 메시지가 나타났습니다. 아직 저장도 하지 않았는데, 에디터가 어떻게 실시간으로 오류를 감지한 걸까요?

**코드 진단(Diagnostics)**은 LSP의 핵심 기능 중 하나로, 코드의 오류, 경고, 힌트를 실시간으로 분석하여 보여줍니다. lsp-diagnostics.ts 파일은 언어 서버로부터 진단 정보를 받아 에디터에 표시하는 역할을 합니다.

문법 오류뿐 아니라 타입 불일치, 사용하지 않는 변수, 잠재적 버그까지 다양한 문제를 감지할 수 있습니다.

다음 코드를 살펴봅시다.

// lsp-diagnostics.ts - 코드 진단 처리 모듈
import { Diagnostic, DiagnosticSeverity } from "vscode-languageserver-types";

interface DiagnosticResult {
  uri: string;                    // 파일 경로
  diagnostics: Diagnostic[];      // 진단 목록
}

// 서버로부터 받은 진단 정보 처리
function handleDiagnostics(result: DiagnosticResult): void {
  const { uri, diagnostics } = result;

  diagnostics.forEach(diag => {
    const severity = getSeverityLabel(diag.severity);
    const position = `${diag.range.start.line + 1}:${diag.range.start.character}`;
    console.log(`[${severity}] ${uri}:${position} - ${diag.message}`);
  });
}

// 심각도를 사람이 읽기 쉬운 형태로 변환
function getSeverityLabel(severity?: DiagnosticSeverity): string {
  const labels = { 1: "Error", 2: "Warning", 3: "Info", 4: "Hint" };
  return labels[severity || 1] || "Unknown";
}

코드를 작성하면서 실시간으로 오류를 확인할 수 있다는 것은 정말 편리한 기능입니다. 예전에는 코드를 저장하고, 컴파일하고, 오류 메시지를 읽고, 다시 해당 줄을 찾아가야 했습니다.

지금은 타이핑하는 즉시 문제를 알 수 있습니다. 박시니어 씨가 김개발 씨에게 설명했습니다.

"이게 바로 실시간 코드 진단이에요. 예전에 비하면 정말 혁명적인 변화죠." 코드 진단은 마치 실시간 맞춤법 검사기와 같습니다.

워드 프로세서에서 오타를 치면 빨간 물결 밑줄이 그어지는 것, 다들 경험해보셨을 겁니다. 코드 진단도 같은 원리입니다.

다만 맞춤법 대신 문법, 타입, 로직을 검사합니다. 진단에는 여러 심각도(Severity) 레벨이 있습니다.

Error는 가장 심각한 문제입니다. 코드가 아예 실행되지 않거나 컴파일되지 않을 때 나타납니다.

빨간색으로 표시되며, 반드시 수정해야 합니다. Warning은 코드는 동작하지만 잠재적 문제가 있을 때 나타납니다.

노란색으로 표시되며, 수정하는 것이 권장됩니다. 예를 들어 선언했지만 사용하지 않는 변수가 여기에 해당합니다.

InfoHint는 참고용 정보입니다. 코드 스타일 제안이나 최적화 힌트 등이 여기에 해당합니다.

위의 코드에서 Diagnostic 객체의 구조를 살펴봅시다. range는 문제가 발생한 위치를 나타냅니다.

시작 줄과 시작 문자, 끝 줄과 끝 문자가 포함됩니다. 에디터는 이 정보를 바탕으로 정확한 위치에 밑줄을 그립니다.

message는 사람이 읽을 수 있는 오류 설명입니다. "Cannot find name 'getUesr'"처럼 무엇이 문제인지 알려줍니다.

severity는 앞서 설명한 심각도입니다. source는 어떤 도구가 이 진단을 생성했는지 알려줍니다.

TypeScript, ESLint, Prettier 등이 될 수 있습니다. 진단 정보는 어떻게 생성될까요?

사용자가 코드를 수정할 때마다 에디터는 변경 사항을 언어 서버에 알립니다. 서버는 해당 파일을 다시 분석하고, 새로운 진단 목록을 에디터에 전송합니다.

이 과정이 수백 밀리초 안에 이루어지기 때문에 실시간처럼 느껴집니다. 김개발 씨는 이제 빨간 밑줄이 무섭지 않습니다.

오히려 고마운 존재입니다. 코드를 저장하기 전에 문제를 발견해주니까요.

실전 팁

💡 - 진단이 너무 많으면 작업에 방해가 될 수 있으니, 중요한 것부터 해결하세요

  • ESLint, Prettier 등의 도구도 LSP를 통해 진단을 제공할 수 있습니다
  • 특정 진단을 무시하고 싶다면 해당 도구의 설정 파일에서 규칙을 비활성화하세요

5. lsp-hover.ts 호버 정보

김개발 씨가 동료의 코드를 읽다가 낯선 함수를 발견했습니다. calculateTaxWithDiscount(price, rate, threshold) 이 함수가 정확히 무슨 일을 하는 걸까요?

함수 위에 마우스를 올리자 함수 시그니처, 파라미터 설명, 반환 타입이 깔끔하게 표시되었습니다. 이것이 바로 호버 정보입니다.

**호버(Hover)**는 코드 위에 마우스를 올렸을 때 해당 심볼에 대한 정보를 보여주는 기능입니다. lsp-hover.ts는 이 기능을 구현하며, 변수의 타입, 함수의 시그니처, 클래스의 문서화 주석 등을 표시합니다.

별도의 문서를 찾아보지 않고도 코드의 의미를 빠르게 파악할 수 있어 코드 이해도가 크게 향상됩니다.

다음 코드를 살펴봅시다.

// lsp-hover.ts - 호버 정보 제공 모듈
import { Hover, MarkupContent, MarkupKind } from "vscode-languageserver-types";

interface HoverParams {
  textDocument: { uri: string };
  position: { line: number; character: number };
}

// 호버 요청 처리
async function handleHover(params: HoverParams): Promise<Hover | null> {
  const { uri, position } = { uri: params.textDocument.uri, ...params.position };

  // 해당 위치의 심볼 정보 조회
  const symbolInfo = await getSymbolAtPosition(uri, position);
  if (!symbolInfo) return null;

  // 마크다운 형식으로 호버 내용 생성
  const contents: MarkupContent = {
    kind: MarkupKind.Markdown,
    value: `**${symbolInfo.name}**: ${symbolInfo.type}\n\n${symbolInfo.documentation}`
  };

  return { contents, range: symbolInfo.range };
}

새로운 코드베이스에 합류했을 때 가장 힘든 점 중 하나는 수많은 함수와 클래스의 역할을 파악하는 것입니다. 문서가 잘 정리되어 있다면 좋겠지만, 현실은 그렇지 않은 경우가 많습니다.

김개발 씨도 같은 고민을 했습니다. 수천 줄의 코드에서 processTransaction이라는 함수를 발견했는데, 이 함수가 정확히 어떤 파라미터를 받고 무엇을 반환하는지 알 수 없었습니다.

박시니어 씨가 힌트를 주었습니다. "그냥 마우스를 올려보세요." 마우스를 함수 이름 위에 올리자, 작은 팝업이 나타났습니다.

(method) processTransaction( transaction: Transaction, options?: ProcessOptions ): Promise<TransactionResult> Processes a financial transaction with optional configuration. @param transaction - The transaction to process @param options - Optional processing configuration @returns Promise resolving to the transaction result 호버 정보는 마치 코드에 붙은 포스트잇과 같습니다.

코드 자체에는 보이지 않지만, 필요할 때 가져다 보면 필요한 정보가 적혀있습니다. 호버가 제공하는 정보는 다양합니다.

타입 정보가 가장 기본입니다. 변수가 string인지 number인지, 함수가 어떤 타입을 반환하는지 알 수 있습니다.

TypeScript처럼 타입 시스템이 있는 언어에서 특히 유용합니다. 함수 시그니처도 중요합니다.

파라미터의 이름, 타입, 기본값까지 한눈에 볼 수 있습니다. 함수 정의로 직접 이동하지 않아도 됩니다.

**문서화 주석(JSDoc, docstring 등)**도 표시됩니다. 개발자가 작성해둔 설명을 호버 팝업에서 바로 읽을 수 있습니다.

그래서 주석을 잘 작성하는 것이 중요합니다. 위의 코드를 살펴보면, 호버 요청에는 파일 경로커서 위치가 포함됩니다.

언어 서버는 이 위치에 어떤 심볼(변수, 함수, 클래스 등)이 있는지 파악하고, 해당 심볼의 정보를 조회합니다. 그리고 마크다운 형식으로 응답을 구성합니다.

마크다운 덕분에 볼드, 코드 블록 등 서식이 적용된 깔끔한 팝업을 볼 수 있습니다. 김개발 씨는 이제 낯선 코드를 읽을 때 두려움이 줄었습니다.

모르는 것이 있으면 마우스만 올리면 되니까요. 다만 호버 정보가 부실한 경우도 있습니다.

타입 정보는 컴파일러가 자동으로 제공하지만, 문서화 주석은 개발자가 직접 작성해야 합니다. 그래서 자신이 작성하는 코드에 JSDoc 주석을 달아두는 습관이 중요합니다.

실전 팁

💡 - 함수나 클래스를 작성할 때 JSDoc 주석을 함께 작성하면 호버 정보가 풍부해집니다

  • 호버 정보가 너무 길면 읽기 불편하니, 문서는 핵심만 간결하게 작성하세요
  • Ctrl/Cmd를 누른 채 호버하면 더 자세한 정보가 표시되는 에디터도 있습니다

6. 커스텀 LSP 서버 설정

김개발 씨의 회사에서는 자체적으로 만든 DSL(Domain Specific Language)을 사용합니다. 이 언어를 위한 자동완성과 오류 검사가 있으면 좋겠다는 생각이 들었습니다.

표준 언어 서버는 없지만, 직접 만들거나 커스텀 서버를 연결할 수 있다면 어떨까요?

커스텀 LSP 서버 설정을 통해 기본 제공되지 않는 언어나 도구에 대한 LSP 지원을 추가할 수 있습니다. 서버 실행 경로, 통신 방식, 초기화 옵션 등을 직접 지정하면 에디터가 해당 서버와 연결됩니다.

회사 내부 도구, 새로운 언어, 특수한 lint 도구 등을 에디터에 통합할 때 유용합니다.

다음 코드를 살펴봅시다.

// 커스텀 LSP 서버 설정 예시
interface CustomServerConfig {
  name: string;                     // 서버 이름
  command: string;                  // 실행 명령어
  args?: string[];                  // 명령어 인자
  filePatterns: string[];           // 이 서버가 담당할 파일 패턴
  initializationOptions?: object;   // 초기화 시 전달할 옵션
}

const customServers: CustomServerConfig[] = [
  {
    name: "internal-dsl",
    command: "/path/to/company-dsl-server",
    args: ["--stdio", "--log-level=info"],
    filePatterns: ["*.cdsl", "*.companydsl"],
    initializationOptions: {
      enableExperimentalFeatures: true,
      customRules: "/path/to/rules.json"
    }
  }
];

// 설정 파일 예시 (settings.json 또는 전용 설정)
const editorSettings = {
  "languageServer.custom": customServers
};

모든 프로젝트가 JavaScript나 Python 같은 범용 언어만 사용하는 것은 아닙니다. 특정 도메인에 최적화된 DSL, 회사 내부에서만 사용하는 설정 파일 형식, 아직 LSP 지원이 없는 새로운 언어 등을 사용하는 경우도 많습니다.

김개발 씨의 회사도 그랬습니다. 자체 개발한 규칙 정의 언어가 있었는데, 에디터에서는 그냥 일반 텍스트 파일로 인식되었습니다.

문법 강조도 없고, 자동완성도 없고, 오류 표시도 없었습니다. "이 언어용 LSP 서버를 만들면 에디터에서도 편하게 쓸 수 있을 텐데..." 김개발 씨가 아이디어를 냈습니다.

박시니어 씨가 말했습니다. "실제로 그렇게 하는 팀들이 많아요.

LSP 서버만 구현하면 에디터 플러그인을 따로 만들 필요가 없으니까요." 커스텀 LSP 서버를 설정하는 것은 마치 새 앱을 설치하는 것과 비슷합니다. 앱 스토어에서 앱을 다운받으면 바로 사용할 수 있듯이, 이미 만들어진 LSP 서버가 있다면 설정만 추가하면 됩니다.

물론 앱 스토어에 없는 앱은 직접 개발해야 하듯이, LSP 서버도 없으면 직접 만들어야 합니다. 설정에 필요한 핵심 요소를 살펴봅시다.

command는 서버 실행 파일의 경로입니다. 절대 경로를 사용하거나, PATH에 등록된 명령어를 사용할 수 있습니다.

args는 서버에 전달할 명령줄 인자입니다. --stdio는 표준 입출력으로 통신한다는 의미입니다.

로그 레벨이나 설정 파일 경로 등을 지정할 수도 있습니다. filePatterns는 이 서버가 어떤 파일을 담당할지 정합니다.

*.cdsl 확장자를 가진 파일을 열면 이 서버가 활성화됩니다. initializationOptions는 서버 초기화 시 전달할 추가 설정입니다.

서버마다 지원하는 옵션이 다르므로, 해당 서버의 문서를 참고해야 합니다. 실제로 커스텀 서버를 연결하는 과정을 살펴봅시다.

먼저 LSP 서버를 설치하거나 빌드합니다. 서버가 정상적으로 실행되는지 터미널에서 직접 테스트해봅니다.

그 다음 에디터의 설정 파일에 서버 정보를 추가합니다. 에디터를 재시작하면 새 서버가 활성화됩니다.

김개발 씨는 팀과 함께 간단한 LSP 서버를 만들었습니다. 처음에는 문법 오류 검사만 지원했지만, 점차 자동완성과 호버 기능도 추가했습니다.

내부 언어를 사용하는 것이 훨씬 편해졌습니다. 모든 에디터에서 같은 서버를 사용할 수 있다는 것도 장점입니다.

VS Code를 쓰는 팀원도, Neovim을 쓰는 팀원도 같은 언어 지원 기능을 누릴 수 있습니다.

실전 팁

💡 - 커스텀 서버를 만들 때는 vscode-languageserver 라이브러리를 활용하면 빠르게 시작할 수 있습니다

  • 서버가 정상 작동하지 않으면 먼저 터미널에서 직접 실행하여 오류 메시지를 확인하세요
  • 서버 로그를 활성화하면 문제 해결에 도움이 됩니다

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#TypeScript#LSP#LanguageServer#Diagnostics#HoverProvider

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.

함께 보면 좋은 카드 뉴스

마이크로서비스 배포 완벽 가이드

Kubernetes를 활용한 마이크로서비스 배포의 핵심 개념부터 실전 운영까지, 초급 개발자도 쉽게 따라할 수 있는 완벽 가이드입니다. 실무에서 바로 적용 가능한 배포 전략과 노하우를 담았습니다.

Application Load Balancer 완벽 가이드

AWS의 Application Load Balancer를 처음 배우는 개발자를 위한 실전 가이드입니다. ALB 생성부터 ECS 연동, 헬스 체크, HTTPS 설정까지 실무에 필요한 모든 내용을 다룹니다. 초급 개발자도 쉽게 따라할 수 있도록 단계별로 설명합니다.

고객 상담 AI 시스템 완벽 구축 가이드

AWS Bedrock Agent와 Knowledge Base를 활용하여 실시간 고객 상담 AI 시스템을 구축하는 방법을 단계별로 학습합니다. RAG 기반 지식 검색부터 Guardrails 안전 장치, 프론트엔드 연동까지 실무에 바로 적용 가능한 완전한 시스템을 만들어봅니다.

에러 처리와 폴백 완벽 가이드

AWS API 호출 시 발생하는 에러를 처리하고 폴백 전략을 구현하는 방법을 다룹니다. ThrottlingException부터 서킷 브레이커 패턴까지, 실전에서 바로 활용할 수 있는 안정적인 에러 처리 기법을 배웁니다.

AWS Bedrock 인용과 출처 표시 완벽 가이드

AWS Bedrock의 Citation 기능을 활용하여 AI 응답의 신뢰도를 높이는 방법을 배웁니다. 출처 추출부터 UI 표시, 검증까지 실무에서 바로 사용할 수 있는 완전한 가이드입니다.