MacOS 환경 vim plugin 구성(w/ configuration)

SRE/DevOps 일을 하다 보면 yaml 하나 고치거나 ansible 플레이북, terraform 파일을 손보는 일이 하루에도 몇 번씩 생긴다. 그때마다 IDE 를 띄우기는 번거롭고, 맨 vim 으로 버티다 보면 YAML 들여쓰기 한 칸 때문에 배포가 깨지는 일도 생긴다. 그래서 맥에서 쓰는 vim 을 아래 기준으로 정리해 봤다. (여기저기서 줍줍해 온 설정을 모아 다듬었다)

  • Python, yaml, ansible, terraform 등 설정 파일의 구문을 알아보고 색을 입힐 것
  • Tmux 안에서도 자연스럽게 쓰고, 린트·구문 강조·마우스 스크롤이 될 것
  • git 변경 표시, Status line 커스터마이징, theme 적용

글의 환경은 macOS(Apple Silicon) + Homebrew + zsh, 터미널은 Ghostty 나 iTerm2 다. Neovim 이 아니라 Vim 9 기준이다.

목차
  1. 1. 사전 준비 (Prerequisites)
  2. 1-1. 클립보드를 쓸 수 있는 vim 인지 먼저 확인
  3. 1-2. 검색 도구, 포매터, vim-plug 설치
  4. 2. .vimrc (Preset Config)
  5. 2-1. 들여쓰기는 파일 형식마다 다르게
  6. 2-2. 저장하면 알아서 정리되는 ALE
  7. 2-3. 클립보드와 마우스
  8. 3. 적용 및 플러그인 설치
  9. 4. tmux 와 vim 창을 같은 키로 오가기
  10. 5. 자주 쓰는 단축키 정리
  11. 6. 잘 안 될 때 확인할 것
  12. 6-1. 복사한 게 다른 앱에 안 붙을 때
  13. 6-2. 저장해도 포맷이 안 될 때
  14. 6-3. 플레이북이 그냥 yaml 로 잡힐 때
  15. 6-4. 색이 흐릿하게 나올 때

1. 사전 준비 (Prerequisites)

1-1. 클립보드를 쓸 수 있는 vim 인지 먼저 확인

vim 에서 복사한 걸 다른 앱에 붙여 넣으려면 vim 이 클립보드 기능(+clipboard)을 넣고 빌드되어 있어야 한다. 뒤에서 .vimrc 에 넣는 clipboard 옵션도 이 기능이 있어야 먹는다. 요즘 macOS 에 들어 있는 /usr/bin/vim 은 이 기능이 들어 있어 그대로 써도 된다(macOS 26 에서 확인했다). 다만 예전 macOS 의 기본 vim 은 클립보드 기능을 빼고 빌드되어 있었으니, 뭘 깔기 전에 확인부터 하자.

vim --version | grep clipboard
# +clipboard 로 나오면 → 지금 vim 을 그대로 쓰면 된다
# -clipboard 로 나오면 → 아래처럼 Homebrew 로 vim 을 깐다
Code language: Bash (bash)

출력에서 어디를 봐야 하는지는 6-1 의 화면에 표시해 두었다. xterm_clipboard, wayland_clipboard 도 같이 걸려 나오는데, 리눅스(X11·Wayland)용이라 맥에서는 - 여도 상관없다. 맨 앞의 clipboard 하나만 보면 된다.

-clipboard 로 나오면 Homebrew 로 vim 을 새로 깐다. 기본 vim 을 지우는 게 아니라, PATH 에서 앞에 오는 /opt/homebrew/bin/vim 이 대신 실행되게 하는 방식이다.

brew install vim

# 새 터미널을 열고 다시 확인하면 +clipboard 로 바뀌어 있다
which vim     # /opt/homebrew/bin/vim
vim --version | grep clipboard
Code language: Bash (bash)

which vim 이 여전히 /usr/bin/vim 을 가리킨다면 PATH 순서 문제다. Homebrew 설치 때 안내대로 eval "$(/opt/homebrew/bin/brew shellenv)" 를 ~/.zprofile 에 넣어 두면 /opt/homebrew/bin 이 앞으로 온다.

1-2. 검색 도구, 포매터, vim-plug 설치

