Next.js App에서 아티클 TOC(Table of Contents) 기능 구현하기
웹에서 긴 아티클을 읽을 때 목차(TOC, Table of Contents) 기능이 있으면 사용자가 원하는 내용을 빠르게 찾을 수 있습니다. 이번 글에서는 Next.js 환경에서 tocbot
을 활용하여 TOC를 생성하고, 해시 네비게이션을 처리하는 방법을 단계별로 설명합니다.
1⃣ tocbot
라이브러리 설치
먼저, tocbot
라이브러리를 설치합니다.
npm install tocbot
tocbot
은 문서의 헤딩 태그(h1
, h2
, h3
등)를 자동으로 감지하여 TOC를 생성해 주는 라이브러리입니다. 이를 활용하면 별도의 수작업 없이 목차를 자동으로 구성할 수 있습니다.
2⃣ 스타일 추가
TOC가 화면의 왼쪽에 고정되도록 styles/toc.css
파일을 생성하고 다음과 같이 작성합니다.
.toc {
position: fixed;
top: 100px;
left: 20px;
width: 250px;
}
이제 기본적인 TOC 스타일이 적용되었습니다.
3⃣ TOC 컴포넌트 구현
다음은 Contents.tsx
파일에서 TOC 기능을 구현하는 코드입니다.
import './styles/toc.css'
import { FC, useEffect, useState } from 'react'
import { HEADER_HEIGHT } from 'src/blog/components/Header'
import tocbot from 'tocbot'
import { contentSelector, headingSelector, tocSelector } from './constants'
import { makeIds } from './utils'
const Contents: FC = () => {
const [hasTocContent, setHasTocContent] = useState(false)
useEffect(() => {
makeIds()
tocbot.init({
tocSelector,
contentSelector,
headingSelector,
hasInnerContainers: true,
headingsOffset: HEADER_HEIGHT,
scrollSmoothOffset: -HEADER_HEIGHT,
})
const tocElement = document.querySelector(tocSelector)
if (tocElement && tocElement.innerHTML.trim() !== '') {
setHasTocContent(true)
}
try {
const hash = decodeURI(window.location.hash)
if (!hash) {
return
}
const element = document.getElementById(hash.slice(1))
if (!(element instanceof HTMLElement)) {
return
}
if (!('scrollRestoration' in window.history)) {
return
}
window.history.scrollRestoration = 'manual'
element.scrollIntoView()
} catch (error) {
console.log('Error decoding hash:', error)
}
return () => {
tocbot.destroy()
if ('scrollRestoration' in window.history) {
window.history.scrollRestoration = 'auto'
}
}
}, [])
return (
<div
style={{ opacity: hasTocContent ? 1 : 0 }}
className="grid gap-2 transition-opacity"
>
<div className="toc" />
</div>
)
}
export default Contents
4️⃣ 주요 구현 사항 단계별 설명
① tocbot
초기화 및 TOC 생성
tocbot.init({
tocSelector,
contentSelector,
headingSelector,
hasInnerContainers: true,
headingsOffset: HEADER_HEIGHT,
scrollSmoothOffset: -HEADER_HEIGHT,
})
tocSelector
: TOC가 들어갈 컨테이너contentSelector
: 문서 본문headingSelector
: TOC에 포함할 헤딩 태그들headingsOffset
: 스크롤 위치 보정 (고정 헤더 고려)scrollSmoothOffset
: 부드러운 스크롤 이동 보정
② TOC가 존재하는지 확인
const tocElement = document.querySelector(tocSelector)
if (tocElement && tocElement.innerHTML.trim() !== '') {
setHasTocContent(true)
}
TOC가 비어있지 않다면 화면에 표시합니다.
③ scrollRestoration
이란?
Next.js에서는 브라우저의 기본 스크롤 복원 기능이 활성화되어 있습니다. 즉, 사용자가 페이지를 새로고침하면 마지막 스크롤 위치를 기억하고 복원합니다.
하지만 TOC 기능을 구현할 때, 페이지가 새로고침될 때 특정 섹션으로 이동해야 하는데, 이 기능이 방해가 됩니다. 따라서 이 기능을 비활성화해야 합니다.
if ('scrollRestoration' in window.history) {
window.history.scrollRestoration = 'manual'
}
이렇게 설정하면 브라우저는 이전 스크롤 위치를 자동으로 복원하지 않으며, 우리가 지정한 위치로 이동할 수 있습니다.
④ 기존 스크롤 복원 기능 해제
return () => {
tocbot.destroy()
if ('scrollRestoration' in window.history) {
window.history.scrollRestoration = 'auto'
}
}
컴포넌트가 언마운트될 때 tocbot
을 해제하고, Next.js의 기본 스크롤 동작을 복구합니다.
🎯 결론
이제 Next.js에서 tocbot
을 활용하여 아티클 TOC 기능을 완벽하게 구현할 수 있습니다.
✅ 주요 기능
TOC 자동 생성 및 표시
클릭 시 부드러운 스크롤 이동
새로고침 후에도 TOC 위치 유지
Next.js의 스크롤 복원 문제 해결
이제 여러분의 블로그나 문서 프로젝트에 쉽게 적용해 보세요! 🚀