긴 글일수록 독자는 먼저 구조를 확인합니다. 스크롤을 내리기 전에 “무엇을, 어떤 순서로” 다루는지 보게 되죠. 이때 가장 강력한 도구가 바로 자동 생성 목차(TOC; Table of Contents)입니다. 수동으로 링크를 일일이 걸 필요 없이, 글의 제목 계층(H2/H3 등)을 읽어 목차를 만들어주고, 클릭하면 해당 섹션으로 부드럽게 이동하며, 현재 위치를 표시(스크롤 스파이) 해 줍니다. 이 글에서는 ① 원리와 설계(헤딩 계층·앵커·접근성), ② 플랫폼별 설정(티스토리/블로그스폿/워드프레스/마크다운), ③ 커스텀 구현(JavaScript로 자동 생성/스크롤 스파이/접근성·SEO 최적화)까지, 실전에서 바로 복붙 가능한 코드와 체크리스트로 정리합니다.
1) 원리와 설계: 헤딩 계층·앵커 링크·접근성(스크린리더)·SEO
자동 목차는 결국 “헤딩을 읽어 앵커 링크를 만든다”는 단순한 원리입니다. 그래서 시작은 제목 계층부터 정돈하는 일입니다. 글마다 H1은 1개, 섹션은 H2, 하위는 H3/H4로 깔끔하게 내려가야 목차도 예쁘게 나옵니다. 다음으로 앵커(고유 id)가 있어야 링크가 걸립니다. 플랫폼/테마에 따라 자동으로 id가 붙기도 하지만, 불안정하면 스크립트로 id
를 생성해 줍니다. 마지막으로 접근성—목차 컨테이너에 nav
·aria-label
을 쓰고, 현재 위치는 aria-current="true"
로 표시하면 스크린리더에서도 훌륭하게 작동합니다. SEO 면에서도 목차(점프링크)는 검색 결과에 사이트 링크처럼 노출될 가능성을 높여 CTR 향상에 도움을 줍니다.
- 헤딩 규칙: H1(제목)=1개, 섹션=H2, 하위=H3/H4. 크기 조절은 CSS로, 숫자는 계층 의미입니다.
- 앵커(id) 규칙: 한 페이지에서 유일(unique)해야 하며, 공백 대신 케밥케이스(예:
data-clean-architecture
) 사용. - 접근성: 목차 래퍼는
<nav aria-label="글 목차">
, 현재 항목에는aria-current="true"
로 표시. - UX: 클릭 시 부드러운 스크롤(
scroll-behavior: smooth;
), 현재 위치 하이라이트(스크롤 스파이).
/* 기본 UX: 부드러운 스크롤 */
html { scroll-behavior: smooth; }
/* 목차 기본 스타일 */
.toc { border:1px solid #e5e7eb; border-radius:10px; padding:14px; background:#f8fafc; }
.toc a { color:#334155; text-decoration:none; }
.toc a[aria-current="true"] { color:#2563eb; font-weight:600; }
.toc ol { margin:0; padding-left:18px; }
2) 플랫폼별 자동 목차: 티스토리·블로그스팟·워드프레스·마크다운
대부분의 블로그 플랫폼은 플러그인/가젯으로 쉽게 목차를 붙일 수 있습니다. 아래는 각 플랫폼에서 흔히 쓰는 경로와, 플러그인을 쓰지 못하는 환경을 위한 간단 삽입 코드입니다.
티스토리(Tistory)
- 스킨 옵션/플러그인: 최신 스킨은 본문 내
[toc]
단축문 또는 스킨 설정에 목차 표시가 있습니다(스킨 별도 문서 확인). - 직접 삽입: 스킨 편집 > skin.html 또는 article.html에 아래 스크립트를 추가하고, 본문 상단에
<div id="toc" class="toc"></div>
를 삽입합니다.
Blogger(블로그스폿)
- 가젯: 기본 가젯은 자동 목차가 없으므로 테마 > HTML 편집에 커스텀 스크립트를 추가하는 편이 일반적입니다.
- 방법: 포스트 템플릿 상단에
<div id="toc" class="toc"></div>
를 넣고, 아래 JS를 테마 하단에 추가.
워드프레스(WordPress)
- 추천 플러그인: Easy Table of Contents, LuckyWP Table of Contents. 설치 > 활성화 > “포스트 자동 삽입” 체크.
- 블록: 일부 테마/페이지빌더(Elementor/Divi)에는 TOC 블록이 내장되어 있습니다.
- 수동: 플러그인 사용이 제한된 환경이면 아래 커스텀 JS를 child theme 또는 Code Snippets로 추가.
마크다운/정적 사이트(Jekyll/Hugo/Hexo)
- 빌드 플러그인 사용: Jekyll의
jekyll-toc
또는 Hugo의{{. TableOfContents }}
단축코드. - 단일 HTML에서도 아래 스크립트로 쉽게 구현 가능합니다.
<div id="toc" class="toc"></div>
<script>
// 간단 TOC 생성기: 본문 내 H2/H3를 읽어 목차 생성 + 스크롤 스파이
(function(){
const container = document.getElementById('toc');
if(!container) return;
const headings = Array.from(document.querySelectorAll('h2, h3'))
.filter(h => h.textContent.trim().length > 0);
if(headings.length === 0) { container.style.display='none'; return; }
// 고유 id 부여(없으면 생성)
const slugify = s => s.toLowerCase().trim()
.replace(/[^\w가-힣\s-]/g,'').replace(/\s+/g,'-').replace(/-+/g,'-');
const ids = new Set();
headings.forEach(h => {
if(!h.id) {
let id = slugify(h.textContent);
while(ids.has(id)) id = id + '-' + Math.floor(Math.random()*1000);
h.id = id; ids.add(id);
}
});
// 목록 구조 만들기
const ol = document.createElement('ol');
let currentOl = ol, lastLevel = 2;
headings.forEach(h => {
const level = +h.tagName.substring(1); // 2 or 3
if(level > lastLevel){
const sub = document.createElement('ol');
currentOl.lastElementChild && currentOl.lastElementChild.appendChild(sub);
currentOl = sub;
} else if(level < lastLevel){
currentOl = currentOl.parentElement.closest('ol') || ol;
}
const li = document.createElement('li');
const a = document.createElement('a');
a.href = '#' + h.id;
a.textContent = h.textContent;
a.setAttribute('data-target', h.id);
li.appendChild(a);
currentOl.appendChild(li);
lastLevel = level;
});
const title = document.createElement('div');
title.style.cssText = 'font-weight:700;margin-bottom:8px;color:#0f172a;';
title.textContent = '목차';
container.appendChild(title);
container.appendChild(ol);
// 스크롤 스파이
const links = container.querySelectorAll('a[data-target]');
const map = {};
links.forEach(a => map[a.getAttribute('data-target')] = a);
const observer = new IntersectionObserver(entries => {
entries.forEach(ent => {
const id = ent.target.id;
const link = map[id];
if(!link) return;
if(ent.isIntersecting) {
links.forEach(l => l.removeAttribute('aria-current'));
link.setAttribute('aria-current','true');
}
});
}, { rootMargin: '0px 0px -70% 0px', threshold: [0, 1] });
headings.forEach(h => observer.observe(h));
})();
</script>
위 스크립트는 본문에 있는 H2/H3를 자동으로 수집해 목차를 만들고, IntersectionObserver로 현재 섹션을 하이라이트 합니다. <div id="toc">
만 본문 상단(또는 사이드바)에 배치하면 동작합니다.
3) 커스텀 UX: 고정 목차(Sticky), 스크롤 스파이, 접기/펼치기, 긴 제목 줄임표
목차가 생겼다면, 이제 읽기 경험을 높이는 마무리 디테일을 더해보세요. 긴 글에서 특히 빛납니다.
사이드 고정(Sticky TOC)
/* 2열 레이아웃 예시 */
@media (min-width: 1024px){
.post-wrap { display:grid; grid-template-columns: 1fr 280px; gap:24px; }
.toc { position: sticky; top: 92px; max-height: calc(100vh - 120px); overflow:auto; }
}
.toc li { margin:6px 0; }
.toc li li { margin-left:8px; }
.toc a { display:block; padding:2px 0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
접기/펼치기
<details class="toc">
<summary style="cursor:pointer;font-weight:700;color:#0f172a;">목차 펼치기/닫기</summary>
<div id="toc-list"></div>
</details>
<script>
// 위 자동 생성 스크립트에서 container를 document.getElementById('toc-list')로만 바꿔도 동작
</script>
길고 중복되는 제목 줄임표 처리
- CSS의
text-overflow: ellipsis;
적용(위 코드 포함), 또는 스크립트에서 60자 이상은 자르기.
현재 위치 강조(스크롤 스파이)
- 앞서 제공한 스크립트에 포함.
aria-current="true"
를 이용해 굵게/색상 강조.
접근성·키보드 내비게이션
nav [aria-label="글 목차"]
로 감싸고, 링크는tabindex
기본 흐름을 따르게 둡니다.- 색상 대비(링크 vs 본문) 4.5:1 이상 확보.
SEO 보너스: 점프링크(분절 링크) 유도
- 핵심 섹션 제목을 질문형/결론형으로 작성하면 검색 결과에 점프링크가 노출될 확률이 조금 올라갑니다. 예) “FAQ: 목차가 안 보일 때?”
- 긴 글의 경우, 상단에 “이 글은…” 한 줄 요약 + 목차를 배치하면 체류에 긍정적입니다.
- 목차가 비어 있음 → 본문에 H2/H3가 실제로 존재하는지 확인(굵기만 키운 단순 p 태그는 감지되지 않음).
- 클릭해도 위치가 안 맞음 → 상단 고정 헤더가 있으면 CSS로
scroll-margin-top: 80px;
을 각 헤딩에 적용. - 한글 제목 id 충돌 → slugify 로직을 사용하거나 수동 id 지정(
<h2 id="설정-방법">
). - 스킨/테마에서 id를 지움 → 게시 직후 개발자도구로
h2#아이디
가 실제 DOM에 남는지 확인.
/* 고정 헤더 보정: 헤딩에 스크롤 여백 */
h2, h3, h4 { scroll-margin-top: 90px; }
결론: ‘자동 목차’는 긴 글의 내비게이션이자 검색 신호다
자동 목차는 단순한 편의 기능이 아닙니다. 글의 구조를 한눈에 보여 주고, 독자의 스캔 속도를 높이며, 검색 결과에서 점프링크 노출까지 노릴 수 있는 강력한 구성 요소입니다. 핵심은 세 가지입니다. 첫째, 헤딩 계층을 올바르게—H1은 1개, 섹션은 H2, 하위는 H3/H4. 둘째, 플랫폼에 맞춰 플러그인/가젯 또는 짧은 커스텀 스크립트로 자동 생성을 붙일 것. 셋째, UX 디테일—부드러운 스크롤, 현재 위치 하이라이트(스크롤 스파이), 스티키 사이드, 접근성 라벨링을 챙길 것. 오늘 글 상단에 <div id="toc">
를 하나 두고, 위 스크립트를 붙여 보세요. 목차가 생기는 순간, 글의 체감 퀄리티와 독자의 체류는 즉시 달라질 것입니다.