FZF 로 파일 이름을, ripgrep 으로 파일 내용을 찾는다. 저장할 때 코드를 정리해 줄 포매터와 린터도 같이 깐다. .vimrc 의 ALE 설정이 이 도구들을 불러다 쓰기 때문에, 빠진 게 있으면 그 파일 형식만 정리가 안 된다.

# FZF, ALE 사전 설치
brew install fzf ripgrep fd

# Linting / Formatting 용 (Python, YAML, Shell, Ansible)
brew install black isort flake8 prettier shfmt shellcheck yamllint ansible-lint

# vim plugin 관리자 설치
curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
    https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
Code language: Bash (bash)

예전엔 pip install black isort flake8 로 깔았는데, 요즘 Homebrew 파이썬에서 그렇게 하면 externally-managed-environment 오류가 나면서 막힌다(PEP 668). 프로젝트마다 버전을 따로 맞출 게 아니면 brew 로 까는 게 편하고, 버전을 나눠야 한다면 pipx install black 처럼 pipx 를 쓰면 된다.

Terraform 파일도 저장할 때 정리하고 싶다면 terraform 이 깔려 있어야 한다. 라이선스가 바뀐 뒤로 Homebrew 기본 저장소에서는 빠졌으니 brew tap hashicorp/tap 을 먼저 하고 brew install hashicorp/tap/terraform 으로 설치한다.

2. .vimrc (Preset Config)

아래 내용을 ~/.vimrc 파일로 저장한다. 쓰던 .vimrc 가 있다면 cp ~/.vimrc ~/.vimrc.bak 으로 먼저 백업해 두자.

" ==========================================
" 1. VIM-PLUG Plugins
" ==========================================
call plug#begin('~/.vim/plugged')

" --- Infrastructure & Syntax ---
Plug 'sheerun/vim-polyglot'          " 500+ 언어 팩 (Python, YAML, Ansible, HCL 등 포함)
Plug 'dense-analysis/ale'            " 비동기 Linting (Pylint, Yamllint, Ruff 등 자동 감지)
Plug 'pearofducks/ansible-vim'       " Ansible YAML 구문 강조 및 헬퍼

" --- Navigation & Search ---
Plug 'preservim/nerdtree'            " 파일 탐색기
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } } " FZF (빠른 파일 검색)
Plug 'junegunn/fzf.vim'
Plug 'christoomey/vim-tmux-navigator' " Tmux와 Vim 창 간 매끄러운 이동 (Ctrl+h/j/k/l)

" --- Git & Status ---
Plug 'tpope/vim-fugitive'            " Git Wrapper (:Git, :Git blame)
Plug 'airblade/vim-gitgutter'        " 변경된 라인 표시 (+/-)
Plug 'vim-airline/vim-airline'       " 상태바 (Status Line)
Plug 'vim-airline/vim-airline-themes'

" --- Theme ---
Plug 'morhetz/gruvbox'               " 눈이 편안한 테마 (DevOps 국룰 테마)

call plug#end()

" ==========================================
" 2. General Settings (기본 설정)
" ==========================================
syntax on
set number              " 줄 번호 표시
set relativenumber      " 상대 줄 번호 (이동 시 편리)
set encoding=utf-8
set mouse=v             " 비주얼 모드에서만 마우스 사용 (노멀 모드 드래그 복사는 터미널이)
set mousehide           " 입력시 마우스 커서 숨김
set scrolloff=8         " 커서 위아래 여백 확보
set clipboard=unnamed   " MacOS 클립보드와 연동 (brew 로 설치한 vim 필요)
set noswapfile          " 스왑 파일 생성 안 함 (깔끔한 디렉토리 유지)

" --- Search ---
set ignorecase          " 검색 시 대소문자 무시
set smartcase           " 대문자 입력 시에만 대소문자 구분
set incsearch           " 점진적 검색
set nohlsearch          " 검색 결과 하이라이트 끄기

" ==========================================
" 3. Indentation (들여쓰기)
" ==========================================
" 기본: 4칸 공백
set tabstop=4
set softtabstop=4
set shiftwidth=4
set expandtab
set autoindent
set smartindent

" --- Python Specific ---
" Python은 PEP8 기준 4칸 유지
autocmd FileType python setlocal expandtab shiftwidth=4 tabstop=4 softtabstop=4

" --- YAML/Ansible Specific (중요) ---
" JSON, YAML, Ansible은 2칸 들여쓰기가 표준
autocmd FileType json,jsonc,yaml,yml,ansible,yaml.ansible setlocal expandtab shiftwidth=2 tabstop=2 softtabstop=2

