본문 바로가기
C.W.K.
Stream
Lesson 01 of 04 · published

nvim-cmp (또는 blink.cmp) 로 completion

~14 min · neovim, completion, lsp

Level 0갇힌 자
0 XP0/35 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

completion 엔진이 왜 필요하냐면

native LSP는 데이터를 줘. "여기 쓸 수 있는 심볼들이고, 시그니처는 이렇다"까지야. 그 데이터를 쓸 만한 팝업으로 바꿔주는 게 completion 엔진이고. 후보에 순위를 매기고, 스니펫을 펼치고, buffer 단어랑 파일 경로를 같이 섞고, Tab이 어떻게 동작할지를 정해줘. 2026년 Neovim에는 좋은 선택지가 둘 있어.

  • nvim-cmp — 오래된 기본값이야. 성숙했고, 생태계가 크고, source plugin도 많아.
  • blink.cmp — 더 새로워 (2024년 이후). Rust 백엔드에 더 빠르고 단순하게 설계했고, 기능 차이도 빠르게 좁히고 있어. 시작 속도가 중요하면 써볼 만해.

이 레슨은 nvim-cmp로 갈게. 한 번 잡아두고 잊고 싶다면 이쪽이 더 안전하거든. 나중에 blink.cmp로 옮기는 것도 어렵지 않아. spec 모양이 비슷해.

뭘 연결해야 하냐면

  • completion 엔진 본체.
  • source — LSP, buffer 단어, 파일 경로, 스니펫.
  • 스니펫을 펼쳐주는 엔진 (LuaSnip).
  • 팝업 안에서 쓸 키 (Tab으로 이동, Enter로 확정, Esc로 닫기).
  • 클라이언트가 뭘 지원하는지 LSP 서버에 알려주는 capabilities 객체. 스니펫 completion이나 자동 import 같은 걸 켜줘.

머릿속 그림

completion은 source마다 받은 후보를 정렬하고, 거르고, 순위 매겨 합친 결과야. nvim-cmp가 source마다 물어보고, 후보를 받고, 순위 규칙을 적용하고, 팝업을 띄워. source끼리는 서로 독립적이라 Markdown 파일에서는 buffer 단어를 끄고 Python 파일에서는 LSP를 위로 올리는 식으로 따로 조절할 수 있어.

LSP source를 먼저 믿어. buffer 단어 completion은 산문에는 좋지만 코드에서는 잡음이 많아. LSP가 주는 후보는 타입과 범위와 import를 알고 있거든. 소스 파일에서는 LSP를 buffer보다 위에 놓고, Markdown이나 commit message에서는 반대로 놔. nvim-cmp의 파일 종류별 설정이 이걸 담는 자리야.

Code

nvim-cmp 풀 셋업 — lazy.nvim spec에 붙여·lua
{
  "hrsh7th/nvim-cmp",
  event = "InsertEnter",
  dependencies = {
    "hrsh7th/cmp-nvim-lsp",     -- LSP source
    "hrsh7th/cmp-buffer",       -- buffer-단어 source
    "hrsh7th/cmp-path",         -- 파일시스템-경로 source
    "L3MON4D3/LuaSnip",         -- 스니펫 엔진
    "saadparwaiz1/cmp_luasnip", -- 스니펫 source
    "rafamadriz/friendly-snippets", -- 언어별 prebuilt 스니펫
  },
  config = function()
    local cmp = require("cmp")
    local luasnip = require("luasnip")
    require("luasnip.loaders.from_vscode").lazy_load()

    cmp.setup({
      snippet = {
        expand = function(args) luasnip.lsp_expand(args.body) end,
      },
      mapping = cmp.mapping.preset.insert({
        ["<C-b>"] = cmp.mapping.scroll_docs(-4),
        ["<C-f>"] = cmp.mapping.scroll_docs(4),
        ["<C-Space>"] = cmp.mapping.complete(),
        ["<C-e>"] = cmp.mapping.abort(),
        ["<CR>"]  = cmp.mapping.confirm({ select = true }),
        ["<Tab>"] = cmp.mapping(function(fallback)
          if cmp.visible() then
            cmp.select_next_item()
          elseif luasnip.expand_or_jumpable() then
            luasnip.expand_or_jump()
          else
            fallback()
          end
        end, { "i", "s" }),
        ["<S-Tab>"] = cmp.mapping(function(fallback)
          if cmp.visible() then
            cmp.select_prev_item()
          elseif luasnip.jumpable(-1) then
            luasnip.jump(-1)
          else
            fallback()
          end
        end, { "i", "s" }),
      }),
      sources = cmp.config.sources({
        { name = "nvim_lsp", priority = 1000 },
        { name = "luasnip",  priority = 750 },
      }, {
        { name = "buffer", priority = 500 },
        { name = "path",   priority = 250 },
      }),
    })
  end,
}
클라이언트가 뭐 지원하는지 LSP 서버에 알리기·lua
-- 한 번 붙이고 매 서버 config 에 재사용
local capabilities = require("cmp_nvim_lsp").default_capabilities()

vim.lsp.config("pyright", {
  cmd = { "pyright-langserver", "--stdio" },
  root_markers = { "pyproject.toml", "setup.py", ".git" },
  capabilities = capabilities,   -- 스니펫 completion 등 활성화
})
blink.cmp 대안 (더 빠르고, 더 단순)·lua
-- 현대 대안 시도하고 싶으면:
{
  "saghen/blink.cmp",
  event = "InsertEnter",
  version = "*",
  opts = {
    keymap = { preset = "default" },
    appearance = { use_nvim_cmp_as_default = true },
    sources = {
      default = { "lsp", "path", "snippets", "buffer" },
    },
  },
}

External links

Exercise

config에 nvim-cmp 설정과 capabilities 연결을 넣고 Neovim을 다시 켜. LSP 서버가 붙는 프로젝트에서 (:checkhealth vim.lsp로 확인) Python 파일이나 자기 언어 파일을 열어. 이름을 치기 시작하면 팝업이 떠야 해. Tab으로 옮겨 다니고, Enter로 확정하고, Esc로 닫아봐. 스니펫도 하나 펼쳐보고 (예를 들면 Python에서 main을 치고 펼치기) LuaSnip이 도는지 확인해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.