내가 만든 블로그는 코드와 글이 구분되지 않는 상태였다. 가독성도 안 좋고 밋밋해보인다.
그래서 UX의 향상을 위해 react-syntax-highlighter
를 사용하여 Code Highlight를 적용해보려고 한다.
목표: MDX 파일을 React 컴포넌트로 변환한 MDXComponent에 react-syntax-highlighter 적용하기!
- 코드 하이라이팅 기능을 React 컴포넌트 형태로 제공
React 프로젝트 내에서 자연스럽게 통합되며, React의 생명주기 관리, 상태 관리 등과 잘 호환된다.
- Props를 통한 쉬운 설정
props를 통해 하이라이터의 설정을 쉽게 변경할 수 있어, 옵션을 손쉽게 조정할 수 있다.
- JSX 지원
JSX 코드를 직접 하이라이팅할 수 있습니다. Prism이나 Highlight.js를 직접 사용할 때보다 훨씬 간편한 방법을 제공한다.
- 커스텀 스타일링
style props를 통해 코드 블록의 스타일을 쉽게 수정하고 조정할 수 있다.
- 성능 최적화
불필요한 리렌더링을 방지하기 위한 최적화가 포함되어 있으며, 필요한 코드 하이라이팅 스크립트만을 로드하여 페이지의 로드 시간을 단축시킬 수 있다.
npm install react-syntax-highlighter
<code>
태그를 대신할 CodeBlock 컴포넌트 만들기import { FC } from "react";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { materialOceanic } from "react-syntax-highlighter/dist/cjs/styles/prism";
interface TProps {
language: string;
value: string;
}
const CodeBlock: FC<TProps> = ({ language, value }) => {
return (
<SyntaxHighlighter language={language} style={materialOceanic}>
{value}
</SyntaxHighlighter>
);
};
export default CodeBlock;
Prism
과 하이라이팅에 적용될 테마 materialOceanic
import하기language
와 style
넣어주기const components = {
code({ node, inline, className, children, ...props }: any) {
const match = /language-(\w+)/.exec(className || "");
return !inline && match ? (
<CodeBlock
value={String(children).replace(/\n$/, "")}
language={match[1]}
{...props}
/>
) : (
<code className={className} {...props}>
{children}
</code>
);
},
};
code 함수: 마크다운에서 코드 블록 또는 인라인 코드를 렌더링할 때 호출된다. 구조 분해 할당을 사용하여 node, inline, className, children, ...props
파라미터를 정의한다.
match: /language-(\w+)/.exec(className || "")
를 사용하여 className에서 language- 접두사를 포함하는 언어 식별자를 추출한다.
(코드 하이라이팅을 적용할 언어를 결정하는 데 사용)
조건부 렌더링: 코드 멀티라인 코드 블록에 대해 CodeBlock
컴포넌트를 사용하여 렌더링하고, 그렇지 않은 경우 기본 HTML <code>
태그를 사용하여 코드를 렌더링한다.
replace(/\n$/, "")
를 사용해 마지막 개행 문자는 제거해준다.<MDXComponent components={components} />
...
import CodeBlock from "@/components/CodeBlock";
const Post = ({ post }: InferGetStaticPropsType<typeof getStaticProps>) => {
const components = {
code({ node, inline, className, children, ...props }: any) {
const match = /language-(\w+)/.exec(className || "");
return !inline && match ? (
<CodeBlock
value={String(children).replace(/\n$/, "")}
language={match[1]}
{...props}
/>
) : (
<code className={className} {...props}>
{children}
</code>
);
},
};
const MDXComponent = useMDXComponent(post ? post.body.code : "");
const customMeta = post && {
title: post.title,
description: post.description,
date: new Date(post.date).toISOString(),
};
return (
<Container customMeta={customMeta}>
{post && (
<PostContent>
<PostTitle>{post.title}</PostTitle>
<article className="markdown-body">
<MDXComponent components={components} />
</article>
</PostContent>
)}
</Container>
);
};
export const getStaticPaths = async () => {
return {
paths: allPosts.map((p) => ({ params: { slug: p._raw.flattenedPath } })),
fallback: false,
};
};
export const getStaticProps = async ({
params,
}: {
params: ParsedUrlQuery;
}) => {
const post = allPosts.find((p) => p._raw.flattenedPath === params.slug);
return {
props: {
post,
},
};
};
export default Post;
: 내 블로그는 거의 JS만 사용하기 때문에 Prism
을 사용했다.
Prism
Highlight.js