" --- Makefile ---
" Makefile은 반드시 Tab을 사용해야 함
autocmd FileType make setlocal noexpandtab tabstop=4 shiftwidth=4

" ==========================================
" 4. Plugin Configurations
" ==========================================
" --- Theme ---
" 첫 실행(:PlugInstall 전)에는 gruvbox 가 없어서 silent! 로 오류를 누른다
set background=dark
silent! colorscheme gruvbox
let g:airline_theme='gruvbox'

" --- ALE (Linting) ---
" 저장 시 자동 수정 (Python: Black/Isort, YAML: Prettier 등)
let g:ale_fixers = {
\   'python': ['black', 'isort'],
\   'yaml': ['prettier'],
\   'sh': ['shfmt'],
\   'terraform': ['terraform'],
\}
let g:ale_fix_on_save = 1
" 상태바에 에러 표시
let g:airline#extensions#ale#enabled = 1

" --- NERDTree ---
" Ctrl+n으로 파일 탐색기 토글
map <C-n> :NERDTreeToggle<CR>
" Vim 시작 시 파일 지정 없으면 NERDTree 열기
autocmd StdinReadPre * let s:std_in=1
autocmd VimEnter * if argc() == 0 && !exists("s:std_in") | NERDTree | endif

" --- FZF ---
" Ctrl+p로 파일 검색 (VSCode 스타일)
nnoremap <C-p> :Files<CR>
" Ctrl+f로 파일 내 텍스트 검색 (Ripgrep 연동)
nnoremap <C-f> :Rg<CR>

" ==========================================
" 5. Key Mappings (단축키)
" ==========================================
let mapleader = " "  " Leader 키를 스페이스바로 설정

" 설정 파일 빠르게 열고/적용하기
nnoremap <leader>ev :vsp $MYVIMRC<CR>
nnoremap <leader>sv :source $MYVIMRC<CR>

" 창 이동 (Tmux Navigator가 없어도 작동하도록 기본 설정)
nnoremap <C-h> <C-w>h
nnoremap <C-j> <C-w>j
nnoremap <C-k> <C-w>k
nnoremap <C-l> <C-w>l
Code language: Vim Script (vim)

2-1. 들여쓰기는 파일 형식마다 다르게

기본은 공백 4칸이고 JSON, YAML, Ansible 파일을 열면 2칸으로 바뀐다(Indent conf: Python=4, YAML=2). YAML 은 들여쓰기가 곧 문법이라 탭 하나, 공백 한 칸 차이로 파싱이 깨진다. Makefile 은 반대로 규칙 줄이 반드시 탭으로 시작해야 해서 expandtab 을 끈다. 이걸 파일마다 손으로 맞추면 꼭 한 번은 틀리니까 FileType 으로 묶어 뒀다.

열린 파일이 어떤 형식으로 잡혔는지는 :set filetype? 으로 볼 수 있다. ansible-vim 은 플레이북을 yaml.ansible 이라는 형식으로 잡기 때문에 autocmd 목록에 그 이름도 넣어 두었다.

2-2. 저장하면 알아서 정리되는 ALE

ALE 는 파일을 열어 둔 동안 뒤에서 린터를 돌리고 문제 있는 줄 옆에 표시를 띄운다. 린터는 깔려 있는 걸 알아서 찾아 쓴다. 여기에 g:ale_fix_on_save 를 켜 두면 :w 할 때마다 포매터가 파일을 고친다. Python 은 black 과 isort, YAML 은 prettier, 셸 스크립트는 shfmt, Terraform 은 terraform fmt 가 돈다.

남의 저장소에서 작업할 땐 이게 오히려 문제가 된다. 한 줄 고쳤는데 파일 전체가 포맷되면서 diff 가 수백 줄로 불어나는 식이다. 그럴 땐 그 버퍼에서만 :let b:ale_fix_on_save = 0 으로 끄거나 :ALEDisable 로 ALE 를 잠깐 멈춘다. 어떤 린터와 포매터가 붙어 있는지는 :ALEInfo 로 확인한다.

2-3. 클립보드와 마우스

