ds-markdown 0.1.4-beta.1 โ†’ 0.1.4

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 DELETED
@@ -1,805 +0,0 @@
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/)
16
-
17
- [DEMO๏ผš๐Ÿ”ง StackBlitz ์ฒดํ—˜](https://stackblitz.com/edit/vitejs-vite-ddfw8avb?file=src%2FApp.tsx)
18
-
19
- ---
20
-
21
- ## ๐Ÿ“‹ ๋ชฉ์ฐจ
22
-
23
- - [โœจ ํ•ต์‹ฌ ๊ธฐ๋Šฅ](#-ํ•ต์‹ฌ-๊ธฐ๋Šฅ)
24
- - [๐Ÿ“ฆ ๋น ๋ฅธ ์„ค์น˜](#-๋น ๋ฅธ-์„ค์น˜)
25
- - [๐Ÿš€ 5๋ถ„ ๋น ๋ฅธ ์‹œ์ž‘](#-5๋ถ„-๋น ๋ฅธ-์‹œ์ž‘)
26
- - [๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•](#๊ธฐ๋ณธ-์‚ฌ์šฉ๋ฒ•)
27
- - [ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋น„ํ™œ์„ฑํ™”](#ํƒ€์ดํ•‘-์• ๋‹ˆ๋ฉ”์ด์…˜-๋น„ํ™œ์„ฑํ™”)
28
- - [์ˆ˜ํ•™ ๊ณต์‹ ์ง€์›](#์ˆ˜ํ•™-๊ณต์‹-์ง€์›)
29
- - [AI ๋Œ€ํ™” ์‹œ๋‚˜๋ฆฌ์˜ค](#ai-๋Œ€ํ™”-์‹œ๋‚˜๋ฆฌ์˜ค)
30
- - [๐ŸŽฏ ๊ณ ๊ธ‰ ์ฝœ๋ฐฑ ์ œ์–ด](#-๊ณ ๊ธ‰-์ฝœ๋ฐฑ-์ œ์–ด)
31
- - [๐Ÿ”„ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์žฌ์‹œ์ž‘ ๋ฐ๋ชจ](#-์• ๋‹ˆ๋ฉ”์ด์…˜-์žฌ์‹œ์ž‘-๋ฐ๋ชจ)
32
- - [โ–ถ๏ธ ์ˆ˜๋™ ์‹œ์ž‘ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ฐ๋ชจ](#๏ธ-์ˆ˜๋™-์‹œ์ž‘-์• ๋‹ˆ๋ฉ”์ด์…˜-๋ฐ๋ชจ)
33
- - [๐Ÿ“š ์™„์ „ API ๋ฌธ์„œ](#-์™„์ „-api-๋ฌธ์„œ)
34
- - [๐Ÿงฎ ์ˆ˜ํ•™ ๊ณต์‹ ์‚ฌ์šฉ ๊ฐ€์ด๋“œ](#-์ˆ˜ํ•™-๊ณต์‹-์‚ฌ์šฉ-๊ฐ€์ด๋“œ)
35
- - [๐Ÿ”Œ ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹œ์Šคํ…œ](#-ํ”Œ๋Ÿฌ๊ทธ์ธ-์‹œ์Šคํ…œ)
36
- - [๐ŸŽ›๏ธ ํƒ€์ด๋จธ ๋ชจ๋“œ ์ƒ์„ธ](#๏ธ-ํƒ€์ด๋จธ-๋ชจ๋“œ-์ƒ์„ธ)
37
- - [๐Ÿ’ก ์‹ค์ „ ์˜ˆ์ œ](#-์‹ค์ „-์˜ˆ์ œ)
38
- - [๐Ÿ”ง ๋ชจ๋ฒ” ์‚ฌ๋ก€](#-๋ชจ๋ฒ”-์‚ฌ๋ก€)
39
-
40
- ---
41
-
42
- ## โ“ ์™œ ds-markdown์„ ์จ์•ผ ํ•˜๋‚˜์š”?
43
-
44
- - **AI ์ฑ„ํŒ… ๊ฒฝํ—˜ ์™„๋ฒฝ ์žฌํ˜„**
45
- DeepSeek ๋“ฑ ์ฃผ์š” AI ์ฑ„ํŒ… UI์˜ ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ ์ŠคํŠธ๋ฆฌ๋ฐ ์‘๋‹ต์„ 1:1๋กœ ์žฌํ˜„, "AI๊ฐ€ ์ƒ๊ฐ/๋‹ต๋ณ€ ์ค‘"์ธ ์ง„์งœ ๊ฐ™์€ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
46
-
47
- - **๋ฐฑ์—”๋“œ ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐ์ดํ„ฐ ์™„๋ฒฝ ๋Œ€์‘**
48
- ๋งŽ์€ AI/LLM ๋ฐฑ์—”๋“œ(OpenAI, DeepSeek ๋“ฑ)๋Š” ํ•œ ๋ฒˆ์— ์—ฌ๋Ÿฌ ๊ธ€์ž๊ฐ€ ํฌํ•จ๋œ chunk๋ฅผ ๋ณด๋ƒ…๋‹ˆ๋‹ค.
49
- **ds-markdown์€ ๊ฐ chunk๋ฅผ ์ž๋™์œผ๋กœ ํ•œ ๊ธ€์ž์”ฉ ๋ถ„๋ฆฌํ•ด, ๋ฐฑ์—”๋“œ๊ฐ€ ์—ฌ๋Ÿฌ ๊ธ€์ž๋ฅผ ํ•œ ๋ฒˆ์— ๋ณด๋‚ด๋„ ํ•ญ์ƒ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ํ•œ ๊ธ€์ž์”ฉ ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.**
50
-
51
- - **์™„๋ฒฝํ•œ Markdown & ์ˆ˜์‹ ์ง€์›**
52
- KaTeX ๋‚ด์žฅ, ๋ชจ๋“  ์ฃผ์š” Markdown ๊ตฌ๋ฌธ๊ณผ ์ˆ˜์‹ ์ง€์›โ€”๊ธฐ์ˆ  Q&A, ๊ต์œก, ์ง€์‹๋ฒ ์ด์Šค์— ์ตœ์ .
53
-
54
- - **์ตœ๊ณ ์˜ ๊ฐœ๋ฐœ ๊ฒฝํ—˜**
55
- ํ’๋ถ€ํ•œ ๋ช…๋ นํ˜• API, ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐ์ดํ„ฐ, ๋น„๋™๊ธฐ ์ฝœ๋ฐฑ, ํ”Œ๋Ÿฌ๊ทธ์ธ ํ™•์žฅ ๋“ฑ์œผ๋กœ ์œ ์—ฐํ•œ ์ œ์–ด ๊ฐ€๋Šฅ.
56
-
57
- - **๊ฐ€๋ณ๊ณ  ๊ณ ์„ฑ๋Šฅ**
58
- ์ž‘์€ ์šฉ๋Ÿ‰, ๋น ๋ฅธ ์†๋„, ๋ชจ๋ฐ”์ผ/๋ฐ์Šคํฌํƒ‘ ๋ชจ๋‘ ์ง€์›. ํ•ต์‹ฌ ์˜์กด์„ฑ์€ [react-markdown](https://github.com/remarkjs/react-markdown) (์—…๊ณ„ ํ‘œ์ค€์˜ ์„ฑ์ˆ™ํ•œ Markdown ๋ Œ๋”๋Ÿฌ) ํ•˜๋‚˜๋ฟ์ด๋ฉฐ, ๊ทธ ์™ธ ๋ฌด๊ฑฐ์šด ์˜์กด์„ฑ์€ ์—†์Šต๋‹ˆ๋‹ค. ๋ฐ”๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
59
-
60
- - **๋‹ค์ค‘ ํ…Œ๋งˆ ๋ฐ ํ”Œ๋Ÿฌ๊ทธ์ธ ์•„ํ‚คํ…์ฒ˜**
61
- ๋ผ์ดํŠธ/๋‹คํฌ ํ…Œ๋งˆ ์ „ํ™˜, remark/rehype ํ”Œ๋Ÿฌ๊ทธ์ธ ํ˜ธํ™˜, ๊ณ ๊ธ‰ ํ™•์žฅ์„ฑ.
62
-
63
- - **๋‹ค์–‘ํ•œ ํ™œ์šฉ ์‚ฌ๋ก€**
64
- - AI ์ฑ—๋ด‡/์–ด์‹œ์Šคํ„ดํŠธ
65
- - ์‹ค์‹œ๊ฐ„ Q&A/์ง€์‹๋ฒ ์ด์Šค
66
- - ๊ต์œก/์ˆ˜ํ•™/ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ฝ˜ํ…์ธ 
67
- - ์ œํ’ˆ ๋ฐ๋ชจ, ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ๋ฌธ์„œ
68
- - "ํƒ€์ž๊ธฐ" ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ ์ŠคํŠธ๋ฆฌ๋ฐ Markdown์ด ํ•„์š”ํ•œ ๋ชจ๋“  ์ƒํ™ฉ
69
-
70
- ---
71
-
72
- ## โœจ ํ•ต์‹ฌ ๊ธฐ๋Šฅ
73
-
74
- ### ๐Ÿค– **AI ๋Œ€ํ™” ์‹œ๋‚˜๋ฆฌ์˜ค**
75
-
76
- - [DeepSeek ์›น์‚ฌ์ดํŠธ](https://chat.deepseek.com/) ์ฑ„ํŒ… ์‘๋‹ต ํšจ๊ณผ๋ฅผ 1:1๋กœ ์žฌํ˜„
77
- - ์‚ฌ๊ณ  ๊ณผ์ •(`thinking`)๊ณผ ๋‹ต๋ณ€ ๋‚ด์šฉ(`answer`) ๋‘ ๊ฐ€์ง€ ๋ชจ๋“œ ์ง€์›
78
- - ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐ์ดํ„ฐ์— ์™„๋ฒฝํ•˜๊ฒŒ ์ ์‘, ์‚ฌ์šฉ์ž ์ž…๋ ฅ์— ๋Œ€ํ•œ ์ง€์—ฐ ์—†๋Š” ์‘๋‹ต
79
-
80
- ### ๐Ÿ“Š **์ฝ˜ํ…์ธ  ํ‘œ์‹œ ์‹œ๋‚˜๋ฆฌ์˜ค**
81
-
82
- - ์ฝ”๋“œ ํ•˜์ด๋ผ์ดํŒ…, ํ…Œ์ด๋ธ”, ๋ชฉ๋ก ๋“ฑ์„ ํฌํ•จํ•œ ์™„์ „ํ•œ Markdown ๊ตฌ๋ฌธ ์ง€์›
83
- - ์ˆ˜์‹ ๋ Œ๋”๋ง (KaTeX), `$...$` ๋ฐ `\[...\]` ๊ตฌ๋ฌธ ์ง€์›
84
- - ๋ผ์ดํŠธ/๋‹คํฌ ํ…Œ๋งˆ ์ง€์›, ๋‹ค์–‘ํ•œ ์ œํ’ˆ ์Šคํƒ€์ผ์— ์ ์‘
85
- - remark/rehype ํ”Œ๋Ÿฌ๊ทธ์ธ ํ™•์žฅ์„ ์ง€์›ํ•˜๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ ์•„ํ‚คํ…์ฒ˜
86
-
87
- ### ๐Ÿ”ง **๊ฐœ๋ฐœ์ž ๊ฒฝํ—˜**
88
-
89
- - ํƒ€์ดํ•‘ ์ค‘๋‹จ `stop` ๋ฐ ์žฌ๊ฐœ `resume` ์ง€์›
90
- - ํƒ€์ดํ•‘ ๋น„ํ™œ์„ฑํ™” ๋ฐ ํ™œ์„ฑํ™” ์ง€์›
91
-
92
- ### ๐ŸŽฌ **๋ถ€๋“œ๋Ÿฌ์šด ์• ๋‹ˆ๋ฉ”์ด์…˜**
93
-
94
- - ๋“€์–ผ ํƒ€์ด๋จธ ๋ชจ๋“œ ์ตœ์ ํ™”, `requestAnimationFrame` ๋ฐ `setTimeout` ๋ชจ๋“œ ์ง€์›
95
- - ๊ณ ์ฃผํŒŒ ํƒ€์ดํ•‘ ์ง€์›(`requestAnimationFrame` ๋ชจ๋“œ์—์„œ `0ms`์— ๊ฐ€๊นŒ์šด ํƒ€์ดํ•‘ ๊ฐ„๊ฒฉ ์ง€์›)
96
- - ํ”„๋ ˆ์ž„ ๋™๊ธฐํ™” ๋ Œ๋”๋ง์œผ๋กœ ๋ธŒ๋ผ์šฐ์ € ์ƒˆ๋กœ๊ณ ์นจ ์†๋„์™€ ์™„๋ฒฝ ๋งค์นญ
97
- - ์Šค๋งˆํŠธ ๋ฌธ์ž ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๋กœ ๋” ์ž์—ฐ์Šค๋Ÿฌ์šด ์‹œ๊ฐ์  ํšจ๊ณผ
98
-
99
- ---
100
-
101
- ## ๐Ÿ“ฆ ๋น ๋ฅธ ์„ค์น˜
102
-
103
- ```bash
104
- # npm
105
- npm install ds-markdown
106
-
107
- # yarn
108
- yarn add ds-markdown
109
-
110
- # pnpm
111
- pnpm add ds-markdown
112
- ```
113
-
114
- ### ESM CDN์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ
115
-
116
- ์„ค์น˜ ์—†์ด ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:
117
-
118
- [DEMO](https://stackblitz.com/edit/stackblitz-starters-7vcclcw7?file=index.html)
119
-
120
- ```html
121
- <!-- ์Šคํƒ€์ผ ๊ฐ€์ ธ์˜ค๊ธฐ, ํ•„์ˆ˜ -->
122
- <link rel="stylesheet" href="https://esm.sh/ds-markdown/dist/style.css" />
123
-
124
- <!-- katex ์ˆ˜ํ•™ ๊ณต์‹ ์Šคํƒ€์ผ ๊ฐ€์ ธ์˜ค๊ธฐ, ํ•„์š”ํ•œ ๊ฒฝ์šฐ๋งŒ -->
125
- <link rel="stylesheet" href="https://esm.sh/ds-markdown/dist/katex.css" />
126
-
127
- <!-- ์ปดํฌ๋„ŒํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ -->
128
- <script type="module">
129
- import Markdown from 'https://esm.sh/ds-markdown';
130
- </script>
131
- ```
132
-
133
- ## ๐Ÿš€ 5๋ถ„ ๋น ๋ฅธ ์‹œ์ž‘
134
-
135
- ### ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•
136
-
137
- [DEMO](https://stackblitz.com/edit/vitejs-vite-z94syu8j?file=src%2FApp.tsx)
138
-
139
- ```tsx
140
- import DsMarkdown from 'ds-markdown';
141
- import 'ds-markdown/style.css';
142
-
143
- function App() {
144
- return (
145
- <DsMarkdown interval={20} answerType="answer">
146
- # Hello ds-markdown ์ด๊ฒƒ์€ **๊ณ ์„ฑ๋Šฅ** ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค! ## ๊ธฐ๋Šฅ - โšก ์ง€์—ฐ ์—†๋Š” ์ŠคํŠธ๋ฆฌ๋ฐ ์ฒ˜๋ฆฌ - ๐ŸŽฌ ๋ถ€๋“œ๋Ÿฌ์šด ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜ - ๐ŸŽฏ ์™„๋ฒฝํ•œ ๊ตฌ๋ฌธ ์ง€์›
147
- </DsMarkdown>
148
- );
149
- }
150
- ```
151
-
152
- ### ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋น„ํ™œ์„ฑํ™”
153
-
154
- ```tsx
155
- import DsMarkdown from 'ds-markdown';
156
- import 'ds-markdown/style.css';
157
-
158
- function StaticDemo() {
159
- const [disableTyping, setDisableTyping] = useState(false);
160
-
161
- return (
162
- <div>
163
- <button onClick={() => setDisableTyping(!disableTyping)}>{disableTyping ? 'ํ™œ์„ฑํ™”' : '๋น„ํ™œ์„ฑํ™”'}ํƒ€์ดํ•‘ ํšจ๊ณผ</button>
164
-
165
- <DsMarkdown interval={20} answerType="answer" disableTyping={disableTyping}>
166
- # ์ •์  ํ‘œ์‹œ ๋ชจ๋“œ `disableTyping`์ด `true`์ผ ๋•Œ, ์ฝ˜ํ…์ธ ๋Š” ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ ์—†์ด ์ฆ‰์‹œ ๋ชจ๋‘ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํŠน์ • ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ๋งค์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค: - ๐Ÿ“„ ์ •์  ๋ฌธ์„œ ํ‘œ์‹œ - ๐Ÿ”„ ํ‘œ์‹œ ๋ชจ๋“œ ์ „ํ™˜ - โšก
167
- ์ฝ˜ํ…์ธ  ๋น ๋ฅธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ
168
- </DsMarkdown>
169
- </div>
170
- );
171
- }
172
- ```
173
-
174
- ### ์ˆ˜ํ•™ ๊ณต์‹ ์ง€์›
175
-
176
- ```tsx
177
- import DsMarkdown from 'ds-markdown';
178
- // ๊ณต์‹์„ ํ‘œ์‹œํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ๊ณต์‹ ๋ณ€ํ™˜ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค
179
- import { katexPlugin } from 'ds-markdown/plugins';
180
- import 'ds-markdown/style.css';
181
- // ๊ณต์‹์„ ํ‘œ์‹œํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์ˆ˜ํ•™ ๊ณต์‹ ์Šคํƒ€์ผ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค
182
- import 'ds-markdown/katex.css';
183
-
184
- function MathDemo() {
185
- return (
186
- <DsMarkdown interval={20} answerType="answer" plugins={[katexPlugin]} math={{ splitSymbol: 'dollar' }}>
187
- # ํ”ผํƒ€๊ณ ๋ผ์Šค ์ •๋ฆฌ ์ง๊ฐ์‚ผ๊ฐํ˜•์—์„œ ๋น—๋ณ€์˜ ์ œ๊ณฑ์€ ๋‘ ์ง๊ฐ๋ณ€์˜ ์ œ๊ณฑ์˜ ํ•ฉ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: $a^2 + b^2 = c^2$ ์—ฌ๊ธฐ์„œ: - $a$์™€ $b$๋Š” ์ง๊ฐ๋ณ€ - $c$๋Š” ๋น—๋ณ€ ๊ณ ์ „์ ์ธ "๊ตฌ์‚ผ๊ณ ์‚ฌํ˜„์˜ค"์˜ ๊ฒฝ์šฐ: $c = \sqrt
188
- {3 ^ (2 + 4) ^ 2} = \sqrt{25} = 5$
189
- </DsMarkdown>
190
- );
191
- }
192
- ```
193
-
194
- ### AI ๋Œ€ํ™” ์‹œ๋‚˜๋ฆฌ์˜ค
195
-
196
- ```tsx
197
- function ChatDemo() {
198
- const [thinking, setThinking] = useState('');
199
- const [answer, setAnswer] = useState('');
200
-
201
- const handleAsk = () => {
202
- setThinking('๐Ÿค” ์งˆ๋ฌธ์„ ๋ถ„์„ ์ค‘์ž…๋‹ˆ๋‹ค...');
203
-
204
- setTimeout(() => {
205
- setAnswer(`# React 19์— ๋Œ€ํ•ด
206
-
207
- React 19๋Š” ๋งŽ์€ ํฅ๋ฏธ๋กœ์šด ์ƒˆ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค:
208
-
209
- ## ๐Ÿš€ ์ฃผ์š” ์—…๋ฐ์ดํŠธ
210
- 1. **React Compiler** - ์ž๋™ ์„ฑ๋Šฅ ์ตœ์ ํ™”
211
- 2. **Actions** - ํผ ์ฒ˜๋ฆฌ ๊ฐ„์†Œํ™”
212
- 3. **Document Metadata** - ๋‚ด์žฅ SEO ์ง€์›
213
-
214
- ์ด๋Ÿฌํ•œ ์ƒˆ ๊ธฐ๋Šฅ๋“ค์„ ํ•จ๊ป˜ ํƒํ—˜ํ•ด๋ด…์‹œ๋‹ค!`);
215
- }, 2000);
216
- };
217
-
218
- return (
219
- <div>
220
- <button onClick={handleAsk}>AI์—๊ฒŒ ์งˆ๋ฌธํ•˜๊ธฐ</button>
221
-
222
- {thinking && (
223
- <DsMarkdown answerType="thinking" interval={30}>
224
- {thinking}
225
- </DsMarkdown>
226
- )}
227
-
228
- {answer && (
229
- <DsMarkdown answerType="answer" interval={15}>
230
- {answer}
231
- </DsMarkdown>
232
- )}
233
- </div>
234
- );
235
- }
236
- ```
237
-
238
- ---
239
-
240
- ## ๐Ÿ“š ์™„์ „ API ๋ฌธ์„œ
241
-
242
- ### ๊ธฐ๋ณธ ๋‚ด๋ณด๋‚ด๊ธฐ DsMarkdown ๋ฐ MarkdownCMD props
243
-
244
- ```js
245
- import DsMarkdown, { MarkdownCMD } from 'ds-markdown';
246
- ```
247
-
248
- | ์†์„ฑ | ํƒ€์ž… | ์„ค๋ช… | ๊ธฐ๋ณธ๊ฐ’ |
249
- | ------------------- | ------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------- |
250
- | `interval` | `number` | ํƒ€์ดํ•‘ ๊ฐ„๊ฒฉ (๋ฐ€๋ฆฌ์ดˆ) | `30` |
251
- | `timerType` | `'setTimeout'` \| `'requestAnimationFrame'` | ํƒ€์ด๋จธ ํƒ€์ž… | ํ˜„์žฌ ๊ธฐ๋ณธ๊ฐ’์€ `setTimeout`, ๋‚˜์ค‘์— `requestAnimationFrame`๋กœ ๋ณ€๊ฒฝ ์˜ˆ์ • |
252
- | `answerType` | `'thinking'` \| `'answer'` | ์ฝ˜ํ…์ธ  ํƒ€์ž… (์Šคํƒ€์ผ ์˜ํ–ฅ) | `'answer'` |
253
- | `theme` | `'light'` \| `'dark'` | ํ…Œ๋งˆ ํƒ€์ž… | `'light'` |
254
- | `plugins` | `IMarkdownPlugin[]` | ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ค์ • | `[]` |
255
- | `math` | [IMarkdownMath](#IMarkdownMath) | ์ˆ˜ํ•™ ๊ณต์‹ ์„ค์ • | `{ splitSymbol: 'dollar' }` |
256
- | `onEnd` | `(data: EndData) => void` | ํƒ€์ดํ•‘ ์™„๋ฃŒ ์ฝœ๋ฐฑ | - |
257
- | `onStart` | `(data: StartData) => void` | ํƒ€์ดํ•‘ ์‹œ์ž‘ ์ฝœ๋ฐฑ | - |
258
- | `onBeforeTypedChar` | `(data: IBeforeTypedChar) => Promise<void>` | ๋ฌธ์ž ํƒ€์ดํ•‘ ์ „ ์ฝœ๋ฐฑ, ๋น„๋™๊ธฐ ์ž‘์—… ์ง€์›, ํ›„์† ํƒ€์ดํ•‘ ์ฐจ๋‹จ | - |
259
- | `onTypedChar` | `(data: ITypedChar) => void` | ๋ฌธ์ž ํƒ€์ดํ•‘ ํ›„ ์ฝœ๋ฐฑ | - |
260
- | `disableTyping` | `boolean` | ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋น„ํ™œ์„ฑํ™” | `false` |
261
- | `autoStartTyping` | `boolean` | ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ž๋™ ์‹œ์ž‘ ์—ฌ๋ถ€, false์ธ ๊ฒฝ์šฐ ์ˆ˜๋™ ํŠธ๋ฆฌ๊ฑฐ | `true` |
262
-
263
- > ์ฐธ๊ณ : ํƒ€์ดํ•‘ ์ค‘์— `disableTyping`์ด `true`์—์„œ `false`๋กœ ๋ณ€๊ฒฝ๋˜๋ฉด ๋‹ค์Œ ํƒ€์ดํ•‘ ํŠธ๋ฆฌ๊ฑฐ ์‹œ ๋‚จ์€ ๋ชจ๋“  ๋ฌธ์ž๊ฐ€ ํ•œ ๋ฒˆ์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.
264
-
265
- ### IBeforeTypedChar
266
-
267
- | ์†์„ฑ | ํƒ€์ž… | ์„ค๋ช… | ๊ธฐ๋ณธ๊ฐ’ |
268
- | -------------- | ------------ | ------------------------------ | ------ |
269
- | `currentIndex` | `number` | ์ „์ฒด ๋ฌธ์ž์—ด์—์„œ ํ˜„์žฌ ์ธ๋ฑ์Šค | `0` |
270
- | `currentChar` | `string` | ํƒ€์ดํ•‘ ์˜ˆ์ • ๋ฌธ์ž | - |
271
- | `answerType` | `AnswerType` | ์ฝ˜ํ…์ธ  ํƒ€์ž… (thinking/answer) | - |
272
- | `prevStr` | `string` | ํ˜„์žฌ ํƒ€์ž… ์ฝ˜ํ…์ธ ์˜ ์ด์ „ ๋ฌธ์ž์—ด | - |
273
- | `percent` | `number` | ํƒ€์ดํ•‘ ์ง„ํ–‰๋ฅ  ๋ฐฑ๋ถ„์œจ (0-100) | `0` |
274
-
275
- ### ITypedChar
276
-
277
- | ์†์„ฑ | ํƒ€์ž… | ์„ค๋ช… | ๊ธฐ๋ณธ๊ฐ’ |
278
- | -------------- | ------------ | -------------------------------- | ------ |
279
- | `currentIndex` | `number` | ์ „์ฒด ๋ฌธ์ž์—ด์—์„œ ํ˜„์žฌ ์ธ๋ฑ์Šค | `0` |
280
- | `currentChar` | `string` | ํƒ€์ดํ•‘๋œ ๋ฌธ์ž | - |
281
- | `answerType` | `AnswerType` | ์ฝ˜ํ…์ธ  ํƒ€์ž… (thinking/answer) | - |
282
- | `prevStr` | `string` | ํ˜„์žฌ ํƒ€์ž… ์ฝ˜ํ…์ธ ์˜ ์ด์ „ ๋ฌธ์ž์—ด | - |
283
- | `currentStr` | `string` | ํ˜„์žฌ ํƒ€์ž… ์ฝ˜ํ…์ธ ์˜ ์™„์ „ํ•œ ๋ฌธ์ž์—ด | - |
284
- | `percent` | `number` | ํƒ€์ดํ•‘ ์ง„ํ–‰๋ฅ  ๋ฐฑ๋ถ„์œจ (0-100) | `0` |
285
-
286
- #### IMarkdownMath
287
-
288
- | ์†์„ฑ | ํƒ€์ž… | ์„ค๋ช… | ๊ธฐ๋ณธ๊ฐ’ |
289
- | ------------- | ------------------------- | ---------------- | ---------- |
290
- | `splitSymbol` | `'dollar'` \| `'bracket'` | ์ˆ˜ํ•™ ๊ณต์‹ ๊ตฌ๋ถ„์ž | `'dollar'` |
291
-
292
- **๊ตฌ๋ถ„์ž ์„ค๋ช…:**
293
-
294
- - `'dollar'`: `$...$` ๋ฐ `$$...$$` ๊ตฌ๋ฌธ ์‚ฌ์šฉ
295
- - `'bracket'`: `\(...\)` ๋ฐ `\[...\]` ๊ตฌ๋ฌธ ์‚ฌ์šฉ
296
-
297
- #### IMarkdownPlugin
298
-
299
- | ์†์„ฑ | ํƒ€์ž… | ์„ค๋ช… | ๊ธฐ๋ณธ๊ฐ’ |
300
- | -------------- | ------------------------- | ---------------- | ------ |
301
- | `remarkPlugin` | `unknown` | remark ํ”Œ๋Ÿฌ๊ทธ์ธ | - |
302
- | `rehypePlugin` | `unknown` | rehype ํ”Œ๋Ÿฌ๊ทธ์ธ | - |
303
- | `type` | `'buildIn'` \| `'custom'` | ํ”Œ๋Ÿฌ๊ทธ์ธ ํƒ€์ž… | - |
304
- | `id` | `any` | ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ณ ์œ  ID | - |
305
-
306
- ### ์ปดํฌ๋„ŒํŠธ ๋…ธ์ถœ ๋ฉ”์„œ๋“œ
307
-
308
- #### ๊ธฐ๋ณธ ๋‚ด๋ณด๋‚ด๊ธฐ DsMarkdown
309
-
310
- | ๋ฉ”์„œ๋“œ | ๋งค๊ฐœ๋ณ€์ˆ˜ | ์„ค๋ช… |
311
- | --------- | -------- | ----------------------------------------------------- |
312
- | `start` | - | ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์ž‘ |
313
- | `stop` | - | ํƒ€์ดํ•‘ ์ผ์‹œ์ •์ง€ |
314
- | `resume` | - | ํƒ€์ดํ•‘ ์žฌ๊ฐœ |
315
- | `restart` | - | ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์žฌ์‹œ์ž‘, ํ˜„์žฌ ์ฝ˜ํ…์ธ ๋ฅผ ์ฒ˜์Œ๋ถ€ํ„ฐ ์žฌ์ƒ |
316
-
317
- #### MarkdownCMD ๋…ธ์ถœ ๋ฉ”์„œ๋“œ
318
-
319
- | ๋ฉ”์„œ๋“œ | ๋งค๊ฐœ๋ณ€์ˆ˜ | ์„ค๋ช… |
320
- | ----------------- | ------------------------------------------- | ----------------------------------------------------- |
321
- | `push` | `(content: string, answerType: AnswerType)` | ์ฝ˜ํ…์ธ  ์ถ”๊ฐ€ ๋ฐ ํƒ€์ดํ•‘ ์‹œ์ž‘ |
322
- | `clear` | - | ๋ชจ๋“  ์ฝ˜ํ…์ธ  ๋ฐ ์ƒํƒœ ์ง€์šฐ๊ธฐ |
323
- | `triggerWholeEnd` | - | ์ˆ˜๋™์œผ๋กœ ์™„๋ฃŒ ์ฝœ๋ฐฑ ํŠธ๋ฆฌ๊ฑฐ |
324
- | `start` | - | ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์ž‘ |
325
- | `stop` | - | ํƒ€์ดํ•‘ ์ผ์‹œ์ •์ง€ |
326
- | `resume` | - | ํƒ€์ดํ•‘ ์žฌ๊ฐœ |
327
- | `restart` | - | ํƒ€์ดํ•‘ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์žฌ์‹œ์ž‘, ํ˜„์žฌ ์ฝ˜ํ…์ธ ๋ฅผ ์ฒ˜์Œ๋ถ€ํ„ฐ ์žฌ์ƒ |
328
-
329
- **์‚ฌ์šฉ ์˜ˆ:**
330
-
331
- ```tsx
332
- markdownRef.current?.start(); // ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์ž‘
333
- markdownRef.current?.stop(); // ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ผ์‹œ์ •์ง€
334
- markdownRef.current?.resume(); // ์• ๋‹ˆ๋ฉ”์ด์…˜ ์žฌ๊ฐœ
335
- markdownRef.current?.restart(); // ์• ๋‹ˆ๋ฉ”์ด์…˜ ์žฌ์‹œ์ž‘
336
- ```
337
-
338
- ---
339
-
340
- ## ๐Ÿงฎ ์ˆ˜ํ•™ ๊ณต์‹ ์‚ฌ์šฉ ๊ฐ€์ด๋“œ
341
-
342
- [DEMO1: ํ”ผํƒ€๊ณ ๋ผ์Šค ์ •๋ฆฌ](https://stackblitz.com/edit/vitejs-vite-z94syu8j?file=src%2FApp.tsx)
343
-
344
- [DEMO2: ๋ฌธ์ œ ํ•ด๋‹ต](https://stackblitz.com/edit/vitejs-vite-xk9lxagc?file=README.md)
345
-
346
- ### ๊ธฐ๋ณธ ๊ตฌ๋ฌธ
347
-
348
- ```tsx
349
- import { katexPlugin } from 'ds-markdown/plugins';
350
-
351
- // 1. ์ˆ˜ํ•™ ๊ณต์‹ ์ง€์› ํ™œ์„ฑํ™”
352
- <DsMarkdown plugins={[katexPlugin]}>
353
- # ์ˆ˜ํ•™ ๊ณต์‹ ์˜ˆ์ œ
354
-
355
- // ์ธ๋ผ์ธ ๊ณต์‹
356
- ์ด๊ฒƒ์€ ์ธ๋ผ์ธ ๊ณต์‹์ž…๋‹ˆ๋‹ค: $E = mc^2$
357
-
358
- // ๋ธ”๋ก ๊ณต์‹
359
- $$\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}$$
360
- </DsMarkdown>
361
- ```
362
-
363
- ### ๊ตฌ๋ถ„์ž ์„ ํƒ
364
-
365
- ```tsx
366
- // ๋‹ฌ๋Ÿฌ ๊ธฐํ˜ธ ๊ตฌ๋ถ„์ž ์‚ฌ์šฉ (๊ธฐ๋ณธ๊ฐ’)
367
- <DsMarkdown
368
- plugins={[katexPlugin]}
369
- math={{ splitSymbol: 'dollar' }}
370
- >
371
- ์ธ๋ผ์ธ: $a + b = c$
372
- ๋ธ”๋ก: $$\sum_{i=1}^{n} x_i = x_1 + x_2 + \cdots + x_n$$
373
- </DsMarkdown>
374
-
375
- // ๊ด„ํ˜ธ ๊ตฌ๋ถ„์ž ์‚ฌ์šฉ
376
- <DsMarkdown
377
- plugins={[katexPlugin]}
378
- math={{ splitSymbol: 'bracket' }}
379
- >
380
- ์ธ๋ผ์ธ: \(a + b = c\)
381
- ๋ธ”๋ก: \[\sum_{i=1}^{n} x_i = x_1 + x_2 + \cdots + x_n\]
382
- </DsMarkdown>
383
- ```
384
-
385
- ### ์ŠคํŠธ๋ฆฌ๋ฐ ์ˆ˜ํ•™ ๊ณต์‹
386
-
387
- ```tsx
388
- // ์ŠคํŠธ๋ฆฌ๋ฐ ์ถœ๋ ฅ์—์„œ ์ˆ˜ํ•™ ๊ณต์‹์„ ์™„๋ฒฝํ•˜๊ฒŒ ์ง€์›
389
- const mathContent = [
390
- 'ํ”ผํƒ€๊ณ ๋ผ์Šค ์ •๋ฆฌ:',
391
- '$a^2 + b^2 = c^2$',
392
- '\n\n',
393
- '์—ฌ๊ธฐ์„œ:',
394
- '- $a$์™€ $b$๋Š” ์ง๊ฐ๋ณ€\n',
395
- '- $c$๋Š” ๋น—๋ณ€\n\n',
396
- '๊ณ ์ „์ ์ธ "๊ตฌ์‚ผ๊ณ ์‚ฌํ˜„์˜ค"์˜ ๊ฒฝ์šฐ:\n',
397
- '$c = \\sqrt{3^2 + 4^2} = \\sqrt{25} = 5$\n\n',
398
- '์ด ์ •๋ฆฌ๋Š” ๊ธฐํ•˜ํ•™์—์„œ ๊ด‘๋ฒ”์œ„ํ•˜๊ฒŒ ์‘์šฉ๋ฉ๋‹ˆ๋‹ค!',
399
- ];
400
-
401
- mathContent.forEach((chunk) => {
402
- markdownRef.current?.push(chunk, 'answer');
403
- });
404
- ```
405
-
406
- ### ์Šคํƒ€์ผ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•
407
-
408
- ```css
409
- /* ์ˆ˜ํ•™ ๊ณต์‹ ์Šคํƒ€์ผ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• */
410
- .katex {
411
- font-size: 1.1em;
412
- }
413
-
414
- .katex-display {
415
- margin: 1em 0;
416
- text-align: center;
417
- }
418
-
419
- /* ๋‹คํฌ ํ…Œ๋งˆ ์ ์‘ */
420
- [data-theme='dark'] .katex {
421
- color: #e1e1e1;
422
- }
423
- ```
424
-
425
- ---
426
-
427
- ## ๐Ÿ”Œ ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹œ์Šคํ…œ
428
-
429
- ### ๋‚ด์žฅ ํ”Œ๋Ÿฌ๊ทธ์ธ
430
-
431
- #### KaTeX ์ˆ˜ํ•™ ๊ณต์‹ ํ”Œ๋Ÿฌ๊ทธ์ธ
432
-
433
- ```tsx
434
- import { katexPlugin } from 'ds-markdown/plugins';
435
-
436
- // ์ˆ˜ํ•™ ๊ณต์‹ ์ง€์› ํ™œ์„ฑํ™”
437
- <DsMarkdown plugins={[katexPlugin]}>์ˆ˜ํ•™ ๊ณต์‹: $E = mc^2$</DsMarkdown>;
438
- ```
439
-
440
- ### ์ปค์Šคํ…€ ํ”Œ๋Ÿฌ๊ทธ์ธ
441
-
442
- ```tsx
443
- import { createBuildInPlugin } from 'ds-markdown/plugins';
444
-
445
- // ์ปค์Šคํ…€ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ƒ์„ฑ
446
- const customPlugin = createBuildInPlugin({
447
- remarkPlugin: yourRemarkPlugin,
448
- rehypePlugin: yourRehypePlugin,
449
- id: Symbol('custom-plugin'),
450
- });
451
-
452
- // ์ปค์Šคํ…€ ํ”Œ๋Ÿฌ๊ทธ์ธ ์‚ฌ์šฉ
453
- <DsMarkdown plugins={[katexPlugin, customPlugin]}>์ฝ˜ํ…์ธ </DsMarkdown>;
454
- ```
455
-
456
- ---
457
-
458
- ## ๐ŸŽ›๏ธ ํƒ€์ด๋จธ ๋ชจ๋“œ ์ƒ์„ธ
459
-
460
- ### `requestAnimationFrame` ๋ชจ๋“œ ๐ŸŒŸ (๊ถŒ์žฅ)
461
-
462
- ```typescript
463
- // ๐ŸŽฏ ํŠน์ง•
464
- - ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜: ์‹ค์ œ ๊ฒฝ๊ณผ ์‹œ๊ฐ„์„ ๋ฐ”ํƒ•์œผ๋กœ ๋ฌธ์ž ์ˆ˜ ๊ณ„์‚ฐ
465
- - ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ: ๋‹จ์ผ ํ”„๋ ˆ์ž„ ๋‚ด์—์„œ ์—ฌ๋Ÿฌ ๋ฌธ์ž ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
466
- - ํ”„๋ ˆ์ž„ ๋™๊ธฐํ™”: ๋ธŒ๋ผ์šฐ์ € 60fps ์ƒˆ๋กœ๊ณ ์นจ ์†๋„์™€ ๋™๊ธฐํ™”
467
- - ๊ณ ์ฃผํŒŒ ์ตœ์ ํ™”: interval < 16ms์˜ ๊ณ ์† ํƒ€์ดํ•‘์„ ์™„๋ฒฝํ•˜๊ฒŒ ์ง€์›
468
-
469
- // ๐ŸŽฏ ์ ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค
470
- - ํ˜„๋Œ€ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ธฐ๋ณธ ์„ ํƒ
471
- - ๋ถ€๋“œ๋Ÿฌ์šด ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ ์ถ”๊ตฌ
472
- - ๊ณ ์ฃผํŒŒ ํƒ€์ดํ•‘ (interval > 0์œผ๋กœ ์ถฉ๋ถ„)
473
- - AI ์‹ค์‹œ๊ฐ„ ๋Œ€ํ™” ์‹œ๋‚˜๋ฆฌ์˜ค
474
- ```
475
-
476
- ### `setTimeout` ๋ชจ๋“œ ๐Ÿ“Ÿ (ํ˜ธํ™˜)
477
-
478
- ```typescript
479
- // ๐ŸŽฏ ํŠน์ง•
480
- - ๋‹จ์ผ ๋ฌธ์ž: ๋งค๋ฒˆ ์ •ํ™•ํžˆ ํ•œ ๋ฌธ์ž์”ฉ ์ฒ˜๋ฆฌ
481
- - ๊ณ ์ • ๊ฐ„๊ฒฉ: ์„ค์ •๋œ ์‹œ๊ฐ„์— ๋”ฐ๋ผ ์—„๊ฒฉํ•˜๊ฒŒ ์‹คํ–‰
482
- - ๋ฆฌ๋“ฌ๊ฐ: ํด๋ž˜์‹ ํƒ€์ž๊ธฐ์˜ ๋ฆฌ๋“ฌ๊ฐ
483
- - ์ •๋ฐ€ ์ œ์–ด: ํŠน์ • ํƒ€์ด๋ฐ ์š”๊ตฌ์‚ฌํ•ญ์— ์ ํ•ฉ
484
-
485
- // ๐ŸŽฏ ์ ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค
486
- - ์ •๋ฐ€ํ•œ ์‹œ๊ฐ„ ์ œ์–ด๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ
487
- - ๋ ˆํŠธ๋กœ ํƒ€์ž๊ธฐ ํšจ๊ณผ ์—ฐ์ถœ
488
- - ํ˜ธํ™˜์„ฑ ์š”๊ตฌ์‚ฌํ•ญ์ด ๋†’์€ ์‹œ๋‚˜๋ฆฌ์˜ค
489
- ```
490
-
491
- ### ๐Ÿ“Š ์„ฑ๋Šฅ ๋น„๊ต
492
-
493
- | ํŠน์ง• | requestAnimationFrame | setTimeout |
494
- | ----------------- | ---------------------------------- | ---------------- |
495
- | **๋ฌธ์ž ์ฒ˜๋ฆฌ** | ํ”„๋ ˆ์ž„๋‹น ์—ฌ๋Ÿฌ ๋ฌธ์ž | ํ•œ ๋ฒˆ์— ํ•œ ๋ฌธ์ž |
496
- | **๊ณ ์ฃผํŒŒ** | โœ… ์šฐ์ˆ˜ (5ms โ†’ ํ”„๋ ˆ์ž„๋‹น 3๋ฌธ์ž) | โŒ ์ง€์—ฐ ๊ฐ€๋Šฅ์„ฑ |
497
- | **์ €์ฃผํŒŒ** | โœ… ์ •์ƒ (100ms โ†’ 6ํ”„๋ ˆ์ž„ ํ›„ 1๋ฌธ์ž) | โœ… ์ •๋ฐ€ |
498
- | **์‹œ๊ฐ์  ํšจ๊ณผ** | ๐ŸŽฌ ๋ถ€๋“œ๋Ÿฌ์šด ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ฐ๊ฐ | โšก ์ •๋ฐ€ํ•œ ๋ฆฌ๋“ฌ๊ฐ |
499
- | **์„ฑ๋Šฅ ์˜ค๋ฒ„ํ—ค๋“œ** | ๐ŸŸข ๋‚ฎ์Œ (ํ”„๋ ˆ์ž„ ๋™๊ธฐํ™”) | ๐ŸŸก ๋ณดํ†ต (ํƒ€์ด๋จธ) |
500
-
501
- ๊ณ ์ฃผํŒŒ๋Š” `requestAnimationFrame` ๊ถŒ์žฅ, ์ €์ฃผํŒŒ๋Š” `setTimeout` ๊ถŒ์žฅ
502
-
503
- ---
504
-
505
- ## ๐Ÿ’ก ์‹ค์ „ ์˜ˆ์ œ
506
-
507
- ### ๐Ÿ“ AI ์ŠคํŠธ๋ฆฌ๋ฐ ๋Œ€ํ™”
508
-
509
- [DEMO: ๐Ÿ”ง StackBlitz ์ฒดํ—˜](https://stackblitz.com/edit/vitejs-vite-2ri8kex3?file=src%2FApp.tsx)
510
-
511
- ````tsx
512
- import { useRef } from 'react';
513
- import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
514
-
515
- function StreamingChat() {
516
- const markdownRef = useRef<MarkdownCMDRef>(null);
517
-
518
- // AI ์ŠคํŠธ๋ฆฌ๋ฐ ์‘๋‹ต ์‹œ๋ฎฌ๋ ˆ์ด์…˜
519
- const simulateAIResponse = async () => {
520
- markdownRef.current?.clear();
521
-
522
- // ์‚ฌ๊ณ  ๋‹จ๊ณ„
523
- markdownRef.current?.push('๐Ÿค” ์งˆ๋ฌธ์„ ๋ถ„์„ ์ค‘์ž…๋‹ˆ๋‹ค...', 'thinking');
524
- await delay(1000);
525
- markdownRef.current?.push('\n\nโœ… ๋ถ„์„ ์™„๋ฃŒ, ๋‹ต๋ณ€ ์‹œ์ž‘', 'thinking');
526
-
527
- // ์ŠคํŠธ๋ฆฌ๋ฐ ๋‹ต๋ณ€
528
- const chunks = [
529
- '# React 19 ์‹ ๊ธฐ๋Šฅ ๋ถ„์„\n\n',
530
- '## ๐Ÿš€ React Compiler\n',
531
- 'React 19์˜ ๊ฐ€์žฅ ํฐ ํ•˜์ด๋ผ์ดํŠธ๋Š” **React Compiler** ๋„์ž…์ž…๋‹ˆ๋‹ค:\n\n',
532
- '- ๐ŸŽฏ **์ž๋™ ์ตœ์ ํ™”**: ์ˆ˜๋™ memo์™€ useMemo ๋ถˆํ•„์š”\n',
533
- '- โšก **์„ฑ๋Šฅ ํ–ฅ์ƒ**: ์ปดํŒŒ์ผ ํƒ€์ž„ ์ตœ์ ํ™”, ๋Ÿฐํƒ€์ž„ ์ œ๋กœ ์˜ค๋ฒ„ํ—ค๋“œ\n',
534
- '- ๐Ÿ”ง **ํ›„๋ฐฉ ํ˜ธํ™˜์„ฑ**: ๊ธฐ์กด ์ฝ”๋“œ ์ˆ˜์ • ๋ถˆํ•„์š”\n\n',
535
- '## ๐Ÿ“ Actions๋กœ ํผ ๊ฐ„์†Œํ™”\n',
536
- '์ƒˆ๋กœ์šด Actions API๋กœ ํผ ์ฒ˜๋ฆฌ๊ฐ€ ๋” ๊ฐ„๋‹จํ•ด์ง‘๋‹ˆ๋‹ค:\n\n',
537
- '```tsx\n',
538
- 'function ContactForm({ action }) {\n',
539
- ' const [state, formAction] = useActionState(action, null);\n',
540
- ' return (\n',
541
- ' <form action={formAction}>\n',
542
- ' <input name="email" type="email" />\n',
543
- ' <button>์ œ์ถœ</button>\n',
544
- ' </form>\n',
545
- ' );\n',
546
- '}\n',
547
- '```\n\n',
548
- '์ด ๋‹ต๋ณ€์ด ๋„์›€์ด ๋˜์—ˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค! ๐ŸŽ‰',
549
- ];
550
-
551
- for (const chunk of chunks) {
552
- await delay(100);
553
- markdownRef.current?.push(chunk, 'answer');
554
- }
555
- };
556
-
557
- return (
558
- <div className="chat-container">
559
- <button onClick={simulateAIResponse}>๐Ÿค– React 19 ์‹ ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด ์งˆ๋ฌธ</button>
560
-
561
- <MarkdownCMD ref={markdownRef} interval={10} timerType="requestAnimationFrame" onEnd={(data) => console.log('์„น์…˜ ์™„๋ฃŒ:', data)} />
562
- </div>
563
- );
564
- }
565
-
566
- const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
567
- ````
568
-
569
- ### ๐Ÿงฎ ์ˆ˜ํ•™ ๊ณต์‹ ์ŠคํŠธ๋ฆฌ๋ฐ ๋ Œ๋”๋ง
570
-
571
- ```tsx
572
- import { katexPlugin } from 'ds-markdown/plugins';
573
-
574
- function MathStreamingDemo() {
575
- const markdownRef = useRef<MarkdownCMDRef>(null);
576
-
577
- const simulateMathResponse = async () => {
578
- markdownRef.current?.clear();
579
-
580
- const mathChunks = [
581
- '# ํ”ผํƒ€๊ณ ๋ผ์Šค ์ •๋ฆฌ ์ƒ์„ธ ์„ค๋ช…\n\n',
582
- '์ง๊ฐ์‚ผ๊ฐํ˜•์—์„œ ๋น—๋ณ€์˜ ์ œ๊ณฑ์€ ๋‘ ์ง๊ฐ๋ณ€์˜ ์ œ๊ณฑ์˜ ํ•ฉ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:\n\n',
583
- '$a^2 + b^2 = c^2$\n\n',
584
- '์—ฌ๊ธฐ์„œ:\n',
585
- '- $a$์™€ $b$๋Š” ์ง๊ฐ๋ณ€\n',
586
- '- $c$๋Š” ๋น—๋ณ€\n\n',
587
- '๊ณ ์ „์ ์ธ "๊ตฌ์‚ผ๊ณ ์‚ฌํ˜„์˜ค"์˜ ๊ฒฝ์šฐ:\n',
588
- '$c = \\sqrt{3^2 + 4^2} = \\sqrt{25} = 5$\n\n',
589
- '์ด ์ •๋ฆฌ๋Š” ๊ธฐํ•˜ํ•™์—์„œ ๊ด‘๋ฒ”์œ„ํ•˜๊ฒŒ ์‘์šฉ๋ฉ๋‹ˆ๋‹ค!',
590
- ];
591
-
592
- for (const chunk of mathChunks) {
593
- await delay(150);
594
- markdownRef.current?.push(chunk, 'answer');
595
- }
596
- };
597
-
598
- return (
599
- <div>
600
- <button onClick={simulateMathResponse}>๐Ÿ“ ํ”ผํƒ€๊ณ ๋ผ์Šค ์ •๋ฆฌ ์„ค๋ช…</button>
601
-
602
- <MarkdownCMD ref={markdownRef} interval={20} timerType="requestAnimationFrame" plugins={[katexPlugin]} math={{ splitSymbol: 'dollar' }} />
603
- </div>
604
- );
605
- }
606
- ```
607
-
608
- ## ๐Ÿ”ง ๋ชจ๋ฒ” ์‚ฌ๋ก€
609
-
610
- ### 1. ์„ฑ๋Šฅ ์ตœ์ ํ™”
611
-
612
- ```tsx
613
- // โœ… ๊ถŒ์žฅ ๊ตฌ์„ฑ
614
- <DsMarkdown
615
- timerType="requestAnimationFrame"
616
- interval={15} // 15-30ms๊ฐ€ ์ตœ์  ๊ฒฝํ—˜
617
- />
618
-
619
- // โŒ ๋„ˆ๋ฌด ์ž‘์€ ๊ฐ„๊ฒฉ ํ”ผํ•˜๊ธฐ
620
- <DsMarkdown interval={1} /> // ์„ฑ๋Šฅ ๋ฌธ์ œ ์•ผ๊ธฐ ๊ฐ€๋Šฅ
621
- ```
622
-
623
- ### 2. ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
624
-
625
- ```tsx
626
- // โœ… ๊ถŒ์žฅ: ๋ช…๋ นํ˜• API
627
- const ref = useRef<MarkdownCMDRef>(null);
628
- useEffect(() => {
629
- ref.current?.push(newChunk, 'answer');
630
- }, [newChunk]);
631
-
632
- // โŒ ํ”ผํ•˜๊ธฐ: ๋นˆ๋ฒˆํ•œ children ์—…๋ฐ์ดํŠธ
633
- const [content, setContent] = useState('');
634
- // ๊ฐ ์—…๋ฐ์ดํŠธ๋งˆ๋‹ค ์ „์ฒด ๋‚ด์šฉ์„ ์žฌ๋ถ„์„
635
- ```
636
-
637
- ### 3. ์ˆ˜ํ•™ ๊ณต์‹ ์ตœ์ ํ™”
638
-
639
- ```tsx
640
- // โœ… ๊ถŒ์žฅ: ํ•„์š”์‹œ ์ˆ˜ํ•™ ๊ณต์‹ ์Šคํƒ€์ผ ๋กœ๋“œ
641
- import 'ds-markdown/style.css';
642
- import 'ds-markdown/katex.css'; // ํ•„์š”ํ•  ๋•Œ๋งŒ ๊ฐ€์ ธ์˜ค๊ธฐ
643
-
644
- // โœ… ๊ถŒ์žฅ: ์ ์ ˆํ•œ ๊ตฌ๋ถ„์ž ์‚ฌ์šฉ
645
- // ๊ฐ„๋‹จํ•œ ๊ณต์‹์—๋Š” $...$๊ฐ€ ๋” ๊ฐ„๊ฒฐ
646
- // ๋ณต์žกํ•œ ๊ณต์‹์—๋Š” $$...$$๊ฐ€ ๋” ๋ช…ํ™•
647
-
648
- // โœ… ๊ถŒ์žฅ: ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ตฌ์„ฑ
649
- import { katexPlugin } from 'ds-markdown/plugins';
650
- <DsMarkdown plugins={[katexPlugin]}>์ˆ˜ํ•™ ๊ณต์‹ ๋‚ด์šฉ</DsMarkdown>;
651
- ```
652
-
653
- ### 4. ํƒ€์ž… ์•ˆ์ „์„ฑ
654
-
655
- ```tsx
656
- import { MarkdownCMDRef } from 'ds-markdown';
657
-
658
- const ref = useRef<MarkdownCMDRef>(null);
659
- // ์™„์ „ํ•œ TypeScript ํƒ€์ž… ํžŒํŠธ
660
- ```
661
-
662
- ### ๐Ÿ”„ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์žฌ์‹œ์ž‘ ๋ฐ๋ชจ
663
-
664
- ```tsx
665
- import { useRef, useState } from 'react';
666
- import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
667
-
668
- function RestartDemo() {
669
- const markdownRef = useRef<MarkdownCMDRef>(null);
670
- const [isPlaying, setIsPlaying] = useState(false);
671
- const [hasStarted, setHasStarted] = useState(false);
672
-
673
- const startContent = () => {
674
- markdownRef.current?.clear();
675
- markdownRef.current?.push(
676
- '# ์• ๋‹ˆ๋ฉ”์ด์…˜ ์žฌ์‹œ์ž‘ ๋ฐ๋ชจ\n\n' +
677
- '์ด ์˜ˆ์ œ๋Š” `restart()` ๋ฉ”์„œ๋“œ์˜ ์‚ฌ์šฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค:\n\n' +
678
- '- ๐Ÿ”„ **์žฌ์‹œ์ž‘**: ํ˜„์žฌ ์ฝ˜ํ…์ธ ๋ฅผ ์ฒ˜์Œ๋ถ€ํ„ฐ ์žฌ์ƒ\n' +
679
- '- โธ๏ธ **์ผ์‹œ์ •์ง€/์žฌ๊ฐœ**: ์–ธ์ œ๋“ ์ง€ ์ผ์‹œ์ •์ง€์™€ ์žฌ๊ฐœ ๊ฐ€๋Šฅ\n' +
680
- '- ๐ŸŽฏ **์ •๋ฐ€ ์ œ์–ด**: ์• ๋‹ˆ๋ฉ”์ด์…˜ ์žฌ์ƒ ์ƒํƒœ์˜ ์™„์ „ํ•œ ์ œ์–ด\n\n' +
681
- 'ํ˜„์žฌ ์ƒํƒœ: ' +
682
- (isPlaying ? '์žฌ์ƒ ์ค‘' : '์ผ์‹œ์ •์ง€') +
683
- '\n\n' +
684
- '์ด๋Š” ๋งค์šฐ ์‹ค์šฉ์ ์ธ ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค!',
685
- 'answer',
686
- );
687
- setIsPlaying(true);
688
- };
689
-
690
- const handleStart = () => {
691
- if (hasStarted) {
692
- // ์ด๋ฏธ ์‹œ์ž‘๋œ ๊ฒฝ์šฐ ์žฌ์‹œ์ž‘
693
- markdownRef.current?.restart();
694
- } else {
695
- // ์ฒซ ๋ฒˆ์งธ ์‹œ์ž‘
696
- markdownRef.current?.start();
697
- setHasStarted(true);
698
- }
699
- setIsPlaying(true);
700
- };
701
-
702
- const handleStop = () => {
703
- markdownRef.current?.stop();
704
- setIsPlaying(false);
705
- };
706
-
707
- const handleResume = () => {
708
- markdownRef.current?.resume();
709
- setIsPlaying(true);
710
- };
711
-
712
- const handleRestart = () => {
713
- markdownRef.current?.restart();
714
- setIsPlaying(true);
715
- };
716
-
717
- const handleEnd = () => {
718
- setIsPlaying(false);
719
- };
720
-
721
- return (
722
- <div>
723
- <div style={{ marginBottom: '10px', display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
724
- <button onClick={startContent}>๐Ÿš€ ์ฝ˜ํ…์ธ  ์‹œ์ž‘</button>
725
- <button onClick={handleStart} disabled={isPlaying}>
726
- {hasStarted ? '๐Ÿ”„ ์žฌ์‹œ์ž‘' : 'โ–ถ๏ธ ์‹œ์ž‘'}
727
- </button>
728
- <button onClick={handleStop} disabled={!isPlaying}>
729
- โธ๏ธ ์ผ์‹œ์ •์ง€
730
- </button>
731
- <button onClick={handleResume} disabled={isPlaying}>
732
- โ–ถ๏ธ ์žฌ๊ฐœ
733
- </button>
734
- <button onClick={handleRestart}>๐Ÿ”„ ์žฌ์‹œ์ž‘</button>
735
- </div>
736
-
737
- <div style={{ margin: '10px 0', padding: '10px', background: '#f5f5f5', borderRadius: '4px' }}>
738
- <strong>์• ๋‹ˆ๋ฉ”์ด์…˜ ์ƒํƒœ:</strong> {isPlaying ? '๐ŸŸข ์žฌ์ƒ ์ค‘' : '๐Ÿ”ด ์ผ์‹œ์ •์ง€'}
739
- </div>
740
-
741
- <MarkdownCMD ref={markdownRef} interval={25} onEnd={handleEnd} />
742
- </div>
743
- );
744
- }
745
- ```
746
-
747
- ### โ–ถ๏ธ ์ˆ˜๋™ ์‹œ์ž‘ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ฐ๋ชจ
748
-
749
- ```tsx
750
- import { useRef, useState } from 'react';
751
- import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
752
-
753
- function StartDemo() {
754
- const markdownRef = useRef<MarkdownCMDRef>(null);
755
- const [isPlaying, setIsPlaying] = useState(false);
756
- const [hasStarted, setHasStarted] = useState(false);
757
-
758
- const loadContent = () => {
759
- markdownRef.current?.clear();
760
- markdownRef.current?.push(
761
- '# ์ˆ˜๋™ ์‹œ์ž‘ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ฐ๋ชจ\n\n' +
762
- '์ด ์˜ˆ์ œ๋Š” `start()` ๋ฉ”์„œ๋“œ์˜ ์‚ฌ์šฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค:\n\n' +
763
- '- ๐ŸŽฏ **์ˆ˜๋™ ์ œ์–ด**: `autoStartTyping=false`์ผ ๋•Œ ์ˆ˜๋™์œผ๋กœ `start()`๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค\n' +
764
- '- โฑ๏ธ **์ง€์—ฐ ์‹œ์ž‘**: ์‚ฌ์šฉ์ž ์ƒํ˜ธ์ž‘์šฉ ํ›„ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค\n' +
765
- '- ๐ŸŽฎ **๊ฒŒ์ž„ํ™”**: ์‚ฌ์šฉ์ž์˜ ์ ๊ทน์„ฑ์ด ํ•„์š”ํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค\n\n' +
766
- '"์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์ž‘" ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ํƒ€์ดํ•‘ ํšจ๊ณผ๋ฅผ ์ˆ˜๋™์œผ๋กœ ํŠธ๋ฆฌ๊ฑฐํ•˜์„ธ์š”!',
767
- 'answer',
768
- );
769
- setIsPlaying(false);
770
- };
771
-
772
- const handleStart = () => {
773
- if (hasStarted) {
774
- // ์ด๋ฏธ ์‹œ์ž‘๋œ ๊ฒฝ์šฐ ์žฌ์‹œ์ž‘
775
- markdownRef.current?.restart();
776
- } else {
777
- // ์ฒซ ๋ฒˆ์งธ ์‹œ์ž‘
778
- markdownRef.current?.start();
779
- setHasStarted(true);
780
- }
781
- setIsPlaying(true);
782
- };
783
-
784
- const handleEnd = () => {
785
- setIsPlaying(false);
786
- };
787
-
788
- return (
789
- <div>
790
- <div style={{ marginBottom: '10px', display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
791
- <button onClick={loadContent}>๐Ÿ“ ์ฝ˜ํ…์ธ  ๋กœ๋“œ</button>
792
- <button onClick={handleStart} disabled={isPlaying}>
793
- {hasStarted ? '๐Ÿ”„ ์žฌ์‹œ์ž‘' : 'โ–ถ๏ธ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์ž‘'}
794
- </button>
795
- </div>
796
-
797
- <div style={{ margin: '10px 0', padding: '10px', background: '#f5f5f5', borderRadius: '4px' }}>
798
- <strong>์ƒํƒœ:</strong> {isPlaying ? '๐ŸŸข ์• ๋‹ˆ๋ฉ”์ด์…˜ ์žฌ์ƒ ์ค‘' : '๐Ÿ”ด ์‹œ์ž‘ ๋Œ€๊ธฐ ์ค‘'}
799
- </div>
800
-
801
- <MarkdownCMD ref={markdownRef} interval={30} autoStartTyping={false} onEnd={handleEnd} />
802
- </div>
803
- );
804
- }
805
- ```