React에서 markdown 랜더링하기

2taeyoon·2023년 12월 12일
5
post-thumbnail
post-custom-banner

🦮Markdown을 랜더링하려면?

React에서 Markdown을 랜더링하려면 우선 MarkdownReact 컴포넌트로 변환해야합니다. 예를 들어, Markdown의 헤딩은 htmlh1, h2로 변환하는 작업이 필요합니다.

이러한 변환을 쉽게해주는 라이브러리가 바로 react-markdown입니다👏👏👏

npm install react-markdown

위의 명령어를 통해 react-markdown을 설치해줍니다. 그리고 리액트 프로젝트에서 다음과 같이 마크다운을 생성합니다.


export default function Blog() {
  const markdownText = `
  # 안녕하세요!
  저는 현재 리액트에서 \`react-markdown\`를 이용하여 **마크다운**을 랜더링하고 있습니다.
  `;
  
  return (
    <div>Blog</div>
  );
}

이어서 react-markdown을 불러옵니다.

import React from "react";
import ReactMarkdown from "react-markdown";

export default function Blog() {
  const markdownText = `
  # 안녕하세요!
  저는 현재 리액트에서 \`react-markdown\`를 이용하여 **마크다운**을 랜더링하고 있습니다.
  `;

  return (
    <div style={{ width: "100%", display: "flex", justifyContent: "center" }}>
      <div style={{ maxWidth: "768px", width: "100%" }}>
        <ReactMarkdown>
          {markdownText}
        </ReactMarkdown>
      </div>
    </div>
  );
}

백틱(backtick) 안에 백틱을 추가해야할때는 역슬래시를 이용하여 이중으로 사용할 수 있습니다.
저는 간단한 스타일을 추가하였습니다. (정렬이 안되어 있으면 꼴보기가 싫..)

그 결과 다음과 같이 마크다운이 랜더링됩니다. 하지만 벨로그의 백틱(backtick)을 이용한 코드 강조처럼 react-markdown이 강조가 되지 않습니다. 이러한 것은 직접 css 작업을 해줘야합니다.


다음과 같이 code 태그의 스타일을 지정해주고 컴포넌트에서 css를 불러와줍니다.

code {
	padding: 3px 6px;
  	border: none;
  	border-radius: 3px;
	background-color: #e9ecef;
	font-size: 1.6rem;
	font-family: "Pretendard", "Malgun Gothic", sans-serif;
}

그랬더니 다음과 같이 제대로 작성됩니다.

🦮Markdown 파일 자체를 랜더링하려면?

직접 마크다운을 작성해서 불러오는 것 외에 .md 파일 자체를 랜더링하려면 react-hookuseEffectuseState를 이용하여 파일을 가져와야합니다.


다음과 같이 작성해줍니다.

import React, { useEffect, useState } from "react";
import ReactMarkdown from "react-markdown";
import "../../styles/markdown.css";

