본문으로 건너뛰기

Neovim 중급 활용

· 약 4분

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

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

텍스트 오브젝트

Vim의 진짜 힘은 동사 + 대상 조합입니다. 텍스트 오브젝트는 "대상"에 해당합니다.

문법

{동사}{범위}{대상}

동사는 d(삭제), c(변경), y(복사), v(선택)이며, 범위는 i(inner, 안쪽), a(around, 바깥 포함)입니다. 대상은 w(단어), ", ', ), }, ], t(태그) 등입니다.

실전 예시

커서가 단어 중간에 있을 때:

명령동작
ciw단어를 지우고 입력 모드 (Change Inner Word)
diw단어 삭제
yiw단어 복사

따옴표 안에 커서가 있을 때:

명령동작
ci"따옴표 안의 내용을 지우고 입력
di"따옴표 안의 내용 삭제
da"따옴표까지 포함해서 삭제

괄호 안에 커서가 있을 때:

명령동작
ci(괄호 안 내용을 변경
da{중괄호를 포함해서 삭제
vi[대괄호 안 내용을 선택

i vs a의 차이

print("hello world")

커서가 hello 위에 있을 때:

  • di"print("") (따옴표 안 내용만 삭제)
  • da"print() (따옴표까지 삭제)

텍스트 오브젝트를 익히면 편집 속도가 비약적으로 올라갑니다.

버퍼, 윈도우, 탭

버퍼

파일을 열면 버퍼에 로드됩니다. 여러 파일을 동시에 열어둘 수 있습니다.

:e file1.lua 파일 열기 (새 버퍼)
:ls 열린 버퍼 목록
:bn 다음 버퍼
:bp 이전 버퍼
:bd 버퍼 닫기
:b {숫자} 해당 번호 버퍼로 이동

윈도우 (분할)

화면을 분할해서 여러 버퍼를 동시에 볼 수 있습니다.

:split 가로 분할 (또는 Ctrl-w s)
:vsplit 세로 분할 (또는 Ctrl-w v)
Ctrl-w h/j/k/l 분할 창 간 이동
Ctrl-w = 분할 창 크기 균등화
:close 현재 창 닫기

여러 윈도우 레이아웃을 탭으로 관리할 수 있습니다.

:tabnew 새 탭
:tabn 다음 탭 (또는 gt)
:tabp 이전 탭 (또는 gT)
:tabclose 탭 닫기

실무 팁: 버퍼 + 윈도우 분할을 주로 쓰고, 탭은 맥락이 완전히 다른 작업을 분리할 때 쓰면 좋습니다.

매크로

반복 작업을 녹화해서 재생하는 기능입니다.

기본 사용법

  1. q{레지스터}: 녹화 시작 (예: qa)
  2. 원하는 작업 수행
  3. q: 녹화 중지
  4. @{레지스터}: 재생 (예: @a)
  5. @@: 마지막 매크로 재생
  6. {숫자}@a: N번 반복 재생

실전 예시

모든 줄 끝에 세미콜론을 추가하고 싶다면:

qa q 레지스터에 녹화 시작
A; 줄 끝에 ; 추가
Esc Normal 모드로
j 다음 줄로
q 녹화 중지
99@a 99번 반복

레지스터

Vim의 복사/붙여넣기는 레지스터라는 저장소를 사용합니다. 클립보드가 여러 개 있는 것과 같습니다.

주요 레지스터

레지스터설명
""기본 레지스터 (마지막 yank/delete)
"0yank 전용 레지스터
"+시스템 클립보드
"a ~ "z이름 지정 레지스터
"_블랙홀 레지스터 (버리기)

사용법

"ay a 레지스터에 복사
"ap a 레지스터에서 붙여넣기
"+y 시스템 클립보드에 복사
"+p 시스템 클립보드에서 붙여넣기
"_dd 줄을 삭제하되 레지스터에 저장하지 않음

레지스터 확인: :reg

: dd로 줄을 지우면 기본 레지스터가 덮어씌워집니다. 복사한 내용을 유지하려면 "0p(yank 레지스터)를 쓰거나, 삭제 시 "_dd(블랙홀)를 쓰면 됩니다.

Marks

특정 위치를 북마크해두고 바로 이동할 수 있습니다.

ma 현재 위치를 a 마크로 저장
'a a 마크의 줄로 이동
`a a 마크의 정확한 위치로 이동
:marks 모든 마크 목록
  • 소문자(a-z): 파일 내 마크
  • 대문자(A-Z): 파일 간 글로벌 마크

비주얼 블록 모드

Ctrl-v로 사각형 영역을 선택할 수 있습니다. 여러 줄을 동시에 편집할 때 유용합니다.

여러 줄 앞에 텍스트 추가

1. Ctrl-v 블록 선택 시작
2. j/k로 줄 선택
3. I 줄 앞에 입력 모드
4. 텍스트 입력
5. Esc 모든 선택된 줄에 적용

여러 줄 뒤에 텍스트 추가

1. Ctrl-v 블록 선택 시작
2. j/k로 줄 선택
3. $ 줄 끝까지 선택 확장
4. A 줄 뒤에 입력 모드
5. 텍스트 입력
6. Esc 적용

내장 터미널

Neovim 안에서 터미널을 열 수 있습니다.

:terminal 터미널 열기

터미널에서 Normal 모드로 전환: Ctrl-\ Ctrl-n

저는 내장 터미널 대신 vim-floaterm 플러그인을 사용해요. Ctrl-;로 플로팅 터미널을 토글할 수 있어서 더 편해요.

기본 설정 시작하기

Neovim 설정 파일은 ~/.config/nvim/init.lua입니다. 간단한 설정부터 시작합니다.

-- 줄 번호 표시
vim.opt.number = true
vim.opt.relativenumber = true

-- 탭 설정 (2칸)
vim.opt.tabstop = 2
vim.opt.shiftwidth = 2
vim.opt.expandtab = true

-- 검색 설정
vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.incsearch = true

-- 스크롤 여유
vim.opt.scrolloff = 10

-- 마우스 지원
vim.opt.mouse:append("a")

이 정도만 설정해도 기본 Neovim보다 훨씬 쾌적해집니다.

다음 글

기본기가 탄탄해졌다면, 플러그인과 LSP를 설정해서 Neovim을 IDE처럼 사용하는 방법을 알아봐요.

Neovim 고급: 플러그인과 LSP로 IDE처럼 쓰기

참고 자료

Neovim 입문

· 약 4분

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

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

Neovim이란?

Neovim은 Vim의 모던 포크(fork)입니다. Vim의 철학(모달 편집, 키보드 중심)을 유지하면서 내부를 현대적으로 재구성했습니다.

Vim과 뭐가 다른가?

VimNeovim
설정 언어VimScriptLua (VimScript도 지원)
비동기 처리제한적내장
내장 LSP없음있음
내장 터미널기본적완전한 터미널 에뮬레이터
플러그인 생태계성숙빠르게 성장 중
설정 파일~/.vimrc~/.config/nvim/init.lua

한 줄로 요약하면 Vim의 편집 철학 + 현대적인 확장성입니다.

설치

macOS

brew install neovim

Rocky Linux / CentOS

# EPEL에서 설치
dnf install -y epel-release
dnf install -y neovim

Ubuntu / Debian

apt install -y neovim

설치 확인:

nvim --version

4가지 모드

Neovim(Vim)이 다른 에디터와 결정적으로 다른 점은 모드가 있다는 것입니다.

Normal 모드

Neovim을 열면 이 모드입니다. 글을 쓰는 게 아니라 글을 조작하는 모드입니다.

  • 커서 이동, 삭제, 복사, 붙여넣기 등
  • 다른 모드에서 Esc를 누르면 항상 Normal로 돌아옵니다

Insert 모드

실제로 글을 타이핑하는 모드입니다.

동작
i커서 앞에 입력
a커서 뒤에 입력
I줄 맨 앞에 입력
A줄 맨 뒤에 입력
o아래에 새 줄 만들고 입력
O위에 새 줄 만들고 입력

Visual 모드

텍스트를 선택하는 모드입니다.

동작
v문자 단위 선택
V줄 단위 선택
Ctrl-v블록(사각형) 선택

Command 모드

:를 누르면 하단에 명령줄이 나타납니다.

:w 저장
:q 종료
:wq 저장 후 종료
:q! 저장하지 않고 종료

커서 이동

Normal 모드에서의 이동이 Vim의 핵심입니다. 처음에는 어색하지만 손에 익으면 마우스보다 빠릅니다.

기본 이동

k
h l
j
방향
h← 왼쪽
j↓ 아래
k↑ 위
l→ 오른쪽

: j의 아래쪽 꼬리를 생각하면 기억하기 쉽습니다.

단어 단위 이동

동작
w다음 단어 시작
b이전 단어 시작
e단어 끝

줄 이동

동작
0줄 맨 앞
$줄 맨 끝
^줄의 첫 글자 (공백 제외)

화면/파일 이동

동작
gg파일 맨 위
G파일 맨 아래
Ctrl-d반 페이지 아래
Ctrl-u반 페이지 위
{숫자}G해당 줄로 이동 (예: 42G)

기본 편집

삭제

동작
x커서 위 문자 삭제
dd줄 삭제
dw단어 삭제
d$ 또는 D커서부터 줄 끝까지 삭제

복사 & 붙여넣기

동작
yy줄 복사 (yank)
yw단어 복사
p커서 뒤에 붙여넣기
P커서 앞에 붙여넣기

실행 취소 & 다시 실행

동작
u실행 취소 (Undo)
Ctrl-r다시 실행 (Redo)

검색과 치환

검색

/검색어 아래 방향으로 검색
?검색어 위 방향으로 검색
n 다음 결과
N 이전 결과

치환

:s/old/new/ 현재 줄에서 첫 번째만
:s/old/new/g 현재 줄에서 모두
:%s/old/new/g 파일 전체에서 모두
:%s/old/new/gc 파일 전체, 하나씩 확인하며

파일 열기

# 터미널에서
nvim filename.txt

# Neovim 안에서
:e filename.txt
:e . 현재 디렉토리 탐색

숫자 + 명령 조합

Vim의 강력한 문법: 숫자 + 동작으로 반복할 수 있습니다.

5j 5줄 아래로
3dd 3줄 삭제
4w 4단어 앞으로
10G 10번째 줄로
2yy 2줄 복사

vimtutor로 연습하기

Neovim에는 대화형 튜토리얼이 내장되어 있습니다.

nvim +Tutor

30분 정도 따라 하면 기본 조작이 손에 익습니다. 처음 Neovim을 설치했다면 반드시 한 번은 해보세요.

다음 글

기본 조작이 익숙해졌다면 텍스트 오브젝트, 매크로, 레지스터 등 생산성을 끌어올리는 기능을 알아봐요.

Neovim 중급: 생산성을 높이는 기능들

참고 자료

커스텀 도메인과 SSL

· 약 4분

이 글은 Hugo + PaperMod 블로그 세팅 시리즈의 마지막 글이에요.

  1. Hugo + PaperMod로 기술 블로그 만들기
  2. NCP 서버에 Hugo 블로그 배포하기
  3. 커스텀 도메인 연결과 Let's Encrypt SSL 설정 ← 현재 글

진행 순서

도메인과 SSL 설정에는 순서가 있습니다. 순서가 바뀌면 인증서 발급이 실패합니다.

도메인 구매 → DNS A 레코드 설정 → Nginx server_name 변경
→ hugo.toml baseURL 변경 → certbot SSL 발급 → 재배포

1. 도메인 구매

도메인 구매처는 여러 곳이 있습니다.

업체특징
Cloudflare원가 판매, DNS 관리 편리
가비아국내 최대, 한글 지원
NCP서버와 같은 곳에서 관리

저는 Cloudflare를 선택했어요. DNS 관리 UI가 깔끔하고 추가 비용이 없어요.

.dev 도메인 참고사항

.dev는 Google이 관리하는 TLD로, HSTS preload가 적용되어 있습니다.

  • 모든 .dev 도메인은 브라우저에서 HTTPS만 허용됩니다.
  • HTTP로는 접속 자체가 불가능합니다.
  • SSL 설정이 필수입니다.

기술 블로그에 어울리는 도메인이지만, SSL 없이는 쓸 수 없다는 점을 알고 있어야 합니다.

2. DNS 설정

Cloudflare DNS에서 A 레코드를 추가합니다.

TypeNameContentProxy
A@서버IPDNS only

중요: Proxy를 반드시 꺼야 합니다(회색 구름). Cloudflare 프록시가 켜져 있으면 서버 IP가 Cloudflare로 대체되어 Let's Encrypt 인증서 발급이 복잡해집니다.

DNS 전파를 확인합니다.

dig +short dbalog.dev
# 서버IP가 나오면 성공

보통 Cloudflare는 수 분 내로 전파됩니다.

3. Hugo 설정 변경

hugo.toml의 baseURL을 변경합니다.

# 변경 전
baseURL = "https://example.com/"
# 변경 후
baseURL = "https://dbalog.dev/"

이 값은 sitemap, RSS, 메타 태그 등 블로그 전체에서 사용되므로 반드시 변경해야 합니다.

4. Nginx server_name 변경

deploy/nginx/blog.conf:

server_name dbalog.dev; # IP 또는 example.com에서 변경

서버에 반영:

scp deploy/nginx/blog.conf ncp-blog:/etc/nginx/conf.d/blog.conf
ssh ncp-blog "nginx -t && systemctl reload nginx"

5. Let's Encrypt SSL 인증서 발급

certbot 설치

Rocky Linux 9 기본 저장소에는 certbot이 없습니다. EPEL 저장소를 먼저 추가합니다.

ssh ncp-blog "dnf install -y epel-release"
ssh ncp-blog "dnf install -y certbot python3-certbot-nginx"

인증서 발급

certbot의 --nginx 플러그인이 인증서 발급과 Nginx 설정 수정을 한번에 처리합니다.

ssh ncp-blog "certbot --nginx -d dbalog.dev \
--non-interactive \
--agree-tos \
--email your@email.com \
--redirect"
  • --nginx: Nginx 설정을 자동으로 수정
  • --redirect: HTTP→HTTPS 301 리다이렉트 자동 추가
  • --non-interactive: 대화형 프롬프트 없이 실행

성공하면 이런 메시지가 나옵니다.

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/dbalog.dev/fullchain.pem
Key is saved at: /etc/letsencrypt/live/dbalog.dev/privkey.pem
This certificate expires on 2026-07-09.

certbot이 Nginx에 추가한 설정

listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/dbalog.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dbalog.dev/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

그리고 HTTP→HTTPS 리다이렉트를 위한 별도 server 블록:

server {
if ($host = dbalog.dev) {
return 301 https://$host$request_uri;
}
listen 80;
server_name dbalog.dev;
return 404;
}

인증서 자동 갱신

Let's Encrypt 인증서는 90일마다 만료됩니다. certbot이 systemd 타이머를 자동 등록하므로 수동 갱신이 필요 없습니다.

갱신 테스트:

ssh ncp-blog "certbot renew --dry-run"

6. 재배포

baseURL이 변경되었으므로 빌드 후 재배포합니다.

./deploy.sh

확인

# HTTPS 접속
curl -s -o /dev/null -w "%{http_code}" https://dbalog.dev/
# 200

# HTTP → HTTPS 리다이렉트
curl -s -o /dev/null -w "%{http_code}" http://dbalog.dev/
# 301

브라우저에서 https://dbalog.dev/에 접속하면 자물쇠 아이콘이 표시됩니다.

시리즈 마무리

3편에 걸쳐 Hugo 블로그를 처음부터 세팅했어요.

  1. Hugo + PaperMod 설치 및 설정
  2. NCP 서버에 Nginx + rsync 배포
  3. 도메인 연결 + SSL 인증서

이제 남은 건 글을 쓰는 것뿐이고, 마크다운 파일을 만들고 ./deploy.sh를 실행하면 끝이에요.

참고 자료

NCP Hugo 블로그 배포

· 약 4분

이 글은 Hugo + PaperMod 블로그 세팅 시리즈의 두 번째 글이에요.

  1. Hugo + PaperMod로 기술 블로그 만들기
  2. NCP 서버에 Hugo 블로그 배포하기 ← 현재 글
  3. 커스텀 도메인 연결과 Let's Encrypt SSL 설정

배포 구조

[로컬 macOS] [NCP Rocky Linux]
content/*.md
↓ hugo --minify
public/ ──── rsync ────→ /var/www/blog/ ←── Nginx 서빙

Hugo가 생성한 정적 파일을 rsync로 서버에 전송하고, Nginx가 서빙하는 단순한 구조입니다. DB도 Node.js도 필요 없습니다.

1. SSH 키 인증 설정

매번 비밀번호를 입력하는 건 번거롭고, 스크립트 자동화도 불가능합니다. SSH 키 인증을 먼저 세팅합니다.

키 생성

ssh-keygen -t ed25519 -C "blog-deploy"
  • ed25519: RSA보다 짧고 안전한 알고리즘
  • passphrase: 비워도 되고, 입력하면 macOS Keychain이 기억해 줍니다

서버에 공개키 등록

ssh-copy-id -i ~/.ssh/id_ed25519.pub root@서버IP

이때 마지막으로 서버 비밀번호를 입력합니다. 이후로는 비밀번호 없이 접속 가능합니다.

SSH config 설정 (선택)

~/.ssh/config에 아래를 추가하면 ssh ncp-blog만으로 접속할 수 있습니다.

Host ncp-blog
HostName 서버IP
User root
IdentityFile ~/.ssh/id_ed25519

2. Nginx 설정 파일

Hugo 정적 파일을 서빙하기 위한 Nginx 설정입니다.

server {
listen 80;
server_name 서버IP;

root /var/www/blog;
index index.html;

# gzip 압축
gzip on;
gzip_types text/plain text/css application/json application/javascript
text/xml application/xml application/xml+rss text/javascript;
gzip_min_length 1000;

# 정적 파일 캐싱 (30일)
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}

# Hugo 정적 사이트 서빙
location / {
try_files $uri $uri/ =404;
}

# 404 페이지
error_page 404 /404.html;
location = /404.html {
internal;
}

# 보안 헤더
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}

핵심 설정:

  • try_files $uri $uri/ =404: 파일 → 디렉토리(index.html) → 404 순서로 탐색
  • gzip: 텍스트 기반 파일을 압축해서 전송 (대역폭 절약)
  • expires 30d: CSS/JS/이미지를 30일 캐싱 (재방문 시 빠른 로딩)

3. 서버 세팅

SSH로 서버에 접속해서 실행합니다.

# Nginx 설치
dnf install -y nginx

# 블로그 디렉토리 생성
mkdir -p /var/www/blog

# Nginx 설정 파일 복사 (로컬에서)
scp deploy/nginx/blog.conf ncp-blog:/etc/nginx/conf.d/blog.conf

# 설정 검사 + 시작
ssh ncp-blog "nginx -t && systemctl enable nginx && systemctl start nginx"

systemctl enable은 서버가 재부팅되어도 Nginx가 자동 시작되게 합니다.

4. 배포 스크립트

매번 명령어를 입력하는 것은 번거로우니 스크립트로 만듭니다.

deploy.sh:

#!/bin/bash
set -e

SERVER_USER="root"
SERVER_HOST="서버IP"
SERVER_PORT="22"
REMOTE_DIR="/var/www/blog"

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"

build() {
echo "=== Hugo 빌드 시작 ==="
hugo --minify
echo "=== 빌드 완료 ==="
}

deploy() {
echo "=== 서버 배포 시작 ==="
rsync -avz --delete \
-e "ssh -p ${SERVER_PORT}" \
public/ \
"${SERVER_USER}@${SERVER_HOST}:${REMOTE_DIR}/"
echo "=== 배포 완료 ==="
}

case "${1:-all}" in
build) build ;;
deploy) deploy ;;
all) build && deploy ;;
*) echo "사용법: $0 {build|deploy|all}" ; exit 1 ;;
esac

사용법:

chmod +x deploy.sh

./deploy.sh build # 빌드만
./deploy.sh deploy # 배포만
./deploy.sh # 빌드 + 배포

rsync의 --delete 옵션은 서버에서 로컬에 없는 파일을 삭제합니다. 글을 지웠을 때 서버에도 반영되게 하려면 필요합니다.

--delete 함정: 서버에 수동으로 올려둔 파일(임시 백업, 정적 다운로드 자료 등)도 같이 지워집니다. 빌드 산출물 외의 파일은 같은 디렉토리에 두지 마십시오. 보존이 필요하면 별도 디렉토리(/var/www/blog-extra/)로 분리하거나 --exclude='backups/' 패턴을 추가합니다.

5. NCP ACG (방화벽) 설정

서버에서 curl localhost는 되는데 외부에서 접속되지 않는다면, NCP ACG 설정을 확인해야 합니다.

NCP 콘솔 → Server → ACG → 인바운드 규칙:

프로토콜포트허용 소스
TCP800.0.0.0/0
TCP4430.0.0.0/0

NCP는 OS 레벨 방화벽(firewalld)과 별개로 ACG라는 네트워크 방화벽을 사용합니다. 둘 다 확인해야 합니다.

배포 확인

curl -s -o /dev/null -w "HTTP %{http_code}, %{time_total}s" http://서버IP/
# HTTP 200, 0.021s

다음 글

IP 주소로 접속하는 블로그는 아무래도 불편해요. 다음 글에서는 커스텀 도메인을 연결하고 HTTPS를 설정할게요.

커스텀 도메인 연결과 Let's Encrypt SSL 설정

Hugo PaperMod 블로그 구축

· 약 5분

이 글은 Hugo + PaperMod 블로그 세팅 시리즈의 첫 번째 글이에요.

  1. Hugo + PaperMod로 기술 블로그 만들기 ← 현재 글
  2. NCP 서버에 Hugo 블로그 배포하기
  3. 커스텀 도메인 연결과 Let's Encrypt SSL 설정

왜 Hugo인가?

블로그를 시작하면서 WordPress, Gatsby, Next.js 등 여러 옵션을 검토했습니다. 최종적으로 Hugo를 선택한 이유는 다음과 같습니다.

  • Go 기반이라 수천 페이지도 수 초 내에 빠르게 빌드합니다.
  • 정적 HTML 파일만 서빙하면 되므로 Nginx 하나로 간단히 배포합니다.
  • .md 파일로 글을 쓰고 git으로 버전을 관리합니다.
  • PHP, Node.js, 데이터베이스 없이 순수 파일만으로 동작합니다.

테마는 PaperMod를 선택했습니다. GitHub Stars 13k+로 Hugo 테마 중 가장 인기가 많고, 다크모드/검색/SEO가 기본 내장되어 있습니다.

1. Hugo 설치

macOS 기준 Homebrew로 설치합니다.

brew install hugo

설치 확인:

hugo version
# hugo v0.160.0+extended+withdeploy darwin/arm64

extended 버전이 설치되어야 SCSS/SASS 처리가 가능합니다. Homebrew로 설치하면 자동으로 extended 버전이 들어갑니다.

Linux(Rocky/CentOS)에서 설치하려면? Hugo는 Go 바이너리라 패키지 매니저 대신 GitHub Releases에서 직접 다운로드하는 것이 버전 관리에 유리합니다.

2. 새 사이트 생성

mkdir blog && cd blog
hugo new site . --force

--force는 빈 디렉토리가 아니어도 생성을 허용하는 플래그입니다. 실행하면 다음 구조가 만들어집니다.

blog/
├── archetypes/ # 새 글의 기본 front matter 템플릿
├── content/ # 마크다운 글 저장
├── layouts/ # 테마 오버라이드 템플릿
├── static/ # 정적 파일 (이미지 등)
├── themes/ # 테마 디렉토리
└── hugo.toml # 사이트 설정 파일

3. PaperMod 테마 설치

git submodule로 설치하면 테마 업데이트가 git submodule update 한 줄로 끝납니다.

git init
git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod
  • --depth=1: 최신 커밋만 가져와서 용량을 줄입니다 (shallow clone)
  • 나중에 테마를 업데이트할 때는 git submodule update --remote --merge를 실행합니다.

4. hugo.toml 설정

이 파일이 블로그의 핵심 설정입니다. 전체 내용을 공유합니다.

baseURL = "https://dbalog.dev/"
title = "dbalog.dev"
paginate = 10
theme = "PaperMod"
languageCode = "ko"
defaultContentLanguage = "ko"
hasCJKLanguage = true

enableRobotsTXT = true
buildDrafts = false

[minify]
disableXML = true
minifyOutput = true

# 검색을 위한 JSON 출력
[outputs]
home = ["HTML", "RSS", "JSON"]

[params]
env = "production"
title = "dbalog.dev"
description = "기술 블로그"
keywords = ["Blog", "Tech", "Development"]
author = "dbalog"
DateFormat = "2006년 1월 2일"
defaultTheme = "auto" # 시스템 다크/라이트 모드를 따름
disableThemeToggle = false

ShowReadingTime = true
ShowCodeCopyButtons = true
ShowPostNavLinks = true
ShowBreadCrumbs = true
ShowWordCount = true
UseHugoToc = true
showtoc = true

[params.homeInfoParams]
Title = "Welcome"
Content = "기술과 개발 이야기를 기록합니다."

[params.fuseOpts]
isCaseSensitive = false
shouldSort = true
location = 0
distance = 1000
threshold = 0.4
minMatchCharLength = 0
limit = 10
keys = ["title", "permalink", "summary", "content"]

# 상단 메뉴
[[menu.main]]
identifier = "posts"
name = "글목록"
url = "/posts/"
weight = 10

[[menu.main]]
identifier = "categories"
name = "카테고리"
url = "/categories/"
weight = 20

[[menu.main]]
identifier = "tags"
name = "태그"
url = "/tags/"
weight = 30

[[menu.main]]
identifier = "search"
name = "검색"
url = "/search/"
weight = 40

[[menu.main]]
identifier = "archives"
name = "아카이브"
url = "/archives/"
weight = 50

# 코드 하이라이팅
[markup]
[markup.highlight]
noClasses = false

주요 설정 해설

설정왜 필요한가
hasCJKLanguagetrue한글 읽기 시간을 글자 수 기반으로 계산
defaultTheme"auto"시스템 설정에 따라 다크/라이트 자동 전환
outputs.home["HTML","RSS","JSON"]JSON을 추가해야 Fuse.js 검색이 동작
ShowCodeCopyButtonstrue코드 블록에 복사 버튼 표시
DateFormat"2006년 1월 2일"Go 레퍼런스 타임을 한국식으로
noClassesfalseChroma 코드 하이라이팅을 CSS 클래스 기반으로

5. 한글 폰트 + AdSense 준비

PaperMod는 layouts/partials/extend_head.html 파일로 <head> 태그를 확장할 수 있습니다.

mkdir -p layouts/partials

layouts/partials/extend_head.html:

{{/* Noto Sans KR 한글 폰트 */}}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Noto Sans KR', sans-serif;
}
</style>

나중에 Google AdSense 승인을 받으면 이 파일에 스크립트를 추가하면 됩니다.

주의: layouts/ 디렉토리에는 .html 파일만 넣어야 합니다. Hugo가 이 디렉토리의 모든 파일을 Go 템플릿으로 파싱하기 때문에, 마크다운 등 다른 파일을 넣으면 빌드 에러가 발생합니다.

6. 특수 페이지 생성

PaperMod의 검색과 아카이브 기능을 위해 전용 페이지를 만듭니다.

content/search.md:

---
title: "검색"
layout: "search"
placeholder: "검색어를 입력하세요"
---

content/archives.md:

---
title: "아카이브"
layout: "archives"
url: "/archives/"
---

layout 값으로 PaperMod 내장 템플릿이 사용됩니다.

7. 첫 번째 포스트 작성

hugo new content posts/hello-world.md

front matter 예시:

---
title: "제목"
date: 2026-04-09
draft: false
tags: ["Hugo", "블로그"]
categories: ["블로그"]
summary: "글 요약"
ShowToc: true
TocOpen: true
---
  • draft: true이면 hugo server -D에서만 보이고 프로덕션 빌드에서 제외됩니다.
  • tagscategories를 지정하면 /tags/, /categories/ 페이지에 자동 집계됩니다.

8. 빌드 및 로컬 확인

# 프로덕션 빌드
hugo --minify

# 로컬 미리보기 (draft 포함)
hugo server -D
# → http://localhost:1313 에서 확인

빌드 결과:

│ KO
──────────────────┼────
Pages │ 23
Paginator pages │ 0
Non-page files │ 0
Static files │ 0
Processed images │ 0
Aliases │ 6

Total in 61 ms

23페이지가 61ms 만에 생성됩니다. public/ 디렉토리에 정적 파일이 만들어지고, 이 파일들을 웹서버로 서빙하면 블로그가 됩니다.

다음 글

다음 글에서는 이 public/ 디렉토리를 NCP(네이버 클라우드) 서버에 배포하는 과정을 다뤄요.

NCP 서버에 Hugo 블로그 배포하기

참고 자료