본문으로 건너뛰기

"lazy.nvim" 태그로 연결된 3개 게시물개의 게시물이 있습니다.

모든 태그 보기

LazyVim 전환기

· 약 9분

이 글은 Neovim 시리즈의 다섯 번째 글입니다.

  1. Neovim 입문: Vim을 넘어서는 첫걸음
  2. Neovim 중급: 생산성을 높이는 기능들
  3. Neovim 고급: 플러그인과 LSP로 IDE처럼 쓰기
  4. 나의 Neovim 설정 전체 공개
  5. LazyVim distro로 갈아타기 ← 현재 글

함께 읽기: LazyVim 키맵 치트시트

들어가며

플러그인 23개를 직접 큐레이션해서 1년 가까이 굴렸지만, 결국 LazyVim distro로 갈아탔어요. 플러그인 수는 23개에서 42개로 늘었지만 제가 직접 관리해야 하는 파일은 15개에서 5개로 줄었고, 이 비대칭이 distro의 핵심이에요.

직전 셋업 공개 글에서 정리했던 lazy.nvim 수동 구성은 잘 동작했습니다. 다만 새 언어 LSP 하나를 붙일 때마다 mason 설정, conform 포매터, nvim-cmp 소스를 손으로 동기화해야 했고, 새 머신에 그대로 옮길 때마다 의존성이 한두 개씩 빠졌습니다. distro는 그 동기화를 일괄로 위임하고, 제 영역은 "취향 차이가 나는 부분"에만 남기는 구조입니다.

이 글은 LazyVim이라는 distro가 무엇이고, 수동 셋업에서 어떤 흐름으로 옮겼고, 어디에 백업을 두고 어떻게 롤백할 수 있는지를 정리합니다.

distro란 무엇인가

Neovim distro는 lazy.nvim, LSP, treesitter, 자동완성, UI 같은 표준 기반을 한 묶음으로 깔아주고, 사용자는 그 위에 얇게 커스텀만 얹는 셋업 패턴입니다. 직접 lua 파일을 한 줄 한 줄 짜는 대신, 프레임워크가 정해 둔 슬롯에 옵션이나 추가 spec만 끼워 넣으면 됩니다.

distro분위기
LazyVim가장 표준적/미니멀. lazy.nvim 작성자가 직접 운영. 본 글 대상
LunarVim자체 CLI(lvim)를 가진 비교적 무거운 셋업
NvChadUI/테마 중심, 화려한 시작 화면이 특징
AstroNvim대시보드, 아이콘, 통계 정보가 많은 풍성한 셋업

LazyVim을 고른 이유는 단순합니다. 이미 쓰던 lazy.nvim 위에 그대로 얹히고, 빠지더라도 ~/.config/nvim을 통째로 갈아엎지 않아도 되는 구조이기 때문입니다. distro 자체가 lazy 플러그인 하나처럼 동작합니다.

직전 상태

수동 lazy.nvim으로 플러그인 23개를 구성한 상태였습니다.

자세한 구성은 나의 Neovim 설정 전체 공개 글에 있습니다. 요약하면:

  • init.lua 1줄 → lua/config/에서 globals/options/keymaps 분리 로드
  • lua/plugins/ 아래 플러그인별 파일 15개 (telescope, neo-tree, lsp, cmp, treesitter, conform, kanagawa 등)
  • 자체 작성한 keyMapper 유틸로 noremap=true silent=true 일괄 적용
  • 테마는 Kanagawa Dragon (저채도 다크)

문제는 없었습니다. 단지 새 언어 추가가 매번 작은 의식이었습니다. Go를 붙이려면 mason.nvim에 gopls를 추가하고, conform.nvim에 gofmt를 등록하고, nvim-lspconfig 호출 라인을 늘리고, treesitter parser 목록도 손봐야 했습니다. 그렇게 해 둔 구성을 새 노트북에 옮기면 :checkhealth에 빨간 줄이 한두 개 떴습니다.

LazyVim이 자동으로 가져오는 것

LazyVim 15.x를 깔면 다음이 한 번에 따라옵니다.

영역들어오는 것
플러그인 매니저lazy.nvim
파일 탐색/검색snacks.picker, snacks.dashboard, neo-tree
키 안내which-key
LSP, 포매터, 린터nvim-lspconfig + mason 자동 설치
자동완성blink.cmp (또는 nvim-cmp 옵션)
구문 파싱nvim-treesitter
Gitgitsigns, lazygit 연동
UIbufferline, lualine, noice, mini.icons
포매팅conform.nvim + format-on-save

