Next.js13과 Contentlayer을 이용하여 MDX파일을 다루는 방법을 정리한 포스트입니다. Next.js를 사용하는 이유는 대부분의 경우 개인 블로그는 정적 컨텐츠이기에 Next.js의 SSG를 사용하면 필요한 페이지를 build time에 미리 만들어서 재사용하므로 페이지 로딩속도가 빨라집니다. 또한 Next.js는 기본적으로 SSR 기반이므로 SEO에 도움을 줍니다. 리액트 기반의 클라이언트 컴포넌트가 필요할때는 코드 상단에
"use client"
를 추가하여 CSR 도 지원하므로 풀스택 프레임워크로 성장하고 있습니다.
시작
Next.js에서는 프로젝트를 create-next-app
을 이용하여 생성할 것을 추천합니다. 필요한 파일들을 자동으로
설치해주기에 웬만해선 안쓸 이유가 없는 것 같습니다. 터미널 창에
npx create-next-app
을 입력하여 Next.js 프로젝트를 생성해줍니다.
기본 설정 값을 물어보는데 저는 기본 값으로 했습니다. (tailwindcss는 강추👍)
Contentlayer
Next.js 공식문서에서는 MDX파일 다루는 법으로 next-mdx-remote
와 contentlayer
두 가지를 소개합니다.
저는 그 중에서 contentlayer를 택했습니다.
npm i contentlayer next-contentlayer
Contentlayer 설치가 다 되었으면 contentlayer
config를 next.config.js
에 추가해줍니다.
mdx
// next.config.js
const { withContentlayer } = require('next-contentlayer')
/** @type {import('next').NextConfig} */
const nextConfig = { reactStrictMode: true, swcMinify: true }
module.exports = withContentlayer(nextConfig)
그리고 프로젝트의 루트에 contentlayer.config.ts
라는 파일을 만들어서 아래 코드를 입력해줍니다.
//contentlayer.config.ts
import { defineDocumentType, makeSource } from "contentlayer/source-files";
const Post = defineDocumentType(() => ({
name: "Post",
filePathPattern: `**/*.mdx`,
contentType: "mdx",
fields: {
title: {
type: "string",
description: "The title of the post",
required: true,
},
date: {
type: "date",
description: "The date of the post",
required: true,
},
},
computedFields: {
url: {
type: "string",
resolve: (doc) => `/posts/${doc._raw.flattenedPath}`,
},
},
}));
export default makeSource({
contentDirPath: "posts",
documentTypes: [Post],
});
위 코드는 Post
documentType을 만들어 주며, flattenedPath
로 url field를 만들어 줍니다.
document types의 field에 tag와 description 등을 추가할 수 있습니다.
MDX 파일 생성 및 렌더링
이제 MDX 파일을 만들어 봅시다. 프로젝트의 루트에 posts
폴더를 만든 후 안에 test-01.mdx
파일을 만듭니다.
---
title: "How to create Next.js app"
date: 2023-04-06
---
# NextJs로 블로그 만들기
## h2 태그
### h3 태그
#### h4 태그
다음으로 npm run dev
로 서버를 엽니다. 콘솔창에 아래와 같이 .contentlayer
폴더에
1개의 문서가 생성되었다는 문구가 나오면 성공입니다.
- event compiled client and server successfully in 1284 ms (20 modules)
Generated 1 documents in .contentlayer
- wait compiling...
- event compiled client and server successfully in 85 ms (20 modules)
위와 같이 .contentlayer
폴더에 제가 만든 test-01.mdx
파일이 json 형태로 저장된 것을 볼 수 있습니다.
포스트의 작성 날짜를 format하기 위해 date-fns
를 설치해줍니다.
npm i date-fns
그리고 tsconfig.json 파일에 아래 코드를 추가하여 generated files을 .contentlayer
폴더에서 가져올 수
있게 해줍니다.
"paths": {
"@/*": ["./*"],
"contentlayer/generated": ["./.contentlayer/generated"]
}
이제 app
폴더안에 posts
폴더와 그 안에 slug
폴더를 만든 후 slug폴더 안에 page.tsx
를 생성하여
다음 코드를 추가해줍니다.
// app/posts/[slug]/page.tsx
import { allPosts } from "contentlayer/generated";
import { getMDXComponent } from "next-contentlayer/hooks";
import { format, parseISO } from "date-fns";
export const generateStaticParams = async () =>
allPosts.map((post:any) => ({ slug: post._raw.flattenedPath }));
export const generateMetadata = ({ params }: any) => {
const post = allPosts.find(
(post: any) => post._raw.flattenedPath === params.slug: any
);
return { title: post?.title, description: post?.description };
};
const PostLayout = ({ params }: { params: { slug: string } }) => {
const post = allPosts.find((post) => post._raw.flattenedPath === params.slug);
let MDXContent;
if (!post) {
return <div>404</div>;
} else {
MDXContent = getMDXComponent(post!.body.code);
}
return (
<div>
<h1>{post.title}</h1>
<p>{format(parseISO(post.date), "LLLL d, yyyy")}</p>
<article>
<MDXContent />
</article>
</div>
);
};
export default PostLayout;
위 코드는 포스트 페이지를 slug parameter
를 기준으로 렌더링 시켜줍니다. 자세한 방식은 Next.Js 공식문서의
동적 라우트 부분에서 확인할 수 있습니다.
코드 작성을 마친 후 http://localhost:3000/posts/test-01
로 접속하면
만들었던 mdx파일을 가지고 잘 렌더링된 화면을 확인할 수 있습니다.
스타일링
그런데 분명 mdx 태그에는 #
을 이용해서 h1~h4 태그를 만들었는데 렌더링된 화면에서는 적용이 되어 있지 않습니다.
global.css
파일에 가서 아래 코드를 추가해주면
article h1 {
font-size: 2em;
}
article h2 {
font-size: 1.5em;
}
article h3 {
font-size: 1.17em;
}
article h4 {
font-size: 1em;
}
위와 같이 태그가 적용된 모습을 볼 수 있습니다. 그런데 이런 식으로 모든 태그를 스타일링 직접 하기에는 너무 번거로우기에
많이들
tailwindcss
를 사용합니다.
npm i -D @tailwindcss/typography
@tailwindcss/typography
플러그인을 설치하고 tailwindcss.config.ts
폴더의 plugins
에 아래 코드를
추가해줍니다
plugins: [
require('@tailwindcss/typography'),
],
global.css
파일에 가셔서 상단에 다음 코드를 추가해줍니다.(Next.js CLI로 프로젝트를 생성할 때 tailwindcss를 기본 옵션으로 설정하셨으면 자동으로 추가되어 있습니다. )
@tailwind base;
@tailwind components;
@tailwind utilities;
그리고 app/posts/[slug]/page.tsx
파일에 ariticle 태그에 prose
를 className으로 추가해줍니다.
태그 스타일이 자동으로 잘 적용된 것을 볼 수 있습니다. prose
로 적용된 스타일을 변경하시고 싶으시면
tailwindcss 공식 문서에서 확인할 수 있습니다.
추가적으로 포스트안에 포함되어있는 소스코드를 스타일링 하시고 싶으시면 rehype-pretty-code를 이용하시면 됩니다.