- Published on
- ·9 min read
Next.js 블로그에 Tocbot 추가하기
개요
글을 읽을 때 목차가 있으면 좋습니다. 그리고 목차가 계속 옆에 보이면 더 좋겠다고 생각했습니다.
그래서 Tocbot 라이브러리를 이용하여 목차가 따라다닐 수 있도록 추가했습니다.
참고로 Tailwind CSS Blog Starter 템플릿에는 컨텐츠 내에 보여지는 목차가 있습니다.
이와는 별도로 Tocbot을 이용하여 우측에 고정된 목차를 추가하였습니다.
최종적으로,
1280px 이상의 화면에서는 컨텐츠 내에 보여지는 목차가 숨겨지고, 우측에 고정된 목차가 보여집니다.1280px 미만의 화면에서는 컨텐츠 내에 보여지는 목차가 보여지고, 우측에 고정된 목차는 숨겨집니다.
아래 이미지에서 좌측에 보여지는 목차는 1280 이상일 때, 우측에는 1280 미만일때입니다.
Tocbot 추가
Tocbot Install
npm install --save tocbot
Tocbot 컴포넌트 추가
/components/Tocbot.js 추가: 내용은 아래와 같습니다.
import { useEffect } from 'react'
import tocbot from 'tocbot'
const TocSide = () => {
useEffect(() => {
tocbot.init({
tocSelector: '.toc',
contentSelector: 'article',
headingSelector: 'h2, h3',
ignoreSelector: '.toc-ignore',
})
return () => tocbot.destroy()
}, [])
return (
<div>
<div className="lg-block hidden pt-6 pb-10 text-gray-500 dark:text-gray-400 xl:border-b xl:border-gray-200 xl:pt-11 xl:dark:border-gray-700">
<span className="font-bold text-gray-600 dark:text-gray-300">Table of Contents</span>
<div className="toc"></div>
</div>
</div>
)
}
export default TocSide
hidden으로 기본은 숨김이고,lg-block으로 1280px 이상의 화면에서 보여지도록 합니다.lg-block는 아래 tocbot.css에서 정의합니다.
Tocbot CSS 추가
https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.18.2/tocbot.css에서 기본 css는 가져왔습니다. 거기에 추가로 몇 가지를 수정하였습니다.
/css/tocbot.css추가: 내용은 아래와 같습니다.
.toc {
overflow-y: auto;
}
.toc > .toc-list {
overflow: hidden;
position: relative;
font-size: 0.9em;
margin-top: 16px;
}
.toc > .toc-list li {
list-style: none;
line-height: 2em;
}
.js-toc {
overflow-y: hidden;
}
.toc-list {
margin: 0;
padding-left: 10px;
}
a.toc-link {
/*color:currentColor;*/
height: 100%;
}
.is-collapsible {
max-height: 1000px;
overflow: hidden;
transition: all 300ms ease-in-out;
}
.is-collapsed {
max-height: 0;
}
.is-position-fixed {
position: fixed !important;
top: 0;
}
.is-active-link {
font-weight: 700;
color: #37c3b3;
}
.toc-link::before {
background-color: #e5e5e5;
content: ' ';
display: inline-block;
height: inherit;
left: 0;
margin-top: -1px;
position: absolute;
width: 2px;
}
.is-active-link::before {
background-color: #37c3b3;
}
/*
.toc-link:hover {
color: white;
}
*/
@media (min-width: 1280px) {
.lg-block {
display: block;
}
}
@media (max-width: 1280px) {
.sm-show {
display: block;
}
}
.lg-block으로 1280px 이상의 화면에서 보여지도록 합니다..sm-show으로 1280px 미만의 화면에서 보여지도록 합니다.
Tocbot을 우측에 보여줄 Layout 추가
/layouts/PostToc.js추가: 내용은 아래와 같습니다.
import PageTitle from '@/components/PageTitle'
import SectionContainer from '@/components/SectionContainer'
import { BlogSEO } from '@/components/SEO'
import Tag from '@/components/Tag'
import siteMetadata from '@/data/siteMetadata'
import Comments from '@/components/comments'
import ScrollTopAndComment from '@/components/ScrollTopAndComment'
import TocSide from '@/components/Tocbot'
const postDateTemplate = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }
export default function PostLayout({ frontMatter, authorDetails, children }) {
const { slug, date, title, tags } = frontMatter
return (
<SectionContainer>
<BlogSEO
url={`${siteMetadata.siteUrl}/blog/${slug}`}
authorDetails={authorDetails}
{...frontMatter}
/>
<ScrollTopAndComment />
<article>
<div className="xl:divide-y xl:divide-gray-200 xl:dark:divide-gray-700">
<header className="pt-6 xl:pb-6">
<div className="space-y-1 text-center">
<dl className="space-y-10">
<div>
<dt className="sr-only">Published on</dt>
<dd className="text-base font-medium leading-6 text-gray-500 dark:text-gray-400">
<time dateTime={date}>
{new Date(date).toLocaleDateString(siteMetadata.locale, postDateTemplate)}
</time>
</dd>
</div>
</dl>
<div>
<PageTitle>{title}</PageTitle>
</div>
</div>
</header>
<div
className="divide-y divide-gray-200 pb-8 dark:divide-gray-700 xl:grid xl:grid-cols-4 xl:gap-x-6 xl:divide-y-0"
style={{ gridTemplateRows: 'auto 1fr' }}
>
<div className="divide-y divide-gray-200 dark:divide-gray-700 xl:col-span-3 xl:row-span-2 xl:pb-0">
<div className="prose max-w-none pt-10 pb-8 dark:prose-dark">{children}</div>
<Comments frontMatter={frontMatter} />
</div>
<footer style={{ position: 'sticky', top: '32px' }}>
<TocSide />
<div className="divide-gray-200 text-sm font-medium leading-5 dark:divide-gray-700 xl:col-start-1 xl:row-start-2 xl:divide-y">
{tags && (
<div className="py-4 xl:py-8">
<h2 className="toc-ignore text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400">
Tags
</h2>
<div className="flex flex-wrap">
{tags.map((tag) => (
<Tag key={tag} text={tag} />
))}
</div>
</div>
)}
</div>
</footer>
</div>
</div>
</article>
</SectionContainer>
)
}
Tocbot 적용 위한 수정
TocInline 컴포넌트 수정
Tailwind CSS Blog 템플릿에 있는 기본 TocInline 컴포넌트를 수정합니다.
detail태그에className="sm-show hidden"을 추가합니다.
(1280px 미만의 화면에서만 보여지도록 합니다.)asDisclosure로 사용할 경우 적용되며, 그렇지 않을 경우 ul 태그에 적용하시면 될겁니다.
<details open className="sm-show hidden">
<summary className="ml-6 pt-2 pb-2 text-xl font-bold">Table of Contents</summary>
<div className="ml-6">{tocList}</div>
</details>
css 적용되도록 수정
/pages/\_app.js에tocbot.cssimport
import '@/css/tocbot.css'
글 작성시 레이아웃 적용
글 마다 적용하려면 frontMatter에 layout을 추가하고, 해당 layout을 적용하면 됩니다.
---
...생략...
layout: PostToc
---
또는 모든 글에 기본으로 적용하려면 /pages/blog/[...slug].js 파일의 DEFAULT_LAYOUT을 수정합니다.
const DEFAULT_LAYOUT = 'PostToc'
- layout 파일 추가의 경우 module을 못 찾아 오류가 납니다. 개발 서버 중지 후 재시작 해줍니다 (
npm start) - 컨텐츠 내에 보여지는 목차는 따로
<TocInline />컴포넌트를 내용에 추가해주어야 합니다. 없어도 무방합니다. 다만 1280px 미만의 화면에서는 목차가 보이지 않을 뿐입니다.
<TOCInline toc={props.toc} exclude="Overview" asDisclosure />
마무리
다음글, 이전글, 작성자 정보 등 제 기준에서 불필요 한 부분들은 제거하였습니다. 필요한 내용들은 PostLayout을 참고하여 추가하시면 됩니다.
코드는 add_tocbot 브랜치 또는 commit history로 확인하실 수 있습니다.
태그와 연관된 글
2023. 09. 27.
Next.js - 블로그 다국어(i18n)로 만들기 Part 42023. 09. 26.
Next.js - 블로그 다국어(i18n)로 만들기 Part 32023. 09. 25.
Next.js - 블로그 다국어(i18n)로 만들기 Part 22023. 09. 24.
Next.js - 블로그 다국어(i18n)로 만들기 Part 12023. 09. 19.
Next.js - 다국어(i18n) 설정 및 static html로 내보내기 Part 2


