이전 포스트와 같은 방법으로 loader를 이용해서 content를 불러온다.
현재 라우팅은 /subBlog/${postId}
처럼 하고 있고, 나중에는 subBlog 자리에 서브 블로그 id 등을 넣어서 서브 블로그마다 다른 카테고리를 로드할 예정이다.
export const loader = async ({ context, params }: LoaderArgs) => {
const supabase = createClient<Database>(
context.env.SUPABASE_URL,
context.env.SUPABASE_KEY
);
const loadData = async (id: string) => {
try {
const { data: postData, error: postError } = await supabase
.from("posts")
.select("content")
.eq("id", id)
.single();
if (postError) throw new Error();
return postData?.content;
} catch (err) {
return null;
}
};
const postId = params.postId;
if (!postId) {
return null;
}
const data = await loadData(postId);
return data;
};
loader 안에서 params를 이렇게 불러올 수 있다. subBlog.$postId.tsx
에서 사용하는 loader이기 때문에 postId가 없는 경우는 없을 것이라고 생각하는데, if문을 넣지 않으면 params.postId가 undefined일 수도 있다는 경고가 떠서 저렇게 두었다. 나중에 공부해봐야지…
그러면 아래처럼 content가 보인다.
지금은 그냥 div 태그에 때려박아서 읽기가 힘들다🥰
이제 글을 읽기 쉽게 보여주자.
모든 글은 markdown(혹은 mdx) 형식이기 때문에 react-markdown을 이용한다. React에서 사용되는 markdown 관련 라이브러리 중에서 다운로드 수가 제일 많기에 선택했다.
우선 npm으로 설치부터 해 준다.
npm install react-markdown
사용법은 간단하다.
import ReactMarkdown from "react-markdown";
처럼 import해주고,
<div>{content}</div>
이랬던 코드를
<ReactMarkdown>{content}</ReactMarkdown>
이렇게 감싸주면 된다.
링크나 리스트들이 잘 적용된 모습이다. 나머지는 우리가 스타일링해 주어야 한다.
스타일링도 간단하다.
<ReactMarkdown
components={{
a: (props: any) => (
<a target="_blank" css={{ color: "red" }} {...props} />
),
}}
>
{content}
</ReactMarkdown>
이렇게 components
props를 넣고, 그 안에 원하는 태그의 렌더링 방법을 정해주면 된다.
실험적으로 a 태그의 텍스트 색상을 빨간색으로 지정해 보았다.
잘 적용된다!!
우선 아래처럼 꾸며줬다. 세세한 디자인은 나중에 적용하…겠지…?
포스팅 맨 아래 부분을 보면 코드가 있는데, 지금은 코드 하이라이팅이 적용되어 있지 않다. 이전 블로그 처럼 syntax highlighter를 사용해서 하이라이팅을 해 주겠다.
하이라이팅은 react-syntax-highlighter 라이브러리를 이용한다.
우선 설치부터 해 준다.
npm install react-syntax-highlighter
앞에서 components props에 다른 태그들을 스타일링한 것처럼, 코드도 이렇게 스타일링 해주면 된다.
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { nord } from "react-syntax-highlighter/dist/cjs/styles/prism";
...
code : (props: any) => {
const match = /language-(\w+)/.exec(props.className || "");
return !props.inline && match ? (
<SyntaxHighlighter
children={String(props.children).replace(/\n$/, "")}
style={nord}
language={match[1]}
PreTag="div"
{...props}
/>
) : (
<code css={styledCode} {...props}>
{props.children}
</code>
);
};
const match = /language-(\w+)/.exec(props.className || "");
는 코드가 어떤 언어로 작성되었는지를 파싱하는 코드이다. ``` 뒤 혹은 ~~~ 뒤에 지정한 언어를 알아내는 것이다. 또한, props중에 inline코드인지 아닌지를 알려 주는 플래그가 있기 때문에 이들을 이용하여 inline 코드와 그렇지 않은 코드를 다르게 스타일링 할 수 있다.
파란색 배경이 인라인 코드 부분이다.
Latex 문법을 일부 적용시켜 보았다. 물론 지금은 따로 옵션을 주지 않았기 때문에 문법에 맞게 파싱되지는 않는다.
설명에 따라 아래 패키지들을 설치하고 적용해주자.
npm install remark-math rehype-katex
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";
import "katex/dist/katex.min.css";
...
<ReactMarkdown
remarkPlugins={[remarkMath]}
rehypePlugins={[rehypeKatex]}
...
/>
아… 왜이럴까…
글씨가 두 번씩 나와서 개발자 도구로 찍어봤는데 aria-hidden=true임에도 불구하고 글씨가 계속 보이고 있다.
아마도 아래 css import가 제대로 먹히지 않은 것 같다. 다시 보니 도 글씨가 수상하게 작다.
import "katex/dist/katex.min.css";
Remix의 styling 가이드를 읽으니 Remix에서 스타일링을 하기 위해서는 을 추가해주어야 한다고 한다. Remix에서는 links를 export해서 stylesheet를 추가할 수 있다고 한다.
import styles from "katex/dist/katex.min.css";
export function links() {
return [{ rel: "stylesheet", href: styles }];
}
이렇게 바꿔주니 잘 작동한다!
* Lists
* [ ] todo
* [x] done
A table:
| a | b |
| - | - |
같은 문법들을 이용하기 위해서는 remark-gfm을 이용해야 한다.
같은 방법으로 설치해주고, 적용해주자. 나는 이미 remarkMath가 적용되어 있기 때문에 그 옆에 써주었다. 만약 remarkMath를 사용하지 않는다면 remarkPlugins={[remarkGfm]}
처럼 쓰면 된다.
npm install remark-gfm
import remarkGfm from "remark-gfm";
…
remarkPlugins={[remarkMath, remarkGfm]}
역시 잘 적용된다.
큰 도움 되었습니다. 감사합니다!