ds-markdown 0.0.10-beta.4 โ†’ 0.0.11-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ko.md ADDED
@@ -0,0 +1,491 @@
1
+ # ds-markdown
2
+
3
+ > ๐Ÿš€ ๊ณ ์„ฑ๋Šฅ React Markdown ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ปดํฌ๋„ŒํŠธ, DeepSeek ์ฑ„ํŒ… ์ธํ„ฐํŽ˜์ด์Šค ํšจ๊ณผ ์™„๋ฒฝ ์žฌํ˜„
4
+
5
+ **[๐Ÿ‡จ๐Ÿ‡ณ ไธญๆ–‡](./README.md) | [๐Ÿ‡บ๐Ÿ‡ธ English](./README.en.md) | [๐Ÿ‡ฏ๐Ÿ‡ต ๆ—ฅๆœฌ่ชž](./README.ja.md) | ๐Ÿ‡ฐ๐Ÿ‡ท ํ•œ๊ตญ์–ด**
6
+
7
+ ํ˜„๋Œ€ AI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•ด ํŠน๋ณ„ํžˆ ์„ค๊ณ„๋œ React ์ปดํฌ๋„ŒํŠธ๋กœ, ๋ถ€๋“œ๋Ÿฌ์šด ์‹ค์‹œ๊ฐ„ ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ ์™„์ „ํ•œ Markdown ๋ Œ๋”๋ง ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
8
+
9
+ [![npm version](https://img.shields.io/npm/v/ds-markdown)](https://www.npmjs.com/package/ds-markdown)
10
+ [![npm downloads](https://img.shields.io/npm/dm/ds-markdown.svg)](https://www.npmjs.com/package/ds-markdown)
11
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/ds-markdown)](https://bundlephobia.com/package/ds-markdown)
12
+ [![React](https://img.shields.io/badge/React-16.8+-blue)](https://react.dev)
13
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)](https://www.typescriptlang.org/)
14
+
15
+ [๐Ÿ“– ๋ผ์ด๋ธŒ ๋ฐ๋ชจ](https://onshinpei.github.io/ds-markdown/) | [๐Ÿ”ง StackBlitz ์ฒดํ—˜](https://stackblitz.com/edit/vitejs-vite-ddfw8avb?file=src%2FApp.tsx)
16
+
17
+ ---
18
+
19
+ ## โœจ ํ•ต์‹ฌ ๊ธฐ๋Šฅ
20
+
21
+ ### ๐ŸŽฏ **์™„๋ฒฝํ•œ ์žฌํ˜„**
22
+
23
+ - [DeepSeek ์›น์‚ฌ์ดํŠธ](https://chat.deepseek.com/) ์ฑ„ํŒ… ์‘๋‹ต ํšจ๊ณผ 1:1 ์žฌํ˜„
24
+ - ์‚ฌ๊ณ  ๊ณผ์ •(`thinking`)๊ณผ ๋‹ต๋ณ€ ๋‚ด์šฉ(`answer`) ๋“€์–ผ ๋ชจ๋“œ ์ง€์›
25
+ - ์ฝ”๋“œ ํ•˜์ด๋ผ์ดํŠธ, ํ…Œ์ด๋ธ”, ๋ฆฌ์ŠคํŠธ ๋“ฑ์„ ํฌํ•จํ•œ ๋„ค์ดํ‹ฐ๋ธŒ Markdown ๊ตฌ๋ฌธ ์ง€์›
26
+
27
+ ### โšก **๊ทนํ•œ์˜ ์„ฑ๋Šฅ**
28
+
29
+ - ์Šค๋งˆํŠธ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๋กœ ๋Œ€์šฉ๋Ÿ‰ ๋ฌธ์„œ๋„ ์ง€์—ฐ ์—†๋Š” ๋ Œ๋”๋ง
30
+ - ๋“€์–ผ ํƒ€์ด๋จธ ๋ชจ๋“œ: `requestAnimationFrame` + `setTimeout`
31
+ - ๋‚ด์žฅ ์ŠคํŠธ๋ฆฌ๋ฐ ๊ตฌ๋ฌธ ๋ฒ„ํผ๋ง์œผ๋กœ ๋ถˆ์™„์ „ํ•œ Markdown ๋ Œ๋”๋ง ์˜ค๋ฅ˜ ๋ฐฉ์ง€
32
+
33
+ ### ๐ŸŽฌ **๋ถ€๋“œ๋Ÿฌ์šด ์• ๋‹ˆ๋ฉ”์ด์…˜**
34
+
35
+ - ๊ณ ์ฃผํŒŒ ํƒ€์ดํ•‘ ์ง€์›(`requestAnimationFrame` ๋ชจ๋“œ์—์„œ `0ms`์— ๊ฐ€๊นŒ์šด ํƒ€์ดํ•‘ ๊ฐ„๊ฒฉ ์ง€์›)
36
+ - ํ”„๋ ˆ์ž„ ๋™๊ธฐํ™” ๋ Œ๋”๋ง์œผ๋กœ ๋ธŒ๋ผ์šฐ์ € 60fps์™€ ์™„๋ฒฝ ๋งค์นญ
37
+ - ์Šค๋งˆํŠธ ๋ฌธ์ž ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๋กœ ๋” ์ž์—ฐ์Šค๋Ÿฌ์šด ์‹œ๊ฐ์  ํšจ๊ณผ
38
+
39
+ ### ๐Ÿ”ง **์œ ์—ฐํ•˜๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฌ์›€**
40
+
41
+ - **์„ ์–ธ์  API**: ๊ฐ„๋‹จํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค์— ์ ํ•ฉ, React ์Šคํƒ€์ผ
42
+ - **๋ช…๋ นํ˜• API**: ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐ์ดํ„ฐ์— ์ ํ•ฉ, ๋” ๋‚˜์€ ์„ฑ๋Šฅ
43
+ - **๋„ค์ดํ‹ฐ๋ธŒ TypeScript ์ง€์›**: ์™„์ „ํ•œ ํƒ€์ž… ํžŒํŠธ
44
+
45
+ ---
46
+
47
+ ## ๐Ÿ“ฆ ๋น ๋ฅธ ์„ค์น˜
48
+
49
+ ```bash
50
+ # npm
51
+ npm install ds-markdown
52
+
53
+ # yarn
54
+ yarn add ds-markdown
55
+
56
+ # pnpm
57
+ pnpm add ds-markdown
58
+ ```
59
+
60
+ ### ESM CDN์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ
61
+
62
+ ์„ค์น˜ ์—†์ด ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:
63
+
64
+ ```html
65
+ <!-- ์Šคํƒ€์ผ ๊ฐ€์ ธ์˜ค๊ธฐ -->
66
+ <link rel="stylesheet" href="https://esm.sh/ds-markdown/style.css" />
67
+
68
+ <!-- ์ปดํฌ๋„ŒํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ -->
69
+ <script type="importmap">
70
+ {
71
+ "imports": {
72
+ "react": "https://esm.sh/react@19.1.0",
73
+ "react-dom/client": "https://esm.sh/react-dom@19.1.0/client",
74
+ "ds-markdown": "https://esm.sh/ds-markdown@0.0.10"
75
+ }
76
+ }
77
+ </script>
78
+ <script type="module" src="https://esm.sh/tsx"></script>
79
+
80
+ <script type="text/babel">
81
+ import { createRoot } from 'react-dom/client';
82
+ import DsMarkdown from 'ds-markdown';
83
+
84
+ const markdown = `
85
+ # Hello ds-markdown
86
+
87
+ ์ด๊ฒƒ์€ **๊ณ ์„ฑ๋Šฅ** ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค!
88
+
89
+ ## ํŠน์ง•
90
+ - โšก ์ง€์—ฐ ์—†๋Š” ์ŠคํŠธ๋ฆฌ๋ฐ
91
+ - ๐ŸŽฌ ๋ถ€๋“œ๋Ÿฌ์šด ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜
92
+ - ๐ŸŽฏ ์™„๋ฒฝํ•œ ๊ตฌ๋ฌธ ์ง€์›
93
+ `;
94
+
95
+ const root = createRoot(document.getElementById('root'));
96
+ root.render(<DsMarkdown interval={20}>{markdown}</DsMarkdown>);
97
+ </script>
98
+ ```
99
+
100
+ ## ๐Ÿš€ 5๋ถ„ ๋น ๋ฅธ ์‹œ์ž‘
101
+
102
+ ### ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•
103
+
104
+ ```tsx
105
+ import DsMarkdown from 'ds-markdown';
106
+ import 'ds-markdown/style.css';
107
+
108
+ function App() {
109
+ return (
110
+ <DsMarkdown interval={20} answerType="answer">
111
+ # Hello ds-markdown ์ด๊ฒƒ์€ **๊ณ ์„ฑ๋Šฅ** ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค! ## ๊ธฐ๋Šฅ - โšก ์ง€์—ฐ ์—†๋Š” ์ŠคํŠธ๋ฆฌ๋ฐ ์ฒ˜๋ฆฌ - ๐ŸŽฌ ๋ถ€๋“œ๋Ÿฌ์šด ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜ - ๐ŸŽฏ ์™„๋ฒฝํ•œ ๊ตฌ๋ฌธ ์ง€์›
112
+ </DsMarkdown>
113
+ );
114
+ }
115
+ ```
116
+
117
+ ### AI ๋Œ€ํ™” ์‹œ๋‚˜๋ฆฌ์˜ค
118
+
119
+ ```tsx
120
+ function ChatDemo() {
121
+ const [thinking, setThinking] = useState('');
122
+ const [answer, setAnswer] = useState('');
123
+
124
+ const handleAsk = () => {
125
+ setThinking('๐Ÿค” ์งˆ๋ฌธ์„ ๋ถ„์„ ์ค‘์ž…๋‹ˆ๋‹ค...');
126
+
127
+ setTimeout(() => {
128
+ setAnswer(`# React 19์— ๋Œ€ํ•ด
129
+
130
+ React 19๋Š” ๋งŽ์€ ํฅ๋ฏธ๋กœ์šด ์ƒˆ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค:
131
+
132
+ ## ๐Ÿš€ ์ฃผ์š” ์—…๋ฐ์ดํŠธ
133
+ 1. **React Compiler** - ์ž๋™ ์„ฑ๋Šฅ ์ตœ์ ํ™”
134
+ 2. **Actions** - ํผ ์ฒ˜๋ฆฌ ๊ฐ„์†Œํ™”
135
+ 3. **Document Metadata** - ๋‚ด์žฅ SEO ์ง€์›
136
+
137
+ ์ด๋Ÿฌํ•œ ์ƒˆ ๊ธฐ๋Šฅ๋“ค์„ ํ•จ๊ป˜ ํƒํ—˜ํ•ด๋ด…์‹œ๋‹ค!`);
138
+ }, 2000);
139
+ };
140
+
141
+ return (
142
+ <div>
143
+ <button onClick={handleAsk}>AI์—๊ฒŒ ์งˆ๋ฌธํ•˜๊ธฐ</button>
144
+
145
+ {thinking && (
146
+ <DsMarkdown answerType="thinking" interval={30}>
147
+ {thinking}
148
+ </DsMarkdown>
149
+ )}
150
+
151
+ {answer && (
152
+ <DsMarkdown answerType="answer" interval={15}>
153
+ {answer}
154
+ </DsMarkdown>
155
+ )}
156
+ </div>
157
+ );
158
+ }
159
+ ```
160
+
161
+ ---
162
+
163
+ ## ๐Ÿ“š ์™„์ „ API ๋ฌธ์„œ
164
+
165
+ ### ์„ ์–ธ์  API (์ดˆ๋ณด์ž ์ถ”์ฒœ)
166
+
167
+ | ์†์„ฑ | ํƒ€์ž… | ์„ค๋ช… | ๊ธฐ๋ณธ๊ฐ’ |
168
+ | ------------- | ------------------------------------------- | -------------------------------- | ------------------------------------------------------------------------ |
169
+ | `interval` | `number` | ํƒ€์ดํ•‘ ๊ฐ„๊ฒฉ (๋ฐ€๋ฆฌ์ดˆ) | `30` |
170
+ | `timerType` | `'setTimeout'` \| `'requestAnimationFrame'` | ํƒ€์ด๋จธ ํƒ€์ž… | ํ˜„์žฌ ๊ธฐ๋ณธ๊ฐ’์€ `setTimeout`, ๋‚˜์ค‘์— `requestAnimationFrame`์œผ๋กœ ๋ณ€๊ฒฝ ์˜ˆ์ • |
171
+ | `answerType` | `'thinking'` \| `'answer'` | ์ฝ˜ํ…์ธ  ํƒ€์ž… (์Šคํƒ€์ผ ํ…Œ๋งˆ์— ์˜ํ–ฅ) | `'answer'` |
172
+ | `onEnd` | `(data: EndData) => void` | ํƒ€์ดํ•‘ ์™„๋ฃŒ ์ฝœ๋ฐฑ | - |
173
+ | `onStart` | `(data: StartData) => void` | ํƒ€์ดํ•‘ ์‹œ์ž‘ ์ฝœ๋ฐฑ | - |
174
+ | `onTypedChar` | `(data: CharData) => void` | ๋ฌธ์ž๋ณ„ ํƒ€์ดํ•‘ ์ฝœ๋ฐฑ | - |
175
+
176
+ ### ๋ช…๋ นํ˜• API (์ŠคํŠธ๋ฆฌ๋ฐ ์‹œ๋‚˜๋ฆฌ์˜ค ์ถ”์ฒœ)
177
+
178
+ ```typescript
179
+ import { MarkdownCMD, MarkdownRef } from 'ds-markdown';
180
+
181
+ interface MarkdownRef {
182
+ push: (content: string, answerType: AnswerType) => void;
183
+ clear: () => void;
184
+ triggerWholeEnd: () => void;
185
+ flushBuffer: (answerType?: AnswerType) => void;
186
+ }
187
+ ```
188
+
189
+ | ๋ฉ”์„œ๋“œ | ๋งค๊ฐœ๋ณ€์ˆ˜ | ์„ค๋ช… |
190
+ | ----------------- | ------------------------------------------- | -------------------------- |
191
+ | `push` | `(content: string, answerType: AnswerType)` | ์ฝ˜ํ…์ธ  ์ถ”๊ฐ€ ๋ฐ ํƒ€์ดํ•‘ ์‹œ์ž‘ |
192
+ | `clear` | - | ๋ชจ๋“  ์ฝ˜ํ…์ธ ์™€ ์ƒํƒœ ์ดˆ๊ธฐํ™” |
193
+ | `triggerWholeEnd` | - | ์ˆ˜๋™์œผ๋กœ ์™„๋ฃŒ ์ฝœ๋ฐฑ ํŠธ๋ฆฌ๊ฑฐ |
194
+ | `flushBuffer` | `answerType?: AnswerType` | ๋ฒ„ํผ ์ฝ˜ํ…์ธ  ๊ฐ•์ œ ํ”Œ๋Ÿฌ์‹œ |
195
+
196
+ ---
197
+
198
+ ## ๐ŸŽ›๏ธ ํƒ€์ด๋จธ ๋ชจ๋“œ ์ƒ์„ธ ์„ค๋ช…
199
+
200
+ ### `requestAnimationFrame` ๋ชจ๋“œ ๐ŸŒŸ (์ถ”์ฒœ)
201
+
202
+ ```typescript
203
+ // ๐ŸŽฏ ํŠน์ง•
204
+ - ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜: ์‹ค์ œ ๊ฒฝ๊ณผ ์‹œ๊ฐ„์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฌธ์ž ์ˆ˜ ๊ณ„์‚ฐ
205
+ - ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ: ๋‹จ์ผ ํ”„๋ ˆ์ž„ ๋‚ด์—์„œ ์—ฌ๋Ÿฌ ๋ฌธ์ž ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
206
+ - ํ”„๋ ˆ์ž„ ๋™๊ธฐํ™”: ๋ธŒ๋ผ์šฐ์ € 60fps ์ƒˆ๋กœ๊ณ ์นจ ์†๋„์™€ ๋™๊ธฐํ™”
207
+ - ๊ณ ์ฃผํŒŒ ์ตœ์ ํ™”: interval < 16ms ๊ณ ์† ํƒ€์ดํ•‘ ์™„๋ฒฝ ์ง€์›
208
+
209
+ // ๐ŸŽฏ ์ ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค
210
+ - ํ˜„๋Œ€ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ธฐ๋ณธ ์„ ํƒ
211
+ - ๋ถ€๋“œ๋Ÿฌ์šด ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ ์ถ”๊ตฌ
212
+ - ๊ณ ์ฃผํŒŒ ํƒ€์ดํ•‘ (interval > 0์ด๋ฉด ์ถฉ๋ถ„)
213
+ - AI ์‹ค์‹œ๊ฐ„ ๋Œ€ํ™” ์‹œ๋‚˜๋ฆฌ์˜ค
214
+ ```
215
+
216
+ ### `setTimeout` ๋ชจ๋“œ ๐Ÿ“Ÿ (ํ˜ธํ™˜์„ฑ)
217
+
218
+ ```typescript
219
+ // ๐ŸŽฏ ํŠน์ง•
220
+ - ๋‹จ์ผ ๋ฌธ์ž: ํ•œ ๋ฒˆ์— ์ •ํ™•ํžˆ ํ•˜๋‚˜์˜ ๋ฌธ์ž ์ฒ˜๋ฆฌ
221
+ - ๊ณ ์ • ๊ฐ„๊ฒฉ: ์„ค์ •๋œ ์‹œ๊ฐ„์— ๋”ฐ๋ผ ์—„๊ฒฉํ•˜๊ฒŒ ์‹คํ–‰
222
+ - ๋ฆฌ๋“ฌ๊ฐ: ํด๋ž˜์‹ ํƒ€์ž๊ธฐ์˜ ๋ฆฌ๋“ฌ
223
+ - ์ •๋ฐ€ ์ œ์–ด: ํŠน์ • ํƒ€์ด๋ฐ ์š”๊ตฌ์‚ฌํ•ญ์— ์ ํ•ฉ
224
+
225
+ // ๐ŸŽฏ ์ ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค
226
+ - ์ •๋ฐ€ํ•œ ์‹œ๊ฐ„ ์ œ์–ด๊ฐ€ ํ•„์š”
227
+ - ๋ ˆํŠธ๋กœ ํƒ€์ž๊ธฐ ํšจ๊ณผ ์ƒ์„ฑ
228
+ - ํ˜ธํ™˜์„ฑ ์š”๊ตฌ์‚ฌํ•ญ์ด ๋†’์€ ์‹œ๋‚˜๋ฆฌ์˜ค
229
+ ```
230
+
231
+ ### ๐Ÿ“Š ์„ฑ๋Šฅ ๋น„๊ต
232
+
233
+ | ๊ธฐ๋Šฅ | requestAnimationFrame | setTimeout |
234
+ | ----------------- | ---------------------------------- | -------------------- |
235
+ | **๋ฌธ์ž ์ฒ˜๋ฆฌ** | ํ”„๋ ˆ์ž„๋‹น ์—ฌ๋Ÿฌ ๋ฌธ์ž ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ | ํ•œ ๋ฒˆ์— ํ•œ ๋ฌธ์ž ์ฒ˜๋ฆฌ |
236
+ | **๊ณ ์ฃผํŒŒ ๊ฐ„๊ฒฉ** | โœ… ์šฐ์ˆ˜ (5ms โ†’ ํ”„๋ ˆ์ž„๋‹น 3๋ฌธ์ž) | โŒ ์ง€์—ฐ ๊ฐ€๋Šฅ์„ฑ |
237
+ | **์ €์ฃผํŒŒ ๊ฐ„๊ฒฉ** | โœ… ์ •์ƒ (100ms โ†’ 6ํ”„๋ ˆ์ž„ ํ›„ 1๋ฌธ์ž) | โœ… ์ •๋ฐ€ |
238
+ | **์‹œ๊ฐ์  ํšจ๊ณผ** | ๐ŸŽฌ ๋ถ€๋“œ๋Ÿฌ์šด ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋А๋‚Œ | โšก ์ •๋ฐ€ํ•œ ๋ฆฌ๋“ฌ๊ฐ |
239
+ | **์„ฑ๋Šฅ ์˜ค๋ฒ„ํ—ค๋“œ** | ๐ŸŸข ๋‚ฎ์Œ (ํ”„๋ ˆ์ž„ ๋™๊ธฐํ™”) | ๐ŸŸก ์ค‘๊ฐ„ (ํƒ€์ด๋จธ) |
240
+
241
+ ๊ณ ์ฃผํŒŒ๋Š” `requestAnimationFrame`, ์ €์ฃผํŒŒ๋Š” `setTimeout` ์ถ”์ฒœ
242
+
243
+ ---
244
+
245
+ ## ๐Ÿ’ก ์‹ค์ „ ์˜ˆ์ œ
246
+
247
+ ### ๐Ÿ“ AI ์ŠคํŠธ๋ฆฌ๋ฐ ๋Œ€ํ™”
248
+
249
+ ````tsx
250
+ import { useRef } from 'react';
251
+ import { MarkdownCMD, MarkdownRef } from 'ds-markdown';
252
+
253
+ function StreamingChat() {
254
+ const markdownRef = useRef<MarkdownRef>(null);
255
+
256
+ // AI ์ŠคํŠธ๋ฆฌ๋ฐ ์‘๋‹ต ์‹œ๋ฎฌ๋ ˆ์ด์…˜
257
+ const simulateAIResponse = async () => {
258
+ markdownRef.current?.clear();
259
+
260
+ // ์‚ฌ๊ณ  ๋‹จ๊ณ„
261
+ markdownRef.current?.push('๐Ÿค” ์งˆ๋ฌธ์„ ๋ถ„์„ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค...', 'thinking');
262
+ await delay(1000);
263
+ markdownRef.current?.push('\n\nโœ… ๋ถ„์„ ์™„๋ฃŒ, ๋‹ต๋ณ€์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค', 'thinking');
264
+
265
+ // ์ŠคํŠธ๋ฆฌ๋ฐ ๋‹ต๋ณ€
266
+ const chunks = [
267
+ '# React 19 ์ƒˆ ๊ธฐ๋Šฅ ๋ถ„์„\n\n',
268
+ '## ๐Ÿš€ React Compiler\n',
269
+ 'React 19์˜ ๊ฐ€์žฅ ํฐ ํ•˜์ด๋ผ์ดํŠธ๋Š” **React Compiler** ๋„์ž…์ž…๋‹ˆ๋‹ค:\n\n',
270
+ '- ๐ŸŽฏ **์ž๋™ ์ตœ์ ํ™”**: ์ˆ˜๋™ memo์™€ useMemo ๋ถˆํ•„์š”\n',
271
+ '- โšก **์„ฑ๋Šฅ ํ–ฅ์ƒ**: ์ปดํŒŒ์ผ ํƒ€์ž„ ์ตœ์ ํ™”, ๋Ÿฐํƒ€์ž„ ์˜ค๋ฒ„ํ—ค๋“œ ์ œ๋กœ\n',
272
+ '- ๐Ÿ”ง **ํ•˜์œ„ ํ˜ธํ™˜์„ฑ**: ๊ธฐ์กด ์ฝ”๋“œ ์ˆ˜์ • ๋ถˆํ•„์š”\n\n',
273
+ '## ๐Ÿ“ Actions๋กœ ํผ ๊ฐ„์†Œํ™”\n',
274
+ '์ƒˆ๋กœ์šด Actions API๋กœ ํผ ์ฒ˜๋ฆฌ๊ฐ€ ๋” ๊ฐ„๋‹จํ•ด์ง‘๋‹ˆ๋‹ค:\n\n',
275
+ '```tsx\n',
276
+ 'function ContactForm({ action }) {\n',
277
+ ' const [state, formAction] = useActionState(action, null);\n',
278
+ ' return (\n',
279
+ ' <form action={formAction}>\n',
280
+ ' <input name="email" type="email" />\n',
281
+ ' <button>์ œ์ถœ</button>\n',
282
+ ' </form>\n',
283
+ ' );\n',
284
+ '}\n',
285
+ '```\n\n',
286
+ '์ด ๋‹ต๋ณ€์ด ๋„์›€์ด ๋˜์—ˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค! ๐ŸŽ‰',
287
+ ];
288
+
289
+ for (const chunk of chunks) {
290
+ await delay(100);
291
+ markdownRef.current?.push(chunk, 'answer');
292
+ }
293
+ };
294
+
295
+ return (
296
+ <div className="chat-container">
297
+ <button onClick={simulateAIResponse}>๐Ÿค– React 19 ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด ์งˆ๋ฌธํ•˜๊ธฐ</button>
298
+
299
+ <MarkdownCMD ref={markdownRef} interval={10} timerType="requestAnimationFrame" onEnd={(data) => console.log('๋‹จ๋ฝ ์™„๋ฃŒ:', data)} />
300
+ </div>
301
+ );
302
+ }
303
+
304
+ const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
305
+ ````
306
+
307
+ ### ๐Ÿ”„ ์ŠคํŠธ๋ฆฌ๋ฐ Markdown ๊ตฌ๋ฌธ ์ฒ˜๋ฆฌ
308
+
309
+ **ํ•ต์‹ฌ ๋ฌธ์ œ**: ์ŠคํŠธ๋ฆฌ๋ฐ ์ถœ๋ ฅ ์‹œ ๋ถˆ์™„์ „ํ•œ Markdown ๊ตฌ๋ฌธ์ด ๋ Œ๋”๋ง ์˜ค๋ฅ˜๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์Œ
310
+
311
+ ```tsx
312
+ // ๐Ÿšจ ๋ฌธ์ œ ์‹œ๋‚˜๋ฆฌ์˜ค
313
+ push('#'); // "# "
314
+ push(' '); // "# "
315
+ push('์ œ๋ชฉ'); // "# ์ œ๋ชฉ"
316
+ push('\n'); // "# ์ œ๋ชฉ\n"
317
+ push('1'); // "# ์ œ๋ชฉ\n1" โ† ์ด๊ฒƒ์€ ๋‹จ๋ฝ์œผ๋กœ ์ž˜๋ชป ํ•ด์„๋จ
318
+ push('.'); // "# ์ œ๋ชฉ\n1." โ† ์˜ฌ๋ฐ”๋ฅธ ๋ฆฌ์ŠคํŠธ ํ˜•์„ฑ
319
+ push(' ํ•ญ๋ชฉ'); // "# ์ œ๋ชฉ\n1. ํ•ญ๋ชฉ"
320
+ ```
321
+
322
+ **โœ… ์Šค๋งˆํŠธ ์†”๋ฃจ์…˜**: ์ปดํฌ๋„ŒํŠธ ๋‚ด์žฅ ๋™๊ธฐ ๋ฒ„ํผ๋ง ๋ฉ”์ปค๋‹ˆ์ฆ˜
323
+
324
+ ```tsx
325
+ // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ตฌ๋ฌธ ๊ฒฝ๊ณ„๋ฅผ ์ง€๋Šฅ์ ์œผ๋กœ ์ฒ˜๋ฆฌ
326
+ const handleStreamingMarkdown = () => {
327
+ const chunks = ['#', ' ', '์‚ฌ์šฉ', '๊ธฐ์ˆ ', '\n', '1', '.', ' ', '๊ธฐ์ˆ 1', '\n', '2', '.', ' ๊ธฐ์ˆ 2'];
328
+
329
+ chunks.forEach((chunk) => {
330
+ markdownRef.current?.push(chunk, 'answer');
331
+ // ์ง€์—ฐ ๋ถˆํ•„์š”, ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ์ง€๋Šฅ์  ๋ฒ„ํผ๋ง
332
+ });
333
+
334
+ // ์„ ํƒ์‚ฌํ•ญ: ๋‚จ์€ ๋ฒ„ํผ ์ฝ˜ํ…์ธ  ์ˆ˜๋™ ํ”Œ๋Ÿฌ์‹œ
335
+ markdownRef.current?.flushBuffer();
336
+ };
337
+
338
+ // ๐Ÿง  ์Šค๋งˆํŠธ ์ฒ˜๋ฆฌ ํ”Œ๋กœ์šฐ:
339
+ // 1. "# ์‚ฌ์šฉ๊ธฐ์ˆ \n1" ๊ตฌ๋ฌธ ๋ถˆ์™„์ „ ์‹ค์‹œ๊ฐ„ ๊ฐ์ง€
340
+ // 2. ์Šค๋งˆํŠธ ๋ฒ„ํผ๋ง, ๋” ๋งŽ์€ ๋ฌธ์ž ๋Œ€๊ธฐ
341
+ // 3. "."์„ ๋ฐ›์•„ "1." ํ˜•์„ฑ ํ›„ ์ฆ‰์‹œ ์ฒ˜๋ฆฌ
342
+ // 4. ์ง€์—ฐ ์—†์Œ, ์ˆœ์ˆ˜ ๋™๊ธฐ ์ฒ˜๋ฆฌ
343
+ ```
344
+
345
+ **๋ฒ„ํผ ๋ฉ”์ปค๋‹ˆ์ฆ˜ ํŠน์ง•**:
346
+
347
+ - โšก **์ง€์—ฐ ์—†์Œ**: setTimeout ์—†์Œ, ์ˆœ์ˆ˜ ๋™๊ธฐ ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ
348
+ - ๐Ÿง  **์Šค๋งˆํŠธ ๊ฒฝ๊ณ„**: Markdown ๊ตฌ๋ฌธ ๊ทœ์น™ ๊ธฐ๋ฐ˜ ๊ฒฝ๊ณ„ ๊ฐ์ง€
349
+ - ๐Ÿ”„ **์‹ค์‹œ๊ฐ„ ๋ถ„ํ• **: ์™„์ „ํ•œ ๊ตฌ๋ฌธ ๋งŒ๋‚  ๋•Œ ์ฆ‰์‹œ ์ฒ˜๋ฆฌ, ๋ถˆ์™„์ „ํ•  ๋•Œ ์Šค๋งˆํŠธ ๋ฒ„ํผ๋ง
350
+ - ๐Ÿ›ก๏ธ **์•ˆ์ „ ๋ณด์žฅ**: ๋‚จ์€ ์ฝ˜ํ…์ธ  ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ `flushBuffer()` ๋ฉ”์„œ๋“œ ์ œ๊ณต
351
+
352
+ **์ง€์›๋˜๋Š” ๊ตฌ๋ฌธ ๊ฐ์ง€**:
353
+
354
+ ````typescript
355
+ // โœ… ์™„์ „ํ•œ ๊ตฌ๋ฌธ (์ฆ‰์‹œ ์ฒ˜๋ฆฌ)
356
+ '## ์ œ๋ชฉ'; // ์™„์ „ํ•œ ์ œ๋ชฉ
357
+ '1. ๋ฆฌ์ŠคํŠธ ํ•ญ๋ชฉ'; // ์™„์ „ํ•œ ๋ฆฌ์ŠคํŠธ ํ•ญ๋ชฉ
358
+ '- ํ•ญ๋ชฉ'; // ์™„์ „ํ•œ ๋ฌด์ˆœ์„œ ๋ฆฌ์ŠคํŠธ
359
+ '> ์ธ์šฉ ์ฝ˜ํ…์ธ '; // ์™„์ „ํ•œ ์ธ์šฉ
360
+ '```javascript'; // ์ฝ”๋“œ ๋ธ”๋ก ์‹œ์ž‘
361
+ '```'; // ์ฝ”๋“œ ๋ธ”๋ก ๋
362
+ '๊ฐœํ–‰์œผ๋กœ ๋๋‚˜๋Š” ์ฝ˜ํ…์ธ \n'; // ๊ฐœํ–‰ ๊ฒฝ๊ณ„
363
+
364
+ // ๐Ÿ”„ ๋ถˆ์™„์ „ํ•œ ๊ตฌ๋ฌธ (์Šค๋งˆํŠธ ๋ฒ„ํผ๋ง)
365
+ '##'; // ์ œ๋ชฉ ๊ธฐํ˜ธ๋งŒ
366
+ '1'; // ์ˆซ์ž๋งŒ
367
+ '```java'; // ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ ๋ธ”๋ก ์‹œ์ž‘
368
+ ````
369
+
370
+ ---
371
+
372
+ ## ๐Ÿ”ง ๋ชจ๋ฒ” ์‚ฌ๋ก€
373
+
374
+ ### 1. ์„ฑ๋Šฅ ์ตœ์ ํ™”
375
+
376
+ ```tsx
377
+ // โœ… ์ถ”์ฒœ ๊ตฌ์„ฑ
378
+ <DsMarkdown
379
+ timerType="requestAnimationFrame"
380
+ interval={15} // 15-30ms๊ฐ€ ์ตœ์  ๊ฒฝํ—˜
381
+ />
382
+
383
+ // โŒ ๋„ˆ๋ฌด ์ž‘์€ ๊ฐ„๊ฒฉ ํ”ผํ•˜๊ธฐ
384
+ <DsMarkdown interval={1} /> // ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์Œ
385
+ ```
386
+
387
+ ### 2. ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
388
+
389
+ ```tsx
390
+ // โœ… ์ถ”์ฒœ: ๋ช…๋ นํ˜• API
391
+ const ref = useRef<MarkdownRef>(null);
392
+ useEffect(() => {
393
+ ref.current?.push(newChunk, 'answer');
394
+ }, [newChunk]);
395
+
396
+ // โŒ ํ”ผํ•˜๊ธฐ: ๋นˆ๋ฒˆํ•œ children ์—…๋ฐ์ดํŠธ
397
+ const [content, setContent] = useState('');
398
+ // ๊ฐ ์—…๋ฐ์ดํŠธ๋งˆ๋‹ค ์ „์ฒด ์ฝ˜ํ…์ธ ๋ฅผ ๋‹ค์‹œ ํŒŒ์‹ฑ
399
+ ```
400
+
401
+ ### 3. ํƒ€์ž… ์•ˆ์ „์„ฑ
402
+
403
+ ```tsx
404
+ import { MarkdownRef } from 'ds-markdown';
405
+
406
+ const ref = useRef<MarkdownRef>(null);
407
+ // ์™„์ „ํ•œ TypeScript ํƒ€์ž… ํžŒํŠธ
408
+ ```
409
+
410
+ ### 4. ์Šคํƒ€์ผ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•
411
+
412
+ ```css
413
+ /* ์‚ฌ๊ณ  ์˜์—ญ ์Šคํƒ€์ผ */
414
+ .ds-markdown-thinking {
415
+ background: rgba(255, 193, 7, 0.1);
416
+ border-left: 3px solid #ffc107;
417
+ padding: 12px;
418
+ border-radius: 6px;
419
+ margin: 8px 0;
420
+ }
421
+
422
+ /* ๋‹ต๋ณ€ ์˜์—ญ ์Šคํƒ€์ผ */
423
+ .ds-markdown-answer {
424
+ color: #333;
425
+ line-height: 1.6;
426
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
427
+ }
428
+
429
+ /* ์ฝ”๋“œ ๋ธ”๋ก ์Šคํƒ€์ผ */
430
+ .ds-markdown pre {
431
+ background: #f8f9fa;
432
+ border-radius: 8px;
433
+ padding: 16px;
434
+ overflow-x: auto;
435
+ }
436
+
437
+ /* ํ…Œ์ด๋ธ” ์Šคํƒ€์ผ */
438
+ .ds-markdown-table {
439
+ border-collapse: collapse;
440
+ width: 100%;
441
+ margin: 16px 0;
442
+ }
443
+
444
+ .ds-markdown-table th,
445
+ .ds-markdown-table td {
446
+ border: 1px solid #ddd;
447
+ padding: 8px 12px;
448
+ text-align: left;
449
+ }
450
+ ```
451
+
452
+ ---
453
+
454
+ ## ๐ŸŒ ํ˜ธํ™˜์„ฑ
455
+
456
+ | ํ™˜๊ฒฝ | ๋ฒ„์ „ ์š”๊ตฌ์‚ฌํ•ญ | ์„ค๋ช… |
457
+ | -------------- | ----------------------------------- | --------------------- |
458
+ | **React** | 16.8.0+ | Hooks ์ง€์› ํ•„์š” |
459
+ | **TypeScript** | 4.0+ | ์„ ํƒ์‚ฌํ•ญ, ํ•˜์ง€๋งŒ ์ถ”์ฒœ |
460
+ | **๋ธŒ๋ผ์šฐ์ €** | Chrome 60+, Firefox 55+, Safari 12+ | ๋ชจ๋˜ ๋ธŒ๋ผ์šฐ์ € |
461
+ | **Node.js** | 14.0+ | ๋นŒ๋“œ ํ™˜๊ฒฝ |
462
+
463
+ ---
464
+
465
+ ## ๐Ÿค ๊ธฐ์—ฌ ๊ฐ€์ด๋“œ
466
+
467
+ Issue์™€ Pull Request ์ œ์ถœ์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค!
468
+
469
+ 1. ์ด ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํฌํฌ
470
+ 2. ๊ธฐ๋Šฅ ๋ธŒ๋žœ์น˜ ์ƒ์„ฑ: `git checkout -b feature/amazing-feature`
471
+ 3. ๋ณ€๊ฒฝ์‚ฌํ•ญ ์ปค๋ฐ‹: `git commit -m 'Add amazing feature'`
472
+ 4. ๋ธŒ๋žœ์น˜ ํ‘ธ์‹œ: `git push origin feature/amazing-feature`
473
+ 5. Pull Request ์ œ์ถœ
474
+
475
+ ---
476
+
477
+ ## ๐Ÿ“„ ๋ผ์ด์„ ์Šค
478
+
479
+ MIT ยฉ [onshinpei](https://github.com/onshinpei)
480
+
481
+ ---
482
+
483
+ <div align="center">
484
+ <strong>์ด ํ”„๋กœ์ ํŠธ๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‹ค๋ฉด, โญ๏ธ Star๋กœ ์ง€์›ํ•ด์ฃผ์„ธ์š”!</strong>
485
+
486
+ <br><br>
487
+
488
+ [๐Ÿ› ๋ฌธ์ œ ๋ณด๊ณ ](https://github.com/onshinpei/ds-markdown/issues) |
489
+ [๐Ÿ’ก ๊ธฐ๋Šฅ ์ œ์•ˆ](https://github.com/onshinpei/ds-markdown/issues) |
490
+ [๐Ÿ“– ๋ฌธ์„œ ๋ณด๊ธฐ](https://onshinpei.github.io/ds-markdown/)
491
+ </div>