๋ฐ๋ก๊ฐ๊ธฐ : https://light9639.github.io/React-Markdown-TS/
โจ React-markdown์ ์ด์ฉํ์ฌ ๋ง๋ ํ์ด์ง์ ๋๋ค. โจ
- React ์์ฑ
npm create-react-app my-app
# or
yarn create react-app my-app
- vite๋ฅผ ์ด์ฉํ์ฌ ํ๋ก์ ํธ๋ฅผ ์์ฑํ๋ ค๋ฉด
npm create vite@latest
# or
yarn create vite
- ํฐ๋ฏธ๋์์ ์คํ ํ ํ๋ก์ ํธ ์ด๋ฆ ๋ง๋ ํ React ์ ํ, Typescirpt-SWC ์ ํํ๋ฉด ์์ฑ ์๋ฃ.
- ํ๋ก์ ํธ๋ฅผ ์งํํ๋ ค๋ฉด
react-markdown
๊ด๋ จ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ์นํด์ ์งํํด์ผ ํ๋ค. ๋ฐ๋ผ์ ์๋ ๋ช ๋ น์ด๋ก ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์นํ๋ค.
$ npm install react-markdown react-syntax-highlighter remark-gfm
# or
$ yarn add react-markdown react-syntax-highlighter remark-gfm
ReactMarkdown
์ผ๋ก ๋งํฌ๋ค์ด์ ๊ฐ์ธ์ฃผ๋ฉด ๋งํฌ๋ค์ด ๋ฌธ๋ฒ์ด react์ ์ ์ฉ๋๋ค.react-syntax-highlighter
๋ฅผ ์ด์ฉํ๋ฉด ์ฝ๋์ ์คํ์ผ์ ์ ์ฉํ ์ ์๋ค. ์ด ๋ฌธ์์์๋prism
์์ฑ์ ์ด์ฉํ๋ค.ReactMarkdown
๋ง ์ฌ์ฉํ๊ฒ ๋๋ฉด ์ฌ์ฉํ ์ ์๋ ๋งํฌ๋ค์ด ๋ฌธ๋ฒ์ด ์ ํ์ ์ด๊ธฐ ๋๋ฌธ์link
,table
,checklist
๋ฑ์ ํํํ ์ ์๊ฒremark-gfm
ํ๋ฌ๊ทธ์ธ์ ๊ฐ์ด ์ค์นํด์ ์ฌ์ฉํ๋ฉด ์ข๋ค.
import React, { useState } from 'react'
import reactLogo from './assets/react.svg'
import './App.css'
import ReactMarkdown from "react-markdown";
import SyntaxHighlighter from "react-syntax-highlighter";
import prism from 'react-syntax-highlighter/dist/esm/styles/prism/prism';
import remarkGfm from 'remark-gfm';
// ์ฌ์ฉํ์ง ์๋ ์คํ์ผ๋ค
import { dark } from "react-syntax-highlighter/dist/esm/styles/prism";
import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs';
const startingText = `# Hello
https://example.org
\`\`\`sh
# Code block
\`\`\`

~~strike~~ this
## TODO
* [ ] This
* [ ] That
* [x] The other
|Fahrenheit|Celsius|Kelvin|
|---:|---:|---:|
|-459.67|-273.15|0|
|-40|-40|233.15|
|32|0|273.15|
|212|100|373.15|
`;
export default function App(): JSX.Element {
const [input, setInput] = useState<string>(startingText);
return (
<div className="App">
<textarea
autoFocus
className='textarea'
value={input}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => { setInput(e.target.value) }}
></textarea>
<ReactMarkdown
remarkPlugins={[remarkGfm]}
children={input}
className='markdown'
components={{
code({ node, inline, className, children, ...props }) {
const match = /language-(\w+)/.exec(className || '')
return !inline && match ? (
<SyntaxHighlighter
children={String(children).replace(/\n$/, '')}
style={prism}
language={match[1]}
PreTag="div"
{...props}
/>
) : (
<code className={className} {...props}>
{children}
</code>
)
},
}}
/>
</div>
)
}
- ๋ฐ์ํ CSS๋ฅผ ์์ฑํ์ฌ ๋์ด๊ฐ
1024px
์ด์์ผ ๋๋ ๊ฐ๊ฐ ์ ๋ฐ์ฉ ํ๋ฉด์ ๋ํ๋์ง๋ง1024px
์ดํ์ผ ๊ฒฝ์ฐ์๋ ์ธ๋ก ์ ๋ ฌ์ด ๋๋๋ก ํ๋ค.
.App {
display: flex;
}
.textarea {
width: 50%;
height: 100vh;
padding: 20px;
font-size: 2rem;
outline: none;
}
.markdown {
width: 50%;
height: 100vh;
padding: 20px;
outline: none;
}
@media screen and (max-width: 1024px) {
.App {
flex-direction: column;
}
.textarea {
max-width: 95%;
width: 100%;
margin: 0 auto;
height: 50vh;
}
.markdown {
max-width: 95%;
width: 100%;
margin: 0 auto;
height: 50vh;
}
}