본문으로 건너뛰기

Vi/Vim 단축키 가이드

· 약 15분

이 글은 Vim 단축키 시리즈의 첫 번째 글이에요.

  1. Vi/Vim 단축키 종합 가이드: 기본부터 실전까지 ← 현재 글
  2. Neovim 단축키 가이드: Vim을 넘어서는 기능들

Vim의 언어: 동사 + 명사

Vim 단축키는 외워야 하는 목록보다 문법에 가깝습니다.

동사(operator)는 "무엇을 할 것인가":

동사의미
d삭제 (delete)
c변경 (change) — 삭제 후 Insert
y복사 (yank)
>들여쓰기
<내어쓰기
gU대문자로
gu소문자로
=자동 정렬

명사(motion/text object)는 "어디까지":

명사의미
w다음 단어까지
$줄 끝까지
gg파일 처음까지
%매칭 괄호까지
iw단어 안쪽 (inner word)
i"따옴표 안쪽
a(괄호 포함 전체

동사와 명사는 다음처럼 조합합니다.

d + iw = diw → 단어 삭제
c + i" = ci" → 따옴표 안 내용 변경
y + $ = y$ → 줄 끝까지 복사
> + ap = >ap → 문단 들여쓰기
gU + iw = gUiw → 단어 대문자로

이 문법을 이해하면 수백 개의 조합을 일일이 외우지 않고 만들어낼 수 있습니다.

모드

Vim은 모드 기반 에디터입니다. 같은 키도 모드에 따라 다르게 동작합니다.

모드진입역할
NormalEsc이동, 편집 명령 실행
Inserti, a, o텍스트 입력
Visualv, V, Ctrl-v영역 선택
Command-line:, /, ?명령 실행, 검색
ReplaceR덮어쓰기 입력

Insert 모드 내 단축키

Insert 모드에서도 몇 가지 유용한 키가 있습니다.

동작
Ctrl-w커서 앞 단어 삭제
Ctrl-u커서 앞 줄 전체 삭제
Ctrl-r "레지스터 내용 붙여넣기
Ctrl-oNormal 모드 명령 1회 실행 후 Insert 복귀
Ctrl-t들여쓰기
Ctrl-d내어쓰기

기본 이동

k

h ← → l

j

숫자를 앞에 붙이면 반복합니다. 5j는 5줄 아래, 10k는 10줄 위로 이동합니다.

단어 단위 이동

동작
w다음 단어 시작
b이전 단어 시작
e현재/다음 단어 끝
ge이전 단어 끝
W/B/EWORD 단위 (공백 기준으로만 구분)

wW의 차이: hello-world에서 w-에서 멈추지만, W는 공백까지 건너뜁니다.

줄 안에서 이동

위치
0줄 맨 처음 (0번째 열)
^줄 첫 번째 글자 (공백 제외)
$줄 끝
g_줄 마지막 글자 (공백 제외)
+다음 줄 첫 글자
-이전 줄 첫 글자

문자 찾기로 이동

줄 안에서 특정 문자로 이동하는 기능입니다. 이동뿐 아니라 동사와 조합하면 강력해집니다.

동작
f{char}오른쪽으로 {char} 위치로 이동
F{char}왼쪽으로 {char} 위치로 이동
t{char}오른쪽으로 {char} 앞까지 이동
T{char}왼쪽으로 {char} 뒤까지 이동
;같은 방향으로 반복
,반대 방향으로 반복

실전에서 자주 쓰는 조합:

dt) → 닫는 괄호 앞까지 삭제
cf, → 쉼표까지 변경 (쉼표 포함)
yt; → 세미콜론 앞까지 복사
vf" → 따옴표까지 선택

괄호와 블록 사이 이동

동작
%매칭 괄호로 점프 — (↔), {↔}, [↔]
[{현재 블록의 여는 {
]}현재 블록의 닫는 }
[(현재 블록의 여는 (
])현재 블록의 닫는 )

%는 코드 읽을 때 특히 유용합니다. 긴 함수의 시작 {에서 %를 누르면 닫는 }로 바로 이동합니다.

화면과 파일 안에서 이동

스크롤

동작
Ctrl-d반 페이지 아래
Ctrl-u반 페이지 위
Ctrl-f한 페이지 아래
Ctrl-b한 페이지 위

화면 내 이동

동작
H화면 상단 (High)
M화면 중단 (Middle)
L화면 하단 (Low)

화면 정렬

동작
zz현재 줄을 화면 가운데로
zt현재 줄을 화면 상단으로
zb현재 줄을 화면 하단으로

파일 내 이동

동작
gg파일 처음
G파일 끝
{n}Gn번째 줄로 (:42와 동일)

검색으로 이동

동작
/pattern아래로 검색
?pattern위로 검색
n다음 결과
N이전 결과
*커서 아래 단어를 아래로 검색
#커서 아래 단어를 위로 검색
gn다음 검색 결과를 Visual 선택
gd현재 파일에서 변수 선언으로

*#은 변수명 위에서 누르면 같은 이름을 바로 찾아줍니다. 검색어를 타이핑할 필요가 없습니다.

텍스트 삽입

위치
i커서 앞
a커서 뒤
I줄 첫 글자 앞
A줄 끝
o아래에 새 줄
O위에 새 줄
gi마지막 Insert 위치로 돌아가서 입력

gi는 편집 중 Normal로 돌아가서 다른 작업을 하다가, 다시 이전 편집 위치로 돌아갈 때 씁니다.

텍스트 삭제

삭제(d)는 동사입니다. 명사와 조합합니다.

