Hugo 한국어 제목의 목차 링크가 #원인--파일 처럼 하이픈 두 개로 나왔습니다
글의 한 섹션으로 바로 가는 링크를 보내려고 제목 태그를 열어봤습니다.
원인 — 파일 하나가 없었습니다라는 제목의 id(페이지 안에서 그 위치를 가리키는 이름표)가
원인--파일-하나가-없었습니다 였습니다. 하이픈이 두 개입니다.

$ curl -s https://birchholt.com/posts/soft-404-on-pages/ | grep -oE '<h2[^>]*>'
<h2 id=처음-의심한-것-전부-아니었습니다>
<h2 id=대조군을-넣어보니-답이-나왔습니다>
<h2 id=원인--파일-하나가-없었습니다>
<h2 id=고치면서-걸린-것-둘>
...
이 id가 어떤 규칙으로 만들어지는지, 설정을 바꾸면 무엇이 달라지는지 빈 사이트에서 직접 빌드해 봤습니다.
Hugo v0.165.0 extended로 2026-10-04에 잰 결과입니다.
한국어 제목 id를 정하는 설정은 하나입니다

Hugo는 마크다운 제목마다 id를 자동으로 붙이고 .TableOfContents(목차를 그려주는 템플릿 변수)는 그 id로 링크를 겁니다.
id를 만드는 방식은 markup.goldmark.parser.autoIDType 한 줄이 정합니다.
[markup.goldmark.parser]
autoIDType = 'github' # github | github-ascii | blackfriday
Hugo 설정 문서에는 기본값이 github이고
github-ascii는 「Drop any non-ASCII characters after accent normalization」,
그러니까 ASCII(영문·숫자·기본 기호) 밖의 글자를 버린다고 적혀 있습니다.
같은 방식이 anchorize 함수에도 쓰인다는 문장도 있습니다.
옛 이름 autoHeadingIDType도 아직 먹습니다.
두 이름으로 각각 빌드해 보니 hugo config 결과에는 똑같이 autoidtype으로 들어갔고
--logLevel info로 돌려도 경고는 한 줄도 없었습니다.
같은 제목 열한 개를 세 값으로 빌드했습니다

공백, 특수문자, 영문 섞임, 중복을 일부러 넣은 h2 제목 열한 개를 한 글에 넣고 값만 바꿔 빌드했습니다.
본문 제목의 id와 목차 링크의 href는 세 경우 모두 한 글자도 다르지 않았습니다.
목차가 따로 깨지는 일은 없고 고민할 것은 id 모양뿐입니다.
| 제목 | github | github-ascii | blackfriday |
|---|---|---|---|
| 설치 방법 | 설치-방법 | - | 설치-방법 |
| 원인 — 파일 하나가 없었습니다 | 원인--파일-하나가-없었습니다 | ---- | 원인-파일-하나가-없었습니다 |
| Hugo 0.165.0 설정하기 | hugo-01650-설정하기 | hugo-01650- | hugo-0-165-0-설정하기 |
| Cloudflare Pages에서 404.html 확인 | cloudflare-pages에서-404html-확인 | cloudflare-pages-404html- | cloudflare-pages에서-404-html-확인 |
| “따옴표"와 (괄호), 물음표? | 따옴표와-괄호-물음표 | -- | 따옴표-와-괄호-물음표 |
| C++ & Go 비교 | c--go-비교 | c--go- | c-go-비교 |
| 한글␣␣두 칸␣␣␣공백 | 한글--두-칸---공백 | ------ | 한글-두-칸-공백 |
| 정리 | 정리 | heading | 정리 |
규칙이 보입니다.
github은 한글을 살리고 기호를 지우되 공백 하나하나를 하이픈으로 바꿉니다.—앞뒤 공백이 남아 하이픈 두 개가 된 게 이것입니다. 점도 지워서0.165.0이01650이 됩니다.blackfriday는 한글을 살리고 연속된 공백·기호를 하이픈 하나로 합칩니다. 가장 읽기 좋습니다.github-ascii는 한국어 사이트에서 쓸 수 없는 수준입니다. 한글이 통째로 사라져 하이픈만 남고 한글뿐인정리는 비어 버려서heading이라는 대체 이름이 붙었습니다.
중복 제목과 이모지는 이렇게 갈립니다

같은 제목이 여러 번 나오면 두 번째부터 -1, -2가 붙습니다. 세 값 모두 그랬습니다.
<h2 id="정리">정리
<h2 id="정리-1">정리
<h2 id="정리-2">정리
번호가 겹치면 건너뜁니다. 설치 방법-1이라는 제목이 먼저 설치-방법-1을 차지하자,
두 번째 설치 방법은 -1을 건너뛰고 설치-방법-2가 됐습니다.
제목 순서를 바꾸면 번호도 바뀝니다. 중복 제목 쪽 링크는 오래 믿기 어렵습니다.
이모지는 지워집니다. 🚀 이모지 제목은 github에서 -이모지-제목처럼 하이픈으로 시작하고
blackfriday에서는 이모지-제목이었습니다.
공유한 주소는 퍼센트 인코딩으로 바뀝니다