clipboard=unnamed 로 두면 yy 로 복사한 줄을 다른 앱에서 Cmd+V 로 붙여 넣을 수 있고, 브라우저에서 복사한 내용도 vim 에서 p 로 바로 붙는다. 앞에서 굳이 brew 로 vim 을 다시 깐 게 이것 때문이다. 처음엔 unnamedplus 로 써 뒀었는데, 그건 리눅스(X11)의 + 레지스터용 값이라 맥에서는 unnamed 쪽이 확실하다.

마우스는 mouse=v 로 비주얼 모드에서만 vim 이 받게 해 뒀다. 노멀 모드에서는 터미널이 마우스를 그대로 쓰니까 드래그로 긁어서 복사하던 습관이 그대로 살아 있다. 분할 창 경계를 마우스로 끌거나 휠로 버퍼를 굴리고 싶으면 mouse=a 로 바꾸면 되는데, 그러면 드래그 선택까지 vim 이 가져가 버린다. 그때는 iTerm2 는 Option, Ghostty 는 Shift 를 누른 채 드래그하면 터미널 쪽 선택이 된다.

3. 적용 및 플러그인 설치

vim 을 실행하고 Command mode 에서 :PlugInstall 을 치면 .vimrc 에 적어 둔 플러그인을 내려받아 설치한다.

끝나면 아래처럼 플러그인마다 결과가 찍힌다. q 로 창을 닫고 vim 을 다시 열면 테마와 상태바까지 입혀진다. 처음 열 때 colorscheme 줄에서 E185 오류가 나지 않게 silent! 를 붙여 둔 것도 이 순서 때문이다.

 [Plugins]  [No Name]                                                                                                                 X
1   Updated. Elapsed time: 0.081482 sec.
  1 [============]
  2
  3 - Finishing ... Done!
  4 - vim-polyglot: Already installed
  5 - fzf: Already installed
  6 - ale: Already installed
  7 - vim-fugitive: Already installed
  8 - ansible-vim: Already installed
  9 - vim-airline: Already installed
 10 - vim-airline-themes: Already installed
 11 - nerdtree: Already installed
 12 - fzf.vim: Already installed
 13 - vim-tmux-navigator: Already installed
 14 - vim-gitgutter: Already installed
 15 - gruvbox: Already installed

-----------------------------------------------------------------------
NORMAL  [No Name]                              [unix]  100% ㏑: 0/1≡℅:1
-----------------------------------------------------------------------
:PlugInstall
Code language: plaintext (plaintext)

플러그인 관리는 명령 몇 개면 된다.

  • :PlugUpdate — 설치된 플러그인을 최신으로 올린다
  • :PlugClean — .vimrc 에서 지운 플러그인을 디스크에서도 치운다
  • :PlugUpgrade — vim-plug 자체를 업데이트한다
  • :PlugStatus — 플러그인별 설치 상태를 본다

4. tmux 와 vim 창을 같은 키로 오가기

vim-tmux-navigator 는 vim 쪽 설정만으로는 반쪽이다. vim 안의 분할 창끼리는 Ctrl+h/j/k/l 로 옮겨 다니지만 tmux 패널 경계를 넘을 수 없다. .tmux.conf 에도 같은 플러그인을 넣어 줘야 vim 창인지 tmux 패널인지 신경 쓰지 않고 같은 키로 이동하기 쉽게 된다. tmux 플러그인 관리자(TPM)로 넣는 게 제일 간단하다.

# TPM 설치
git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
Code language: Bash (bash)
# ~/.tmux.conf
set -g mouse on                               # tmux 에서도 휠 스크롤
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'christoomey/vim-tmux-navigator'

# Ctrl+l 을 navigator 가 가져가므로 화면 지우기는 prefix 다음 Ctrl+l 로
bind C-l send-keys 'C-l'

# 이 줄은 항상 파일 맨 끝에
run '~/.tmux/plugins/tpm/tpm'
Code language: Bash (bash)

저장하고 tmux 안에서 tmux source ~/.tmux.conf 로 다시 읽은 다음, prefix(기본 Ctrl+b) 를 누르고 대문자 I 를 치면 플러그인이 설치된다. 셸에서 화면을 지우던 Ctrl+l 이 창 이동으로 바뀌는 게 처음엔 좀 어색한데, 위 설정의 bind 한 줄로 prefix 다음 Ctrl+l 을 치면 예전처럼 지워진다. .vimrc 5번 블록의 Ctrl+h/j/k/l 매핑은 tmux 밖에서 vim 을 쓸 때를 위한 거라 그대로 둬도 부딪히지 않는다.

