2025. 03
CSS로 커스텀하는 Markdown 렌더러
마크다운 렌더러…그런데 이제 이것저것 많이 곁들인
- 오픈소스
- React
Github: https://github.com/frfla/react-md-renderer/pkgs/npm/react-md-renderer
Docs: https://frfla.github.io/react-md-renderer/docs
npm install @frfla/react-md-renderer@0.13.3
yarn add @frfla/react-md-renderer@0.13.3
마크다운? CSS만 커스텀하면 될 일이 아닌가요
노션 블로그를 만들면서 마크다운의 한계를 체감하고, 마크다운을 AST로 파싱하는 파서를 직접 만드는 게 아니라면 (다른 파서를 이용해야 한다면) 그 과정을 한 번 더 단축시켜주는 라이브러리를 만드는 것도 괜찮은 일이라고 생각했습니다. 실제로 react markdown같은 라이브러리를 이용해 보면서, 컴포넌트를 등록하는 일과 remark, rehype같은 플러그인을 등록해 사용하는 일은 꽤나 성가신 일이었습니다. 마크다운을 파싱해 컴포넌트로 렌더링할 일이 많이 발생하는 게 아니기 때문에 더더욱이요. 어차피 한계가 명확한 것이고, 간단히 사용만 해야 하는 상황이라면 이런 게 하나쯤 있어도 괜찮지 않을까 하는 생각도 해 보았습니다.
이왕 만드는 것, 스타일링 도구에 의존하지 않도록…
지원하는 마크업
a
, blockquote
, checkbox
, code
, inlinecode
, h1-h6
, hr
, img
, li
, ol
, ul
, p
, table
, strike
, strong
, italic
위의 마크업들에 대해 CSS를 커스텀할 수 있도록 하되, 스타일링 도구에 의존하지 않도록 하기 위해 API 설계를 고심해야 했습니다.
CSS 변환
주요 스타일링 도구들의 작성 결과물은 다음과 같은데요,
일반 style prop:
object
일반
className
prop:string
vanilla extract:
className={스타일 변수 할당(string)}
emotion, stitches: css prop:
object
or() => object
위의 특징들을 모두 포괄하기 위한 작업을 했습니다. 그래서 최종적인 API는 아래와 같은데요…
return <Renderer markdown={...}
CSS={{
h1: ...,
code: { fontSize: '0.75rem' },
p: 'paragraph'
}}
classNamePrefix='abcd'
/>
위와 같이 ...
부분에 string
, object
, ()=>object
를 다 넣을 수 있습니다. 내부적으로 스타일을 잘 변환해서 삽입합니다.
Pure/module CSS 지원
또, classNamePrefix
prop으로 원하는 접두사를 사용할 수 있습니다. css classname 컨벤션이나 캐시 문제 등으로 해시값을 사용할 때 사용할 수 있습니다.
스타일링 타입 검사

스타일링 라이브러리인 stitches의 타입을 통해 타입 검사 또한 지원합니다. 쉽게 스타일링할 수 있으면서도 Stitches가 클라이언트에서 더 가볍게 동작하기 때문에 Stitches를 선택했습니다. 빌드 단계에서 emotion과는 달리 컴포넌트 단위가 아닌 스타일 단위의 캐싱(e.g. margin: 0
)을 진행하기 때문입니다.
문서 구조(hn) 보정
return <Renderer markdown={...}
adjustHeadings={true}
/>
adjustHeadings
가 true
일 때 h1-h6
을 보정합니다. 문서 제목 외 h1
이 있다면 모든 hn 태그를 hn+1
로 만들어 해당 문서에 제목이 단 하나만 있도록 만듭니다. 다만 스타일링은 그대로 유지되기 때문에 보기에는 전혀 다른 점이 없고, 마크업만 바뀝니다. 이 기능은 실험적인 기능이었는데, SEO에 조금이라도 도움이 되도록 하고 싶었기 때문에 넣어 보았습니다. 이 패키지의 README가 이 보정을 보여드리기 위해 작성되어 있는데요,


문서의 제목, 즉 첫 번째로 사용한 h1은 h1
마크업으로 잘 렌더하지만, 두 번째로 사용한 h1은 h2
로 렌더하면서 스타일링은 h1로 유지합니다. 이렇게 함으로써 문서의 시맨틱한 구조는 보정해 주면서도 스타일링에 대한 혼동은 피할 수 있습니다.
마무리
확장판인 MDX도 있고 더 좋은 게 많긴 하지만, 어쨌든 간단하게 마크다운만 딱 렌더링하는 상황일 때 사용하면 더없이 좋다고 생각합니다. 간단한 문서 페이지같은 것들을 만들 때 저도 잘 사용하고 있습니다. 필요한 건(이미 좋은 게 나와 있어도, 아직은 연습하는 차원에서) 직접 만들어서 쓰자는 주의이기도 하고, CSS 라이브러리 종속성에 대해 고민하느라 만들어 보게 된 패키지였습니다.
이왕 간단하게 쓸 수 있도록 하는 김에 욕심도 생겨서 타입 검사가 되면 멋지지 않을까, 문서 구조 보정도 해 주면 좋지 않을까 하면서 하나둘씩 기능도 넣게 되었습니다. 자세한 API 문서는 https://frfla.github.io/react-md-renderer/docs 에서 보실 수 있습니다.