
1편에서 init.lua 구조, Lua 문법, 키매핑, autocmd를 다뤘습니다. 이번 편에서는 플러그인을 어떻게 관리하는지, LSP를 어떻게 붙이는지를 정리합니다.
솔직히 말하면 이 부분이 Neovim 설정에서 가장 헷갈리는 구간이기도 합니다. 플러그인 매니저도 여러 가지고, LSP 세팅도 컴포넌트가 몇 개 얽혀 있어서 처음 보면 뭐가 뭔지 파악이 잘 안 됩니다. 하나씩 역할을 정리하고 나면 그 뒤로는 패턴이 반복됩니다.
플러그인 매니저 — lazy.nvim
Neovim 플러그인 매니저로는 vim-plug, packer.nvim 등이 있었는데, 현재는 lazy.nvim이 사실상 표준처럼 쓰입니다. 이름처럼 플러그인을 필요한 시점에 로드하는 지연 로딩(lazy loading)이 기본이라 시작 속도가 빠릅니다.
설치
lazy.nvim은 별도 설치 없이 init.lua에 아래 코드를 넣으면 처음 실행할 때 자동으로 설치됩니다.
-- init.lua 상단에 넣기
local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim'
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({
'git', 'clone',
'--filter=blob:none',
'https://github.com/folke/lazy.nvim.git',
'--branch=stable',
lazypath,
})
end
vim.opt.rtp:prepend(lazypath)
이 코드는 lazy.nvim이 없으면 git clone으로 받아오고, 있으면 그냥 경로에 추가합니다. 한 번 설치된 뒤로는 git 부분은 실행되지 않습니다.
기본 사용법
require('lazy').setup({
-- 여기에 플러그인 목록 작성
'nvim-lua/plenary.nvim', -- 단순 설치 (GitHub user/repo 형식)
{ 'folke/which-key.nvim' }, -- 테이블 형태로도 가능
{
'nvim-treesitter/nvim-treesitter',
build = ':TSUpdate', -- 설치 후 실행할 명령
config = function() -- 설치 후 설정
require('nvim-treesitter.configs').setup({
highlight = { enable = true },
indent = { enable = true },
})
end,
},
})
자주 쓰는 옵션들
{
'plugin/name',
lazy = true, -- 명시적으로 지연 로딩 설정
event = 'BufReadPre', -- 이 이벤트 발생 시 로드
cmd = 'SomeCommand', -- 이 명령어 실행 시 로드
ft = 'lua', -- 이 파일 타입에서만 로드
keys = { -- 이 키를 누를 때 로드
{ '<leader>f', '<cmd>Telescope find_files<CR>', desc = '파일 찾기' }
},
dependencies = { -- 먼저 로드해야 할 의존 플러그인
'nvim-lua/plenary.nvim',
},
}
설치된 플러그인 목록을 보거나 업데이트할 때는 :Lazy 명령어로 UI를 엽니다.
파일 구조 — 플러그인을 파일로 나누기
플러그인이 많아지면 init.lua에 전부 쓰기 불편합니다. lazy.nvim은 lua/plugins/ 디렉토리 안의 파일들을 자동으로 불러오는 방식을 지원합니다.
-- init.lua
require('lazy').setup('plugins') -- lua/plugins/ 디렉토리 전체 로드
~/.config/nvim/
├── init.lua
└── lua/
├── config/
│ ├── options.lua
│ └── keymaps.lua
└── plugins/
├── treesitter.lua
├── telescope.lua
└── lsp.lua ← 플러그인 파일마다 테이블 반환
각 파일은 플러그인 스펙 테이블을 반환하면 됩니다.
-- lua/plugins/treesitter.lua
return {
'nvim-treesitter/nvim-treesitter',
build = ':TSUpdate',
config = function()
require('nvim-treesitter.configs').setup({
highlight = { enable = true },
indent = { enable = true },
ensure_installed = { 'lua', 'python', 'javascript', 'bash' },
})
end,
}
LSP — 구성 요소 먼저 파악하기
LSP(Language Server Protocol)는 자동완성, 정의로 이동, 에러 표시 같은 기능을 제공하는 프로토콜입니다. Neovim에서 LSP를 쓰려면 컴포넌트가 몇 개 필요합니다.
| 컴포넌트 | 역할 |
|---|---|
| mason.nvim | LSP 서버, 포매터, 린터를 설치/관리하는 패키지 매니저 |
| mason-lspconfig.nvim | mason과 nvim-lspconfig 사이를 연결 |
| nvim-lspconfig | 각 언어 서버의 설정을 Neovim에 연결 |
간단히 말하면, mason이 언어 서버를 설치하고, lspconfig가 그걸 Neovim에 연결해줍니다. mason-lspconfig는 그 사이 연결고리입니다.
설치
-- lua/plugins/lsp.lua
return {
{
'williamboman/mason.nvim',
config = function()
require('mason').setup()
end,
},
{
'williamboman/mason-lspconfig.nvim',
dependencies = { 'williamboman/mason.nvim' },
config = function()
require('mason-lspconfig').setup({
-- 자동으로 설치할 언어 서버 목록
ensure_installed = {
'lua_ls', -- Lua
'pyright', -- Python
'ts_ls', -- TypeScript/JavaScript
'bashls', -- Bash
'clangd', -- C/C++
},
})
end,
},
{
'neovim/nvim-lspconfig',
dependencies = { 'williamboman/mason-lspconfig.nvim' },
config = function()
local lspconfig = require('lspconfig')
local capabilities = require('cmp_nvim_lsp').default_capabilities()
-- 공통 on_attach (서버 연결 시 실행할 함수)
local on_attach = function(_, bufnr)
local map = vim.keymap.set
local opts = { buffer = bufnr, noremap = true, silent = true }
map('n', 'gd', vim.lsp.buf.definition, opts) -- 정의로 이동
map('n', 'gr', vim.lsp.buf.references, opts) -- 참조 찾기
map('n', 'K', vim.lsp.buf.hover, opts) -- 문서 호버
map('n', '<leader>rn', vim.lsp.buf.rename, opts) -- 이름 변경
map('n', '<leader>ca', vim.lsp.buf.code_action, opts) -- 코드 액션
map('n', '<leader>e', vim.diagnostic.open_float, opts) -- 에러 상세
map('n', '[d', vim.diagnostic.goto_prev, opts) -- 이전 에러
map('n', ']d', vim.diagnostic.goto_next, opts) -- 다음 에러
end
-- 서버별 설정 (공통 설정 + 언어별 추가 옵션)
local servers = {
lua_ls = {
settings = {
Lua = {
diagnostics = { globals = { 'vim' } }, -- vim 전역 변수 경고 끄기
},
},
},
pyright = {},
ts_ls = {},
bashls = {},
clangd = {},
}
for server, config in pairs(servers) do
lspconfig[server].setup(vim.tbl_deep_extend('force', {
on_attach = on_attach,
capabilities = capabilities,
}, config))
end
end,
},
}
:Mason 명령어로 UI를 열면 설치된 언어 서버 목록과 설치/업데이트를 할 수 있습니다.
자동완성 — nvim-cmp
LSP 자동완성을 화면에 보여주려면 별도의 자동완성 플러그인이 필요합니다. nvim-cmp가 가장 많이 쓰입니다.
-- lua/plugins/cmp.lua
return {
'hrsh7th/nvim-cmp',
dependencies = {
'hrsh7th/cmp-nvim-lsp', -- LSP 자동완성 소스
'hrsh7th/cmp-buffer', -- 버퍼 단어 자동완성
'hrsh7th/cmp-path', -- 파일 경로 자동완성
'L3MON4D3/LuaSnip', -- 스니펫 엔진
'saadparwaiz1/cmp_luasnip',
},
config = function()
local cmp = require('cmp')
local luasnip = require('luasnip')
cmp.setup({
snippet = {
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},
mapping = cmp.mapping.preset.insert({
['<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' }),
}),
sources = cmp.config.sources({
{ name = 'nvim_lsp' },
{ name = 'luasnip' },
{ name = 'buffer' },
{ name = 'path' },
}),
})
end,
}
알아두면 편한 플러그인들
LSP와 자동완성 외에 자주 쓰이는 플러그인들입니다.
telescope.nvim — 퍼지 파인더
파일 검색, 텍스트 검색, 버퍼 전환 등을 퍼지 검색으로 처리합니다.
return {
'nvim-telescope/telescope.nvim',
dependencies = { 'nvim-lua/plenary.nvim' },
config = function()
local builtin = require('telescope.builtin')
vim.keymap.set('n', '<leader>ff', builtin.find_files, { desc = '파일 찾기' })
vim.keymap.set('n', '<leader>fg', builtin.live_grep, { desc = '텍스트 검색' })
vim.keymap.set('n', '<leader>fb', builtin.buffers, { desc = '버퍼 목록' })
end,
}
neo-tree.nvim — 파일 탐색기
return {
'nvim-neo-tree/neo-tree.nvim',
dependencies = {
'nvim-lua/plenary.nvim',
'nvim-tree/nvim-web-devicons',
'MunifTanjim/nui.nvim',
},
config = function()
vim.keymap.set('n', '<leader>e', ':Neotree toggle<CR>', { silent = true })
end,
}
conform.nvim — 포매터
저장 시 자동으로 코드를 포맷합니다.
return {
'stevearc/conform.nvim',
config = function()
require('conform').setup({
formatters_by_ft = {
lua = { 'stylua' },
python = { 'black' },
javascript = { 'prettier' },
typescript = { 'prettier' },
},
format_on_save = {
timeout_ms = 500,
lsp_fallback = true,
},
})
end,
}
포매터 자체는 mason으로 설치합니다. ensure_installed에 'stylua', 'black', 'prettier' 같은 걸 추가하면 됩니다.
lualine.nvim — 상태바
return {
'nvim-lualine/lualine.nvim',
dependencies = { 'nvim-tree/nvim-web-devicons' },
config = function()
require('lualine').setup({
options = { theme = 'auto' },
})
end,
}
진단(Diagnostic) 표시 설정
LSP 에러나 경고를 어떻게 표시할지 설정합니다.
vim.diagnostic.config({
virtual_text = true, -- 줄 끝에 인라인으로 표시
signs = true, -- 좌측 기호 열에 표시
underline = true, -- 밑줄 표시
update_in_insert = false, -- Insert 모드에서는 갱신 안 함
severity_sort = true, -- 심각도 순으로 정렬
float = {
border = 'rounded',
source = 'always', -- 어떤 서버에서 온 에러인지 표시
},
})
정리 표
| 분류 | 플러그인 | 역할 |
|---|---|---|
| 플러그인 매니저 | lazy.nvim | 플러그인 설치/관리/지연 로딩 |
| LSP 설치 | mason.nvim | 언어 서버, 포매터, 린터 설치 |
| LSP 연결 | nvim-lspconfig | 언어 서버를 Neovim에 연결 |
| LSP 브릿지 | mason-lspconfig | mason ↔ lspconfig 연결 |
| 자동완성 | nvim-cmp | 자동완성 UI |
| 하이라이팅 | nvim-treesitter | 문법 기반 구문 강조 |
| 퍼지 검색 | telescope.nvim | 파일/텍스트/버퍼 검색 |
| 파일 탐색 | neo-tree.nvim | 파일 탐색기 |
| 포매팅 | conform.nvim | 저장 시 자동 포맷 |
| 상태바 | lualine.nvim | 하단 상태바 |
자주 쓰는 LSP 키맵 요약
| 키 | 동작 |
|---|---|
gd |
정의로 이동 |
gr |
참조 목록 |
K |
문서 호버 (타입/설명) |
<leader>rn |
이름 일괄 변경 |
<leader>ca |
코드 액션 |
[d / ]d |
이전 / 다음 진단 |
<leader>e |
진단 상세 팝업 |
'블로그, 컴퓨터 > Cheatsheets' 카테고리의 다른 글
| Git 명령어 정리 (2편) — 브랜치, 원격, 되돌리기, stash (0) | 2026.05.26 |
|---|---|
| Git 명령어 정리 (1편) — 기본 개념, 설정, 커밋, 로그 (0) | 2026.05.26 |
| Neovim 설정 정리 (1편) — init.lua 구조와 Lua 기초 (0) | 2026.05.25 |
| Vim 명령어 정리 (2편) — 검색/치환, Visual 모드, 분할 화면, 설정 (0) | 2026.05.24 |
| Vim 명령어 정리 (1편) — 모드, 이동, 편집, 저장 (0) | 2026.05.24 |