여기에 제가 켠 LazyExtras 6종(go, typescript, markdown, json, yaml, python)이 마저 붙습니다. 합치면 42개입니다. 직접 관리할 필요는 없고, 동작에 손대고 싶으면 같은 spec을 opts만 덮어씌우는 식으로 가볍게 끼어듭니다.

제가 짠 파일은 5개만 남았습니다. 나머지는 distro 본체가 가져간 책임입니다.

백업과 롤백 안전망

distro 전환은 비가역 작업이 아니어야 합니다. Neovim의 상태는 네 군데에 흩어져 있어서, 그걸 다 같이 빼두지 않으면 깔끔한 롤백이 어렵습니다.

기존 nvim 상태 4종을 .backup-2026-05-07 suffix로 옮긴 뒤 새 LazyVim을 깔았습니다.

원본백업
~/.config/nvim~/.config/nvim.backup-2026-05-07
~/.local/share/nvim (164MB, 컴파일된 플러그인)~/.local/share/nvim.backup-2026-05-07
~/.local/state/nvim~/.local/state/nvim.backup-2026-05-07
~/.cache/nvim~/.cache/nvim.backup-2026-05-07

문제 생겼을 때 롤백은 4쌍을 역순으로 mv하면 됩니다. 새 LazyVim을 같은 suffix로 옮긴 뒤 백업을 원래 자리로:

DATE=2026-05-07
mv ~/.config/nvim ~/.config/nvim.lazyvim-$DATE
mv ~/.local/share/nvim ~/.local/share/nvim.lazyvim-$DATE
mv ~/.local/state/nvim ~/.local/state/nvim.lazyvim-$DATE
mv ~/.cache/nvim ~/.cache/nvim.lazyvim-$DATE

mv ~/.config/nvim.backup-$DATE ~/.config/nvim
mv ~/.local/share/nvim.backup-$DATE ~/.local/share/nvim
mv ~/.local/state/nvim.backup-$DATE ~/.local/state/nvim
mv ~/.cache/nvim.backup-$DATE ~/.cache/nvim

이 안전망이 있어야 distro 전환을 부담 없이 시도해볼 수 있습니다.

새 디렉토리 구조

~/.config/nvim/
├── init.lua # LazyVim 부트스트랩 (starter 그대로)
├── lua/
│ ├── config/
│ │ ├── options.lua # 사용자 추가: scrolloff=10
│ │ ├── keymaps.lua # 사용자 추가: <leader>h, <C-;>
│ │ ├── autocmds.lua # 사용자 추가: 마크다운 wrap/linebreak/spell=false 등
│ │ └── lazy.lua # LazyVim 부트스트랩 (수정 X)
│ └── plugins/
│ ├── colorscheme.lua # 신규: Solarized + Everforest + Gruvbox Material
│ ├── markdown.lua # 신규: render-markdown.nvim 옵션 강화
│ ├── ufo.lua # 신규: nvim-ufo (LSP 폴딩)
│ ├── floaterm.lua # 신규: vim-floaterm + <C-;>
│ ├── lang-extras.lua # 신규: LazyExtras 6종 일괄 import
│ └── example.lua # starter 동봉 예제 (`if true then return {} end`로 비활성)
├── lazyvim.json # LazyVim 자동 생성
├── lazy-lock.json # lazy.nvim이 잠금 관리
└── stylua.toml # starter 동봉 (lua 포매터 설정)

config/는 LazyVim의 starter 템플릿이 깔아준 그대로입니다. 사용자 영역은 plugins/ 아래 5개 파일과 config/의 keymaps/options/autocmds 추가분이며, 그게 전부입니다.

커스텀 5개 플러그인 파일

distro가 가져가지 않은 "제 취향"은 다섯 갈래로 정리됐습니다.

lua/plugins/colorscheme.lua

테마 후보 3종을 동시에 등록했습니다.

직전엔 Kanagawa Dragon 단일 테마였는데, 마크다운 작성 비중이 늘면서 헤딩 위계와 코드블록 배경이 더 또렷한 테마가 필요해졌습니다. 즉시 비교를 위해 셋을 같이 등록했습니다.

  • maxmx03/solarized.nvim (variant=winter) — 기본
  • neanias/everforest-nvim (background=soft)
  • sainnhe/gruvbox-material (background=medium)

런타임에 :colorscheme everforest처럼 즉시 갈아끼울 수 있고, 영구화는 LazyVim 기본 colorscheme 라인을 바꾸면 됩니다. UI에서 라이브 프리뷰로 고르고 싶으면 <Space>uC.