5. 자주 쓰는 단축키 정리

설정에 넣은 단축키를 한곳에 모았다. Leader 는 스페이스바다.

키동작플러그인
Ctrl+n파일 탐색기 열고 닫기NERDTree
Ctrl+p파일 이름으로 찾기 (:Files)fzf.vim
Ctrl+f파일 내용으로 찾기 (:Rg)fzf.vim + ripgrep
Ctrl+h/j/k/l왼쪽·아래·위·오른쪽 창(패널)으로 이동vim-tmux-navigator
Space e v.vimrc 를 옆 창에 열기직접 매핑
Space s v고친 .vimrc 다시 읽기직접 매핑
:Git / :Git blamegit 상태 보기, 줄마다 누가 고쳤는지 보기vim-fugitive
]c / [c다음·이전 변경 덩어리로 이동vim-gitgutter

Ctrl+f 는 원래 vim 에서 한 화면 아래로 내리는 키다. 그 동작이 손에 익었다면 :Rg 매핑을 <leader>f 같은 다른 키로 옮기자. 화면 이동은 Ctrl+d, Ctrl+u(반 화면씩)로도 충분하긴 하다.

6. 잘 안 될 때 확인할 것

항목마다 화면을 붙였다. 빨간 박스의 번호는 그림 아래 설명의 번호다.

6-1. 복사한 게 다른 앱에 안 붙을 때

대부분 클립보드 기능이 없는 vim 이 실행되고 있는 경우다. which vim 으로 지금 어떤 vim 이 뜨는지 보고, vim --version 의 Features included (+) or not (-) 아래 기능 목록에서 clipboard 앞에 + 가 붙었는지 본다. 목록이 길어서 vim --version | grep clipboard 로 좁혀 보는 편이 빠르다.

-clipboard 면 1-1 대로 Homebrew vim 을 깔고, 깔았는데도 그대로라면 which vim 이 /opt/homebrew/bin/vim 인지 본다.

6-2. 저장해도 포맷이 안 될 때

:ALEInfo 를 열어 fixer 로 무엇이 잡혔고 실행 결과가 어땠는지 본다. 1-2 의 brew 명령에서 빠뜨린 도구가 있으면 여기서 바로 드러난다. 아래는 prettier 와 yamllint 를 깔지 않은 맥에서 yaml 파일을 저장한 뒤 :ALEInfo 를 연 화면이다. 내용이 길어서 설정은 가운데쯤 Global Variables 에, 실행 결과는 맨 아래 Command History 에 있다(G 로 맨 아래로 간다).

이렇게 나오면 빠진 도구를 깔고 다시 저장해 보면 된다. 이 화면이라면 brew install prettier yamllint 한 줄이다.

6-3. 플레이북이 그냥 yaml 로 잡힐 때

ansible-vim 은 경로(tasks/, roles/, handlers/, group_vars/, host_vars/ 아래)나 파일 이름(playbook.yml, site.yml, main.yml 등)을 보고 Ansible 인지 판단한다. deploy.yml 처럼 이 규칙에 안 걸리는 이름이면 그냥 yaml 로 잡히는데, 이때는 :set filetype=yaml.ansible 로 직접 지정해 주면 된다. 지금 무엇으로 잡혔는지는 :set filetype? 이나 상태줄 오른쪽에서 보인다.

이런 이름을 자주 쓴다면 .vimrc 에 let g:ansible_ftdetect_filename_regex = '\v(playbook|site|main|local|requirements|deploy)\.ya?ml$' 처럼 이름 규칙을 넓혀 두면 매번 지정하지 않아도 된다.

6-4. 색이 흐릿하게 나올 때

.vimrc 에 set termguicolors 를 추가해 보자. Ghostty 나 iTerm2 에서는 gruvbox 원래 색이 나온다. 다만 tmux 안에서 켰더니 색이 통째로 빠져 보인다면, 그냥 빼고 256색으로 쓰는 게 속 편하다.

아래는 같은 파일을 256색과 termguicolors 로 띄워 비교한 것이다. .vimrc 를 고치기 전에 vim 안에서 :set termguicolors 로 먼저 켜 봐도 된다.

플러그인은 욕심내면 끝이 없는데, 이 정도가 매일 쓰면서 무겁다고 느끼지 않은 선이었다. 서버 설정 파일 고치는 정도는 IDE 없이도 충분하다.

댓글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다