export default function Blog() {
  const [markdown, setMarkdown] = useState("");

  useEffect(() => {
    fetch("./01.md")
      .then((response) => response.text())
      .then((text) => setMarkdown(text));
  }, []);

  return (
    <div style={{ width: "100%", justifyContent: "center", display: "flex" }}>
      <div style={{ maxWidth: "768px", width: "100%" }}>
        <ReactMarkdown>
          {markdown}
        </ReactMarkdown>
      </div>
    </div>
  );

  • useEffect 내부에서 fetch를 사용하여 ./01.md 경로의(index.html 기준) Markdown 파일을 호출합니다.
  • fetch로 반환된 response에서 text() 메서드를 사용하여 텍스트 데이터를 추출합니다.
  • 추출한 텍스트를 setMarkdown 함수를 사용하여 markdown 상태에 업데이트합니다.
  • 이로써 컴포넌트가 처음 렌더링될 때 Markdown 파일의 내용이 로딩되고, 그 후에는 변경되는 것이 없습니다.
  • 의존성 배열을 빈 배열로 두어 컴포넌트가 처음 랜더링될 때만 Markdown 파일이 랜더링되게 합니다.

01.md 파일의 위치는 다음과 같습니다.

🦮pre 태그 안의 code 태그가 이상하다!

다음과 같은 .md 파일을 랜더링 시켰더니..

코드를 나타내는 마크다운은 개발자 모드를 열어서 확인해보니 pre 태그 자식 요소로 code 태그로 구성됩니다. 그런데 이러한 코드블록이 벨로그처럼 표시되지 않습니다..😭

이때에는 언어별로 class를 생성해주는 rehype-highlight 라이브러리를 사용해야합니다.

npm install rehype-highlight


import rehypeHighlight from "rehype-highlight";

  return (
    <div style={{ width: "100%", justifyContent: "center", display: "flex" }}>
      <div style={{ maxWidth: "768px", width: "100%" }}>
        <ReactMarkdown rehypePlugins={[rehypeHighlight]}>
          {markdown}
        </ReactMarkdown>
      </div>
    </div>
  );

rehype-highlightimport해주고 ReactMarkdownrehypePlugins를 추가하여 rehypeHighlight를 옵션값으로 넣어줍니다.

그 후, 개발자 모드에서 열어보면 다음과 같이 pre code 태그에 class가 추가된 것을 확인하실 수 있습니다.

그럼 이제 언어별로 직접 css 수정을 하🥊
언어별로 css를 직접 수정할 필요없이 이미 만들어져있는 라이브러리를 사용하면됩니다.

npm install highlight.js

highlight.js를 설치하시고 css파일을 import 해주면됩니다.


import "highlight.js/styles/a11y-dark.css";

그럼 다음과 같이 언어별로 마크다운이 아주 예쁘게 생성된 것을 보실 수 있습니다.

다른 스타일을 원하시면 node_modules > highlight.js > styles 폴더에 너무나도 많은 코드 스타일의 css가 있습니다. 이중에서 마음에 드는걸 사용하시면됩니다.

추가로 highlightjs 공식홈페이지에서 미리보기로 코드 스타일을 확인하실 수 있습니다!

코드 태그가 너무 이뻐졌당~😻😻

🦮태그 네임이 그대로 출력된다!!

위와 같은 간단한 md 파일을 랜더링한다고 했을 때

태그 네임이 그대로 출력되는 불상사가 일어나게 된다.. 이러한 이슈를 해결하기 위해서는 rehype-raw 라이브러리를 이용해야합니다.

🦄rehype-raw란?

rehype-rawReact Markdown에서 사용되는 rehype 플러그인 중 하나입니다. 해당 플러그인은 MarkdownHTML로 변환할 때 HTML 태그를 랜더링하는 방법을 제어하는 플러그인입니다.

rehype-rawReact Markdown에 추가하면 HTML 코드가 특수 문자로 이스케이프되어 안녕하세요가 태그 네임 없이 올바르게 랜더링됩니다.

아래의 명령어로 rehype-raw를 설치합니다.

npm install rehype-raw

그런 다음 다음과 같이 코드를 작성합니다.


import React, { useEffect, useState } from "react";
import ReactMarkdown from "react-markdown";
import rehypeHighlight from "rehype-highlight";
import rehypeRaw from "rehype-raw"; // 추가된 부분
import "../../styles/markdown.css";
import "highlight.js/styles/a11y-dark.css";

export default function Blog() {
  const [markdown, setMarkdown] = useState("");

  useEffect(() => {
    fetch("./01.md")
      .then((response) => response.text())
      .then((text) => setMarkdown(text));
  }, []);

  return (
    <div style={{ width: "100%", justifyContent: "center", display: "flex" }}>
      <div style={{ maxWidth: "768px", width: "100%" }}>
        // 추가된 부분
        <ReactMarkdown rehypePlugins={[rehypeHighlight, rehypeRaw]}>
          {markdown}
        </ReactMarkdown>
      </div>
    </div>
  );
}

그럼 정상적으로 태그 네임없이 출력되는 모습!

굿드👍👍

profile
정리하면서 공부하기
post-custom-banner

1개의 댓글

comment-user-thumbnail
2024년 9월 24일

감사합니다. 이런 간단한 방법이 있었네요

답글 달기