lua/plugins/markdown.lua

render-markdown.nvim 옵션을 강화했습니다.

LazyExtras lang.markdown이 이미 render-markdown.nvim의 spec을 등록합니다. 거기에 opts만 덮어씌워 가독성 옵션을 강화했습니다.

return {
{
"MeanderingProgrammer/render-markdown.nvim",
opts = {
heading = {
sign = false,
icons = { "◉ ", "○ ", "✸ ", "✿ ", "✤ ", "✜ " },
backgrounds = {
"RenderMarkdownH1Bg", "RenderMarkdownH2Bg",
"RenderMarkdownH3Bg", "RenderMarkdownH4Bg",
"RenderMarkdownH5Bg", "RenderMarkdownH6Bg",
},
},
code = {
style = "full",
position = "left",
width = "block",
left_pad = 2,
},
quote = { icon = "┃" },
bullet = { icons = { "●", "○", "◆", "◇" } },
checkbox = {
unchecked = { icon = "󰄱 " },
checked = { icon = "󰱒 " },
},
pipe_table = { style = "full", alignment_indicator = "━" },
link = { image = "󰥶 ", hyperlink = "󰌹 " },
},
},
}
옵션효과
heading.iconsH1~H6 아이콘으로 위계 구분
heading.backgrounds헤딩 줄 전체 배경 틴트 (테마의 RenderMarkdownH* highlight 그룹 위임)
code.style = "full"코드블록 전체 배경 + 좌측 언어 라벨
code.width = "block"코드블록 너비를 본문보다 좁게 (들여쓰기 효과)
quote.icon인용문 좌측 세로바
bullet.icons리스트 깊이별 글리프 (● ○ ◆ ◇)
checkbox[ ]/[x]를 Nerd Font 글리프로
pipe_table.style = "full"표 경계 전체 표시

색상은 테마의 highlight 그룹에 위임했기 때문에 colorscheme를 바꿔도 헤딩 틴트가 자동으로 따라옵니다.

lua/plugins/ufo.lua

LSP 인지 폴딩을 설정했습니다.

nvim-ufo를 사용합니다. foldcolumn=0, foldlevel=99로 기본은 모두 펼친 상태로 두고, 필요할 때만 zc/zo로 접고 폅니다.

lua/plugins/floaterm.lua

<C-;>로 토글하도록 설정했습니다.

LazyVim 기본 <C-/> snacks.terminal과 별개로, 손에 익은 floaterm을 같이 붙였습니다. <C-;>로 토글하며 둘은 충돌하지 않고 병존합니다.

lua/plugins/lang-extras.lua

LazyExtras를 일괄 활성화했습니다.

return {
{ import = "lazyvim.plugins.extras.lang.go" },
{ import = "lazyvim.plugins.extras.lang.typescript" },
{ import = "lazyvim.plugins.extras.lang.markdown" },
{ import = "lazyvim.plugins.extras.lang.json" },
{ import = "lazyvim.plugins.extras.lang.yaml" },
{ import = "lazyvim.plugins.extras.lang.python" },
}

이 한 파일이 mason에게 marksman, gopls, ts_ls, pyright, ruff, jsonls, yamlls를 자동 설치하도록 위임합니다. 직전 셋업에서 손으로 동기화하던 그 영역입니다.

사용자 영역 추가

keymaps (config/keymaps.lua)

LazyVim 기본 키맵으로 이미 커버되는 건 이식하지 않았습니다. 기존에 손에 박힌 두 개만 살렸습니다.

동작
<leader>hsearch highlight 끄기
<C-;>floaterm 토글

이미 LazyVim이 잡아주는 것: <leader>e neo-tree, <C-h/j/k/l> 패인 이동, < > visual indent stay, <leader>ff 파일 찾기 등. 전체는 LazyVim 키맵 치트시트 참고합니다.

마크다운 filetype 동작 (config/autocmds.lua)

