birchholt 직접 해보고 남기는 기록

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 모양뿐입니다.

제목githubgithub-asciiblackfriday
설치 방법설치-방법-설치-방법
원인 — 파일 하나가 없었습니다원인--파일-하나가-없었습니다----원인-파일-하나가-없었습니다
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정리

규칙이 보입니다.

중복 제목과 이모지는 이렇게 갈립니다

같은 이름이 적힌 우편함 세 개에 작은 번호표가 차례로 붙어 있는 골목 담장

같은 제목이 여러 번 나오면 두 번째부터 -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}를 붙여 고정하는 쪽이 손해가 적습니다.

바꾸기 전에 확인할 순서만 적어두면 이렇습니다.

  1. hugo config --format json으로 지금 autoidtype 값을 봅니다.
  2. 빌드 결과에서 grep -oE '<h2 id="[^"]*"'로 id 목록을 뽑아 둡니다.
  3. 값을 바꿔 다시 뽑고 두 목록을 비교합니다. 달라진 줄이 깨질 링크입니다.
  4. 수동 {#id}가 한 페이지에서 겹치지 않는지 같은 방법으로 셉니다.