Blog
Web
Frontend
Web - November 29, 2024
지금까지 gatsby↗ 를 이용해서 블로그를 개발하고, 사용해왔다. 하지만, 몇몇 불편함이나 거슬리는 부분이 있어서 next.js 로 옮겨야겠다고 생각했다. 그 몇몇 이유를 간단하게 언급해보자면,
너무 오래 걸리는 빌드 시간
graphql 의 필요성
API 요청의 어려움
gatsby-node.js
에서 처리한 뒤 graphql 로 저장하고 이후에 불러와야 하는데… 굉장히 번거로웠다.라이브러리 버전 충돌이 잦음
제대로 알고 하는 건지에 대한 의문
그냥 next.js 를 해보고 싶었음
패키지 매니저로는 yarn 을 사용하였다. next는 14를 사용하였다.
저번 블로그에서 mdx 를 이용했을 때 좋은 인상을 받았기 때문에 이번에도 블로그 포스팅은 mdx 를 쓸 예정이었다. 따라서, 다음과 같은 패키지들을 추가했다.
yarn add @mdx-js/loader @mdx-js/react @next/mdx next-mdx-remote remark-gfm
yarn add @types/mdx -D
next-mdx-remote 가 지원하는 추가 플러그인이 많아서 일단 그것을 사용했다.
만일 turbopack을 사용하고 있다면, next.config.mjs
에 다음과 같이 transpilePackages
를 추가해야 한다.
/** @type {import('next').NextConfig} */
const nextConfig = {
pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
transpilePackages: ['next-mdx-remote'],
};
export default nextConfig;
일단 나는 포스팅을 content/blog
에 [category]/[slug].mdx
형태로 저장하고 있다. 이를 그대로 사용하기 위해서 next 의 app routing 을 사용하기로 하였다.
server component 에서는 로컬의 파일을 읽어올 수 있기 때문에, 불러온 mdx 파일을 next-mdx-remote 의 compileMDX
를 이용해서 파싱하고, 이를 보여주는 형식을 생각했다.
// src/app/[category]/[slug]/page.tsx
// import 생략
const generateStaticParams = async () => {
const fullPaths = getAllPostPaths();
return fullPaths.map((fullPath) => {
const splited = fullPath.split('/');
const category = splited[splited.length - 2];
const slug = splited[splited.length - 1].replace(/\.mdx$/, ''); // omit extension
return {
category,
slug,
};
}) satisfies TPostPageProps['params'][];
};
const PostPage = async ({ params }: TPostPageProps) => {
const { category, slug } = params;
const fullPath = getPostPath(category, slug);
const postFile = fs.readFileSync(fullPath);
const { content, frontmatter } = await compileMDX<TFrontmatter>({
source: postFile,
options: {
parseFrontmatter: true,
mdxOptions: {
remarkPlugins: [remarkGfm, remarkMath],
rehypePlugins: [rehypeKatex],
},
},
components: CustomMDXComponents,
});
return (
<div>
<h1>{frontmatter.title}</h1>
{content}
</div>
);
};
export { generateStaticParams };
export default PostPage;
getPostPath
나 getAllPostPaths
와 같은 유틸 함수들은 다음과 같다.
// src/lib/getBlogPost.ts
const postsDirectory = path.join(process.cwd(), 'content/blog');
const getPostPath = (category: string, slug: string) => {
return path.join(postsDirectory, category, `${slug}.mdx`);
};
const getAllPostPaths = () => {
// 재귀적으로 mdx 파일을 찾는다.
const categories = fs.readdirSync(postsDirectory);
const fileNames = categories.flatMap((category) => {
const filePath = path.join(postsDirectory, category);
const names = fs
.readdirSync(filePath)
.map((name) => path.join(postsDirectory, category, name));
return names;
});
return fileNames;
};
export { getPostPath, getAllPostPaths };
그러면 이렇게 못생겼지만 잘 라우팅 되는 것을 확인할 수 있었다.
이젠 스타일링을 해야할 것 같다.