vim.api.nvim_create_autocmd("FileType", {
pattern = "markdown",
callback = function()
vim.opt_local.wrap = true
vim.opt_local.linebreak = true
vim.opt_local.conceallevel = 2
vim.opt_local.spell = false
vim.opt_local.cursorline = true
vim.opt_local.signcolumn = "no"
end,
})
옵션효과
wrap = true한글 긴 줄 자동 래핑
linebreak = true단어 경계에서만 래핑 (어절 중간 잘림 방지)
conceallevel = 2마크업 문자(**, # 등) 숨김 (실제로는 render-markdown이 3으로 덮어씀 — 더 강한 숨김. 의도된 동작)
spell = false한글 문서에서 영어 spell 체크 끔
cursorline = true현재 줄 강조 (긴 문서 위치 추적)
signcolumn = "no"좌측 여백 줄여 본문 폭 확보

클립보드는 별도 설정 없음

비주얼 모드에서 y만 눌러도 macOS 시스템 클립보드로 복사됩니다. LazyVim 15.x가 clipboard=unnamedplus를 기본값으로 잡아주기 때문입니다.

직전 수동 셋업에서는 제 setup 글에 적었듯 opt.clipboard = "unnamedplus" 한 줄을 직접 넣어야 했는데, distro 전환 후에는 이 줄도 지웠습니다. + 레지스터(시스템 클립보드)와 무명 레지스터(")가 자동 연결돼서 y/p가 양방향 동기화됩니다.

확인:

:set clipboard?

clipboard=unnamedplus 출력되면 OK.

provider 레벨 진단:

:checkhealth provider

또는 직접:

:let @+ = 'test'

→ 다른 앱에서 Cmd+V 했을 때 test가 붙으면 provider 정상.

tmux 안에서만 안 될 때는 ~/.tmux.conf에 OSC 52 전달 설정이 필요할 수 있습니다.

set -g set-clipboard on

macOS 로컬 tmux + pbcopy 조합은 보통 이 설정 없이도 동작합니다. SSH 원격 nvim에서 OSC 52 핸들링이 깨지는 환경이라면 LazyVim 기본값을 의도적으로 끄고 싶을 수 있습니다 — options.luavim.opt.clipboard = "".

의존성 (brew 설치)

LazyVim 본체와 무관하게 PATH에 있어야 하는 외부 도구입니다.

도구용도
ripgrep빠른 grep (snacks.picker <leader>sg)
fd빠른 파일 찾기 (snacks.picker <leader>ff)
lazygit<leader>gg git TUI
node, npmLSP 서버들 (ts_ls, yamlls 등)
python3pynvim, mason 도구

mason이 자동 설치하는 도구는 처음 해당 언어 파일을 열 때 받아옵니다: marksman, gopls, ts_ls, pyright, ruff, jsonls, yamlls, lua_ls, markdownlint-cli2, prettier, stylua, gofumpt, goimports, golangci-lint, shfmt 등입니다.

검증 결과

항목결과
Lazy sync통과 (42개 플러그인 설치)
세 colorscheme 로드통과 (solarized, everforest, gruvbox-material)
마크다운 autocmd 적용통과 (ft=markdown wrap=true cole=3 spell=false)
nvim startup 에러없음

전부 첫 실행에 떨어졌습니다. 백업해 둔 .backup-2026-05-07은 한 달 정도 묵혀 두고, 회귀가 없으면 정리할 예정입니다.

사용 팁

  • <Space> 누르고 1초 대기 → which-key가 카테고리별 메뉴를 띄웁니다. 가장 빠른 키맵 학습 방법
  • :LazyExtras → 추가 언어팩 ON/OFF UI
  • :Mason → LSP/포매터/린터 설치 상태
  • :LazyHealth, :checkhealth → 의존성/문제 진단
  • :colorscheme <name>으로 테마 즉시 전환. 영구화는 lua/plugins/colorscheme.lua 마지막 블록의 colorscheme = "..." 변경
  • :RenderMarkdown toggle — 렌더링을 잠깐 끄고 원본 마크다운(**bold**, # H1)을 보고 싶을 때

정리

수동 lazy.nvim 셋업은 잘 동작했고, 그 자체로 충분히 좋았어요. 다만 "제가 직접 관리해야 하는 것"의 표면적이 넓을수록 새 머신/새 언어를 붙일 때마다 작은 의식이 늘어났습니다. distro는 그 표면적을 줄여줍니다. 자동으로 처리될 수 있는 영역(LSP 자동완성, 포매팅, 치트시트, Git UI)을 일괄로 위임하고, 사용자는 "취향 차이가 나는 부분"에만 집중합니다.

LazyVim의 좋은 점은 lazy.nvim 위에 그대로 얹혀 있다는 점입니다. 빠지더라도 ~/.config/nvim을 통째로 갈아엎지 않아도 되고, 위 백업 절차로 언제든 직전 상태로 돌아올 수 있습니다. 그 안전망 위에서 가볍게 시도해볼 만한 전환이었어요.

키맵은 별도 글로 분리했어요: LazyVim 키맵 치트시트.

참고

나의 Neovim 설정 전체 공개

· 약 5분

이 글은 Neovim 시리즈의 마지막 글이에요.

  1. Neovim 입문: Vim을 넘어서는 첫걸음
  2. Neovim 중급: 생산성을 높이는 기능들
  3. Neovim 고급: 플러그인과 LSP로 IDE처럼 쓰기
  4. 나의 Neovim 설정 전체 공개 ← 현재 글

설정 철학

설정은 Lua 기반으로 VimScript 대신 Lua로 모두 작성하고, 모듈화를 위해 기능별로 파일을 분리해서 관리합니다. 최소주의에 따라 꼭 필요한 플러그인만 23개로 유지하며, 일관된 키매핑을 위해 커스텀 keyMapper 유틸리티로 통일했습니다.

디렉토리 구조

~/.config/nvim/
├── init.lua # 진입점 (1줄)
├── lazy-lock.json # 플러그인 버전 고정
└── lua/
├── config/
│ ├── init.lua # config 모듈 진입점
│ ├── globals.lua # 전역 변수 (leader 키 등)
│ ├── options.lua # Neovim 옵션
│ └── keymaps.lua # 글로벌 키매핑
├── plugins/
│ ├── alpha.lua # 시작 화면
│ ├── comment.lua # 주석 토글
│ ├── conform.lua # 코드 포매팅
│ ├── indent-blankline.lua # 들여쓰기 가이드
│ ├── kanagawa.lua # 컬러스킴
│ ├── lsp.lua # LSP 설정
│ ├── lualine.lua # 상태줄
│ ├── neo-tree.lua # 파일 탐색기
│ ├── nvim-autopairs.lua # 자동 괄호
│ ├── nvim-cmp.lua # 자동완성
│ ├── nvim-treesitter.lua # 구문 하이라이팅
│ ├── nvim-ufo.lua # 코드 폴딩
│ ├── render-markdown.lua # 마크다운 렌더링
│ ├── telescope.lua # 퍼지 파인더
│ └── vim-floaterm.lua # 플로팅 터미널
└── utils/
└── keyMapper.lua # 키매핑 헬퍼

핵심은 init.lua가 단 1줄이라는 점입니다.

require("config")

config/init.lua에서 globals, options, keymaps, lazy.nvim 순서로 로드합니다.

핵심 옵션

-- lua/config/options.lua
opt = vim.opt

-- 2칸 탭
opt.tabstop = 2
opt.shiftwidth = 2
opt.softtabstop = 2
opt.expandtab = true
opt.smartindent = true
opt.wrap = false

-- 검색
opt.incsearch = true
opt.ignorecase = true
opt.smartcase = true -- 대문자가 포함되면 대소문자 구분

-- 줄 번호
opt.number = true
opt.relativenumber = true -- 상대 줄 번호 (이동에 유용)

-- 기타
opt.termguicolors = true
opt.signcolumn = "yes"
opt.scrolloff = 10 -- 커서 위아래 10줄 여유
opt.mouse:append("a")

-- 클립보드
opt.clipboard = "unnamedplus" -- y/p가 시스템 클립보드와 자동 동기화

-- 마크다운 전용 설정
vim.api.nvim_create_autocmd("FileType", {
pattern = "markdown",
callback = function()
vim.opt_local.wrap = true -- 줄 바꿈 활성화
vim.opt_local.linebreak = true -- 단어 단위로 줄 바꿈
vim.opt_local.conceallevel = 2 -- 문법 마커 숨기기
end,
})

relativenumber는 처음에는 어색하지만, 5j, 12k 같은 상대 이동을 할 때 줄 수를 바로 알 수 있어서 매우 편합니다.

시스템 클립보드 연동

기본 Neovim에서 y는 내부 무명 레지스터에만 저장되고 macOS 시스템 클립보드와는 분리돼 있습니다. 시스템 클립보드로 복사하려면 매번 "+y처럼 접두사를 붙여야 하는데, 한글 IME 상태나 tmux 안에서 "(Shift+') 입력이 종종 씹혀서 번거롭습니다.

opt.clipboard = "unnamedplus" 한 줄로 해결됩니다. 비주얼 모드에서 y만 눌러도 macOS 클립보드에 바로 들어가고, 다른 앱에서 Cmd+C 한 내용도 p로 바로 붙여넣어집니다.

LazyVim 배포판에서는 기본값이지만, lazy.nvim만 직접 쓰는 수동 구성에서는 이 한 줄을 빼먹기 쉽습니다.

값 선택

의미
unnamed* 레지스터 (Linux X11의 primary selection)
unnamedplus+ 레지스터 (시스템 클립보드) — 권장

macOS에서는 *+가 실질적으로 같지만, 크로스 플랫폼 호환성 측면에서 unnamedplus가 표준입니다. LazyVim 기본값도 이것.

확인

:set clipboard?

clipboard=unnamedplus가 출력되면 OK.

:checkhealth provider

→ clipboard provider 섹션에 에러가 없어야 합니다.

안 될 때

  • which pbcopy/usr/bin/pbcopy 확인 (macOS 기본 탑재).
  • Neovim 빌드에 클립보드 지원이 있는지: :echo has('clipboard')1.
  • tmux 안에서만 안 되는 경우, ~/.tmux.confset -g set-clipboard on. 다만 macOS 로컬 tmux + pbcopy 조합은 대부분 이 설정 없이도 동작합니다.

keyMapper 유틸리티

모든 키매핑에 일관되게 noremapsilent를 적용하기 위해 만든 헬퍼입니다.

-- lua/utils/keyMapper.lua
local keyMapper = function(from, to, mode, opts)
local options = { noremap = true, silent = true }
mode = mode or "n"

if opts then
options = vim.tbl_extend("force", options, opts)
end

vim.keymap.set(mode, from, to, options)
end

return { mapKey = keyMapper }

사용법:

local mapKey = require("utils.keyMapper").mapKey

mapKey("<leader>e", ":Neotree toggle<cr>") -- Normal 모드 (기본)
mapKey("<", "<gv", "v") -- Visual 모드 지정

전체 키매핑

글로벌 키매핑

동작모드
SpaceLeader 키-
<leader>eNeo-tree 파일 탐색기 토글N
<leader>h검색 하이라이트 제거N
Ctrl-h/j/k/l분할 창 이동N
< / >들여쓰기 유지하며 인덴트V
Ctrl-;플로팅 터미널 토글N

Telescope 키매핑

동작
<leader>ff파일 이름 검색
<leader>fg파일 내용 검색 (grep)
<leader>fb버퍼 목록
<leader>fh도움말 검색

LSP 키매핑

동작
K호버 문서
gd정의로 이동
<leader>ca코드 액션

플러그인 전체 목록 (23개)

핵심

플러그인역할
lazy.nvim플러그인 매니저
telescope.nvim퍼지 파인더 (파일/텍스트/버퍼 검색)
neo-tree.nvim사이드바 파일 탐색기
nvim-cmp자동완성 엔진
nvim-lspconfigLSP 클라이언트 설정
mason.nvim언어 서버/포매터 설치 관리
nvim-treesitter구문 파싱 & 하이라이팅
conform.nvim저장 시 자동 포매팅

자동완성 소스

플러그인소스
cmp-nvim-lspLSP 자동완성
cmp-buffer버퍼 텍스트
cmp-path파일 경로
cmp_luasnip스니펫
LuaSnip스니펫 엔진
friendly-snippetsVS Code 스니펫 모음

UI & 외관

플러그인역할
kanagawa.nvim컬러스킴 (dragon 테마)
lualine.nvim하단 상태줄
alpha-nvim시작 화면 대시보드
nvim-web-devicons파일 아이콘
indent-blankline.nvim들여쓰기 시각 가이드

편집 보조

플러그인역할
Comment.nvimgcc로 주석 토글
nvim-autopairs괄호/따옴표 자동 닫기
nvim-ufoLSP 기반 코드 폴딩
vim-floaterm플로팅 터미널
render-markdown.nvim마크다운 실시간 렌더링

테마: Kanagawa Dragon

Kanagawa의 Dragon 변형을 사용합니다. 일본 전통 색상에서 영감을 받은 다크 테마로, 눈의 피로가 적습니다.

커스터마이징 포인트:

overrides = function(colors)
local theme = colors.theme
return {
-- 플로팅 윈도우 배경 투명화
NormalFloat = { bg = "none" },
FloatBorder = { bg = "none" },
FloatTitle = { bg = "none" },

-- Telescope UI 커스터마이징
TelescopePromptNormal = { bg = theme.ui.bg_p1 },
TelescopeResultsNormal = { fg = theme.ui.fg_dim, bg = theme.ui.bg_m1 },
TelescopePreviewNormal = { bg = theme.ui.bg_dim },

-- 자동완성 팝업
Pmenu = { fg = theme.ui.shade0, bg = theme.ui.bg_p1 },
PmenuSel = { fg = "NONE", bg = theme.ui.bg_p2 },
}
end,
theme = "dragon",

상태줄(lualine)은 Gruvbox 테마를 사용해서 본문과 미묘하게 다른 톤을 줍니다.

마크다운 작성 환경

블로그를 Neovim으로 작성하기 때문에 마크다운 환경을 신경 썼습니다.

마크다운 환경에서는 render-markdown.nvim으로 헤딩, 코드 블록, 테이블, 체크박스를 시각적으로 렌더링하고, 줄 바꿈 활성화를 위해 마크다운 파일에서만 wrap = true로 설정합니다. conceallevel 2**bold** 같은 마커를 숨기고 bold 형태로 표시합니다.

LSP 구성

Mason으로 3개 언어 서버를 관리합니다.

서버언어포매터
lua_lsLuastylua
ts_lsTypeScript/JavaScriptprettierd
goplsGogofmt (내장)

새 언어를 추가하려면:

  1. :Mason에서 언어 서버 설치
  2. lsp.luaensure_installed에 추가
  3. conform.lua에 포매터 추가 (필요 시)

정리

이 설정은 계속 발전 중이에요. Neovim의 장점은 제 워크플로우에 맞게 모든 것을 조정할 수 있다는 점이에요. 처음에는 남의 설정을 복사하더라도, 하나씩 이해하면서 자기 것으로 만들어가는 과정이 중요해요.

가장 좋은 Neovim 설정은 제가 이해하고 있는 설정입니다.

Neovim 고급 활용

· 약 5분

이 글은 Neovim 시리즈의 세 번째 글이에요.

  1. Neovim 입문: Vim을 넘어서는 첫걸음
  2. Neovim 중급: 생산성을 높이는 기능들
  3. Neovim 고급: 플러그인과 LSP로 IDE처럼 쓰기 ← 현재 글
  4. 저의 Neovim 설정 전체 공개

플러그인 매니저: lazy.nvim

Neovim 플러그인 매니저 중 가장 인기 있는 것이 lazy.nvim입니다. 지연 로딩(lazy loading)을 기본으로 지원해서 시작 속도가 빠릅니다.

설치 (부트스트랩)

~/.config/nvim/init.lua에 아래 내용을 추가하면 lazy.nvim이 없을 때 자동으로 설치됩니다.

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)

require("lazy").setup("plugins")

마지막 줄의 "plugins"~/.config/nvim/lua/plugins/ 디렉토리를 의미합니다. 이 디렉토리에 파일을 추가하면 자동으로 플러그인이 로드됩니다.

플러그인 추가 방법

lua/plugins/ 안에 파일을 만들고 테이블을 반환하면 됩니다.

-- lua/plugins/example.lua
return {
"작성자/플러그인이름",
config = function()
require("플러그인").setup({
-- 옵션
})
end
}

플러그인 관리: :Lazy

LSP (Language Server Protocol)

LSP는 에디터와 언어 서버 사이의 표준 프로토콜입니다. 코드 자동완성, 정의로 이동, 에러 표시, 리팩토링 등 IDE 기능의 핵심입니다.

구조

Neovim ←→ nvim-lspconfig ←→ Language Server
(lua_ls, gopls, ts_ls, ...)

Mason으로 언어 서버 설치

Mason은 언어 서버, 포매터, 린터를 Neovim 안에서 설치/관리하는 도구입니다.

-- lua/plugins/lsp.lua
return {
{
"williamboman/mason.nvim",
config = function()
require("mason").setup()
end
},
{
"williamboman/mason-lspconfig.nvim",
config = function()
require("mason-lspconfig").setup({
ensure_installed = { "lua_ls", "ts_ls", "gopls" }
})
end
},
{
"neovim/nvim-lspconfig",
config = function()
vim.lsp.enable({ "lua_ls", "ts_ls", "gopls" })

-- LSP 키매핑
vim.keymap.set("n", "K", vim.lsp.buf.hover)
vim.keymap.set("n", "gd", vim.lsp.buf.definition)
vim.keymap.set("n", "<leader>ca", vim.lsp.buf.code_action)
end
}
}
동작
K커서 위 심볼의 문서 표시
gd정의로 이동
<leader>ca코드 액션 (자동 수정 등)

Mason UI: :Mason (설치된 서버 확인/추가/삭제)

자동완성: nvim-cmp

nvim-cmp는 Neovim의 자동완성 엔진입니다. 여러 소스(LSP, 버퍼, 파일 경로, 스니펫)에서 후보를 가져옵니다.

-- lua/plugins/nvim-cmp.lua
return {
"hrsh7th/nvim-cmp",
dependencies = {
"hrsh7th/cmp-nvim-lsp", -- LSP 소스
"hrsh7th/cmp-buffer", -- 버퍼 텍스트 소스
"hrsh7th/cmp-path", -- 파일 경로 소스
"L3MON4D3/LuaSnip", -- 스니펫 엔진
"saadparwaiz1/cmp_luasnip", -- 스니펫 소스
"rafamadriz/friendly-snippets", -- 스니펫 모음
},
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-Space>"] = cmp.mapping.complete(),
["<CR>"] = cmp.mapping.confirm({ select = true }),
["<Tab>"] = cmp.mapping.select_next_item(),
["<S-Tab>"] = cmp.mapping.select_prev_item(),
}),
sources = cmp.config.sources({
{ name = "nvim_lsp" },
{ name = "luasnip" },
{ name = "buffer" },
{ name = "path" },
}),
})
end
}