동작
x커서 아래 문자
X커서 앞 문자
dw다음 단어까지
de단어 끝까지
db이전 단어까지
dd줄 전체
D = d$줄 끝까지
d0줄 처음까지
d^첫 글자까지
dG파일 끝까지
dgg파일 처음까지
diw단어 (inner)
daw단어 + 주변 공백 (around)
di"따옴표 안
di(괄호 안

텍스트 변경

변경(c)은 삭제 후 Insert 모드로 진입합니다. d와 같은 문법을 따르며 곧바로 입력할 수 있습니다.

동작
cw단어 끝까지 변경
ciw단어 전체 변경
ci"따옴표 안 변경
ci(괄호 안 변경
cc줄 전체 변경
C = c$줄 끝까지 변경
s문자 하나 변경 (= cl)
S줄 전체 변경 (= cc)
r{char}커서 문자를 {char}로 교체 (Normal 유지)
RReplace 모드 진입 (타이핑이 기존 텍스트를 덮어씀)

ci"는 아마 Vim에서 가장 많이 쓰는 조합 중 하나입니다. 따옴표 안에 커서를 놓고 ci"를 누르면 안의 내용이 지워지고 바로 새 내용을 입력하게 됩니다.

복사와 붙여넣기

동작
yy줄 복사
yw단어 복사
y$줄 끝까지 복사
yi"따옴표 안 복사
ya(괄호 포함 전체 복사
p커서 뒤에 붙여넣기
P커서 앞에 붙여넣기
]p들여쓰기 맞춰서 붙여넣기
[p위에 들여쓰기 맞춰서 붙여넣기

자주 쓰는 조합:

조합결과
yyp현재 줄 복제
ddp현재 줄과 아래 줄 교환
xp커서 아래 두 문자 교환

줄 조작

동작
J현재 줄과 아래 줄 합치기 (공백 추가)
gJ줄 합치기 (공백 없이)
>>현재 줄 들여쓰기
<<현재 줄 내어쓰기
5>>5줄 들여쓰기
==현재 줄 자동 정렬
=G현재 줄부터 파일 끝까지 자동 정렬
:m+1현재 줄을 한 줄 아래로 이동
:m-2현재 줄을 한 줄 위로 이동

>> 후에 .을 누르면 한 단계씩 더 들여씁니다. 들여쓰기 레벨을 조절할 때 편리합니다.

대소문자와 숫자 편집

동작
~커서 아래 문자 대소문자 토글
gUiw단어 대문자로
guiw단어 소문자로
gU$줄 끝까지 대문자로
gUU줄 전체 대문자로
guu줄 전체 소문자로
Ctrl-a커서 아래(또는 뒤) 숫자 +1
Ctrl-x커서 아래(또는 뒤) 숫자 -1
5Ctrl-a숫자 +5

Ctrl-a/Ctrl-x는 코드에서 인덱스나 포트 번호를 바꿀 때 유용합니다. 커서가 숫자 위에 없어도, 같은 줄의 다음 숫자를 찾아서 변경합니다.

반복의 힘: . 명령어

.마지막 편집 동작을 반복합니다. Vim에서 가장 강력한 키 중 하나입니다.

패턴 1: 검색 + 반복 치환

1. * → 커서 아래 단어 검색
2. ciw → 단어 변경
3. 새 단어 → 입력
4. Esc → Normal 복귀
5. n → 다음 결과
6. . → 같은 변경 반복

n.n.를 반복하면 원하는 것만 골라서 치환할 수 있습니다. :%s와 달리 하나씩 확인하면서 바꿀 수 있습니다.

패턴 2: 여러 줄에 같은 편집

1. A; → 줄 끝에 세미콜론 추가
2. Esc
3. j → 다음 줄
4. . → 같은 동작 반복

패턴 3: 점진적 들여쓰기

1. >> → 들여쓰기
2. . → 한 레벨 더
3. . → 한 레벨 더

.과 잘 어울리는 명령어

명령어.으로 반복하면
ciw + 입력다른 단어를 같은 내용으로 변경
A;줄 끝에 세미콜론 추가
I// 줄 앞에 주석 추가
dd줄 삭제
>>들여쓰기
r{char}문자 교체

텍스트 오브젝트

i(inner)는 안쪽만, a(around)는 주변 공백이나 구분자를 포함합니다.

오브젝트inner (i)around (a)대상
단어iwawword
WORDiWaWWORD (공백 구분)
문장isassentence
문단ipapparagraph
"i"a""큰따옴표"
'i'a''작은따옴표'
`i`a``백틱`
( / )i(a((소괄호)
{ / }i{a{{중괄호}
[ / ]i[a[[대괄호]
< / >i<a<<꺾쇠>
태그itatHTML 태그

실전 조합:

ciw → 단어 내용 변경
da" → 따옴표 포함 삭제
vi( → 괄호 안 Visual 선택
yat → HTML 태그 전체 복사
>ip → 현재 문단 들여쓰기
gUiw → 단어 대문자로

Visual 모드 기본

세 가지 Visual 모드

모드선택 단위
vVisual문자 단위
VVisual Line줄 단위
Ctrl-vVisual Block사각형 블록

선택 확장

동작
o선택 영역의 양 끝을 토글 (시작↔끝 커서 이동)
OVisual Block에서 같은 줄의 반대쪽 모서리로

o는 선택을 시작한 후 반대 방향으로도 확장하고 싶을 때 씁니다.

모드 전환

선택을 유지한 채 모드를 바꿀 수 있습니다. vV로 문자에서 줄로, VCtrl-v로 줄에서 블록으로 바꿀 수 있습니다.

재선택

동작
gv마지막 Visual 선택 영역을 다시 선택

>로 들여쓰기 후 gv로 재선택하면 한 번 더 들여쓸 수 있습니다. gv>gv> 순서입니다.

선택 + 모션

조합결과
v2w2단어 선택
vit태그 안 선택
vi{중괄호 안 선택
vap문단 선택
v$줄 끝까지 선택

Visual 모드에서 선택 영역 조작

영역을 선택한 후 다음 명령을 사용합니다.

기본 조작

동작
d삭제
c변경 (삭제 후 Insert)
y복사

들여쓰기

동작
>한 레벨 들여쓰기
<한 레벨 내어쓰기
3>3레벨 들여쓰기

V로 여러 줄을 선택하고 >를 누르면 한꺼번에 들여쓸 수 있습니다. .으로 반복할 수 있습니다.

대소문자

동작
~대소문자 토글
U전부 대문자
u전부 소문자

정렬과 외부 명령

동작
=자동 정렬 (indentation)
J선택한 줄 합치기
!sort선택 영역을 알파벳 정렬
!sort -n숫자 정렬
!column -t컬럼 정렬 (표 형태)

ex 명령

선택 후 :를 누르면 :'<,'>가 자동 입력됩니다. 선택 범위에 ex 명령을 적용할 수 있습니다.

:'<,'>s/old/new/g " 선택 영역에서 치환
:'<,'>normal A; " 선택한 모든 줄 끝에 ; 추가
:'<,'>normal I// " 선택한 모든 줄 앞에 // 추가

컬럼 편집 심화 (Visual Block)

Ctrl-v로 진입하는 Visual Block 모드는 사각형 영역을 선택합니다. 일반 에디터의 "멀티 커서"와 비슷한 결과를 만들 수 있습니다.

컬럼 선택

Ctrl-v → Visual Block 진입
j/k → 위아래 행 확장
l/h → 좌우 열 확장
$ → 각 줄 끝까지 (줄 길이 달라도 OK)

다중 행 앞에 삽입

여러 줄 앞에 동시에 텍스트를 넣습니다.

1. Ctrl-v → Visual Block 진입
2. jjj → 4줄 선택
3. I → 맨 앞에 Insert
4. // (스페이스) → 입력
5. Esc → 모든 줄에 적용

예: 주석 추가

실행 전: 실행 후:
let a = 1 // let a = 1
let b = 2 // let b = 2
let c = 3 // let c = 3
let d = 4 // let d = 4

다중 행 끝에 추가

1. Ctrl-v → Visual Block 진입
2. jjj → 4줄 선택
3. $ → 각 줄 끝까지 (길이 달라도 OK)
4. A → 끝에 Append
5. ; → 입력
6. Esc → 모든 줄에 적용

컬럼 삭제

선택한 사각형 영역을 통째로 삭제합니다.

1. Ctrl-v → Visual Block 진입
2. jjj → 행 선택
3. ll → 열 확장
4. d → 삭제

예: 들여쓰기 일괄 제거

실행 전: 실행 후:
line 1 line 1
line 2 line 2
line 3 line 3

4칸 공백 위치에 Ctrl-vjjllld.

예: 주석 일괄 제거

실행 전: 실행 후:
// let a = 1 let a = 1
// let b = 2 let b = 2
// let c = 3 let c = 3

// 위에 Ctrl-vjjlld.

컬럼 치환

선택한 사각형 영역을 새로운 텍스트로 교체합니다.

1. Ctrl-v → Visual Block 진입
2. jjj → 행 선택
3. e → 단어 끝까지 열 확장
4. c → 변경
5. newText → 새 텍스트 입력
6. Esc → 모든 줄에 적용

컬럼 문자 교체

선택 영역의 모든 문자를 같은 문자로 바꿉니다.

1. Ctrl-v → 블록 선택
2. r- → 모든 문자를 -로 교체

구분선을 만들거나, 공백을 특정 문자로 채울 때 유용합니다.

컬럼 들여쓰기

1. Ctrl-v → Visual Block 진입
2. jjj → 행 선택
3. > → 들여쓰기
4. gv → 재선택
5. > → 한 레벨 더

또는 Vjjj3>로 3레벨 한 번에 들여쓰기.

컬럼 대소문자

Ctrl-v → 영역 선택 → ~ (토글)
Ctrl-v → 영역 선택 → U (대문자)
Ctrl-v → 영역 선택 → u (소문자)

순차 번호 매기기

g Ctrl-a는 Visual Block으로 선택한 숫자들을 순차적으로 증가시킵니다.

실행 전 (모두 0): 실행 후:
0. item 1. item
0. item 2. item
0. item 3. item
0. item 4. item

방법: Ctrl-vjjj (0들을 선택) → g Ctrl-a.

실전 예제 모음

상황방법
여러 줄 앞에 // 주석Ctrl-vjjjI// Esc
여러 줄 // 주석 제거Ctrl-vjjjlld
여러 줄 끝에 ; 추가Ctrl-vjjj$A;Esc
CSV 특정 컬럼 삭제해당 열에 Ctrl-vGd
변수명 일괄 교체해당 열에 Ctrl-vjjjec → 새 이름 → Esc
여러 줄 들여쓰기Ctrl-vjjj> (또는 Vjjj>)
0부터 순차 번호0을 입력 → 복사해서 여러 줄 → Ctrl-v로 선택 → g Ctrl-a
HTML 속성 일괄 추가태그 뒤에 Ctrl-vjjjA class="new"Esc

검색과 치환

검색

동작
/pattern아래 방향 검색
?pattern위 방향 검색
n다음 결과
N이전 결과
*커서 아래 단어 검색 (아래로)
#커서 아래 단어 검색 (위로)

치환

:s/old/new/ " 현재 줄, 첫 번째만
:s/old/new/g " 현재 줄, 전체
:%s/old/new/g " 파일 전체
:%s/old/new/gc " 파일 전체, 하나씩 확인
:'<,'>s/old/new/g " Visual 선택 영역
:5,20s/old/new/g " 5~20번째 줄

정규식 기초

패턴의미
.아무 문자 1개
.*아무 문자 0개 이상
\d숫자
\w영문/숫자/밑줄
\s공백
^줄 시작
$줄 끝
\(\)캡처 그룹
\1첫 번째 캡처 역참조

예: 순서 뒤집기

:%s/\(\w\+\), \(\w\+\)/\2, \1/g
" "Kim, John" → "John, Kim"

다중 줄 일괄 편집 (ex 명령)

특정 줄 범위나 패턴에 매칭되는 줄에 일괄 명령을 적용합니다.

줄 범위 조작

명령동작
:5,20d5~20줄 삭제
:5,20y5~20줄 복사
:10,20m3010~20줄을 30줄 뒤로 이동
:10,20co3010~20줄을 30줄 뒤에 복사

:g (global)로 패턴 매칭 일괄 실행

:g/pattern/d " pattern 포함 줄 전부 삭제
:v/pattern/d " pattern 미포함 줄 전부 삭제 (v = inverse)
:g/TODO/normal A; " TODO 포함 줄 끝에 ; 추가
:g/^$/d " 빈 줄 전부 삭제
:g/console.log/d " console.log 줄 전부 삭제

:normal로 줄 범위에 Normal 명령 적용

:5,20normal >> " 5~20줄 들여쓰기
:5,20normal I// " 5~20줄 앞에 // 추가
:5,20normal A; " 5~20줄 끝에 ; 추가
:5,20normal dd " 5~20줄 삭제

:g:normal을 조합하면 Vim 안에서 거의 모든 일괄 편집이 가능합니다.

매크로

매크로는 키 입력을 녹화하고 재생하는 기능입니다.

기본 사용법

1. qa → a 레지스터에 녹화 시작
2. (편집 동작) → 원하는 작업 수행
3. q → 녹화 종료
4. @a → 매크로 실행
5. @@ → 마지막 매크로 반복
6. 10@a → 10회 반복 실행

실전 예제: CSV를 SQL INSERT로 변환

원본:

John,30,Seoul
Jane,25,Busan

변환 결과:

INSERT INTO users VALUES ('John', 30, 'Seoul');
INSERT INTO users VALUES ('Jane', 25, 'Busan');

첫 줄에서 매크로를 녹화하고, 나머지 줄에 @a로 적용하면 됩니다.

재귀 매크로

매크로 안에서 자기 자신을 호출할 수도 있습니다.

qaq → a 레지스터 초기화
qa → 녹화 시작
(편집 동작)
j → 다음 줄
@a → 자기 자신 호출
q → 녹화 종료
@a → 실행 (파일 끝까지 반복)

파일 끝에 도달하면 j가 실패해서 자동으로 멈춥니다.

레지스터

Vim은 삭제하거나 복사한 텍스트를 여러 레지스터에 저장합니다.

레지스터이름용도
""무명마지막 d/c/y 결과
"0yank마지막 y 결과만 (d/c 제외)
"1~"9숫자최근 삭제 이력
"+시스템 클립보드OS 복사/붙여넣기
"a~"z이름 지정사용자 지정 저장
"A~"Z이름 추가기존 레지스터에 추가(append)
"_블랙홀삭제해도 어디에도 저장 안 됨
"/검색마지막 검색 패턴
":명령마지막 ex 명령
".입력마지막 Insert 입력

사용법

"ay → a 레지스터에 복사
"ap → a 레지스터에서 붙여넣기
"+y → 시스템 클립보드에 복사
"+p → 시스템 클립보드에서 붙여넣기
"_dd → 줄 삭제하되 레지스터 건드리지 않기
:reg → 모든 레지스터 확인

"0이 유용한 상황: yy로 복사한 후 dd로 다른 줄을 삭제하면, p는 삭제한 줄을 붙여넣습니다. 원래 복사한 줄을 붙여넣으려면 "0p.

마크

파일 내 위치를 북마크처럼 저장합니다.

동작
m{a-z}현재 위치를 로컬 마크로 저장
m{A-Z}현재 위치를 글로벌 마크로 저장 (파일 간 이동 가능)
`a마크 a의 정확한 위치로 이동
'a마크 a가 있는 줄의 처음으로 이동
:marks마크 목록 보기

자동 마크

Vim이 자동으로 설정하는 마크:

마크위치
`.마지막 편집 위치
`"마지막으로 파일을 닫았던 위치
`[마지막 변경/붙여넣기 시작
`]마지막 변경/붙여넣기 끝
''마지막 점프 이전 위치

''(작은따옴표 두 번)는 "아까 있던 곳으로 돌아가기"입니다. 검색이나 G로 이동한 후 원래 위치로 돌아갈 때 씁니다.

윈도우/버퍼/탭

버퍼

열려 있는 파일 목록입니다.

명령동작
:e file파일 열기
:ls열린 버퍼 목록
:bn / :bp다음 / 이전 버퍼
:bd현재 버퍼 닫기
:b{n}n번 버퍼로 이동
:b name이름으로 버퍼 이동 (탭 자동완성)

윈도우

화면을 분할해서 여러 파일을 동시에 봅니다.

명령동작
:split / :sp가로 분할
:vsplit / :vs세로 분할
Ctrl-w h/j/k/l윈도우 간 이동
Ctrl-w =크기 동일하게
Ctrl-w o현재 윈도우만 남기고 닫기
Ctrl-w > / <너비 조절
Ctrl-w + / -높이 조절
Ctrl-w r윈도우 위치 회전

명령동작
:tabnew새 탭
gt / gT다음 / 이전 탭
:tabclose탭 닫기
{n}gtn번 탭으로 이동

접기 (Folding)

코드의 일부를 접어서 숨길 수 있습니다.

동작
zf{motion}접기 생성 (예: zfap 문단 접기)
za접기 토글
zo접기 열기
zc접기 닫기
zR전체 열기
zM전체 닫기
zj다음 접기로 이동
zk이전 접기로 이동

실전 편집 레시피

"이걸 하고 싶은데 어떻게 하지?"라는 상황에 맞춘 단축키 조합입니다.

하고 싶은 것방법
이 단어를 전부 바꾸고 싶다*ciw → 새 단어 → Esc → n. 반복
여러 줄 앞에 주석 추가Ctrl-vjjjI// → Esc
여러 줄 주석 제거Ctrl-vjjjlld
괄호 안 내용만 교체ci(
따옴표 안 내용 복사yi"
태그 안 내용 삭제dit
현재 줄 복제yyp
두 줄 순서 바꾸기ddp
여러 줄 들여쓰기Vjjj> (반복: .)
여러 줄 내어쓰기Vjjj<
선택 영역 정렬V → 선택 → !sort
단어를 대문자로gUiw
줄 전체 대문자로gUU
0~N 순차 번호 매기기0 여러 줄 → Ctrl-v로 선택 → g Ctrl-a
함수 블록 전체 복사여는 {에서 V%y
빈 줄 전부 삭제:g/^$/d
TODO 줄만 남기기:v/TODO/d
console.log 전부 삭제:g/console.log/d
5~20줄에 주석 추가:5,20normal I//

마치며

이 글에서 다룬 것은 모두 Vi/Vim에서 동작하는 보편적인 단축키라서 Neovim, Vim, Vi 어디서든 쓸 수 있어요.

기억할 것은 세 가지예요.

  1. 문법 이해 — 동사 + 명사 조합으로 암기량 줄이기
  2. . 습관화 — 반복 가능한 편집으로 생산성 높이기
  3. Visual Block 익히기 — 멀티 커서 없이 다중 행 편집하기

다음 글에서는 LSP, 터미널, 플러그인, 마우스 활용까지 Neovim만의 단축키를 다뤄요.

Claude HUD 상태 표시줄

· 약 3분

Claude Code를 쓸 때 궁금한 것들

Claude Code로 작업하다 보면 이런 생각이 들어요:

  • 컨텍스트를 얼마나 썼지? 곧 한계에 도달하나?
  • 지금 무슨 도구를 실행 중이지? 파일을 읽고 있나, 편집 중인가?
  • 서브에이전트가 돌고 있는데 얼마나 걸리고 있지?
  • 할 일 목록에서 몇 번째까지 끝났지?

이 정보들을 터미널 하단에 항상 보여주는 플러그인이 Claude HUD예요. GitHub Stars 18.7k로 Claude Code 플러그인 중 가장 인기가 많아요.

뭘 보여주는가

Claude HUD는 터미널 입력창 바로 아래에 상태 표시줄을 추가합니다.

[Opus] │ my-project git:(main*)
Context █████░░░░░ 45% │ Usage ██░░░░░░░░ 25% (1h 30m / 5h)

기본 표시 항목

항목설명
모델명현재 사용 중인 모델 (Opus, Sonnet 등)
프로젝트 경로작업 디렉토리 (depth 조절 가능)
Git 브랜치현재 브랜치 + dirty 상태(*), ahead/behind 표시
Context 바컨텍스트 윈도우 사용률 (1M 토큰 기준)
Usage 바구독 사용량 (주간 한도 대비)

선택 표시 항목

설정으로 다음 정보를 추가할 수 있습니다.

도구 활동

◐ Edit: auth.ts | ✓ Read ×3 | ✓ Grep ×2

지금 Claude가 어떤 파일을 편집 중인지, 몇 번 읽기/검색을 했는지 실시간으로 보입니다.

에이전트 상태

◐ explore [haiku]: Finding auth code (2m 15s)

서브에이전트가 돌고 있을 때 어떤 에이전트가 뭘 하고 있는지, 얼마나 걸렸는지 표시합니다.

할 일 진행률

▸ Fix authentication bug (2/5)

TaskCreate로 만든 할 일 목록의 진행 상황을 보여줍니다.

왜 유용한가

1. 컨텍스트 관리

Claude Code는 대화가 길어지면 컨텍스트 윈도우가 차오릅니다. 가득 차면 이전 내용이 압축되면서 맥락을 잃을 수 있습니다. Context 바를 보면서 적절한 시점에 새 대화를 시작할 수 있습니다.

2. 대기 시간 활용

Claude가 뭘 하고 있는지 보이면, 기다릴지 다른 일을 할지 판단할 수 있습니다. "에이전트가 탐색 중이니 2분은 걸리겠군" 같은 판단이 가능해집니다.

3. 사용량 추적

구독 사용자라면 주간 사용량 한도가 있습니다. Usage 바를 보면서 속도를 조절할 수 있습니다. 80% 이상이면 경고 표시도 됩니다.

설치

Claude Code 안에서 3줄이면 됩니다.

/plugin marketplace add jarrodwatts/claude-hud
/plugin install claude-hud
/claude-hud:setup

setup에서 프리셋을 선택할 수 있습니다.

프리셋표시 내용
Full도구, 에이전트, 할 일, Git, 사용량 전부 표시
Essential활동 상태 + Git, 적당한 밀도
Minimal모델명 + 컨텍스트 바만

설치 후 Claude Code를 재시작하면 적용됩니다.

설정 커스터마이징

~/.claude/plugins/claude-hud/config.json에서 세부 설정을 조정할 수 있습니다.

{
"language": "en",
"lineLayout": "expanded",
"pathLevels": 1,
"gitStatus": { "enabled": true },
"display": {
"showTools": true,
"showAgents": true,
"showTodos": true,
"showDuration": false,
"showMemoryUsage": false,
"showCost": false
}
}

레이아웃

  • expanded: 여러 줄로 펼쳐서 표시 (정보 밀도 높음)
  • compact: 한 줄로 압축 (화면 절약)

경로 깊이

pathLevels: 1 → my-project
pathLevels: 2 → apps/my-project
pathLevels: 3 → dev/apps/my-project

색상

named color(dim, red, green 등), 256-color 번호(0-255), hex 값(#rrggbb) 모두 지원합니다.

동작 원리

Claude HUD는 Claude Code의 statusline API를 사용합니다.

  • Claude Code가 약 300ms 간격으로 세션 데이터를 JSON으로 전달
  • HUD가 이를 파싱해서 컨텍스트 사용량, 토큰 속도 등을 계산
  • transcript JSONL 파일을 읽어 도구 사용, 에이전트, 할 일 상태를 추출
  • 포맷팅된 텍스트를 터미널 하단에 출력

별도 창이나 tmux 없이, Claude Code 터미널 자체에 통합됩니다.

요구사항

  • Claude Code v1.0.80 이상
  • Node.js 18+ 또는 Bun

마치며

Claude Code를 많이 쓸수록 "지금 뭘 하고 있는지"가 중요해집니다. 특히 서브에이전트를 여러 개 띄우거나, 긴 작업을 시킬 때 HUD가 있으면 체감이 크게 다릅니다.

설치에 1분, 설정에 1분이면 되니까 한번 써보는 걸 추천해요.

GitHub: jarrodwatts/claude-hud

Obsidian CLI 자동화

· 약 6분

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

  1. Obsidian CLI 입문: 터미널에서 노트를 다루는 새로운 방법
  2. Obsidian CLI 비교: 공식 CLI vs URI vs 직접 파일 조작
  3. Obsidian CLI 자동화: 스크립트와 워크플로우 실전 가이드 ← 현재 글

왜 자동화하는가

노트를 꾸준히 쓰기는 어렵습니다. 데일리 노트를 열고, 양식을 채우고, 태스크를 옮기는 작업은 반복적이고 귀찮습니다. 귀찮으면 안 하게 되고, 안 하면 쌓이고, 쌓이면 포기합니다.

자동화의 핵심은 반복적인 부분을 제거해서 본질(기록)에만 집중하게 만드는 것입니다.

Obsidian CLI 덕분에 이 반복 작업을 셸 스크립트로 만들 수 있습니다.

셸 별칭과 함수

~/.zshrc에 추가해두면 매일 쓰게 되는 것들:

기본 별칭

# 데일리 노트 단축키
alias dn="obsidian daily"
alias dna="obsidian daily:append"

# 빠른 검색
alias qs="obsidian search"

# 태그 현황
alias tags="obsidian tags counts"

memo() 빠른 메모

타임스탬프와 함께 데일리 노트에 한 줄 메모를 추가합니다.

memo() {
local timestamp=$(date +%H:%M)
obsidian daily:append content="- **${timestamp}** $*"
}
# 사용
memo "내일 블로그 배포 일정 확인할 것"
memo "obsidian CLI 포스트 초안 완성"

# 데일리 노트에 이렇게 추가된다:
# - **14:23** 내일 블로그 배포 일정 확인할 것
# - **15:10** obsidian CLI 포스트 초안 완성

til() TIL 노트 자동 생성

오늘 배운 것(Today I Learned)을 빠르게 기록합니다.

til() {
local today=$(date +%Y-%m-%d)
local slug=$(echo "$1" | tr ' ' '-' | tr '[:upper:]' '[:lower:]')
obsidian create name="TIL/${today}-${slug}" content="---
tags: [til]
date: ${today}
---
# $1

$2"
}
# 사용
til "Obsidian CLI" "터미널에서 데일리 노트를 관리할 수 있다"

cron으로 정기 작업

반복 스케줄이 정해진 작업은 cron에 맡깁니다.

매일 아침 데일리 노트 자동 생성

# crontab -e
0 9 * * * obsidian daily

매일 오전 9시에 데일리 노트가 자동 생성됩니다. Obsidian을 열면 이미 오늘 노트가 준비되어 있습니다.

주간 리뷰 노트

# 매주 금요일 오후 5시에 주간 리뷰 노트 생성
0 17 * * 5 obsidian create name="Review/$(date +\%Y-W\%V)" template="Templates/weekly-review"

전일 태스크 알림

# 매일 오전 9시 30분에 어제 미완료 태스크 확인
30 9 * * * obsidian tasks daily --offset=-1

주의: cron에서 Obsidian CLI가 동작하려면, Obsidian 데스크톱 앱이 이미 실행 중이어야 합니다. macOS라면 로그인 시 Obsidian이 자동 실행되도록 설정해두면 됩니다.

macOS sleep 함정: 노트북이 sleep 상태일 때는 cron 자체가 발사되지 않습니다. 데스크톱이라면 무관하지만 노트북에서 매일 9시 데일리 노트가 안 만들어지면 이 때문입니다. caffeinate -s로 일시 차단하거나, 아예 launchd (launchctl)의 StartCalendarInterval로 옮기면 sleep 후 깨어났을 때 한 번 따라잡습니다.

vault 경로: Obsidian CLI는 OBSIDIAN_VAULT 환경변수 또는 ~/.obsidian-cli/config.jsonvault 항목으로 vault 위치를 찾습니다. cron 환경은 셸 환경변수를 자동 상속받지 않으므로, crontab 상단에 OBSIDIAN_VAULT=/Users/<you>/Documents/MyVault 한 줄을 명시해야 안전합니다.

tmux 연동

tmux를 쓰고 있다면, Obsidian CLI와의 조합이 강력합니다.

작업용 레이아웃

tmux 패널 하나를 Obsidian 전용으로 쓰면, 코드를 쓰면서 노트를 바로 확인할 수 있습니다.

┌──────────────────┬──────────────────┐
│ Neovim │ obsidian read │
│ │ │
├──────────────────┼──────────────────┤
│ Shell │ obsidian search │
│ │ │
└──────────────────┴──────────────────┘

좌상: 코드 편집, 우상: 노트 확인, 좌하: 빌드/테스트, 우하: 검색

자동화 스크립트

프로젝트 시작 시 tmux 워크스페이스를 한 번에 구성하는 스크립트입니다.

#!/bin/bash
# obsidian-workspace.sh

SESSION="obsidian-work"

tmux new-session -d -s $SESSION

# 왼쪽: 에디터
tmux send-keys -t $SESSION "nvim" C-m

# 오른쪽: Obsidian 노트
tmux split-window -h -t $SESSION
tmux send-keys -t $SESSION "obsidian read path='Projects/current.md'" C-m

# 하단: 터미널
tmux split-window -v -t $SESSION:0.0
tmux send-keys -t $SESSION "obsidian daily" C-m

tmux attach -t $SESSION

tmux에서 빠른 메모

tmux의 아무 패널에서 memo 함수를 실행하면 작업 흐름을 끊지 않고 메모를 남길 수 있습니다. 에디터를 떠나지 않아도 됩니다.

Git 연동

커밋 로그를 데일리 노트에 자동 기록

git의 post-commit hook을 활용합니다.

#!/bin/bash
# .git/hooks/post-commit

COMMIT_MSG=$(git log -1 --pretty=format:"%s")
REPO_NAME=$(basename $(git rev-parse --show-toplevel))

obsidian daily:append content="- **commit** \`${REPO_NAME}\`: ${COMMIT_MSG}"

hook에 실행 권한을 부여합니다.

chmod +x .git/hooks/post-commit

이제 커밋할 때마다 데일리 노트에 자동으로 기록됩니다.

- **commit** `blog-repo`: Obsidian CLI 시리즈 포스트 추가
- **commit** `blog-repo`: 오타 수정

하루가 끝나면 데일리 노트에 오늘의 작업 이력이 쌓여 있습니다.

프로젝트 노트 자동 생성

새 프로젝트를 시작할 때 git repo와 Obsidian 노트를 동시에 만드는 함수입니다.

new-project() {
local name=$1
mkdir -p ~/Projects/$name && cd ~/Projects/$name
git init
obsidian create name="Projects/$name" template="Templates/project"
echo "프로젝트 '$name' 생성 완료 — git repo + Obsidian 노트"
}

실전 워크플로우 예제

아침 루틴 스크립트

매일 아침 실행하는 스크립트 하나로 하루를 시작합니다.

#!/bin/bash
# morning.sh — 아침 루틴

echo "☀️ 아침 루틴 시작"

# 1. 오늘 데일리 노트 생성
obsidian daily
echo "✓ 데일리 노트 생성"

# 2. 어제 미완료 태스크 확인
echo ""
echo "📋 어제 미완료 태스크:"
obsidian tasks daily --offset=-1

# 3. 태그 현황
echo ""
echo "🏷️ 태그 현황:"
obsidian tags counts
# 실행
chmod +x morning.sh
./morning.sh

프로젝트 대시보드 생성

볼트의 현재 상태를 요약하는 대시보드 노트를 자동 생성합니다.

#!/bin/bash
# dashboard.sh — 프로젝트 대시보드 갱신

VAULT=~/Documents/MyVault
TODAY=$(date +%Y-%m-%d)

# 통계 수집
TOTAL_NOTES=$(find $VAULT -name "*.md" | wc -l | tr -d ' ')
TODO_COUNT=$(grep -rc "\- \[ \]" $VAULT --include="*.md" | awk -F: '{sum+=$2} END{print sum}')
DONE_COUNT=$(grep -rc "\- \[x\]" $VAULT --include="*.md" | awk -F: '{sum+=$2} END{print sum}')

# 최근 수정된 노트 (상위 5개)
RECENT=$(find $VAULT -name "*.md" -mtime -1 -exec basename {} .md \; | head -5 | sed 's/^/- /')

# 대시보드 노트 갱신
obsidian create name="Dashboard" content="---
tags: [dashboard]
---
# 프로젝트 대시보드
> 마지막 갱신: ${TODAY}

## 통계
| 항목 | 수 |
|------|-----|
| 전체 노트 | ${TOTAL_NOTES} |
| 미완료 태스크 | ${TODO_COUNT} |
| 완료 태스크 | ${DONE_COUNT} |

## 최근 수정된 노트
${RECENT}
"

cron으로 매일 실행하면 항상 최신 상태의 대시보드를 유지할 수 있습니다.

마치며

Obsidian CLI의 진짜 가치는 명령어 자체보다 자동화 가능성에 있습니다.

셸 별칭으로 매일 쓰는 명령을 줄이고, cron으로 반복 작업을 없애고, tmux와 결합해 워크플로우에 녹여넣고, git hook으로 기록을 자동화합니다. 이 모든 것이 CLI가 있기 때문에 가능해졌습니다.

터미널에서 Neovim으로 코드를 쓰고, tmux로 세션을 관리하고, Obsidian CLI로 노트까지 다뤄요. GUI로 전환할 이유가 하나 줄었어요.

참고 자료

Obsidian CLI 비교

· 약 5분

이 글은 Obsidian CLI 시리즈의 두 번째 글입니다.

  1. Obsidian CLI 입문: 터미널에서 노트를 다루는 새로운 방법
  2. Obsidian CLI 비교: 공식 CLI vs URI vs 직접 파일 조작 ← 현재 글
  3. Obsidian CLI 자동화: 스크립트와 워크플로우 실전 가이드

세 가지 접근법

Obsidian을 터미널에서 제어하는 방법은 크게 세 가지입니다.

  1. 공식 CLI: Obsidian v1.12부터 내장된 커맨드라인 도구
  2. URI scheme: obsidian:// 프로토콜로 앱에 명령을 전달
  3. 직접 파일 조작: 볼트가 마크다운 디렉토리라는 점을 이용

각각 동작 방식이 다르고, 잘하는 것과 못하는 것이 다릅니다.

공식 CLI

1편에서 자세히 다뤘습니다. 핵심만 정리하면 다음과 같습니다.

동작 방식

obsidian 명령어 → 로컬 소켓 → Obsidian 프로세스 → 볼트 조작

Obsidian 내부 API를 직접 호출합니다. 파일을 이동하면 위키링크가 자동 업데이트되고, 속성 변경은 인덱스에 즉시 반영됩니다.

강점은 기능이 온전하다는 데 있습니다. 데일리 노트, 태스크, 검색, 태그, 템플릿 등 Obsidian 기능을 그대로 쓸 수 있고, 변경이 캐시와 그래프에 즉시 반영되며, 파일을 옮기거나 이름을 바꿔도 위키링크가 자동으로 따라 업데이트됩니다. Templater나 Dataview 같은 플러그인과도 호환됩니다.

대신 앱에 묶입니다. Obsidian 데스크톱 앱이 켜져 있어야 하고, 그래서 SSH로 접속한 원격 서버에서는 쓸 수 없습니다. 기능 자체가 v1.12 이상에서만 제공되므로 이전 버전 사용자는 업데이트가 필요합니다.

대표 명령어

obsidian daily:append content="메모 내용"
obsidian search query="키워드"
obsidian create name="노트명" template="템플릿"
obsidian tags counts

Obsidian URI Scheme

obsidian://로 시작하는 URL을 통해 Obsidian에 명령을 전달하는 방법입니다.

동작 방식

open "obsidian://..." → OS URL 핸들러 → Obsidian 앱 → 동작 실행

macOS에서는 open 명령어, Linux에서는 xdg-open을 사용합니다.

기본 URI

# 특정 노트 열기
open "obsidian://open?vault=MyVault&file=Projects/blog"

# 새 노트 생성
open "obsidian://new?vault=MyVault&name=Meeting/standup&content=회의%20메모"

# 검색
open "obsidian://search?vault=MyVault&query=TODO"

URL이므로 특수문자는 퍼센트 인코딩이 필요합니다. 한글도 마찬가지:

# 한글 포함 시
open "obsidian://open?vault=MyVault&file=$(python3 -c 'import urllib.parse; print(urllib.parse.quote("프로젝트/일정"))')"

Advanced URI 플러그인

obsidian-advanced-uri 플러그인을 설치하면 URI의 기능이 크게 확장됩니다.

# 데일리 노트에 클립보드 내용 추가
open "obsidian://adv-uri?vault=MyVault&daily=true&clipboard=true&mode=append"

# 특정 헤딩 아래에 내용 추가
open "obsidian://adv-uri?vault=MyVault&filepath=Projects/blog.md&heading=TODO&data=새%20항목&mode=append"

# Obsidian 커맨드 실행
open "obsidian://adv-uri?vault=MyVault&commandid=editor:toggle-bold"

URI 방식의 매력은 진입 장벽이 낮다는 데 있습니다. CLI를 따로 활성화하지 않아도 앱만 설치돼 있으면 동작하고, 브라우저나 Alfred, Raycast, Shortcuts 같은 외부 앱에서 호출할 수 있으며, macOS, Windows, Linux를 모두 지원합니다.

한계도 뚜렷합니다. 기본 URI로는 열기, 생성, 검색 정도만 가능하고, 한글이나 특수문자가 들어가면 URL 인코딩이 번거로워집니다. 무엇보다 명령을 보내기만 할 뿐 결과를 터미널로 받아올 수 없고, 고급 기능은 Advanced URI 플러그인에 의존합니다.

직접 파일 조작 (Vault as Code)

Obsidian 볼트의 본질은 마크다운 파일이 들어 있는 디렉토리입니다. 따라서 표준 유닉스 도구로 직접 조작할 수 있습니다.

사용 예시

# 볼트 구조 확인
ls ~/Documents/MyVault/

# 키워드 검색
grep -r "TODO" ~/Documents/MyVault/ --include="*.md"

# 특정 태그가 달린 파일 찾기
grep -rl "#project" ~/Documents/MyVault/ --include="*.md"

# 노트 생성
cat << 'EOF' > ~/Documents/MyVault/TIL/obsidian-cli.md
---
tags: [til, obsidian]
---
# Obsidian CLI
오늘 배운 것
EOF

# 일괄 태그 변경
find ~/Documents/MyVault -name "*.md" -exec sed -i '' 's/#old-tag/#new-tag/g' {} +

fzf와 조합

# fzf로 노트 선택해서 Neovim으로 열기
find ~/Documents/MyVault -name "*.md" | fzf --preview 'head -20 {}' | xargs nvim

이 방식의 장점은 앱에서 완전히 자유롭다는 것입니다. Obsidian이 설치돼 있지 않아도, SSH로 접속한 원격 서버에서도 동작합니다. grep, sed, awk, find 같은 유닉스 도구를 전부 끌어다 쓸 수 있어 유연성이 가장 크고, 수백 개 파일을 일괄 수정하는 대량 처리도 간편합니다.

문제는 Obsidian을 거치지 않는다는 데서 그대로 나옵니다. 변경 사항은 앱을 다시 열거나 리로드해야 인덱스에 반영되고, 파일을 옮기거나 이름을 바꾸면 위키링크가 자동으로 따라오지 않습니다. Templater나 Dataview 같은 플러그인 기능도 적용되지 않으며, 프론트매터 YAML 포맷은 직접 맞춰야 합니다.

종합 비교

기능공식 CLIURI Scheme직접 파일 조작
앱 실행 필요필수필수불필요
원격 서버 사용지원
검색 결과 반환터미널 출력grep 등
위키링크 자동 업데이트지원
인덱스 즉시 반영지원지원지연
플러그인 연동지원일부
템플릿 지원지원Advanced URI수동
대량 일괄 처리루프 필요지원
외부 앱 연동지원
설정 난이도낮음낮음없음

이럴 때는 이것을

  • 터미널에서 빠르게 노트를 쓰고 싶다 → 공식 CLI
  • 브라우저나 Alfred에서 Obsidian을 열고 싶다 → URI scheme
  • 수백 개 파일의 태그를 일괄 수정하고 싶다 → 직접 파일 조작
  • 서버에서 마크다운 파일을 관리하고 싶다 → 직접 파일 조작
  • 데일리 노트를 자동으로 만들고 싶다 → 공식 CLI
  • Shortcuts/Raycast에서 Obsidian 연동 → URI scheme

조합해서 쓰기

실제로는 하나만 쓰기보다 상황에 따라 조합하는 편이 낫습니다.

예시 워크플로우는 다음과 같습니다.

# 1. 공식 CLI로 데일리 노트 작성 (인덱스 반영 + 템플릿 적용)
obsidian daily:append content="## 작업 로그"

# 2. grep으로 볼트 전체에서 미완료 태스크 카운트 (대량 검색에 강함)
grep -rc "\- \[ \]" ~/Documents/MyVault/ --include="*.md" | sort -t: -k2 -rn | head -5

# 3. URI로 특정 노트를 Obsidian GUI에서 열기 (그래프 뷰 등 GUI 기능 활용)
open "obsidian://open?vault=MyVault&file=Projects/dashboard"

각 방법의 강점을 살려서 쓰면 됩니다. CLI로 안 되는 대량 처리는 직접 파일 조작으로, 터미널에서 안 되는 GUI 기능은 URI로 보완합니다.

마치며

세 방법은 맞고 틀리고의 문제가 아니라 각각 잘하는 것이 다르고, 상황에 따라 최적의 도구가 달라집니다.

일상적인 노트 작성에는 공식 CLI가 가장 편하고, 대량 처리에는 직접 파일 조작이, 외부 앱 연동에는 URI scheme이 낫습니다.

다음 글에서는 이 도구들을 활용한 실전 자동화 워크플로우를 정리해요. 셸 별칭, cron, tmux 연동, git hook까지 다룰게요.

참고 자료

Claude Code 햅틱 알림

· 약 4분

문제: Claude Code는 기다려주지 않는다

Claude Code를 쓰다 보면 이런 상황이 반복돼요.

  1. Claude에게 작업을 시키고 브라우저로 전환
  2. 한참 뒤에 돌아와 보니 "권한 승인 대기 중"에서 멈춰 있습니다
  3. 또는 이미 끝났는데 모르고 한참을 기다립니다

터미널을 계속 쳐다보고 있을 수도 없고, 다른 일을 하자니 Claude가 언제 저를 부르는지 알 수가 없습니다.

그런데 손에 쥐고 있는 마우스가 진동으로 알려준다면?

claude-code-logitech-haptic-plugin

claude-code-logitech-haptic-plugin은 Claude Code의 이벤트를 Logitech MX Master 4의 햅틱 모터와 연결해주는 플러그인입니다.

작동 원리는 단순합니다.

Claude Code 이벤트 → Hook → 쉘 스크립트 → HTTP API → HapticWebPlugin → MX Master 4 진동

Claude Code는 hooks 시스템을 통해 특정 이벤트 발생 시 외부 스크립트를 실행할 수 있습니다. 이 플러그인은 그 hook에 햅틱 API 호출을 연결한 것입니다.

어떤 상황에서 진동하는가

세 가지 이벤트에 각각 다른 진동 패턴이 매핑되어 있습니다.

상황진동 패턴느낌
권한 승인 요청knock톡톡 — 노크하듯 두 번
60초 이상 입력 대기ringing르르르 — 전화 오듯 울림
작업 완료completed뚝 — 깔끔한 완료 펄스

손바닥에 닿아 있는 마우스에서 미세한 진동이 오기 때문에, 다른 모니터를 보거나 문서를 읽고 있어도 바로 알 수 있습니다. 소리 알림과 달리 주변 사람을 방해하지도 않습니다.

MX Master 4 사용자에게 추천하는 이유

MX Master 4는 Logitech 마우스 중 햅틱 스크롤 휠이 탑재된 몇 안 되는 모델입니다. 이 햅틱 모터를 스크롤 피드백 외에도 활용할 수 있게 해주는 것이 HapticWebPlugin이고, 이 플러그인은 그것을 Claude Code와 연결합니다.

이미 MX Master 4를 쓰고 있다면 추가 하드웨어 없이 소프트웨어만으로 완성됩니다.

설치

1단계: HapticWebPlugin 설치

Logi Options+에서 플러그인을 설치합니다.

  1. Logi Options+ 열기 → MX Master 4 선택
  2. HAPTIC FEEDBACK 탭 → 설정(⚙️) 클릭
  3. INSTALL AND UNINSTALL PLUGINS 버튼
  4. 레포의 plugin/HapticWeb.lplug4 파일을 더블클릭
  5. Continue로 설치 완료

설치 확인: haptics.jmw.nz/playground에서 진동 패턴을 테스트해볼 수 있습니다.

2단계: Claude Code 플러그인 설치

git clone https://github.com/ChefJodlak/claude-code-logitech-haptic-plugin.git
cd claude-code-logitech-haptic-plugin
./install.sh

설치 스크립트가 다음 작업을 자동으로 수행합니다.

  • OS 감지
  • HapticWebPlugin 연결 확인
  • ~/.claude/hooks/ 디렉토리에 스크립트 설치
  • ~/.claude/settings.json에 hook 설정 추가
  • 테스트 진동 실행

3단계: Claude Code 재시작

Hook 설정은 시작 시 로드되므로, 실행 중인 Claude Code를 재시작하면 바로 적용됩니다.

진동 패턴 커스터마이징

기본 매핑이 마음에 들지 않으면 ~/.claude/hooks/haptic-trigger.sh에서 변경할 수 있습니다.

WAVEFORM_PERMISSION="knock" # 권한 요청
WAVEFORM_IDLE="ringing" # 입력 대기
WAVEFORM_COMPLETE="completed" # 작업 완료

선택 가능한 진동 패턴은 15가지입니다.

분류패턴
정밀 피드백sharp_collision, damp_collision, subtle_collision, damp_state_change
진행/완료sharp_state_change, completed, firework, happy_alert, wave
긴급 알림angry_alert, mad, square
수신 알림knock, ringing, jingle

작업 완료 시 firework(축하 폭죽 패턴)을 쓰면 기분이 좋아질 수도 있습니다.

진동 강도 조절

Logi Options+ → MX Master 4 → Haptic Feedback → Strength 슬라이더로 조절합니다. Subtle, Low, Medium, High 4단계가 있습니다. API로는 강도 제어가 안 되고 Logi Options+에서만 가능합니다.

동작 확인 및 트러블슈팅

# HapticWebPlugin 동작 확인
curl -s https://local.jmw.nz:41443/

# 수동으로 진동 테스트
curl -X POST -d "" https://local.jmw.nz:41443/haptic/completed

# hook 스크립트 수동 테스트
echo '{"hook_event_name": "Stop"}' | ~/.claude/hooks/haptic-trigger.sh

진동이 안 된다면 다음 사항을 확인합니다.

  • Logi Options+가 실행 중인지 확인
  • MX Master 4가 연결되어 있는지 확인
  • ~/.claude/hooks/haptic-trigger.sh에 실행 권한이 있는지 확인 (chmod +x)

제거

cd claude-code-logitech-haptic-plugin
./uninstall.sh

마치며

이 플러그인의 가치는 기술적 복잡성이 아니라 컨텍스트 스위칭 비용을 줄여준다는 점에 있습니다. Claude Code에게 작업을 시키고 다른 일을 할 수 있게 되면, AI 코딩의 실질적인 생산성이 달라집니다.

MX Master 4 사용자라면 한 번 설치해볼 만해요. 설치에 5분도 안 걸리고, 되돌리기도 쉬워요.

GitHub: ChefJodlak/claude-code-logitech-haptic-plugin

Obsidian CLI 입문

· 약 5분

이 글은 Obsidian CLI 시리즈의 첫 번째 글이에요.

  1. Obsidian CLI 입문: 터미널에서 노트를 다루는 새로운 방법 ← 현재 글
  2. Obsidian CLI 비교: 공식 CLI vs URI vs 직접 파일 조작
  3. Obsidian CLI 자동화: 스크립트와 워크플로우 실전 가이드

문제: 노트 하나 쓰려고 GUI를 열어야 하는가

터미널에서 코드를 쓰다가 떠오른 생각을 메모하고 싶을 때가 있습니다.

  1. ⌘+Tab으로 Obsidian 창을 찾습니다
  2. 데일리 노트를 엽니다
  3. 적당한 위치에 커서를 놓고 입력합니다
  4. 다시 ⌘+Tab으로 터미널로 돌아옵니다

한 줄 메모에 4단계. 이런 상황이 반복되면 점점 메모를 안 하게 됩니다.

자동화도 마찬가지입니다. 매일 데일리 노트를 만들고, 커밋 로그를 기록하고, 태그를 정리하고 싶은데, GUI에서는 이걸 스크립트로 만들 수가 없습니다.

2026년 2월, Obsidian v1.12에서 공식 CLI가 추가됐습니다.

Obsidian CLI란

Obsidian CLI는 Obsidian v1.12.0에서 Early Access로 출시되고, v1.12.4부터 모든 사용자에게 무료로 공개된 공식 커맨드라인 인터페이스입니다.

100개 이상의 명령어를 제공하며, 데일리 노트 작성부터 검색, 태그 관리, 노트 생성, 파일 비교까지 Obsidian의 핵심 기능을 터미널에서 사용할 수 있습니다.

동작 구조는 다음과 같습니다:

터미널 명령어 → CLI → 로컬 소켓 → Obsidian 프로세스 → 볼트 조작

CLI가 Obsidian 내부 API를 통해 작동하기 때문에, 파일을 이동하면 위키링크가 자동으로 업데이트되고, 속성을 변경하면 인덱스에 즉시 반영됩니다. 단, Obsidian 데스크톱 앱이 실행 중이어야 합니다 — 헤드리스 도구가 아닙니다.

활성화

1. CLI 활성화

Obsidian을 열고 Settings → General → Command line interface를 켭니다.

화면의 안내에 따라 시스템 PATH에 CLI를 추가하고, 터미널을 재시작합니다.

2. 설치 확인

obsidian --version

정상이면 버전 번호가 출력됩니다.

3. 볼트 확인

# 등록된 볼트 목록
obsidian vault list

여러 볼트를 사용한다면 기본 볼트를 지정해두면 편리합니다:

# 기본 볼트 설정
obsidian vault set-default "MyVault"

이후 명령어에서 vault= 옵션을 생략할 수 있습니다.

데일리 노트

CLI의 킬러 피처입니다. 터미널에서 데일리 노트를 열고, 내용을 추가하고, 태스크를 확인할 수 있습니다.

기본 사용

# 오늘의 데일리 노트 열기 (없으면 자동 생성)
obsidian daily

# 어제 데일리 노트 열기
obsidian daily --offset=-1

# 특정 날짜
obsidian daily --offset=-7

내용 추가

# 데일리 노트에 텍스트 추가
obsidian daily:append content="## 오후 회의 메모
- 배포 일정 확인
- 버그 #234 논의"

태스크 확인

# 오늘 데일리 노트의 태스크 목록
obsidian tasks daily

실전 예제: git 커밋 로그를 데일리 노트에 기록

터미널에서 하루 작업을 마치고, 오늘의 커밋을 데일리 노트에 자동으로 기록할 수 있습니다:

obsidian daily:append content="## Git 커밋 요약
$(git log --oneline --since='08:00' --until='now')"

이 한 줄이면 됩니다. GUI를 열 필요도, 복사-붙여넣기를 할 필요도 없습니다.

검색과 읽기

볼트 검색

# 키워드로 볼트 전체 검색
obsidian search query="프로젝트 일정"

# 특정 경로 안에서만 검색
obsidian search query="배포" path="Work/"

검색 옵션:

옵션설명예시
query키워드 검색obsidian search query="TODO"
tag태그로 필터링obsidian search tag="#project"
path경로 제한obsidian search query="회의" path="Daily/"

노트 읽기

# 노트 내용을 터미널에 출력
obsidian read path="Projects/blog-migration.md"

# 활성 파일 읽기
obsidian read

터미널에서 노트를 읽을 수 있다는 건, grep이나 fzf와 조합할 수 있다는 뜻입니다.

파일 비교

# 노트의 변경 이력 확인
obsidian diff path="Projects/blog-migration.md"

노트 생성

# 새 노트 생성
obsidian create name="Meeting/2026-04-14-standup"

# 템플릿을 적용해서 생성
obsidian create name="Projects/new-feature" template="Templates/project"

# 내용과 함께 생성
obsidian create name="TIL/obsidian-cli" content="# Obsidian CLI
오늘 배운 것: 터미널에서 노트를 다루는 방법"

GUI에서 템플릿을 쓰는 것과 동일하게, template 옵션으로 지정한 템플릿이 적용됩니다. Obsidian 내부 API를 통해 생성되므로 플러그인(Templater 등)도 정상 작동합니다.

태그 관리

# 볼트의 모든 태그와 사용 횟수
obsidian tags counts

출력 예시:

태그노트 수
#project23
#til45
#meeting12
#blog8

특정 태그가 달린 노트를 찾으려면:

# 태그로 노트 목록 조회
obsidian tags list tag="#project"

태그 사용 현황을 한눈에 파악하고, 정리가 필요한 태그를 빠르게 찾을 수 있습니다.

주요 명령어 정리

명령어설명
obsidian daily오늘의 데일리 노트 열기/생성
obsidian daily:append content="..."데일리 노트에 내용 추가
obsidian daily --offset=-1어제 데일리 노트 열기
obsidian tasks daily데일리 노트의 태스크 목록
obsidian search query="..."볼트 전체 검색
obsidian read path="..."노트 내용을 터미널에 출력
obsidian diff path="..."노트 변경 이력 확인
obsidian create name="..."새 노트 생성
obsidian create ... template=...템플릿으로 노트 생성
obsidian tags counts태그별 사용 횟수
obsidian tags list tag="..."태그로 노트 필터링
obsidian vault list볼트 목록
obsidian vault set-default "..."기본 볼트 설정

마치며

Obsidian CLI는 Obsidian을 터미널 시민으로 만들어줍니다. 생각이 떠오른 순간에서 기록까지의 거리가 짧아집니다.

설정에 1분, 첫 명령어 실행에 10초예요. 이미 Obsidian을 쓰고 있다면 한번 켜보세요.

다음 글에서는 공식 CLI 외에 URI scheme과 직접 파일 조작까지 포함해서, Obsidian을 터미널에서 제어하는 세 가지 방법을 비교해요.

참고 자료

나의 tmux 설정

· 약 7분

tmux란?

tmux(Terminal Multiplexer)는 하나의 터미널 안에서 여러 셸 세션을 관리할 수 있는 도구예요.

왜 쓰는가?

화면 분할은 하나의 터미널을 여러 패널로 나눠서 동시에 작업합니다. 세션 유지는 SSH 연결이 끊어져도 작업이 계속 유지됩니다. 원격 작업은 서버에서 tmux를 띄워두면 언제든 다시 붙을 수 있습니다 (detach/attach). 자동화는 스크립트로 복잡한 윈도우/패널 레이아웃을 한 번에 구성합니다.

설치

# macOS
brew install tmux

# Rocky Linux / CentOS
dnf install -y tmux

# Ubuntu / Debian
apt install -y tmux

기본 개념

tmux
├── Session (세션)
│ ├── Window 1 (윈도우 = 탭)
│ │ ├── Pane 1 (패널 = 분할 영역)
│ │ └── Pane 2
│ └── Window 2
│ └── Pane 1
└── Session 2
└── ...

Session은 독립적인 작업 단위입니다. 프로젝트별로 세션을 만들면 좋습니다. Window는 세션 안의 탭으로, 화면 전체를 차지합니다. Pane은 윈도우를 분할한 패널입니다. 여러 셸을 동시에 볼 수 있습니다.

필수 단축키

tmux의 모든 단축키는 Prefix 키를 먼저 누른 뒤 명령키를 누릅니다.

저는 Prefix를 Ctrl+Space로 설정했어요 (기본값 Ctrl+b도 유지).

세션

동작
tmux new -s 이름새 세션 생성
Prefix d세션에서 분리 (detach)
tmux attach -t 이름세션에 다시 붙기
tmux ls세션 목록
tmux kill-session -t 이름세션 삭제

윈도우

동작
Prefix c새 윈도우
Prefix n / Prefix p다음/이전 윈도우
Prefix 숫자해당 번호 윈도우로 이동
Prefix ,윈도우 이름 변경
Prefix &윈도우 닫기

패널

동작
Prefix %세로 분할
Prefix "가로 분할
Prefix h/j/k/l패널 이동 (내 설정)
Prefix x패널 닫기
Prefix z패널 풀스크린 토글 (zoom)
Prefix Space레이아웃 순환

Prefix z는 유용합니다. 패널 하나를 전체화면으로 확대했다가 다시 원래 크기로 되돌릴 수 있습니다.

저의 tmux.conf 전체 설정

Prefix 키

# Ctrl+Space를 메인 Prefix로, Ctrl+b도 유지
set -g prefix C-Space
set -g prefix2 C-b
bind C-Space send-prefix

기본 Ctrl+b는 손이 불편해요. Ctrl+Space가 훨씬 자연스럽습니다.

기본 옵션

# 히스토리 50000줄 유지
set-option -g history-limit 50000

# 분할 시 현재 경로 유지
bind % split-window -h -c "#{pane_current_path}"
bind '"' split-window -v -c "#{pane_current_path}"

# 트루컬러 지원
set -g default-terminal "tmux-256color"
set -ag terminal-overrides ",xterm-256color:RGB"

# 윈도우 이름 자동 변경 끄기
set -g automatic-rename off

# 마우스 지원
set -g mouse on

# 스크롤 여유
set -g allow-passthrough on

패널 테두리 커스터마이징

# 패널 상단에 인덱스와 명령어 표시
set -g pane-border-format " #{pane_index} #{pane_current_command} "
set -g pane-border-status top
set -g pane-border-lines double

# 활성 패널 강조 (파란색)
set -g pane-active-border-style "fg=#89b4fa"
set -g pane-border-style "fg=#585b70"

# 활성/비활성 윈도우 배경색 차이
set -g window-active-style "bg=#1e1e2e"
set -g window-style "bg=#11111b"

비활성 패널의 배경을 약간 어둡게 하면 현재 작업 중인 패널이 바로 눈에 들어옵니다.

vi 복사 모드

setw -g mode-keys vi

# v로 선택 시작, y로 복사 (시스템 클립보드에)
bind-key -T copy-mode-vi v send-keys -X begin-selection
bind-key -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "pbcopy"
bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "pbcopy"

# 마우스 드래그로 선택 시 클립보드에 복사
bind-key -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-no-clear "pbcopy"

Prefix [로 복사 모드에 진입하면 Vim처럼 v로 선택, y로 복사할 수 있습니다. pbcopy로 macOS 클립보드에 바로 들어갑니다.

마우스 더블클릭으로 단어 선택

# 단어 구분자에서 -./?를 제외 (URL, 경로를 한 덩어리로 선택)
set -g word-separators " =()[]{};,!@#$%^&*+|<>\"'"

# 더블클릭으로 단어 선택
bind -T copy-mode-vi DoubleClick1Pane select-pane \; send-keys -X select-word
bind -n DoubleClick1Pane select-pane \; copy-mode -M \; send-keys -X select-word

마우스 스크롤 속도

# 기본 3줄 → 5줄씩 스크롤
bind-key -T copy-mode-vi WheelUpPane send-keys -X -N 5 scroll-up
bind-key -T copy-mode-vi WheelDownPane send-keys -X -N 5 scroll-down

패널 이동 (hjkl)

bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
bind L last-window # 마지막 윈도우로 이동
bind r source-file ~/.tmux.conf \; display "Reloaded!"

Neovim과 동일한 hjkl 방향키를 사용합니다. Prefix r로 설정을 다시 불러옵니다.

yazi 파일 탐색기 연동

# Prefix + Tab: 오른쪽 50% 분할에서 yazi 실행
bind Tab split-window -h -l 50% -c "#{pane_current_path}" "yazi"

yazi는 터미널 파일 관리자입니다. Prefix Tab으로 현재 디렉토리에서 바로 열 수 있습니다.

플러그인

TPM (Tmux Plugin Manager)

# 설치
git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm

tmux 실행 후 Prefix + I(대문자)로 플러그인을 설치합니다.

사용 중인 플러그인

set -g @plugin 'tmux-plugins/tpm' # 플러그인 매니저
set -g @plugin 'catppuccin/tmux' # 테마
set -g @plugin 'tmux-plugins/tmux-cpu' # CPU 사용률
set -g @plugin 'tmux-plugins/tmux-battery' # 배터리 표시
set -g @plugin 'tmux-plugins/tmux-resurrect' # 세션 저장/복원
set -g @plugin 'tmux-plugins/tmux-continuum' # 자동 저장/복원
플러그인역할
catppuccin/tmuxCatppuccin Mocha 테마
tmux-cpu상태바에 CPU/메모리 사용률 표시
tmux-battery상태바에 배터리 잔량 표시
tmux-resurrectPrefix Ctrl-s로 세션 저장, Prefix Ctrl-r로 복원
tmux-continuum15분마다 자동 저장, tmux 시작 시 자동 복원

tmux-resurrect + continuum

set -g @continuum-restore 'on' # tmux 시작 시 자동 복원
set -g @continuum-save-interval '15' # 15분마다 자동 저장

이 조합 덕분에 시스템을 재시작해도 tmux 세션이 그대로 살아납니다. 윈도우 레이아웃, 패널 구성, 작업 디렉토리까지 복원됩니다.

Catppuccin 테마 + 상태바

set -g @catppuccin_flavor 'mocha'
set -g @catppuccin_window_status_style 'rounded'

상태바 구성

왼쪽: 세션 이름

set -g status-left "#{E:@catppuccin_status_session}"

오른쪽: 디렉토리 → Git 브랜치 → CPU → 메모리 → 배터리 → 시간

set -g status-right "#{E:@catppuccin_status_directory}"
set -ag status-right " #(cd #{pane_current_path}; git rev-parse --abbrev-ref HEAD 2>/dev/null || echo '-') "
set -ag status-right " #{cpu_percentage} "
set -ag status-right " #{ram_percentage} "
set -ag status-right " #{battery_icon} #{battery_percentage} "
set -ag status-right "#{E:@catppuccin_status_date_time}"

상태바에 Git 브랜치가 표시되어서 어떤 브랜치에서 작업 중인지 바로 알 수 있습니다.

실전 활용: 다중 서버 접속 자동화

tmux의 진짜 힘은 스크립트로 레이아웃을 자동 구성할 수 있다는 점입니다.

예를 들어 운영 서버 여러 대에 동시에 접속하는 스크립트를 만들 수 있습니다:

#!/bin/bash
SESSION="my-servers"

# 이미 세션이 있으면 재사용
tmux has-session -t $SESSION 2>/dev/null && {
tmux attach -t $SESSION
exit 0
}

# 윈도우 1: 전체 서버 모니터링 (tiled 레이아웃)
tmux new-session -d -s $SESSION -n "all" "ssh server1"
tmux split-window -t $SESSION:1 "ssh server2"
tmux split-window -t $SESSION:1 "ssh server3"
tmux split-window -t $SESSION:1 "ssh server4"
tmux select-layout -t $SESSION:1 tiled

# 윈도우 2: DB 서버만
tmux new-window -t $SESSION -n "db" "ssh db-primary"
tmux split-window -t $SESSION:2 -h "ssh db-replica"

tmux select-window -t $SESSION:1
tmux attach -t $SESSION

이 스크립트를 실행하면:

┌─────────────┬─────────────┐
│ server1 │ server2 │
├─────────────┼─────────────┤
│ server3 │ server4 │
└─────────────┴─────────────┘

이런 레이아웃이 한 번에 만들어집니다. 저는 이 방식으로 환경별(beta/prod) x 리전별 서버 접속 스크립트를 만들어서 사용하고 있어요.

synchronize-panes

모든 패널에 동일한 명령을 동시에 입력할 수도 있습니다:

# 동기화 모드 토글
Prefix :setw synchronize-panes on

이 상태에서 타이핑하면 모든 패널에 같은 명령이 입력됩니다. 여러 서버에 동시에 같은 작업을 할 때 유용합니다.

설정 적용 방법

1. TPM 설치

git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm

2. tmux.conf 저장

위 설정 내용을 ~/.tmux.conf에 저장합니다.

3. 플러그인 설치

tmux # tmux 실행
# Prefix + I (Shift+i) # 플러그인 설치

4. 설정 리로드

# tmux 안에서
Prefix r

# 또는 터미널에서
tmux source-file ~/.tmux.conf

5. yazi 설치 (선택)

# macOS
brew install yazi

# 기타: https://yazi-rs.github.io/docs/installation

정리

기능설정
PrefixCtrl+Space (+ Ctrl+b)
패널 이동Prefix h/j/k/l
복사 모드vi 스타일 (v 선택, y 복사)
테마Catppuccin Mocha
상태바세션, 디렉토리, Git, CPU, RAM, 배터리, 시간
세션 복원resurrect + continuum (자동)
파일 탐색Prefix Tab (yazi)
설정 리로드Prefix r

tmux는 터미널에서 작업하는 시간이 길수록 효과가 큽니다. 특히 원격 서버 작업이 많다면 세션 유지와 다중 접속 자동화만으로도 생산성이 크게 올라갑니다.

부록: tmux 단축키 & 플러그인 학습 페이지

별도로 정리한 인터랙티브 학습 페이지도 있어요. 단축키를 카테고리별로 정리해두었으니 참고하면 좋습니다.

tmux 단축키 & 플러그인 학습

참고 자료

나의 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 설정 전체 공개

참고 자료

SSH ProxyJump 접속

· 약 4분

문제 상황

클라우드 환경에서 흔히 볼 수 있는 구성이 있어요. 보안을 위해 내부 서버(DB, 앱 서버 등)는 외부에서 직접 접속할 수 없고, Bastion 서버(점프 서버)를 통해서만 접근할 수 있는 구조예요.

[내 PC] → [Bastion 서버] → [내부 서버]
(공인 IP) (사설 IP)

이런 구조에서 내부 서버에 접속하려면 보통 두 단계를 거칩니다.

# 1단계: Bastion 서버에 접속
ssh user@bastion-host

# 2단계: Bastion에서 내부 서버에 접속
ssh user@internal-host

매번 두 번 SSH를 치는 건 번거롭고, 파일 전송(scp)은 더 복잡해집니다. SSH ProxyJump를 사용하면 이 과정을 한 줄로 줄일 수 있습니다.

ProxyJump란?

OpenSSH 7.3(2016년)부터 추가된 기능으로, SSH 접속 시 중간 서버를 점프 호스트로 지정할 수 있습니다. 중간 서버에 셸을 열지 않고, TCP 포워딩만 수행합니다.

주요 특징은 다음과 같습니다.

  • Bastion 서버에 로그인하지 않고 통과만 합니다.
  • 최종 목적지까지 암호화가 유지됩니다(Bastion에서 트래픽을 볼 수 없습니다).
  • 파일 전송(scp, rsync)도 동일하게 동작합니다.

기본 사용법: -J 플래그

ssh -J user@bastion-host user@internal-host

이 한 줄로 Bastion을 거쳐 내부 서버에 바로 접속합니다.

예시

# Bastion(203.0.113.10)을 거쳐 내부 DB 서버(10.0.1.50)에 접속
ssh -J admin@203.0.113.10 dbadmin@10.0.1.50

포트가 다른 경우

# Bastion이 2222 포트를 사용하는 경우
ssh -J admin@203.0.113.10:2222 dbadmin@10.0.1.50

SSH Config로 영구 설정

매번 -J 플래그를 타이핑하는 건 여전히 번거롭습니다. ~/.ssh/config에 설정해두면 ssh internal-db만으로 접속할 수 있습니다.

# Bastion 서버
Host bastion
HostName 203.0.113.10
User admin
IdentityFile ~/.ssh/id_ed25519

# 내부 DB 서버 (Bastion 경유)
Host internal-db
HostName 10.0.1.50
User dbadmin
ProxyJump bastion
IdentityFile ~/.ssh/id_ed25519

이제 다음과 같이 접속합니다.

ssh internal-db

파일 전송도 자연스럽게 동작합니다.

# 내부 서버로 파일 복사
scp backup.sql internal-db:/tmp/

# 내부 서버에서 파일 가져오기
scp internal-db:/var/log/app.log ./

# rsync도 동일
rsync -avz ./deploy/ internal-db:/var/www/app/

다중 점프 (Multi-hop)

Bastion이 여러 단계인 경우도 지원합니다.

[내 PC] → [Bastion 1] → [Bastion 2] → [최종 서버]

-J 플래그 방식

ssh -J user@bastion1,user@bastion2 user@final-host

쉼표로 구분하면 순서대로 점프합니다.

SSH Config 방식

Host bastion1
HostName 203.0.113.10
User admin

Host bastion2
HostName 10.0.1.1
User admin
ProxyJump bastion1

Host final-server
HostName 10.0.2.50
User deploy
ProxyJump bastion2

final-serverbastion2bastion1 → 인터넷 순서의 체인으로 연결됩니다.

ProxyJump vs ProxyCommand

SSH Config에는 비슷한 역할을 하는 ProxyCommand가 있습니다. 차이점은 다음과 같습니다.

# ProxyJump (간단)
Host internal
ProxyJump bastion

# ProxyCommand (구버전 호환)
Host internal
ProxyCommand ssh -W %h:%p bastion
ProxyJumpProxyCommand
도입OpenSSH 7.3+OpenSSH 5.4+
문법간단복잡
다중 점프쉼표로 구분중첩 필요
커맨드라인-J 플래그-o ProxyCommand=...

OpenSSH 7.3 이상이면 ProxyJump를 쓰는 편이 낫습니다. 더 간단하고 다중 점프도 깔끔합니다.

OpenSSH 버전은 ssh -V로 확인합니다.

실전 팁

1. Bastion 서버에 에이전트 포워딩

내부 서버 접속 시 로컬의 SSH 키를 사용하려면 에이전트 포워딩을 활성화합니다.

Host bastion
HostName 203.0.113.10
User admin
ForwardAgent yes

보안 주의: ForwardAgent는 Bastion 서버의 root가 에이전트 소켓에 접근할 수 있으므로, 신뢰할 수 있는 서버에서만 사용해야 합니다.

2. 접속 끊김 방지 (KeepAlive)

점프 호스트를 거치면 타임아웃이 발생하기 쉽습니다.

Host *
ServerAliveInterval 60
ServerAliveCountMax 3

60초마다 keepalive 패킷을 보내서 연결을 유지합니다.

3. 포트 포워딩과 함께 사용

Bastion을 거쳐 내부 DB 포트를 로컬로 포워딩할 수도 있습니다.

# 내부 PostgreSQL(5432)을 로컬 15432로 포워딩
ssh -J bastion -L 15432:10.0.1.50:5432 admin@10.0.1.50 -N

이제 localhost:15432로 내부 DB에 접속할 수 있습니다.

psql -h localhost -p 15432 -U postgres

SSH Config로 설정하면 더 깔끔합니다.

Host db-tunnel
HostName 10.0.1.50
User dbadmin
ProxyJump bastion
LocalForward 15432 localhost:5432
ssh -N db-tunnel
# 다른 터미널에서: psql -h localhost -p 15432 -U postgres

정리

상황명령어
단일 점프ssh -J bastion internal
다중 점프ssh -J bastion1,bastion2 final
파일 전송scp -J bastion file internal:/path
포트 포워딩ssh -J bastion -L 15432:db:5432 db-host -N

SSH ProxyJump는 Bastion 서버 환경에서 꼭 알아둘 만한 기능이에요. SSH Config에 한 번 설정해두면 내부 서버도 직접 연결된 것처럼 쓸 수 있어요.