이 글은 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")

-- 마크다운 전용 설정
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 같은 상대 이동을 할 때 줄 수를 바로 알 수 있어서 매우 편하다.

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 설정은 내가 이해하고 있는 설정이다.