자동완성 키:

동작
Ctrl-Space수동으로 완성 목록 열기
Tab다음 후보
Shift-Tab이전 후보
Enter선택 확정

Treesitter: 구문 하이라이팅

Treesitter는 코드를 파싱해서 정확한 구문 하이라이팅을 제공합니다. 정규식 기반의 기존 하이라이팅보다 훨씬 정확합니다.

-- lua/plugins/nvim-treesitter.lua
return {
"nvim-treesitter/nvim-treesitter",
build = ":TSUpdate",
config = function()
require("nvim-treesitter.configs").setup({
ensure_installed = {
"lua", "go", "javascript", "html",
"markdown", "markdown_inline",
},
highlight = { enable = true },
indent = { enable = true },
})
end
}

새 언어 추가: :TSInstall python

Telescope: 퍼지 파인더

Telescope는 파일, 텍스트, 버퍼, Git 등 모든 것을 검색할 수 있는 퍼지 파인더입니다.

-- lua/plugins/telescope.lua
return {
"nvim-telescope/telescope.nvim",
tag = "0.1.5",
dependencies = { "nvim-lua/plenary.nvim" },
config = function()
local builtin = require("telescope.builtin")
vim.keymap.set("n", "<leader>ff", builtin.find_files)
vim.keymap.set("n", "<leader>fg", builtin.live_grep)
vim.keymap.set("n", "<leader>fb", builtin.buffers)
vim.keymap.set("n", "<leader>fh", builtin.help_tags)
end
}
동작
<leader>ff파일 이름 검색
<leader>fg파일 내용 검색 (grep)
<leader>fb열린 버퍼 목록
<leader>fh도움말 검색