한글 id로 링크를 걸면 주소 뒤 # 부분에 한글이 들어갑니다.
브라우저(Chromium)로 그 주소를 열고 location.href를 읽어 보니 이렇게 바뀌어 있었습니다.
연 주소 https://birchholt.com/posts/soft-404-on-pages/#원인--파일-하나가-없었습니다
href https://birchholt.com/posts/soft-404-on-pages/#%EC%9B%90%EC%9D%B8--%ED%8C%8C%EC%9D%BC-%ED%95%98%EB%82%98%EA%B0%80-%EC%97%86%EC%97%88%EC%8A%B5%EB%8B%88%EB%8B%A4
퍼센트 인코딩(한글을 %EC%9B%90 같은 바이트 표기로 바꾼 것)입니다.
길어 보여도 망가진 게 아닙니다. 같은 페이지에서 그 id를 찾아 그 제목 위치로 내려가 있었습니다(제목 위쪽 끝이 화면 맨 위, 0px).
Hugo도 같은 일을 합니다. 본문에 [본문 링크](#원인--파일-하나가-없었습니다)라고 쓰면
결과 HTML에는 인코딩된 href="#%EC%9B%90%EC%9D%B8--..."로 나가고, 목차 링크는 한글 그대로 나갑니다.
두 쪽 모두 같은 곳을 가리킵니다.
하나 알아둘 점이 있습니다. # 뒤는 서버로 가지 않습니다.
$ curl -sv -o /dev/null "https://birchholt.com/posts/soft-404-on-pages/#원인--파일-하나가-없었습니다" 2>&1 | grep '^> GET'
> GET /posts/soft-404-on-pages/ HTTP/2
그래서 id가 틀린 링크도 페이지는 정상으로 열립니다. blackfriday 식 id인
#원인-파일-하나가-없었습니다로 새로 열어 보니 오류 없이 페이지 맨 위(스크롤 0)에 머물렀습니다.
id를 바꾸면 이미 보낸 링크는 조용히 맨 위로만 데려갑니다. 로그에도 남지 않으니 따로 찾아내기 어렵습니다.
오래 살아야 할 링크는 {#id}로 고정합니다

제목 끝에 {#이름}을 붙이면 자동 id 대신 그 이름을 씁니다.
제목 속성을 켜는 parser.attribute.title이 기본값 true라 설정은 필요 없습니다.
## 캐시가 안 풀릴 때 {#cache-purge}
## 설치 {#설치}
## 수동 id {#install-guide .note}
<h2 id="cache-purge">캐시가 안 풀릴 때
<h2 id="설치">설치
<h2 id="install-guide" class="note">수동 id
{#cache-purge}는 제목 글자와 목차 글자에서 빠지고 링크만 #cache-purge로 바뀝니다.
세 값 어느 쪽으로 빌드해도 결과가 같았습니다. 한글 이름({#설치})도 그대로 들어갑니다.
id가 제목 글자에서 만들어지지 않으니, 밖에 공유할 섹션에는 이쪽이 안전합니다.
주의할 점이 하나 있습니다. 수동 id는 중복을 막아주지 않습니다.
같은 {#install-guide}를 두 제목에 붙였더니 id="install-guide"가 두 개 나왔고
번호도 붙지 않았고 빌드는 경고 없이 성공했습니다(exit 0).
HTML에서 id는 한 페이지에 하나여야 하니, 직접 붙인 이름은 직접 겹치지 않게 챙겨야 합니다.
템플릿에서 id를 만들어 쓸 때는 anchorize가 같은 규칙을 따르는지도 봤습니다.
github과 blackfriday에서는 제목 id와 같았지만 github-ascii에서 anchorize "정리"는
빈 문자열이었습니다. 제목 쪽은 heading이었는데 말입니다.
다시 고른다면

새 사이트라면 blackfriday 를 고르겠습니다. 한글이 살고 공백과 기호가 하이픈 하나로 줄어 공유 주소가 가장 덜 지저분합니다.
이미 글이 쌓인 사이트라면 바꾸지 않겠습니다. 기본값 github에서 blackfriday로 바꾸면
원인--파일...이 원인-파일...이 되어 그동안 나간 링크 중 기호가 섞인 제목 쪽이 전부 맨 위로만 떨어집니다.
위에서 본 대로 그걸 알려주는 오류는 없습니다.
대신 앞으로 공유할 섹션에만 {#id}를 붙여 고정하는 쪽이 손해가 적습니다.
바꾸기 전에 확인할 순서만 적어두면 이렇습니다.
hugo config --format json으로 지금autoidtype값을 봅니다.- 빌드 결과에서
grep -oE '<h2 id="[^"]*"'로 id 목록을 뽑아 둡니다. - 값을 바꿔 다시 뽑고 두 목록을 비교합니다. 달라진 줄이 깨질 링크입니다.
- 수동
{#id}가 한 페이지에서 겹치지 않는지 같은 방법으로 셉니다.