Next.js 블로그에 mdx 적용하기

Next.js로 만든 블로그에 mdx 적용하기


mdxMarkdown with JSX의 약자다. mdx 파일에서는 특정 컴포넌트를 import 하거나 파일 내부에서 JSX 문법을 사용할 수 있다.

아래의 코드를 mdx 파일에 바로 추가하면 출력된 결과는 그 아래 화면과 같다.

<div>
  <h4>JSX 문법 바로 사용하기!</h4>
  > 인용과 중괄호 ({1 + 1})
</div>

출력 예제 👇

JSX 문법 바로 사용하기!

인용과 중괄호 (2)

mdx 파일 생성

mdx를 저장할 특정 폴더를 생성한 후 안에 mdx 파일을 작성한다. mdx 파일엔 --- dash 3개로 metadata를 저장할 수 있기 때문에 파일의 최상단에 metadata 또한 같이 적어준다.

mdx 라이브러리 설치

Next.js 공식문서에서 관련 라이브러리를 설치 한다. 처음엔 Mdx-remote 라이브러리를 설치했는데, 추후 mdx 파일의 타입을 정의할 수 있고 async, await 키워드 없이 바로 빌드된 파일을 불러올 수 있는 Contentlayer를 설치하여 mdx 파일을 불러왔다.

> yarn add contentlayer next-contentlayer

mdx 파일 읽고 불러오기

Contentlayer 시작하기를 참고하여 설정 파일을 만들었다.

contentlayer.config.ts에서 정의한 타입을 프로젝트에서 그대로 받아올 수 있다는 장점이 있다. 👏

설정된 경로에 파일을 생성하면 contentlayer/generated가 생성되고, 이후 next가 실행되어 생성된 파일을 불러오게 된다.

import { allPosts, Post } from 'contentlayer/generated'

📍 contentlayer/generated 가 생성되지 않는 문제

Contentlayer의 공식문서와 모든 걸 똑같이 했는데 contentlayer/generated가 생성되지 않아서 위의 { allPosts, Post }가 import 되지 않는 문제가 생겼다.

구글링을 해보니 next 뿐만 아니라 contentlayer를 별도로 실행해야 했다. 나는 개발 도중에 추가되는 파일 또한 실시간으로 확인하기 위해 개발 환경에서 contentlayer dev도 실행되도록 스크립트에 같이 정의했다.

"scripts": {
    "dev": "contentlayer dev & next dev",
  },

mdx 파일의 metadata 가져오기

import { allPosts, Post } from 'contentlayer/generated'

위의 allPosts에는 설정 경로로부터 생성된 mdx 파일들이 배열 구조로 담겨있는데, 나는 blog, memo, issue 각각의 게시글이 있기에 3개의 게시글을 별도로 나눴다.

게시글 목록에선 게시글의 metadata만 보여주면 되기에 util 함수를 별도로 만들어 metadata를 배열로 받아와 목록에 뿌려준다.

metadata 외에도 게시글 metadata의 모든 tag를 가져오는 util을 활용하여 화면 왼쪽에 모든 tag를 보여준다.

게시글 목록 클릭하여 mdx 보여주기

Next.js의 <Link> 태그를 사용하여 해당 게시글을 보여주도록 하자.

page.tsx 상단에 metadata와 관련된 내용들을 별도로 보여주고 하단에 content를 보여주면 완성! 자세한 활용 방법은 Contentlayer 공식문서를 참고하자.

단, 스타일링이 되어 있지 않은데 나는 tailwind.css의 prose 클래스를 활용했다. 하지만 prose 클래스도 완벽하지는 않아 tailwind.config.js에 추가로 스타일링을 해줬다. 아래 예시는 code, img 태그를 수정하는 방법이다.

추가) code 블럭 스타일링 방법은 아래 게시글을 참고

mdx 내부 codeblock 스타일링 하기 (feat. rehype-pretty-code)

extend: {
  typography: {
    DEFAULT: {
      css: {
        code: {
          //...
        },
        img: {
          //...
        }
      },
    },
  },
}