live_grep를 사용하려면 ripgrep이 설치되어 있어야 합니다: brew install ripgrep

파일 탐색기: Neo-tree

-- lua/plugins/neo-tree.lua
return {
"nvim-neo-tree/neo-tree.nvim",
branch = "v3.x",
dependencies = {
"nvim-lua/plenary.nvim",
"nvim-tree/nvim-web-devicons",
"MunifTanjim/nui.nvim",
},
}

<leader>e로 사이드바 파일 탐색기를 토글할 수 있습니다. VS Code의 Explorer와 비슷한 역할입니다.

코드 포매팅: conform.nvim

-- lua/plugins/conform.lua
return {
"stevearc/conform.nvim",
config = function()
require("conform").setup({
formatters_by_ft = {
lua = { "stylua" },
javascript = { "prettierd", "prettier", stop_after_first = true },
typescript = { "prettierd", "prettier", stop_after_first = true },
},
format_on_save = {
timeout_ms = 500,
lsp_format = "fallback",
},
})
end
}

파일 저장 시 자동으로 포매팅됩니다. 포매터는 Mason으로 설치할 수 있습니다: :Mason에서 stylua, prettierd를 검색합니다.

추천 플러그인 조합

최소한의 IDE 환경을 위한 추천 조합:

카테고리플러그인역할
플러그인 관리lazy.nvim플러그인 매니저
LSPmason + lspconfig언어 서버
자동완성nvim-cmp코드 완성
구문treesitter하이라이팅
검색telescope퍼지 파인더
파일 탐색neo-tree파일 트리
포매팅conform.nvim자동 포매팅
테마kanagawa / tokyonight / catppuccin컬러스킴

다음 글

이 플러그인들을 실제로 어떻게 조합하고 커스터마이징하는지, 저의 전체 Neovim 설정을 공개할게요.

저의 Neovim 설정 전체 공개

참고 자료