๐ ์ด ํ๋ก์ ํธ๋ ๋ง์ interaction๊ณผ ์ ๋๋ฉ์ด์ , 3d ๊ธฐ์ ์ด ํฌํจ๋์ด ์์ต๋๋ค. ๐ฃ VizBeat๋ฅผ ๋ฐฉ๋ฌธํด์ ์ ์ ์ญ๋๊ณผ ๊ฐ๋ฅ์ฑ์ ํ์ธํด์ฃผ์ธ์ ๋ฐ๋ชจ ๋ณด๋ฌ ๊ฐ๊ธฐ
๋ง์ ๋ ธ๋ ฅ๊ณผ ์๊ฐ, ๊ทธ๋ฆฌ๊ณ ์ ์ ์ ๋ด์ ์กธ์ ํ๋ก์ ํธ๋ฅผ ์์ฑํ์ต๋๋ค. ํ๋ก ํธ์๋ ๊ฐ๋ฐ ๋ฟ ์๋๋ผ, ๋์์ธ, UI/UX, ์๋ฒ, ์ธํ๋ผ ๋ชจ๋ ์ง์ ๊ตฌ์ฑํ์ต๋๋ค.
VizBeat๋ ์์์ ๋ณด์ปฌ, ๊ธฐํ, ๋ฒ ์ด์ค, ํผ์๋ ธ, ๋๋ผ์ ์์ ํธ๋์ผ๋ก ๋ถ๋ฆฌํ๋ค ๊ฐ ์์๋ค์ ์ฃผํ์๋ฅผ ์ด์ฉํด ์๊ฐํํ๋ ํ๋ก์ ํธ์ ๋๋ค.
(์์ ๋ถ๋ฆฌ๋ ํ์ด์ฌ ์๋ฒ์์ AI ๋ชจ๋ธ์ ์ด์ฉํ์ฌ ์ฌ๋ฌ ์ ๊ธฐ์ ์์์ผ๋ก ๋ถ๋ฆฌํ์์ต๋๋ค.)
์ผ์ชฝ ์๋จ์ ๋ฎค์ง ํ๋ ์ด ๋ฆฌ์คํธ๋ฅผ ์ด์ฉํ์ฌ ์์ ์ฌ์/์ ์ง, seek bar ๊ตฌํ, ์ด์ ๋ ธ๋ ๋ค์ ๋ ธ๋ ๋ฑ์ ๊ธฐ๋ฅ์ ๊ตฌํํ์์ต๋๋ค. GIF์์ ๋ณผ ์ ์๋ฏ์ด ์์ ์ฌ์ ๋ชฉ๋ก๊ณผ ์ค๋ฅธ์ชฝ์ Three.js ์จ๋ฒ๋ค์ ์ํ๋ฅผ ๊ณต์ ํฉ๋๋ค.
์์ ์ ๋ก๋ | ์ ๋ก๋ ์๋ฃ |
---|---|
์ผ์ชฝ navbar์ ์์ ์ถ๊ฐํ๊ธฐ๋ฅผ ํตํด ์๋ก์ด ์์์ ๋ฑ๋กํ ์ ์์ต๋๋ค. ์์์ ์๋ 2๊ฐ์ง ๋ฐฉ๋ฒ์ ํตํด ์ถ๊ฐ๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
-
์์ ํ์ผ ์ ๋ก๋ ์ฌ์ฉ์๊ฐ ๊ฐ์ง๊ณ ์๋ ์์์ ์ง์ ์ ๋ก๋ํ ์ ์์ต๋๋ค. ์์์ ๋ฉํ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํ์ฌ ์ํฐ์คํธ, ์์ ์ ๋ชฉ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
-
์ ํ๋ธ ๊ฒ์ ์ ํ๋ธ ๊ฒ์ api๋ฅผ ํตํด ์ ํ๋ธ ์์์ ์ฐพ์ ์ ์์ต๋๋ค.
์ ์ ๊ฐ ์ ๋ก๋ ๋๋ ์ ํ๋ธ ๊ฒ์์ผ๋ก ์์์ ์ ํํ๊ณ ์ ์ถํ๋ฉด ์์์ ํ์ด์ฌ ์๋ฒ์ ์ ๋ฌํ๊ณ , ์๋ฒ์์ AI ๋ชจ๋ธ์ ์ด์ฉํ์ฌ ์์์ ๋ถ๋ฆฌํฉ๋๋ค.
์ค์ฌ ์ ๊ธฐ์ค์ผ๋ก cos, sin ํจ์๋ฅผ ํตํ์ฌ ๋์ฌ์์ ํ์ฑํ๊ณ , ๋์ด์ ๋๋น๋ ์์ ์ฃผํ์์ ์ ๊ณฑ์ ๋น๋กํ์ฌ ๋ณํํฉ๋๋ค.
๋ฉํฐ ํธ๋ ํ๋ ์ด์ด์์ ์ง์ํ๋ ๊ธฐ๋ฅ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
-
๋ง์คํฐ ๋ณผ๋ฅจ ๋ฐ ์ฑ๊ธ ๋ณผ๋ฅจ ์กฐ์ , ์ฌ์/์ ์ง
-
์์ ๋ค์ด๋ก๋
์๋ณธ ์์, ๋ถ๋ฆฌ๋ ์์๋ค๊ณผ mixed ์์๊น์ง ๋ค์ด๋ก๋ ํ ์ ์์ต๋๋ค.
VizBeat์์๋ ๋ฌด๊ฑฐ์ด 3d๋ฅผ ๋ง์ด ๋ค๋ฃจ์ด์ผ ํ๋ค ๋ณด๋, ์ฑ๋ฅ ์ต์ ํ๊ฐ ๊ผญ ํ์ํ์ด์
โ WASM์ ์ฌ์ฉํ์ฌ ์์ ์ธ์ฝ๋ฉ ์ฑ๋ฅ์ ๊ฐ์ (JavaScript ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋๋น 20๋ฐฐ ์๋)
๋ฉํฐ ํธ๋ ํ๋ ์ด์ด
์์๋ ์ฌ๋ฌ ๊ฐ์ ์์ ํธ๋๋ค์ ํ๋์ mp3 ํ์ผ๋ก ๋ง๋ค์ด์ ๋ค์ด๋ก๋ ํ๋ ๊ธฐ๋ฅ์ ์ง์ํฉ๋๋ค.
์ฌ๋ฌ Blob ํํ์ ์์์ ํ๋์ mp3๋ก ๋ง๋ค๊ธฐ ์ํด์๋ ์ธ์ฝ๋ฉ ๊ณผ์ ์ด ํ์ํ๋ฐ์, JavaScript library๋ฅผ ์ฌ์ฉํ ์, 3๋ถ ๊ธธ์ด์ ์์ ๊ธฐ์ค 3๋ถ์ ์ธ์ฝ๋ฉ ์๊ฐ์ด ๊ฑธ๋ ค์, ์๋น์ค์์ ์ ํฉํ์ง ์์์ต๋๋ค.
๋ํ, 7๊ฐ ์์ ํ์ผ์(7mb *8 = 56mb) ์๋ฒ์ ๋งค๋ฒ ์ฃผ๊ณ ๋ฐ๊ธฐ๋ ์ ์ ํ์ง ์๋ค๊ณ ํ๋จํ์ฌ์ FFmpeg WASM์ ์ฑํํ์์ต๋๋ค.
FFmpeg๋ ๋ฏธ๋์ด์ ์ค๋์ค ํ์ผ์ ๋ค๋ฃจ๋ ์คํ ์์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๊ณ c๋ก ์์ฑ๋์ด์ ๋น ๋ฅธ ์๋๋ฅผ ๋ณด์ฌ์ค๋๋ค. ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ธ๋ผ์ฐ์ ์์ ๋๋ฆด ์ ์๋๋ก ๋ง๋ ๊ฒ์ด FFmpeg WASM์ ๋๋ค.
-
WASM์ ๋์ ํ๋ฉด์ ์ด๋ฐ ์ด๋ ค์์ด ์์์ด์
import { fetchFile, toBlobURL } from '@ffmpeg/util'; export async function mergeAudios(blobs: Blob[], onProgress: (progress: number) => void) { const { FFmpeg } = await import('@ffmpeg/ffmpeg'); // more code... })
```
ํ๋ก์ ํธ์์๋ Next.js ๋ฅผ ์ฌ์ฉํ๊ณ ์์๊ธฐ ๋๋ฌธ์, WASM์ ์ต์๋จ์์ importํ ์์ ์๋ฒ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ์์ต๋๋ค. ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด dynamic import๋ฅผ ์ฌ์ฉํด์ ํด๋ผ์ด์ธํธ์์๋ง importํ๋๋ก ๋ณ๊ฒฝํ์์ต๋๋ค.
WASM ์ฑ๋ฅ์ธก์ (ํธ๋ 6๊ฐ ๊ธฐ์ค)
| ์์ | ๋ ธ๋ ๊ธธ์ด | JavaScript(audio-encoder) | FFmpeg wasm | | --- | --- | --- | --- | | ์์์๋น ์์ด๋ | 3:34 | 182์ด | 6.5์ด | | Basket case | 3:02 | 170์ด | 6.1์ด |
182์ด โ 6.5์ด, 170์ด โ 6.1์ด๋ก 20๋ฐฐ ์ด์์ ์ฑ๋ฅ ํฅ์์ด ์์์ต๋๋ค.
1๋ฒ ํญ๋ชฉ์์ ์ดํด๋ณด์๋ฏ์ด, ๋ฉํฐ ํธ๋ ํ๋ ์ด์ด์์๋ 7๊ฐ ์์ ํ์ผ์(7mb *8 = 56mb) ์ S3์ ์์ฒญํ๊ฒ ๋๋๋ฐ์
์ฌ์ฉ์๊ฐ ํ์ด์ง๋ฅผ ๋ ๋๋งํ๋ฉด์ ์ค๋์ค๋ฅผ ์์ฒญํ๋๋ฐ, ๋ค์ด๋ก๋ ํ ๋๋ง๋ค ๊ฐ์ ํ์ผ์ ์ค๋ณต์ ์ผ๋ก ์์ฒญํ๊ณ ์์์ต๋๋ค.
๋ํ, ์ด๋ฏธ ๋ฐฉ๋ฌธํ ํ์ด์ง์ ๊ฒฝ์ฐ ๋คํธ์ํฌ ์์ฒญ ๋์ ์ IndexedDB๋ฅผ ์ฌ์ฉํ์ฌ ๋คํธ์ํฌ ๋ณ๋ชฉ์ ์ค์ฌ ํ์ด์ง ๋ ๋๋ง ์๊ฐ์ด ์ค์ด๋ค๊ณ , S3 ์๊ธ๋ ์ค์ผ ์ ์์ต๋๋ค.
-
Local Storage ๋์ ์ IndexedDB๋ฅผ ์ ํํ ์ด์
- Chrome ๊ธฐ์ค์ผ๋ก LocalStorage๋ domain ๋ณ๋ก ์ต๋ 10mb๋ฅผ ์ง์ํฉ๋๋ค. ์ฐ๋ฆฌ๋ 50mb์ด์์ mp3 ํ์ผ์ ์ ์ฅํด์ผ ํจ์ผ๋ก, ์คํ ์ ๋ถ๊ฐ๋ฅํฉ๋๋ค.
๋ฐ๋ฉด, IndexedDB๋ ์ฌ์ฉ์ ๋์คํฌ์ 80%๊น์ง ์ฌ์ฉํ ์ ์์ด์ audio blob์ ์ ์ฅํ๊ธฐ์ ์ ํฉํฉ๋๋ค.
- audio์ blob์ผ๋ก ๊ด๋ฆฌํ๋๋ฐ, string ๋ง ์ ์ฅ๊ฐ๋ฅํ local Storage๋ ์ค๋์ค ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๊ธฐ์ ์ ํฉํ์ง ์์ต๋๋ค.
CSS๋ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์์ ๋ฒ์๋ผ๊ณ ์๊ฐํ๊ณ , ํ๋ก์ ํธ์์ ํ์ํ๋ค๋ฉด ์ ์ ํ ์ ๋๋ฉ์ด์ ๊ณผ ์ ์ ์ธํฐ๋ ์ ์ UI์ ๋ น์ฌ๋ผ ์ ์์ด์ผ ํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
๋๋ฉ ํ์ด์ง์ framer motion๊ณผ scoll ๊ธฐ๋ฐ ์ ๋๋ฉ์ด์ ๋ค์ ๋ง๋ค์ด์ ์๊ฐ์ ์ธ ์ฆ๊ฑฐ์์ ๊ตฌํํ์์ต๋๋ค.
- Headless library๋ฅผ ์ฌ์ฉํ์ด์.
Headelss library๋ ๊ฐ๋ฐ์๊ฐ ์๋น์ค ๋ก์ง์ ์ง์คํ ์ ์๊ฒ ๋ง๋ค๊ณ , ์๊ฒ ์๋น์ค๋ฅผ ๊ฐ๋ฐํ๊ณ ๋น ๋ฅด๊ฒ ๊ฒ์ฆํ ์ ์๊ฒ ์๊ฐํฉ๋๋ค.
Material ui, Chakra ๋ฑ์ ui ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์์ง๋ง, ์ ์ ์๋น์ค๋ UI ํต์ผ์ฑ์ ์ฃผ๊ณ ์ถ์๊ธฐ ๋๋ฌธ์ headless library๋ฅผ ์ฑํํ์ต๋๋ค.
๊ทธ ์ค์์๋ Headless ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ํ ์ ์ด์ ๊ธฐ๋ณธ ์คํ์ผ์ ๊ฐ์ง๊ณ ์๋ shadcn/ui
๋ฅผ ์ ํํ์ต๋๋ค.
โ ๋ธ๋ผ์ฐ์ ๋ฉ์ถค ํ์ ํด๊ฒฐ (PR)
useFrame
์ ๋งค ํ๋ ์๋ง๋ค ๋ด๋ถ ๋ก์ง์ ์คํํ๋ 3d util ๊ธฐ๋ฅ์
๋๋ค. (๋งค ํ๋ ์ 16.6ms ๋ง๋ค ์คํ๋ฉ๋๋ค)
3d ์ค๋ธ์ ํธ์ ์์ง์์ ํจ์ ๋ด๋ถ์์ setState
๋ฅผ ์ด์ฉํ์ฌ position์ ๋ณ๊ฒฝํ๊ณ ์์์ต๋๋ค.
์๋๋ ๋ณ๊ฒฝ ์ ํ ์ฝ๋์ ๋๋ค.
์ธ๋ป ๋ณด๊ธฐ์๋ ๋ฌธ์ ๊ฐ ์๋ ๋ฏ ๋ณด์ด์ง๋ง, useFrame์ ๋งค ํ๋ ์๋ง๋ค ์คํ๋๋๋ฐ์,
์ฆ, setState๋ฅผ ํตํด height๋ฅผ ๋ณ๊ฒฝํ๋ฉด ๋งค frame๋ง๋ค ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ์ฌ ๋ธ๋ผ์ฐ์ ๋ฉ์ถค์ด ๋ฐ์ํฉ๋๋ค.
`before`
useFrame(() => {
setHeight(height - height * 0.2);
})
์์ธ์ ์์๋ด๋ฉด ํด๊ฒฐํ๋ ๊ฒ์ ๊ฐ๋จํฉ๋๋ค. useRef๋ฅผ ํตํ์ฌ height๋ฅผ ๋ณ๊ฒฝํด์ฃผ๋ฉด ๋ฉ๋๋ค.
`after`
useFrame(() => {
heightRef.current = height - height * 0.2;
})
๊ฐ์ ์ => ๊ฐ์ ํ ์ฝ scripting 77%
๊ฐ์
ํ ์คํธ ์ฝ๋๋ ์์ง ๋ฐฐ์์ผ ํ ๋ด์ฉ์ด ๋ง์ง๋ง, ๊ธฐ์ค์ ์ธ์์ ํ ์คํธ๋ฅผ ์์ฑํ๋ ค๊ณ ๋ ธ๋ ฅํ์ต๋๋ค.
- ์ค์ํ ๋ก์ง์ ๋ํด์ ํ ์คํธํ์, ๋๋๋ก ํตํฉํ ์คํธ๋ก
ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ๋ ๊ฒ๋ ๋ฆฌ์์ค์ด๊ธฐ ๋๋ฌธ์ ์ค์ํ๊ณ ๊ฒ์ฆํ๊ธฐ ํ๋ ๋ก์ง์ ๋ํด์ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํด์ผํ๊ณ , ํ๋์ ํ ์คํธ๊ฐ ์ ์ ํ ๋ฒ์๋ฅผ ์ปค๋ฒํด์ผ ํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
๋ํ, `์ด๋ค ํจ์๊ฐ ํธ์ถ๋๋๋`๋ณด๋ค `์ด๋ค ์ผ์ด ๋ฐ์ํ๋์ง` ๋ฅผ ํ
์คํธ ํด์ผํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
ํจ์๊ฐ ๋ช ๋ฒ ํธ์ถ๋์๋์ง๋ณด๋ค ์ ์ ๊ฐ A๋ผ๋ ํ๋์ ํ์ ๋์ B๋ผ๋ ๊ฒฐ๊ณผ๋ฅผ ๊ฒ์ฆํ๋ ๊ฒ์ด ๋์ ํ
์คํธ ๋ฒ์๋ฅผ ๊ฐ์ง ์ ์๋ค๊ณ ์๊ฐํฉ๋๋ค.
Kent์ ํ ์คํธ ๊ฐ์ด๋๋ฅผ ํตํด ํ ์คํธ์ ์ง์์ ๋ฐฐ์ธ ์ ์์์ต๋๋ค.
ํ๋ก์ ํธ๋ฅผ ํ๊ณ ํ๋ ์์ ์์ ๋์ด์ผ๋ณด๋ฉด ์ ๋ง ๋ง์ ์ฑ์ฅ๊ณผ ๋ค์ํ ์๋๋ฅผ ํด๋ดค๋ ๊ฒ ๊ฐ์์.
์กธ์ ํ๋ก์ ํธ ์ฃผ์ ๋ฅผ ์ ์ ํ ๋, 2๊ฐ์ง ๊ธฐ์ค์ ์ธ์ ์ด์
-
6๊ฐ์ ์ด์ ๋ชฐ์ ํ ์ ์๋ ์ฃผ์ ์ธ๊ฐ?
ํ๋ก์ ํธ์ ๊ดํด ์๋ ์ง๋ฌธ์ ๋ฐ์ ์ ์ด ์์ด์.
์ด ํ๋ก์ ํธ๋ ์ด๋ค ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ณ ์์ด?
BM์ด ์ด๋ป๊ฒ๋ผ?
> ๋ฌผ๋ก ์ค์ํ ์ง๋ฌธ์ด์ง๋ง ์ ๊ฐ ์จ์ ํ 6๊ฐ์ ์ด์ ๋ชฐ์
ํ ์ ์๋ ์ฃผ์ ์ฌ์ผ ํ์ด์
- ๊ธฐ์ ์ ์ญ๋๊ณผ ์ ์ฌ๋ ฅ์ ๋ณด์ฌ์ค ์ ์๋ ํ๋ก์ ํธ์ธ๊ฐ?
์ด๋์ฑ๋์ ๊ธ "๊ทธ ์ฐ์ฐจ์น๊ณค ์ํ๋ค"์ ํจ์ ์ ์ฝ์ผ๋ฉด์ ๊ต์ฅํ ๊ณต๊ฐ๊ฐ๋๋ฐ์,
์ ๋ ์ฃผ๋์ด ๊ฐ๋ฐ์ ์น๊ณ ์ ๋ง๋ ํ๋ก์ ํธ
๊ฐ ์๋ ์ ๋ง๋ ํ๋ก์ ํธ
๋ฅผ ๋ง๋ค๊ณ ์ถ์์ต๋๋ค.
์์ฅ์ ์ฃผ๋์ด ๊ฐ๋ฐ์๋ค์ด ๋ง์์ก๊ณ , ๊ทธ ์ค์์ ๋์ ๋๊ธฐ ์ํด์ ๋์ ํ๋ฆฌํฐ์ ํ๋ก์ ํธ๋ฅผ ๋ง๋ค๊ณ ์ ํ์ด์
์์ฑ๋ ๊ฒฐ๊ณผ๋ฌผ์ ๋ดค์ ๋ ๊ธฐ๋ณธ๊ธฐ์ ํธ๋ ๋ ์ฌ์ด์์ ์ ์ ํ ๊ท ํ์ ๋ง์ถ๋ฉด์ ๋ค์ํ ์๋๋ฅผ ํ๋ฉด์ ์ฑ์ฅํ ์ ์์๊ณ , ๋ง์กฑ์ค๋ฌ์ด ๊ฒฐ๊ณผ๋ฌผ์ ์ป์ ์ ์์์ต๋๋ค.
VizBeat
ํ๋ก์ ํธ ํ๊ณ ์์ ์คํ์์ค ๊ธฐ์ฌ๋ฅผ ๋นผ๋์ ์๊ฐ ์์ ๊ฒ ๊ฐ์์
์คํ์์ค ๊ธฐ์ฌ๋ ์ ์ ๊ฑฐ๋ฆฌ๊ฐ ๋จผ ์ด์ผ๊ธฐ๋ผ๊ณ ์๊ฐํ๊ณ , ๋ช ๋ ํ์ ์ค๋ ฅ์ ํค์์ ์๋ํด๋ณด์๊ณ ์๊ฐํ์ด์
๊ฒฐ๊ณผ์ ์ผ๋ก, Vizbeat๋ฅผ ์งํํ๋ฉด์ 3๊ฐ์ pull request๋ฅผ ํตํด ์คํ์์ค์ ๊ธฐ์ฌํ์ด์
์ ๊ฐ ์ฌ์ฉํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ฌธ์ ๋ฅผ ๋ฐ๊ฒฌํ๊ณ , ํ์ํ ๊ธฐ๋ฅ์ ์ง์ ๊ตฌํํ์ฌ ํด๊ฒฐํ๊ธฐ ๋๋ฌธ์ ๋์ฑ ๋ป ๊น์์ต๋๋ค.
๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ์์ ์ฝ๋์ ๋ํ ์๋ก์ ์๊ฐ์ ๋๋๊ณ PR์ ์์ฑํด๋๊ฐ๋ ๊ฒ์ ํนํ ์ฆ๊ฑฐ์ด ๊ฒฝํ์ด์์ต๋๋ค๐
์ฌํด 2์์ ์น๊ตฌ์ ํจ๊ป ์กธ์ ํ๋ก์ ํธ๋ฅผ ์์ํ์๋๋ฐ, ๋ฐ๋ ์ด ์ง๋ ํ๋ก์ ํธ๋ฅผ ๋ค ๋ง๋ค ์ ์์์ต๋๋ค. ํ๊ต, ๊ฐ๋ฐ ๋์๋ฆฌ, ์ธํด์ ๋ณํํ๋ฉด์ ๊ฝค ๋ฐ์ 4ํ๋ ์ ๋ณด๋ธ ๊ฒ ๊ฐ์ต๋๋ค. ๋น์ฅ ์ด๋ฒ ๋ฌ๋ถํฐ ์ทจ์ ์์ฅ์ ๋ฐ์ด๋ค ์๊ฐ์ ํ๋ ๋จ๋ฆฌ๊ธฐ๋ ํ๊ณ , ๊ฑฑ์ ๋ ๋์ง๋ง, ๊ฐ์ด ๋ฐ๋ ์ผ์ ํ๊ณ ์ถ์ต๋๋ค.
https://jojoldu.tistory.com/68 https://kentcdodds.com/blog/write-tests https://all-dev-kang.tistory.com/entry/Nextjs-๋ถ๋ฆฌ๋-์์์-๋ค์-ํ๋๋ก-Web-Assembly-web-worker