한 줄로
코드를 문자열이 아니라 문법으로 이해하는 도구다.
grep 은 비슷한 글자를 찾고, LSP 는 같은 것을 찾는다. 이 차이가 전부다.
문제 — save 를 찾아줘
같은 코드에서 save 를 검색한다고 하자.
user.save() // ← 진짜 그 함수
// 나중에 save 하기 ← 주석
imageSaved = true // ← 다른 변수
"saved ok" // ← 문자열
grep 은 4개를 찾는다. 전부 save 라는 글자가 들어 있으니 맞는 동작이다.
그런데 내가 원한 건 1개다.
LSP 는 1개만 찾는다. 나머지가 이름만 비슷한 남이라는 걸 알기 때문이다.
주석인지, 문자열인지, 다른 변수인지를 문법으로 구분한다.
이게 왜 중요한가. AI 가 코드를 고칠 때 “이 함수를 쓰는 곳을 전부 바꿔라” 같은 일을
한다. grep 기준으로 하면 주석과 문자열까지 바꾼다.
LSP 가 뭔가 — 원래는 사람용이다
Language Server Protocol. 직역하면 “언어 서버 규약” 이다.
VS Code 에서 함수 이름에 마우스를 올리면 설명이 뜨고, ⌘+클릭 하면 정의로 점프한다.
그걸 해주는 게 언어 서버다. 에디터 옆에 늘 켜져 있는 도우미 프로그램이다.
여기서 “규약(Protocol)” 이 핵심이다. 원래는 이런 문제가 있었다.
에디터 3개 × 언어 10개 = 30개를 각각 만들어야 함
VS Code 용 자바 지원, IntelliJ 용 자바 지원, Vim 용 자바 지원을 따로 만들었다.
마이크로소프트가 2016년에 이걸 표준 대화 규약으로 바꿨다.
에디터 3개 + 언어 10개 = 13개만 만들면 됨
에디터는 “정의가 어디야?” 라고 같은 형식으로 묻고, 언어 서버는 같은 형식으로 답한다.
서로의 내부를 몰라도 된다.
그리고 AI 도 이 규약에 그냥 끼어들 수 있다. 사람이 쓰던 걸 그대로 빌려 쓰는 것이다.
Claude 입장에서는 “에디터인 척” 하고 물어보면 된다.
무엇을 물어볼 수 있나
내 환경(OMC 플러그인)에는 LSP 관련 도구가 11개 붙어 있다. 대표적인 것들이다.
| 묻는 것 | 도구 |
|---|---|
| 이거 어디서 만들었어? | lsp_goto_definition |
| 누가 쓰고 있어? | lsp_find_references |
| 이름 바꿔줘 | lsp_rename |
| 이게 뭐야? (타입·설명) | lsp_hover |
| 에러 있어? | lsp_diagnostics |
| 이 파일 뭐 들었어? | lsp_document_symbols |
lsp_rename 이 특히 값지다. 이름 변경은 겉보기에 단순한데 실제로는 위험하다.
grep + 치환으로 하면 주석·문자열·동명이인까지 바꾼다. LSP 는 진짜 그 심볼만 바꾼다.
lsp_diagnostics 도 유용하다. 컴파일 안 돌리고 문법 오류를 안다. 빌드가 오래 걸리는
프로젝트에서 차이가 크다.
그런데 언어마다 따로 깔아야 한다
여기가 함정이다. LSP 는 하나가 아니다. 언어별로 서버가 따로 있다.
- 자바 →
jdtls - 타입스크립트 →
typescript-language-server - 파이썬 →
ty - 러스트 →
rust-analyzer
안 깔린 언어에서는 이 능력이 그냥 없다. AI 는 조용히 grep 으로 되돌아간다.
에러가 안 나므로 못 쓰고 있다는 사실조차 모른다.
내 환경을 실제로 조회해봤다
이 글을 쓰면서 확인해보고 좀 놀랐다.
설치됨(3): clangd(C/C++) · dart · sourcekit-lsp(Swift)
미설치(15): Java · TypeScript · Python · Rust · Go ...
내가 주로 쓰는 언어가 전부 빠져 있었다. 그리고 켜져 있는 셋은 잘 안 쓰는 언어다.
왜 이렇게 됐나 추적해보니 답이 나왔다. 직접 깐 게 아니라 딸려온 것들이었다.
| 서버 | 실제 출처 |
|---|---|
clangd, sourcekit-lsp
|
Xcode 명령줄 도구에 기본 포함 |
dart |
Flutter 설치 시 함께 |
근거는 경로에 있었다. 앞의 둘은 /usr/bin 아래 root 소유이고 설치 시각이 초 단위까지
같다. 개별로 깔면 이렇게 될 수 없다.
$ ls -la /usr/bin/clangd /usr/bin/sourcekit-lsp
-rwxr-xr-x root 13 Aug 11:51 /usr/bin/clangd
-rwxr-xr-x root 13 Aug 11:51 /usr/bin/sourcekit-lsp
Dart 는 더 명확했다. brew list | grep dart 의 결과가 flutter 였다. Dart 를 깐 게
아니라 Flutter 가 Dart 로 만들어져서 딸려온 것이다.
“내가 쓰는 언어” 가 아니라 “내 컴퓨터에 이미 있던 것” 이 켜져 있었다.
이유는 단순하다. 도구가 “PATH 에 있나?” 만 확인하기 때문이다. 누가 왜 깔았는지는
따지지 않는다. 그래서 우연히 있던 게 켜지고, 정작 필요한 건 꺼져 있는 상태가 된다.
고쳤다
주력 언어 넷을 깔았다. 3개 → 7개가 됐다.
npm install -g typescript-language-server typescript # TypeScript
brew install ty # Python
brew install jdtls # Java
brew install rust-analyzer # Rust
설치 후 도구로 다시 조회해 7개 전부 인식되는 것을 확인했다.
한 가지 알아둘 점이 있다. TypeScript 만 설치 위치가 다르다.
TypeScript → ~/.nvm/versions/node/v24.16.0/bin/ ← 노드 버전에 묶임
나머지 셋 → /opt/homebrew/bin/ ← 안전
npm -g 는 현재 노드 버전 아래에 깔린다. nvm 으로 노드를 바꾸면 TypeScript LSP 만
조용히 사라진다. 그때 다시 깔면 된다.
안 쓰는 건 지워도 되나 — 아니다
정리하고 싶어져서 따져봤는데, 지우지 않는 게 맞았다. 이유가 셋 다 달랐다.
첫째, 애초에 못 지운다. clangd 와 sourcekit-lsp 는 /usr/bin 아래 root 소유이고,
SIP(System Integrity Protection, 맥이 시스템 파일을 지키는 기능)가 켜져 있으면
sudo 를 써도 거부된다.
둘째, 얻는 게 없다. 용량을 재봤다.
| 대상 | 용량 |
|---|---|
clangd |
119 KB |
sourcekit-lsp |
119 KB |
둘 합쳐 240KB 다. 사진 한 장보다 작다.
셋째, 다른 게 깨진다. 이것들은 LSP 전용 프로그램이 아니라 Xcode 명령줄 도구의
일부다. 그 묶음에는 git 과 컴파일러가 같이 들어 있다. 건드리면 엉뚱한 게 멈춘다.
Dart 는 사정이 또 달랐다. Flutter 를 지우면 같이 사라지는데, 확인해보니
한 달 전까지 커밋한 회사 Flutter 프로젝트가 실재했다. 지웠으면 곤란해질 뻔했다.
켜져 있어도 비용은 없다
“안 쓰는 게 켜져 있으면 뭔가 돌고 있는 것 아닌가” 싶지만 아니다.
언어 서버는 해당 확장자 파일을 열 때만 실행된다. .swift 파일을 안 건드리면
sourcekit-lsp 는 평생 안 뜬다.
"설치됨" = 디스크에 파일이 있다 ← 비용 0
"실행중" = 메모리·CPU 사용 ← 그 언어 파일 열 때만
목록의 “설치됨” 은 “필요하면 쓸 수 있다” 는 뜻이지 “지금 돌고 있다” 가 아니다.
정리
- LSP 는 코드를 글자가 아니라 문법으로 이해한다.
grep은 비슷한 글자, LSP 는 같은 것 - 원래 사람용 에디터 기능이다. 규약이라 AI 도 그대로 끼어들 수 있다
- 에디터×언어 조합을 덧셈으로 바꾼 게 이 규약의 발명이다
-
언어마다 따로 깔아야 한다. 안 깔리면 조용히
grep으로 되돌아간다 - ⚠️ 도구는 “PATH 에 있나” 만 본다. 그래서 우연히 딸려온 것이 켜지고 주력 언어는
꺼져 있는 상태가 되기 쉽다. 한 번 확인해볼 값어치가 있다 - 안 쓰는 건 지우지 마라 — 못 지우거나(SIP), 실익이 없거나(240KB), 다른 게 딸려 나간다
- 켜져 있어도 평소엔 안 돈다. 그 언어 파일을 열 때만 뜬다
같이 읽을 글:
MCP 12개, 도구 251개 ·
OMC 란 ·
Claude Code 설정을 맥 4대에 나눠 담기