이 글은 Neovim 시리즈의 마지막 글이다.
- Neovim 입문: Vim을 넘어서는 첫걸음
- Neovim 중급: 생산성을 높이는 기능들
- Neovim 고급: 플러그인과 LSP로 IDE처럼 쓰기
- 나의 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.conf에set -g set-clipboard on. 다만 macOS 로컬 tmux + pbcopy 조합은 대부분 이 설정 없이도 동작한다.
keyMapper 유틸리티
모든 키매핑에 일관되게 noremap과 silent를 적용하기 위해 만든 헬퍼다:
-- 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 모드 지정
전체 키매핑
글로벌 키매핑
| 키 | 동작 | 모드 |
|---|---|---|
Space | Leader 키 | - |
<leader>e | Neo-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-lspconfig | LSP 클라이언트 설정 |
| mason.nvim | 언어 서버/포매터 설치 관리 |
| nvim-treesitter | 구문 파싱 & 하이라이팅 |
| conform.nvim | 저장 시 자동 포매팅 |
자동완성 소스
| 플러그인 | 소스 |
|---|---|
| cmp-nvim-lsp | LSP 자동완성 |
| cmp-buffer | 버퍼 텍스트 |
| cmp-path | 파일 경로 |
| cmp_luasnip | 스니펫 |
| LuaSnip | 스니펫 엔진 |
| friendly-snippets | VS Code 스니펫 모음 |
UI & 외관
| 플러그인 | 역할 |
|---|---|
| kanagawa.nvim | 컬러스킴 (dragon 테마) |
| lualine.nvim | 하단 상태줄 |
| alpha-nvim | 시작 화면 대시보드 |
| nvim-web-devicons | 파일 아이콘 |
| indent-blankline.nvim | 들여쓰기 시각 가이드 |
편집 보조
| 플러그인 | 역할 |
|---|---|
| Comment.nvim | gcc로 주석 토글 |
| nvim-autopairs | 괄호/따옴표 자동 닫기 |
| nvim-ufo | LSP 기반 코드 폴딩 |
| 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_ls | Lua | stylua |
| ts_ls | TypeScript/JavaScript | prettierd |
| gopls | Go | gofmt (내장) |
새 언어를 추가하려면:
:Mason에서 언어 서버 설치lsp.lua의ensure_installed에 추가conform.lua에 포매터 추가 (필요 시)
정리
이 설정은 계속 발전 중이다. Neovim의 장점은 내 워크플로우에 맞게 모든 것을 조정할 수 있다는 점이다. 처음에는 남의 설정을 복사하더라도, 하나씩 이해하면서 자기 것으로 만들어가는 과정이 중요하다.
가장 좋은 Neovim 설정은 내가 이해하고 있는 설정이다.