title | description | i18nReady |
---|---|---|
Markdown & MDX |
Astro์์ Markdown ๋๋ MDX๋ฅผ ์ฌ์ฉํ์ฌ ์ฝํ
์ธ ๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์. |
true |
import Since from '/components/Since.astro';
import { FileTree } from '@astrojs/starlight/components';
import RecipeLinks from "/components/RecipeLinks.astro";
import ReadMore from '~/components/ReadMore.astro';
import { Steps } from '@astrojs/starlight/components';
Markdown์ ์ผ๋ฐ์ ์ผ๋ก ๋ธ๋ก๊ทธ ๊ฒ์๋ฌผ ๋ฐ ๋ฌธ์์ ๊ฐ์ด ํ ์คํธ๊ฐ ๋ง์ ์ฝํ ์ธ ๋ฅผ ์์ฑํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. Astro์๋ title, description, tags์ ๊ฐ์ ์ฌ์ฉ์ ์ ์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ ์ํ๊ธฐ ์ํด ํ๋ฐํธ๋งคํฐ YAML๋ ํฌํจํ ์ ์๋ ํ์ค Markdown ํ์ผ์ ๋ํ ๊ธฐ๋ณธ ์ง์์ด ํฌํจ๋์ด ์์ต๋๋ค.
@astrojs/mdx ํตํฉ์ด ์ค์น๋๋ฉด Astro๋ Markdown ์ฝํ
์ธ ์ JavaScript ํํ์ ๋ฐ ์ปดํฌ๋ํธ ์ง์๊ณผ ๊ฐ์ ์ถ๊ฐ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ MDX (.mdx
) ํ์ผ๋ ์ง์ํฉ๋๋ค.
Markdown ์ฝํ ์ธ ๋ฅผ ์์ฑํ๋ ค๋ฉด ๋ ๊ฐ์ง ์ ํ์ ํ์ผ ์ค ํ๋ ๋๋ ๋ ๊ฐ์ง๋ฅผ ๋ชจ๋ ์ฌ์ฉํ์ธ์!
Astro์ ํน๋ณํ src/content/
ํด๋์์ Markdown ๋ฐ MDX ํ์ผ์ ๊ด๋ฆฌํ ์ ์์ต๋๋ค. ์ฝํ
์ธ ์ปฌ๋ ์
์ ์ฝํ
์ธ ๋ฅผ ๊ตฌ์ฑํ๊ณ , ํ๋ฐํธ๋งคํฐ์ ์ ํจ์ฑ์ ๊ฒ์ฌํ๊ณ , ์ฝํ
์ธ ์์
์ค ์๋ TypeScript ํ์
์์ ์ฑ์ ์ ๊ณตํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
Astro์ ์ฝํ ์ธ ์ปฌ๋ ์ ์ฌ์ฉ์ ๋ํด ์์ธํ ์์๋ณด์ธ์.
Astro๋ /src/pages/
๋๋ ํฐ๋ฆฌ์ .md
(๋๋ ๋์ฒด ์ง์ ํ์ฅ์) ๋๋ .mdx
ํ์ผ์ ํ์ด์ง๋ก ์ฒ๋ฆฌํฉ๋๋ค.
์ด ๋๋ ํฐ๋ฆฌ๋ ํ์ ๋๋ ํฐ๋ฆฌ์ ํ์ผ์ ๋ฐฐ์นํ๋ฉด ํ์ผ์ ๊ฒฝ๋ก ์ด๋ฆ์ ์ฌ์ฉํ์ฌ ํ์ด์ง ๊ฒฝ๋ก๊ฐ ์๋์ผ๋ก ๋น๋๋ฉ๋๋ค.
---
# Example: src/pages/page-1.md
title: Hello, World
---
# Hi there!
This Markdown file creates a page at `your-domain.com/page-1/`
It probably isn't styled much, but Markdown does support:
- **bold** and _italics._
- lists
- [links](https://astro.build)
- and more!
Astro์ ํ์ผ ๊ธฐ๋ฐ ๋ผ์ฐํ ๋๋ ๋์ ๊ฒฝ๋ก ์์ฑ ์ต์ ์ ๋ํด ์์ธํ ์์๋ณด์ธ์.
Astro๋ Markdown ๋ฐ MDX ํ์ผ์ ์ฌ์ฉํ ๋ ์ฌ์ฉํ ์ ์๋ ๋ช ๊ฐ์ง ์ถ๊ฐ ๋ด์ฅ Markdown ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
Astro๋ Astro ๋ ์ด์์ ์ปดํฌ๋ํธ์ ๋ํ ์๋ ๊ฒฝ๋ก (๋๋ ๋ณ์นญ)๋ฅผ ์ง์ ํ ์ ์๋ ํน๋ณํ ํ๋ฐํธ๋งคํฐ layout
์์ฑ์ด ์๋ Markdown ๋ฐ MDX ํ์ด์ง (src/pages/
์ ์์นํ๋)๋ฅผ ์ ๊ณตํฉ๋๋ค.
---
# src/pages/posts/post-1.md
layout: ../../layouts/BlogPostLayout.astro
title: Astro in brief
author: Himanshu
description: Find out what makes Astro awesome!
---
This is a post written in Markdown.
๊ทธ๋ฐ ๋ค์ Astro.props
๋ฅผ ํตํด ๋ ์ด์์ ์ปดํฌ๋ํธ์ ํน์ ์์ฑ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด Astro.props.frontmatter
๋ฅผ ํตํด frontmatter ์์ฑ์ ์ก์ธ์คํ ์ ์์ต๋๋ค.
---
// src/layouts/BlogPostLayout.astro
const {frontmatter} = Astro.props;
---
<html>
<!-- ... -->
<h1>{frontmatter.title}</h1>
<h2>Post author: {frontmatter.author}</h2>
<p>{frontmatter.description}</p>
<slot /> <!-- Markdown ์ฝํ
์ธ ๊ฐ ์ฌ๊ธฐ์ ์ฝ์
๋ฉ๋๋ค. -->
<!-- ... -->
</html>
๋ ์ด์์ ์ปดํฌ๋ํธ์์ Markdown ์คํ์ผ์ ์ง์ ํ ์๋ ์์ต๋๋ค.
Markdown ๋ ์ด์์์ ๋ํด ์์ธํ ์์๋ณด์ธ์.
Markdown ๋ฐ MDX์ ์ ๋ชฉ์ ์ฌ์ฉํ๋ฉด ์๋์ผ๋ก ์ต์ปค ๋งํฌ๊ฐ ์ ๊ณต๋๋ฏ๋ก ํ์ด์ง์ ํน์ ์น์ ์ ์ง์ ์ฐ๊ฒฐํ ์ ์์ต๋๋ค.
---
title: My page of content
---
## Introduction
I can link internally to [my conclusion](#conclusion) on the same page when writing Markdown.
## Conclusion
I can use the URL `https://example.com/page-1/#introduction` to navigate directly to my Introduction on the page.
Astro๋ github-slugger
๋ฅผ ๊ธฐ๋ฐ์ผ๋ก id
๋ผ๋ ์ ๋ชฉ์ ์์ฑํฉ๋๋ค. github-slugger ๋ฌธ์์์ ๋ ๋ง์ ์์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.
ํน์ ๋ฌธ์๋ Markdown ๋ฐ MDX์์ ํน๋ณํ ์๋ฏธ๋ฅผ ๊ฐ์ต๋๋ค. ํ์ํ๋ ค๋ฉด ๋ค๋ฅธ ๊ตฌ๋ฌธ์ ์ฌ์ฉํด์ผ ํ ์๋ ์์ต๋๋ค. ์ด๋ ๊ฒ ํ๋ ค๋ฉด ํด๋น ๋ฌธ์์ ๋ํด HTML ์ํฐํฐ๋ฅผ ๋์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด <
๊ฐ HTML ์์์ ์์ ๋ถ๋ถ์ผ๋ก ํด์๋๋ ๊ฒ์ ๋ฐฉ์งํ๋ ค๋ฉด <
๋ฅผ ์์ฑํ์ธ์. ๋๋ {
๊ฐ MDX์์ JavaScript ํํ์์ ์์ ๋ถ๋ถ์ผ๋ก ํด์๋๋ ๊ฒ์ ๋ฐฉ์งํ๋ ค๋ฉด {
๋ฅผ ์์ฑํ์ธ์.
Astro MDX ํตํฉ์ ์ถ๊ฐํ๋ฉด JSX ๋ณ์, ํํ์, ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ์ฌ Markdown ์์ฑ์ด ํฅ์๋ฉ๋๋ค.
๋ํ MDX์ Markdown ์คํ์ผ ํ๋ฐํธ๋งคํฐ์ ๋ํ ์ง์์ ํฌํจํ์ฌ ํ์ค MDX์ ์ถ๊ฐ ๊ธฐ๋ฅ์ ์ถ๊ฐํฉ๋๋ค. ์ด๋ฅผ ํตํด ํ๋ฐํธ๋งคํฐ layout
์์ฑ๊ณผ ๊ฐ์ Astro์ ๋ด์ฅ Markdown ๊ธฐ๋ฅ ๋๋ถ๋ถ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
.mdx
ํ์ผ์ Astro์ HTML๊ณผ ๊ฐ์ ๊ตฌ๋ฌธ์ด ์๋ MDX ๊ตฌ๋ฌธ์ผ๋ก ์์ฑ๋์ด์ผ ํฉ๋๋ค.
MDX๋ export
๊ตฌ๋ฌธ์ ์ฌ์ฉํ์ฌ MDX ์ฝํ
์ธ ์ ๋ณ์๋ฅผ ์ถ๊ฐํ ์ ์๋๋ก ์ง์ํฉ๋๋ค. ์ด๋ฌํ ๋ณ์๋ ํ
ํ๋ฆฟ ์์ฒด์์ ์ก์ธ์คํ ์ ์์ ๋ฟ๋ง ์๋๋ผ ํ์ผ์ ๋ค๋ฅธ ๊ณณ์ผ๋ก ๊ฐ์ ธ์ฌ ๋ ๋ช
๋ช
๋ ์์ฑ์ผ๋ก ์ก์ธ์คํ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด MDX ํ์ด์ง ๋๋ ์ปดํฌ๋ํธ์์ title
ํ๋๋ฅผ ๋ด๋ณด๋ด {JSX ํํ์}
์ ์ ๋ชฉ์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
export const title = 'My first MDX post'
# {title}
Astro MDX ํตํฉ์๋ ๊ธฐ๋ณธ์ ์ผ๋ก MDX์์ ํ๋ฐํธ๋งคํฐ ์ฌ์ฉ์ ๋ํ ์ง์์ด ํฌํจ๋์ด ์์ต๋๋ค. Markdown ํ์ผ์์์ ๋ง์ฐฌ๊ฐ์ง๋ก ํ๋ฐํธ๋งคํฐ ์์ฑ์ ์ถ๊ฐํ๋ฉด ์ด๋ฌํ ๋ณ์์ ์ก์ธ์คํ์ฌ ํ
ํ๋ฆฟ, layout
์ปดํฌ๋ํธ, ํ์ผ์ ๋ค๋ฅธ ๊ณณ์ผ๋ก ๊ฐ์ ธ์ฌ ๋ ๋ช
๋ช
๋ ์์ฑ์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
---
layout: '../../layouts/BlogPostLayout.astro'
title: 'My first MDX post'
---
# {frontmatter.title}
MDX ํตํฉ์ ์ค์นํ ํ ๋ค๋ฅธ Astro ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ๋ ๊ฒ์ฒ๋ผ Astro ์ปดํฌ๋ํธ์ UI ํ๋ ์์ํฌ ์ปดํฌ๋ํธ๋ฅผ MDX (.mdx
) ํ์ผ๋ก ๊ฐ์ ธ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
ํ์ํ ๊ฒฝ์ฐ UI ํ๋ ์์ํฌ ์ปดํฌ๋ํธ์ client:directive
๋ฅผ ํฌํจํ๋ ๊ฒ์ ์์ง ๋ง์ธ์!
MDX ๋ฌธ์์์ ๊ฐ์ ธ์ค๊ธฐ ๋ฐ ๋ด๋ณด๋ด๊ธฐ ๊ตฌ๋ฌธ ์ฌ์ฉ์ ๋ํ ์ถ๊ฐ ์์๋ฅผ ์ฐธ์กฐํ์ธ์.
---
layout: ../layouts/BaseLayout.astro
title: About me
---
import Button from '../components/Button.astro';
import ReactCounter from '../components/ReactCounter.jsx';
I live on **Mars** but feel free to <Button title="Contact me" />.
Here is my counter component, working in MDX:
<ReactCounter client:load />
MDX๋ฅผ ์ฌ์ฉํ๋ฉด Markdown ๊ตฌ๋ฌธ์ ํ์ค HTML ์์ ๋์ ์ฌ์ฉ์ ์ ์ ์ปดํฌ๋ํธ์ ๋งคํํ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ํ์ค Markdown ๊ตฌ๋ฌธ์ผ๋ก ์์ฑํ ์ ์์ง๋ง ์ ํํ ์์์ ํน์ ์ปดํฌ๋ํธ ์คํ์ผ์ ์ ์ฉํ ์ ์์ต๋๋ค.
์ฌ์ฉ์ ์ ์ ์ปดํฌ๋ํธ๋ฅผ .mdx
ํ์ผ๋ก ๊ฐ์ ธ์จ ๋ค์ ํ์ค HTML ์์๋ฅผ ์ฌ์ฉ์ ์ ์ ์ปดํฌ๋ํธ์ ๋งคํํ๋ components
๊ฐ์ฒด๋ฅผ ๋ด๋ณด๋
๋๋ค.
import Blockquote from '../components/Blockquote.astro';
export const components = {blockquote: Blockquote}
> This quote will be a custom Blockquote
---
const props = Astro.props;
---
<blockquote {...props} class="bg-blue-50 p-4">
<span class="text-4xl text-blue-600 mb-2">โ</span>
<slot /> <!-- ํ์ ์ฝํ
์ธ ์๋ `<slot/>`์ ์ถ๊ฐํ์ธ์! -->
</blockquote>
์ฌ์ฉ์ ์ ์ ์ปดํฌ๋ํธ๋ก ๋ฎ์ด์ธ ์ ์๋ HTML ์์์ ์ ์ฒด ๋ชฉ๋ก์ ๋ณด๋ ค๋ฉด MDX ์น์ฌ์ดํธ๋ฅผ ๋ฐฉ๋ฌธํ์ธ์.
Markdown ๋ฐ MDX ํ์ผ์ Astro ํ์ผ๋ก ์ง์ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด Markdown ์ฝํ ์ธ ๋ฟ๋ง ์๋๋ผ Astro์ JSX ์ ์ฌ ํํ์์์ ์ฌ์ฉํ ์ ์๋ ํ๋ฐํธ๋งคํฐ ๊ฐ๊ณผ ๊ฐ์ ๊ธฐํ ์์ฑ์ ์ก์ธ์คํ ์ ์์ต๋๋ค.
import
๋ฌธ์ ์ฌ์ฉํ์ฌ ํ๋์ ํน์ ํ์ด์ง๋ฅผ ๊ฐ์ ธ์ค๊ฑฐ๋ Astro.glob()
์ ์ฌ์ฉํ์ฌ ์ฌ๋ฌ ํ์ด์ง๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
---
// ๋จ์ผ ํ์ผ ๊ฐ์ ธ์ค๊ธฐ
import * as myPost from '../pages/post/my-post.md';
// Astro.glob์ ์ฌ์ฉํ์ฌ ์ฌ๋ฌ ํ์ผ ๊ฐ์ ธ์ค๊ธฐ
const posts = await Astro.glob('../pages/post/*.md');
---
Astro ์ปดํฌ๋ํธ์์ Markdown ๋ฐ MDX ํ์ผ์ ๊ฐ์ ธ์ค๋ฉด ํด๋น ๋ด๋ณด๋ธ ์์ฑ์ด ํฌํจ๋ ๊ฐ์ฒด๋ฅผ ์ป๊ฒ ๋ฉ๋๋ค.
---
title: 'The greatest post of all time'
author: 'Ben'
---
Here is my _great_ post!
---
import * as greatPost from '../pages/post/great-post.md';
const posts = await Astro.glob('../pages/post/*.md');
---
<p>{greatPost.frontmatter.title}</p>
<p>Written by: {greatPost.frontmatter.author}</p>
<p>Post Archive:</p>
<ul>
{posts.map(post => <li><a href={post.url}>{post.frontmatter.title}</a></li>)}
</ul>
MDX ํ์ผ์์๋ frontmatter ๋ฐ export
๋ฌธ ๋ชจ๋์์ ์์ฑ์ ์ก์ธ์คํ ์ ์์ต๋๋ค.
---
title: 'The greatest post of all time'
author: 'Ben'
---
export const description = 'Get comfortable! This is going to be a great read.'
Here is my _great_ post!
---
import * as greatPost from '../pages/post/mdx-post.mdx';
---
<p>{greatPost.frontmatter.title}</p>
<p>Written by: {greatPost.frontmatter.author}</p>
<p>{greatPost.description}</p>
TypeScript ์ ๋ค๋ฆญ์ ์ฌ์ฉํ์ฌ frontmatter
๋ณ์์ ๋ํ ํ์
์ ์ ํ์ ์ผ๋ก ์ ๊ณตํ ์ ์์ต๋๋ค.
---
interface Frontmatter {
title: string;
description?: string;
}
const posts = await Astro.glob<Frontmatter>('../pages/post/*.md');
---
<ul>
{posts.map(post => <li>{post.frontmatter.title}</li>)}
<!-- post.frontmatter.title์ `string`์ด ๋ฉ๋๋ค! -->
</ul>
:::note[Astro ๋ ์ด์์์ ์ฌ์ฉํ์๋์?] Astro์ ํน์ํ ํ๋ฐํธ๋งคํฐ layout์ ์ฌ์ฉํ ๋ Astro ๋ ์ด์์ ์ปดํฌ๋ํธ๋ก ๋ด๋ณด๋ธ ์์ฑ์ ํ์ธํ์ธ์. :::
import
๋ฌธ์ด๋ Astro.glob()
์ ์ฌ์ฉํ ๋ .astro
์ปดํฌ๋ํธ์ ๋ค์ ์์ฑ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
file
- ์ ๋ ํ์ผ ๊ฒฝ๋ก (์:/home/user/projects/.../file.md
).url
- ํ์ด์ง์ธ ๊ฒฝ์ฐ ํ์ด์ง์ URL (์:/en/guides/markdown-content
).frontmatter
- ํ์ผ์ YAML ํ๋ฐํธ๋งคํฐ์ ์ง์ ๋ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ํฌํจํฉ๋๋ค.getHeadings
- ํ์ผ์ ๋ชจ๋ ์ ๋ชฉ (์:h1 -> h6
์์)์ ๋ฐฐ์ด์ ๋ฐํํ๋ ๋น๋๊ธฐ ํจ์์ ๋๋ค. ๊ฐ ์ ๋ชฉ์slug
๋ ํน์ ์ ๋ชฉ์ ๋ํด ์์ฑ๋ ID์ ํด๋นํ๋ฉฐ, ์ต์ปค ๋งํฌ์ ์ฌ์ฉ๋ ์ ์์ต๋๋ค. ์ด ๋ชฉ๋ก์ ๋ค์ ํ์ ์ ๋ฐ๋ฆ ๋๋ค:{ depth: number; slug: string; text: string }[]
.Content
- ํ์ผ์ ์ ์ฒด ๋ ๋๋ง๋ ์ฝํ ์ธ ๋ฅผ ๋ฐํํ๋ ์ปดํฌ๋ํธ์ ๋๋ค.- (Markdown๋ง ํด๋น)
rawContent()
- ์์ Markdown ๋ฌธ์๋ฅผ ๋ฌธ์์ด๋ก ๋ฐํํ๋ ํจ์์ ๋๋ค. - (Markdown๋ง ํด๋น)
compiledContent()
- HTML ๋ฌธ์์ด๋ก ์ปดํ์ผ๋ Markdown ๋ฌธ์๋ฅผ ๋ฐํํ๋ ํจ์์ ๋๋ค. ์ฌ๊ธฐ์๋ ํ๋ฐํธ๋งคํฐ์ ๊ตฌ์ฑ๋ ๋ ์ด์์์ด ํฌํจ๋์ง ์์ต๋๋ค! Markdown ๋ฌธ์ ์์ฒด๋ง HTML๋ก ๋ฐํ๋ฉ๋๋ค. - (MDX์๋ง ํด๋น) - MDX ํ์ผ์
export
๋ฌธ์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ณด๋ผ ์๋ ์์ต๋๋ค.
Markdown ๋๋ MDX ํ์ผ์ ์ ์ฒด ๋ ๋๋ง๋ ์ฝํ
์ธ ๋ฅผ ๋ฐํํ๋ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ๋ ค๋ฉด Content
๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
---
import {Content as PromoBanner} from '../components/promoBanner.md';
---
<h2>Today's promo</h2>
<PromoBanner />
ํ์ด์ง ๊ฒฝ๋ก๋ฅผ ์์ฑํ๊ธฐ ์ํด Markdown/MDX ํ์ผ์ src/pages/
๋๋ ํฐ๋ฆฌ์ ๋๋ ๋์ ํ์ด์ง๋ฅผ ๋์ ์ผ๋ก ์์ฑํ ์ ์์ต๋๋ค.
Markdown ์ฝํ
์ธ ์ ์ก์ธ์คํ๋ ค๋ฉด Astro ํ์ด์ง์ props
๋ฅผ ํตํด <Content/>
์ปดํฌ๋ํธ๋ฅผ ์ ๋ฌํ์ธ์. ๊ทธ๋ฐ ๋ค์ Astro.props
์์ ์ปดํฌ๋ํธ๋ฅผ ๊ฒ์ํ์ฌ ํ์ด์ง ํ
ํ๋ฆฟ์ ๋ ๋๋งํ ์ ์์ต๋๋ค.
---
export async function getStaticPaths() {
const posts = await Astro.glob('../posts/**/*.md')
return posts.map(post => ({
params: {
slug: post.frontmatter.slug
},
props: {
post
},
}))
}
const { Content } = Astro.props.post
---
<article>
<Content/>
</article>
MDX ํ์ผ์ export
๋ฌธ์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ณด๋ผ ์๋ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด MDX ํ์ด์ง ๋๋ ์ปดํฌ๋ํธ์์ title
ํ๋๋ฅผ ๋ด๋ณด๋ผ ์ ์์ต๋๋ค.
export const title = 'My first MDX post'
์ด title
์ import
๋ฐ Astro.glob() ๋ฌธ์์ ์ก์ธ์คํ ์ ์์ต๋๋ค.
---
// src/pages/index.astro
const posts = await Astro.glob('./*.mdx');
---
{posts.map(post => <p>{post.title}</p>)}
๊ฐ์ ธ์จ MDX ์ฝํ
์ธ ๋ฅผ ๋ ๋๋งํ ๋ ์ฌ์ฉ์ ์ ์ ์ปดํฌ๋ํธ๋ components
prop์ ํตํด ์ ๋ฌ๋ ์ ์์ต๋๋ค.
---
import { Content, components } from '../content.mdx';
import Heading from '../Heading.astro';
---
<!-- # ๊ตฌ๋ฌธ์ ๋ํ ์ฌ์ฉ์ ์ ์ <h1>์ ์์ฑํ๊ณ `content.mdx`์ ์ ์๋ ๋ชจ๋ ์ฌ์ฉ์ ์ ์ ์ปดํฌ๋ํธ๋ฅผ ์ ์ฉํฉ๋๋ค. -->
<Content components={{...components, h1: Heading }} />
:::note
MDX ํ์ผ์ ์ ์๋๊ณ ๋ด๋ณด๋ธ ์ฌ์ฉ์ ์ ์ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ ธ์จ ๋ค์ components
์์ฑ์ ํตํด <Content />
์ปดํฌ๋ํธ์ ๋ค์ ์ ๋ฌํด์ผ ํฉ๋๋ค.
:::
Astro์ Markdown ์ง์์ ํ์ฑ ์ํ๊ณ๋ฅผ ๊ฐ์ถ ๊ฐ๋ ฅํ ๊ตฌ๋ฌธ ๋ถ์ ๋ฐ ์ฒ๋ฆฌ ๋๊ตฌ์ธ remark๋ฅผ ํตํด ์ ๊ณต๋ฉ๋๋ค. Pandoc ๋ฐ markdown-it๊ณผ ๊ฐ์ ๋ค๋ฅธ Markdown ํ์๋ ํ์ฌ ์ง์๋์ง ์์ต๋๋ค.
Astro๋ ๊ธฐ๋ณธ์ ์ผ๋ก GitHub-flavored Markdown ๋ฐ SmartyPants ํ๋ฌ๊ทธ์ธ์ ์ ์ฉํฉ๋๋ค. ์ด๋ ํ ์คํธ์์ ํด๋ฆญ ๊ฐ๋ฅํ ๋งํฌ ์์ฑ, ์ธ์ฉ ๋ฐ em-dashes ์์ ์ง์ ๊ณผ ๊ฐ์ ๋ช ๊ฐ์ง ์ฅ์ ์ ์ ๊ณตํฉ๋๋ค.
astro.config.mjs
ํ์ผ์์ remark๊ฐ Markdown์ ๊ตฌ๋ฌธ ๋ถ์ํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ฉ์ ์ ์ํ ์ ์์ต๋๋ค. Markdown ๊ตฌ์ฑ ์ต์
์ ์ ์ฒด ๋ชฉ๋ก์ ํ์ธํ์ธ์.
Astro๋ Markdown ๋ฐ MDX์ ๋ํ ํ์ฌ remark ๋ฐ rehype ํ๋ฌ๊ทธ์ธ ์ถ๊ฐ๋ฅผ ์ง์ํฉ๋๋ค. ์ด๋ฌํ ํ๋ฌ๊ทธ์ธ์ ์ฌ์ฉํ๋ฉด ๋ชฉ์ฐจ ์๋ ์์ฑ, ์ ๊ทผ ๊ฐ๋ฅํ ์ด๋ชจํฐ์ฝ ๋ผ๋ฒจ ์ ์ฉ, Markdown ์คํ์ผ ์ง์ ๊ณผ ๊ฐ์ ์๋ก์ด ๊ธฐ๋ฅ์ผ๋ก Markdown์ ํ์ฅํ ์ ์์ต๋๋ค.
awesome-remark ๋ฐ awesome-rehype์ ๊ฐ์ ์ธ๊ธฐ ํ๋ฌ๊ทธ์ธ๋ค์ ์ฐพ์๋ณด์๊ธฐ ๋ฐ๋๋๋ค! ๊ตฌ์ฒด์ ์ธ ์ค์น ์ง์นจ์ ๊ฐ ํ๋ฌ๊ทธ์ธ์ ์์ฒด README๋ฅผ ์ฐธ์กฐํ์ธ์.
์ด ์์์์๋ Markdown ๋ฐ MDX ํ์ผ ๋ชจ๋์ remark-toc
๋ฐ rehype-accessible-emojis
๋ฅผ ์ ์ฉํฉ๋๋ค.
import { defineConfig } from 'astro/config';
import remarkToc from 'remark-toc';
import { rehypeAccessibleEmojis } from 'rehype-accessible-emojis';
export default defineConfig({
markdown: {
// .md ๋ฐ .mdx ํ์ผ์ ์ ์ฉ๋จ
remarkPlugins: [remarkToc, { heading: 'toc', maxDepth: 3 }],
rehypePlugins: [rehypeAccessibleEmojis],
},
});
Astro๋ Markdown ๋ฐ MDX ํ์ผ์ ๋ชจ๋ ์ ๋ชฉ ์์ (<h1>
~ <h6>
)์ id
์์ฑ์ ์ฝ์
ํ๊ณ Markdown ๋ด๋ณด๋ธ ์์ฑ์์ ์ด๋ฌํ ID๋ฅผ ๊ฒ์ํ๊ธฐ ์ํ getHeadings()
์ ํธ๋ฆฌํฐ๋ฅผ ์ ๊ณตํฉ๋๋ค.
id
์์ฑ (์: rehype-slug
)์ ์ฝ์
ํ๋ rehype ํ๋ฌ๊ทธ์ธ์ ์ถ๊ฐํ์ฌ ์ด๋ฌํ ์ ๋ชฉ ID๋ฅผ ๋ง์ถค์ค์ ํ ์ ์์ต๋๋ค. Astro์ ๊ธฐ๋ณธ๊ฐ ๋์ ์ฌ์ฉ์ ์ ์ ID๊ฐ HTML ์ถ๋ ฅ๊ณผ getHeadings()
์ ์ํด ๋ฐํ๋ ํญ๋ชฉ์ ๋ฐ์๋ฉ๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก Astro๋ rehype ํ๋ฌ๊ทธ์ธ์ด ์คํ๋ ํ id
์์ฑ์ ์ฃผ์
ํฉ๋๋ค. ์ฌ์ฉ์ ์ ์ rehype ํ๋ฌ๊ทธ์ธ ์ค ํ๋๊ฐ Astro์์ ์ฝ์
ํ ID์ ์ก์ธ์คํด์ผ ํ๋ ๊ฒฝ์ฐ Astro์ rehypeHeadingIds
ํ๋ฌ๊ทธ์ธ์ ์ง์ ๊ฐ์ ธ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ด๋ฅผ ์ฌ์ฉํ๋ ํ๋ฌ๊ทธ์ธ ์์ rehypeHeadingIds
๋ฅผ ์ถ๊ฐํ์ธ์.
import { defineConfig } from 'astro/config';
import { rehypeHeadingIds } from '@astrojs/markdown-remark';
import { otherPluginThatReliesOnHeadingIDs } from 'some/plugin/source';
export default defineConfig({
markdown: {
rehypePlugins: [
rehypeHeadingIds,
otherPluginThatReliesOnHeadingIDs,
],
},
});
:::note
getHeadings()
๋ Markdown ๋๋ MDX ํ์ผ์ ์ง์ ์์ฑ๋ ์ ๋ชฉ๋ง ๋ฐํํฉ๋๋ค. MDX ํ์ผ์ด ์์ฒด ์ ๋ชฉ์ ํฌํจํ๋ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒฝ์ฐ, getHeadings()
์ ์ํด ๋ฐํ๋์ง ์์ต๋๋ค.
:::
ํ๋ฌ๊ทธ์ธ์ ์ฌ์ฉ์ ์ ์ํ๋ ค๋ฉด ์ค์ฒฉ ๋ฐฐ์ด์์ ํ๋ฌ๊ทธ์ธ ๋ค์ ์ต์ ๊ฐ์ฒด๋ฅผ ์ ๊ณตํ์ธ์.
์๋ ์์์์๋ remarkToc
ํ๋ฌ๊ทธ์ธ์ heading ์ต์
์ ์ถ๊ฐํ์ฌ ๋ชฉ์ฐจ ์์น๋ฅผ ๋ณ๊ฒฝํ๊ณ , rehype-autolink-headings
ํ๋ฌ๊ทธ์ธ์ behavior
์ต์
์ ์ถ๊ฐํ์ฌ ์ ๋ชฉ ํ
์คํธ ๋ค์ ์ต์ปค ํ๊ทธ๋ฅผ ์ถ๊ฐํ์ต๋๋ค.
import remarkToc from 'remark-toc';
import rehypeSlug from 'rehype-slug';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
export default {
markdown: {
remarkPlugins: [ [remarkToc, { heading: "contents"} ] ],
rehypePlugins: [rehypeSlug, [rehypeAutolinkHeadings, { behavior: 'append' }]],
},
}
:::note ์ฝํ ์ธ ์ปฌ๋ ์ ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, "ํ๋ฐํธ๋งคํฐ๋ฅผ Remark๋ก ์์ ํ๊ธฐ"์ ์ฐธ์กฐํ์ธ์. :::
remark ๋๋ rehype ํ๋ฌ๊ทธ์ธ์ ์ฌ์ฉํ์ฌ ๋ชจ๋ Markdown ๋ฐ MDX ํ์ผ์ ํ๋ฐํธ๋งคํฐ ์์ฑ์ ์ถ๊ฐํ ์ ์์ต๋๋ค.
1. ํ๋ฌ๊ทธ์ธ์ `file` ์ธ์์์ `data.astro.frontmatter` ์์ฑ์ `customProperty`๋ฅผ ์ถ๊ฐํฉ๋๋ค.```js title="example-remark-plugin.mjs"
export function exampleRemarkPlugin() {
// ๋ชจ๋ remark ๋ฐ rehype ํ๋ฌ๊ทธ์ธ์ ๋ณ๋์ ํจ์๋ฅผ ๋ฐํํฉ๋๋ค.
return function (tree, file) {
file.data.astro.frontmatter.customProperty = 'Generated property';
}
}
```
:::tip
<Since v="2.0.0" />
`data.astro.frontmatter`์๋ ํน์ Markdown ๋๋ MDX ๋ฌธ์์ ๋ชจ๋ ์์ฑ์ด ํฌํจ๋์ด ์์ต๋๋ค. ์ด๋ฅผ ํตํด ๊ธฐ์กด ํ๋ฐํธ๋งคํฐ ์์ฑ์ ์์ ํ๊ฑฐ๋ ๊ธฐ์กด ํ๋ฐํธ๋งคํฐ์์ ์ ์์ฑ์ ๊ณ์ฐํ ์ ์์ต๋๋ค.
:::
-
์ด ํ๋ฌ๊ทธ์ธ์
markdown
๋๋mdx
ํตํฉ ๊ตฌ์ฑ์ ์ ์ฉํ์ธ์.import { defineConfig } from 'astro/config'; import { exampleRemarkPlugin } from './example-remark-plugin.mjs'; export default defineConfig({ markdown: { remarkPlugins: [exampleRemarkPlugin] }, });
๋๋
import { defineConfig } from 'astro/config'; import { exampleRemarkPlugin } from './example-remark-plugin.mjs'; export default defineConfig({ integrations: [ mdx({ remarkPlugins: [exampleRemarkPlugin], }), ], });
์ด์ ๋ชจ๋ Markdown ๋๋ MDX ํ์ผ์ ํ๋ฐํธ๋งคํฐ์ customProperty
๊ฐ ์์ผ๋ฏ๋ก Markdown์ ๊ฐ์ ธ์ฌ ๋ ๋ ์ด์์์ Astro.props.frontmatter
์์ฑ์์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
<RecipeLinks slugs={["ko/recipes/reading-time"]} />
Astro์ MDX ํตํฉ์ ๊ธฐ๋ณธ์ ์ผ๋ก ํ๋ก์ ํธ์ ๊ธฐ์กด Markdown ๊ตฌ์ฑ์ ํ์ฅํฉ๋๋ค. ๊ฐ๋ณ ์ต์ ์ ์ฌ์ ์ํ๋ ค๋ฉด MDX ๊ตฌ์ฑ์์ ํด๋น ์ต์ ์ ์ง์ ํ๋ฉด ๋ฉ๋๋ค.
๋ค์ ์์์์๋ GitHub ๊ธฐ๋ฐ Markdown์ ๋นํ์ฑํํ๊ณ MDX ํ์ผ์ ๋ํด ๋ค๋ฅธ remark ํ๋ฌ๊ทธ์ธ ์ธํธ๋ฅผ ์ ์ฉํฉ๋๋ค.
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
export default defineConfig({
markdown: {
syntaxHighlight: 'prism',
remarkPlugins: [remarkPlugin1],
gfm: true,
},
integrations: [
mdx({
// Markdown์์ ์์๋ `syntaxHighlight`
// Markdown `remarkPlugins`๊ฐ ๋ฌด์๋์์ต๋๋ค.
// `remarkPlugin2`๋ง ์ ์ฉ๋ฉ๋๋ค.
remarkPlugins: [remarkPlugin2],
// `gfm`์ด `false`๋ก ์ฌ์ ์๋จ
gfm: false,
})
]
});
MDX์์ Markdown ๊ตฌ์ฑ์ ํ์ฅํ์ง ์์ผ๋ ค๋ฉด extendMarkdownConfig
์ต์
(๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํ๋จ)์ false
๋ก ์ค์ ํ์ธ์.
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
export default defineConfig({
markdown: {
remarkPlugins: [remarkPlugin],
},
integrations: [
mdx({
// Markdown ๊ตฌ์ฑ์ ์ด์ ๋ฌด์๋ฉ๋๋ค.
extendMarkdownConfig: false,
// 'remarkPlugins'๊ฐ ์ ์ฉ๋์ง ์์์ต๋๋ค.
})
]
});
Astro์๋ Shiki ๋ฐ Prism์ ๋ํ ์ง์์ด ๋ด์ฅ๋์ด ์์ต๋๋ค. ์ด๋ ๋ค์์ ๋ํ ๊ตฌ๋ฌธ ๊ฐ์กฐ๋ฅผ ์ ๊ณตํฉ๋๋ค.
- Markdown ๋๋ MDX ํ์ผ์ ์ฌ์ฉ๋๋ ๋ชจ๋ ์ฝ๋ ํ์ค(```).
- ๋ด์ฅ
<Code />
์ปดํฌ๋ํธ์ ์ฝํ ์ธ (Shiki ์ ๊ณต). <Prism />
์ปดํฌ๋ํธ์ ์ฝํ ์ธ (Prism ์ ๊ณต).
Shiki๋ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํ๋์ด ์์ผ๋ฉฐ github-dark
ํ
๋ง๋ก ์ฌ์ ๊ตฌ์ฑ๋์ด ์์ต๋๋ค. ์ปดํ์ผ๋ ์ถ๋ ฅ์ ์ธ๋ถ CSS ํด๋์ค, ์คํ์ผ์ํธ ๋๋ ํด๋ผ์ด์ธํธ ์ธก JS๊ฐ ์๋ ์ธ๋ผ์ธ ์คํ์ผ
๋ก ์ ํ๋ฉ๋๋ค.
Shiki๋ ๊ธฐ๋ณธ ๊ตฌ๋ฌธ ๊ฐ์กฐ ๋๊ตฌ์
๋๋ค. ๋ค์๊ณผ ๊ฐ์ด shikiConfig
๊ฐ์ฒด๋ฅผ ํตํด ๋ชจ๋ ์ต์
์ ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
import { defineConfig } from 'astro/config';
export default defineConfig({
markdown: {
shikiConfig: {
// Shiki์ ๋ด์ฅ๋ ํ
๋ง ์ค์์ ์ ํํ๊ฑฐ๋ ์ง์ ์ถ๊ฐํ์ธ์.
// https://shiki.style/themes
theme: 'dracula',
// ๋๋ ์ฌ๋ฌ ํ
๋ง๋ฅผ ์ ๊ณตํ์ธ์.
// https://shiki.style/guide/dual-themes#light-dark-dual-themes
themes: {
light: 'github-light',
dark: 'github-dark',
},
// ๋ง์ถค ์ธ์ด ์ถ๊ฐ
// ์ฐธ๊ณ : Shiki์๋ .astro๋ฅผ ํฌํจํ์ฌ ์๋ง์ ์ธ์ด๊ฐ ๋ด์ฅ๋์ด ์์ต๋๋ค!
// https://shiki.style/languages
langs: [],
// ๊ฐ๋ก ์คํฌ๋กค์ ๋ฐฉ์งํ๋ ค๋ฉด word wrap์ ํ์ฑํํ์ธ์.
wrap: true,
// ๋ง์ถค transformers ์ถ๊ฐ: https://shiki.style/guide/transformers
// ์ผ๋ฐ transformers ์ฐพ๊ธฐ: https://shiki.style/packages/transformers
transformers: [],
},
},
});
Shiki์ ์ฌ์ ์ ์๋ ํ ๋ง ์ค ํ๋๋ฅผ ์ฌ์ฉํ๋ ๋์ ๋ก์ปฌ ํ์ผ์์ ์ฌ์ฉ์ ์ ์ ํ ๋ง๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
import { defineConfig } from 'astro/config';
import customTheme from './my-shiki-theme.json';
export default defineConfig({
markdown: {
shikiConfig: { theme: customTheme },
},
});
๋ํ ํ ๋ง, ๋ฐ์ ๋ชจ๋์ ์ด๋์ด ๋ชจ๋ ์ ํ ๋๋ CSS ๋ณ์๋ฅผ ํตํ ์คํ์ผ ์ง์ ์ ๋ํด ์์ธํ ์์๋ณด๋ ค๋ฉด Shiki์ ์์ฒด ํ ๋ง ๋ฌธ์๋ฅผ ์ฝ์ด๋ณด๋ ๊ฒ์ด ์ข์ต๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก prism'
์ผ๋ก ์ ํํ๊ฑฐ๋ ๊ตฌ๋ฌธ ๊ฐ์กฐ๋ฅผ ์์ ํ ๋นํ์ฑํํ๋ ค๋ฉด markdown.syntaxHighlight
๊ตฌ์ฑ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
import { defineConfig } from 'astro/config';
export default defineConfig({
markdown: {
// 'shiki' (๊ธฐ๋ณธ๊ฐ), 'prism' ๋๋ ๊ฐ์กฐ๋ฅผ ๋นํ์ฑํ ํ๊ธฐ ์ํ false์ผ ์ ์์ต๋๋ค.
syntaxHighlight: 'prism',
},
});
Prism์ ์ฌ์ฉํ๊ธฐ๋ก ์ ํํ ๊ฒฝ์ฐ Astro๋ ๋์ Prism์ CSS ํด๋์ค๋ฅผ ์ ์ฉํฉ๋๋ค. ๊ตฌ๋ฌธ ๊ฐ์กฐ๋ฅผ ํ์ํ๋ ค๋ฉด ์์ ๋ง์ CSS ์คํ์ผ์ํธ๋ฅผ ๊ฐ์ ธ์์ผ ํฉ๋๋ค.
1. ์ฌ์ฉ ๊ฐ๋ฅํ [Prism ํ ๋ง](https://github.com/PrismJS/prism-themes)์์ ๋ฏธ๋ฆฌ ๋ง๋ค์ด์ง ์คํ์ผ์ํธ๋ฅผ ์ ํํฉ๋๋ค.-
์ด ์คํ์ผ์ํธ๋ฅผ ํ๋ก์ ํธ์
public/
๋๋ ํฐ๋ฆฌ์ ์ถ๊ฐํ์ธ์. -
<link>
ํ๊ทธ๋ฅผ ํตํด ๋ ์ด์์ ์ปดํฌ๋ํธ์ ์๋ ํ์ด์ง์<head>
์ ์ด๋ฅผ ๋ก๋ํฉ๋๋ค. (Prism ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ์ ์ฐธ๊ณ ํ์ธ์.)
์ต์ ๋ฐ ์ฌ์ฉ๋ฒ์ ์์๋ณด๋ ค๋ฉด Prism์์ ์ง์ํ๋ ์ธ์ด ๋ชฉ๋ก์ ๋ฐฉ๋ฌธํ์ธ์.
Astro๋ ์ฃผ๋ก ํ๋ก์ ํธ ๋๋ ํฐ๋ฆฌ์ ์ ์ฅํ ์ ์๋ ๋ก์ปฌ Markdown ํ์ผ์ฉ์ผ๋ก ์ค๊ณ๋์์ต๋๋ค. ๊ทธ๋ฌ๋ ์๊ฒฉ ์์ค์์ Markdown์ ๊ฐ์ ธ์์ผ ํ๋ ํน์ ๊ฒฝ์ฐ๊ฐ ์์ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ์น ์ฌ์ดํธ๋ฅผ ๊ตฌ์ถํ ๋ (๋๋ SSR์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, ์ฌ์ฉ์๊ฐ ์น ์ฌ์ดํธ์ ์์ฒญํ ๋) ์๊ฒฉ API์์ Markdown์ ๊ฐ์ ธ์ค๊ณ ๋ ๋๋งํด์ผ ํ ์ ์์ต๋๋ค.
Astro์๋ ์๊ฒฉ Markdown์ ๋ํ ๊ธฐ๋ณธ ์ง์์ด ํฌํจ๋์ด ์์ง ์์ต๋๋ค! ์๊ฒฉ Markdown์ ๊ฐ์ ธ์ HTML๋ก ๋ ๋๋งํ๋ ค๋ฉด npm์์ ์์ฒด Markdown ํ์๋ฅผ ์ค์นํ๊ณ ๊ตฌ์ฑํด์ผ ํฉ๋๋ค. ์ด๋ Astro์ ๊ธฐ๋ณธ ์ ๊ณต Markdown ๋ฐ ์ฌ์ฉ์๊ฐ ๊ตฌ์ฑํ MDX ์ค์ ์์ ์์๋์ง ์์ต๋๋ค. ํ๋ก์ ํธ์ ์ด๋ฅผ ๊ตฌํํ๊ธฐ ์ ์ ์ด๋ฌํ ์ ํ ์ฌํญ์ ์ดํดํด์ผ ํฉ๋๋ค.
---
// ์: ์๊ฒฉ API์์ ๋งํฌ๋ค์ด ๊ฐ์ ธ์ค๊ธฐ
// ๋ฐํ์์ HTML๋ก ๋ ๋๋งํฉ๋๋ค.
// "marked" ์ฌ์ฉ (https://github.com/markedjs/marked)
import { marked } from 'marked';
const response = await fetch('https://raw.githubusercontent.com/wiki/adam-p/markdown-here/Markdown-Cheatsheet.md');
const markdown = await response.text();
const content = marked.parse(markdown);
---
<article set:html={content} />