본문 바로가기
개발/리액트

[리액트] 마크다운 문서에서 HTML 랜더링하기(rehype-raw, next-intl)

by 핸디(Handy) 2024. 9. 26.

들어가며

이 글을 읽고나면 next-intl로 들어오면 번역언어의 HTML를 파싱하여 랜더링할 수 있게 됩니다.

대상 독자는 리액트 프로젝트로 다국어를 지원하는 개발자, 블로그 서비스를 마크다운으로 관리하는 개발자입니다.

문제 인식

이번 분기에 만들고 있는 제품을 홍보 및 가이드하기 위해 블로그 기능을 추가하였다. 다만 현재 제품은 11개의 언어로 번역되어 있고, 각각의 번역 파일은 구글 시트로 관리하며 자동화스크립트에 의해 프론트엔드 개발자가 사용할수 있도록 locale 별 json으로 떨어진다.

그리고 내부적으로 next.js를 사용하고 있고 i18n 라이브러리는  next-intl 를 사용하고 있었다. 여기까지 문제될건 없었다. 그런데 갑자기 특정 문자열에서 에러가 발생하기 시작했다. 

저기 보이는 "blog_best_tips.post_0008_body" 는 blog_best_tips 로케일문서의 8번 post의 body를 가져오는 것이었다.
다른 문자열 예를 들어 "iPhone으로 손쉽게 고품질 녹음을 할 수 있습니다"는 blog_best_tips.post_0008_title 인데 제대로 번역되어 받아오는 모습을 확인할 수 있었다.

body는 마크다운 문서다. 그리고 이를 ReactMarkdown를 하여 랜더링 해주고 있었다. 그런데 마크다운 문서에 iframe이 들어가면서부터 에러가 발생하기 시작했다.

그런데 이 문서를 작성하는 동료는 이미 온라인 마크다운 편집기로 테스트를 해서 가져온다. (그러니깐 테스트서버로 배포했지~~). 그런데 여기에서 문제가 발생했던 것이었다.

문제 해결

우선 우리팀 시니어 개발자 챗형한테 물어봤다.

일반적으로 가능하다고 한다. 즉 마크다운 랜더러들이 별도로 지원하는 기능이라는 것으로 생각할 수 있었다. 그다음 개발자의 영원한 동반자 구글링을 해보자.

 

Any way to render HTML in react-markdown

I am using tinymce to accept markdown data from user. The output data is html. I want to display that data on a page. I am using react-markdown for it. I can see data on page but it's HTML tags. Is...

stackoverflow.com

아주 매력적인 질문이 있다. react-markdown으로 HTML을 랜더링하는 어떤 방법이든 있는지 묻는 나와 같은 고민을 한 개발자의 피눈물을 찾았다.

댓글을 보니 rehype-raw 이거 깔면 된다고 한다.

rehype-raw

rehype-raws는 Rehype의 플러그인 중 하나로, 마크다운에서 HTML을 파싱할 때 HTML 태그를 처리하기 위해 사용된다. 보통의 마크다운 파서는 HTML 태그를 일반 텍스트처럼 처리하거나 무시하는데 이때 rehype-raw를 사용하면 실제 HTML 요소로 변환할 수 있도록 해준다. 이제 한번 해보자.

import ReactMarkdown from "react-markdown";
import rehypeRaw from "rehype-raw";

interface MarkdownRendererProps {
  children: string;
  className?: string;
}

const MarkdownRenderer = ({ children, className }: MarkdownRendererProps) => {
  return (
    <>
      <ReactMarkdown rehypePlugins={[rehypeRaw]} className={className}>
        {children}
      </ReactMarkdown>
    </>
  );
};

export default MarkdownRenderer;

결과는~~

실패~~ (같은 사진아니다. 다른사진이다)

하.. 나한테는 이게 해답이 아닌건가 싶을때.. 한가지 의문점이 또 발생한다.

next-intl의 html markup

현재 제품에서 사용하는 next-intl에서는 i18n된 문서를 사용하기 위해 여러가지 추가기능을 제공하는데 그 중에 markup 예시도 있었다.

그래서 이것이 문제일까 싶어서 여러옵션을 뒤져보다가 하나를 더 찾았다.

next-intl의 Raw messages

갓챠~ 원래라면 dangerouslySetInnerHTML를 사용하여 div에 직접 넣어야하는데 현재 markdown 랜더러를 사용하고 있기에 이 기능을 통해서 값을 전달해줘야한다.

dangerouslySetInnerHTML 대신에 기능 content를 바로 랜더링하면 아래와 같은 결과가 나온다.

그리고 이 결과를 마크다운 랜더러에 한번더 전달하면

아주 깔끔하게 잘 나온다. 그리고 문제가 됬던 iframe를 확인해보면 

결과물이 잘 나온다.

만약 여기에서 rehypeRaw를 제거하면 어떻게 보일까?

이렇게 깨져서 보인다.

마무리

이번 글에서는 react-markdown 내부에서 html를 제대로 랜더링하는 방법에 대해 살펴보았다. 중간에 끼어서 힘들게 했던 next-intl의 옵션을 살펴보고 rehype-raw, next-intl의 콜라보를 통해 결과물을 완성할 수 있었다.

번외로 우리 팀이 만들고 있는 서비스를 한번 소개해보고자 한다. 

 

Online AI Vocal Remover & Stem Splitter | Gaudio Studio

Try it for free♬ The best AI-powered tool for making karaoke tracks, removing background music and more. Split your music into vocal and instrumental tracks.

studio.gaudiolab.io

끝.

댓글