์ ๋ ํ์ฌ์์ ์ฃผ๋ก TypeScript๋ก Node.js ํ๊ฒฝ์์ ๊ฐ๋ฐ์ ํ๊ณ ์์ต๋๋ค. ๋๋์ ์์
๋ฐ์ดํฐ๋ฅผ ์
๋ก๋ํด์ bulk ์ฒ๋ฆฌํ๋ ๊ธฐ๋ฅ์ด ํ์ํ๊ณ , ๊ทธ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๊ธฐ ์ํ ํ
ํ๋ฆฟ ์์
ํ์ผ๋ ๋์ ์ผ๋ก ์์ฑํด์ผ ํ์ต๋๋ค. ํ
ํ๋ฆฟ ์์๋ ๋ฐ์ดํฐ ์ ํจ์ฑ ๊ฒ์ฌ์ ์กฐ๊ฑด๋ถ ์์, ๋๋กญ๋ค์ด ๊ฐ์ ๊ธฐ๋ฅ์ด ๋ค์ด๊ฐ์ผ ํ๊ณ ์.
๊ธฐ์กด์ ์ฐ๋ Node.js ์์
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ๊ฐ์ ํ๊ณ๊ฐ ์์์ต๋๋ค. ํ๋๋ ์ปค๋ฎค๋ํฐ ๋ฒ์ ๊ณผ ์ ๋ฃ ๋ฒ์ ์ด ๋ถ๋ฆฌ๋์ด ์์ด ๊ธฐ๋ฅ ์ ์ฝ์ด ์์ฌ์ ์ต๋๋ค. ๋ค๋ฅธ ํ๋๋ ๋ด๋ถ ๊ตฌํ๊ณผ TypeScript ํ์ดํ ์ฌ์ด์ ๊ดด๋ฆฌ๊ฐ ์์๊ณ , ์ฑ๋ฅ ๋ฌธ์ ๋ก ์ธํด ์ํ๋ ์์
์ ๋น ๋ฅด๊ฒ ์ฒ๋ฆฌํ๊ธฐ ์ด๋ ค์ ์ต๋๋ค. ์ ์ฅ์์๋ PR์ด ์์ฌ ์์์ง๋ง ๋ ์ด์ ์
๋ฐ์ดํธ๋์ง ์๋ ์ํ์์ต๋๋ค.
ํ์์ ์๊ณ ์๋ Go ์ํ๊ณ์ Excelize ํ๋ก์ ํธ๋ฅผ ๋ค์ ๋ค์ฌ๋ค๋ณด์์ต๋๋ค. ์ฐจํธ, ์กฐ๊ฑด๋ถ ์์, ์์, ๋ฐ์ดํฐ ๊ฒ์ฆ์ฒ๋ผ OOXML ์คํ์ ํฐ ๊ธฐ๋ฅ๋ค์ ์ ๊ตฌํํด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ต๋๋ค. Excelize๋ฅผ ๋ณด๋ฉด์ ์ด ์ ๋์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ TypeScript์์๋ ์ธ ์ ์์ผ๋ฉด ์ข๊ฒ ๋ค๋ ์๊ฐ์ด ๋ค์์ต๋๋ค.
์ฝ๋ฉ ์์ด์ ํธ๋ค์ ์ญ๋์ ๊ณ์ ์ข์์ง๊ณ ์๋ค๋ ๊ฐ๊ฐ์ ๊พธ์คํ ๋๊ผ๊ณ , ์ ๊ฐ ์ค๊ณ์ ์์ฌ๊ฒฐ์ ์ ํ๋ ๋ชจ๋ ๊ตฌํ์ ์์ด์ ํธ์๊ฒ ์์ํ๋ ๋ฐฉ์์ผ๋ก ๋ง๋ค์ด๋ณด๋ฉด ์ด๋จ๊นํ๋ ์๊ฐ์ด ๋ค์์ต๋๋ค. ์ ๋ ์ง๋์ฃผ ์์์ผ(2์ 4์ผ)์ Excelize์ ์ฌ๋ฌ ์์
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ๊ธฐ๋ฅ ๋ชฉ๋ก๊ณผ ๊ตฌํ ๋ฐฉ์์ ๋ถ์ํ๊ณ , ์ง๋์ฃผ ํ ์์ผ(7์ผ) ๋ฐค๋ถํฐ ์ค์ ์ฝ๋ ์์ฑ์ ๋ค์ด๊ฐ์ต๋๋ค.
๊ทธ๋ ๊ฒ SheetKit์ ๋ง๋ค์์ต๋๋ค.
- ์ ์ฅ์
- ๋ฌธ์ (Getting Started)
- ๋ฒค์น๋งํฌ ๊ฒฐ๊ณผ (์คํ ํ๊ฒฝ, ์คํ ๋ฐฉ๋ฒ, ํฝ์ค์ฒ ํฌํจ)
- Node.js ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋น๊ต
- Rust ๋น๊ต
- ํฝ์ค์ฒ ์ ์
์ด ๊ธ์ ๋ ํํธ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค. ์ง๊ธ ์ฝ๊ณ ๊ณ์ ๊ธ์์๋ SheetKit ์๊ฐ์ ํจ๊ป, ์ฒซ ๋ฆด๋ฆฌ์ฆ๋ถํฐ ์ค๋(2์ 14์ผ) ๋ฐฐํฌํ v0.5.0๊น์ง 1์ฃผ์ผ๊ฐ์ ๊ฐ๋ฐ ๊ณผ์ ์ ์๊ฐ์์ผ๋ก ๊ธฐ๋กํฉ๋๋ค. ๋ค์ ๊ธ์์๋ ์ฝ๋ฉ ์์ด์ ํธ์ ์ด๋ป๊ฒ ํ์
ํ๋์ง, ์ด๋ค ์์
์์ ์ฌ๋์ด ํ๋จํด์ผ ํ๋์ง ๊ฐ์ ์ด์ผ๊ธฐ๋ฅผ ๋ ๊ตฌ์ฒด์ ์ผ๋ก ๋ค๋ฃฐ ์์ ์
๋๋ค.
์ผ์ฃผ์ผ๊ฐ์ ๋ฆด๋ฆฌ์ฆ ํ์๋ผ์ธ
ํ์ ๋ ์ง๋ crates.io์ npm ๋ฐฐํฌ๋ฅผ ๊ธฐ์ค์
๋๋ค. (์ ํํ ํ์์คํฌํ๋ณด๋ค "๋ฌด์จ ์ผ์ด ์ธ์ ์์๋์ง"๋ฅผ ๋ณด์ฌ์ฃผ๊ธฐ ์ํ ํ์
๋๋ค.)
| ๋ฒ์ |
์์ (์๋) |
๋ ์ง |
ํต์ฌ |
| v0.1.0 |
์ผ์์ผ(์ง๋์ฃผ) |
2026-02-08 |
์ฒซ ๋ฐฐํฌ(์ด๊ธฐ ํํ) |
| v0.1.2 |
์์์ผ ์๋ฒฝ(์ง๋์ฃผ) |
2026-02-09 |
์ฒซ ๊ณต๊ฐ๋ก ๋ถ๋ฅผ ๋งํ ์ค๋
์ท |
| v0.2.0 |
์์์ผ ์์นจ(์ง๋์ฃผ) |
2026-02-09 |
Buffer I/O, ์์ ํฌํผ |
| v0.3.0 |
ํ์์ผ ์๋ฒฝ(์ง๋์ฃผ) |
2026-02-10 |
raw buffer FFI, ๋ฐฐ์น API, ๋ฒค์น๋งํฌ ๊ตฌ์ถ |
| v0.4.0 |
ํ์์ผ ์คํ(์ง๋์ฃผ) |
2026-02-10 |
๊ธฐ๋ฅ ํ์ฅ + ๋ฌธ์ ์ฌ์ดํธ |
| v0.5.0 |
ํ ์์ผ ์์นจ(์ค๋) |
2026-02-14 |
lazy loading / stream, COW save, ๋ฒค์น๋งํฌ ๋ฃฐ ๊ฐ์ |
SheetKit์ ์ด๋ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ๊ฐ์?
SheetKit์ Rust๋ก ์์ฑ๋ ์คํ๋ ๋์ํธ(.xlsx, .xlsm ๋ฑ์ OOXML ๊ท๊ฒฉ) ๋ผ์ด๋ธ๋ฌ๋ฆฌ์
๋๋ค. Rust ์ฝ์ด ์์ napi-rs ๊ธฐ๋ฐ Node.js ๋ฐ์ธ๋ฉ์ด ์ฌ๋ผ๊ฐ๋ ๊ตฌ์กฐ์ด๊ณ , Bun๊ณผ Deno์ ๊ฐ์ด Node-API๋ฅผ ์ง์ํ๋ ๋ค๋ฅธ ๋ฐํ์์์๋ ๊ทธ๋๋ก ์ธ ์ ์์ต๋๋ค.
์ด๋ฐ ํ์ผ๋ค์ OOXML(Office Open XML) ํ์์ผ๋ก ๋ด๋ถ์ ์ผ๋ก ZIP ์์นด์ด๋ธ ์์ XML ํํธ๋ค์ด ๋ค์ด ์๋ ๊ตฌ์กฐ์
๋๋ค. SheetKit์ ์ด ZIP ํ์ผ์ ์ด์ด์ ๊ฐ XML ํํธ๋ฅผ Rust ๊ตฌ์กฐ์ฒด๋ก ์ญ์ง๋ ฌํํ๊ณ , ์กฐ์ํ ๋ค ๋ค์ ์ง๋ ฌํํด์ ์ ์ฅํฉ๋๋ค.
Rust ์ชฝ์ ์ธ ๊ฐ์ crate์ผ๋ก ๋๋ฉ๋๋ค.
sheetkit-xml: OOXML ์คํค๋ง์ ๋์ํ๋ ์ ์์ค XML ๋ฐ์ดํฐ ๊ตฌ์กฐ
sheetkit-core: ๋ชจ๋ ๋น์ฆ๋์ค ๋ก์ง
sheetkit: ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ์๋ฅผ ์ํ facade crate
Node.js ๋ฐ์ธ๋ฉ์ packages/sheetkit์ ์๊ณ , #[napi] ๋งคํฌ๋ก๋ก Rust API๋ฅผ JavaScript์ ๋
ธ์ถํฉ๋๋ค.
๋ฐ๋ก ์จ๋ณด๊ณ ์ถ๋ค๋ฉด Getting Started ๋ฌธ์๊ฐ ๊ฐ์ฅ ๋น ๋ฆ
๋๋ค.
ํ ์์ผ ๋ฐค๋ถํฐ ์ฒซ ๋ฐฐํฌ๊น์ง (v0.1.x)
์ง๋์ฃผ ํ ์์ผ(2์ 7์ผ) ๋ฐค์ ์ฝ๋ ์์ฑ์ ์์ํ๊ณ , ๋ค์ ๋ ์ ์ฒซ ๋ฐฐํฌ(v0.1.0)๋ฅผ ์ฐ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ 9์ผ ์์์ผ ์๋ฒฝ์๋ "์ด ์ ๋๋ฉด ์ฐ์ ๊ณต๊ฐํด๋ณผ ์ ์๊ฒ ๋ค" ์ถ์ MVP(v0.1.2)๋ฅผ ๋ง๋ค์์ต๋๋ค.
์์์ผ์ OOXML ์คํ๊ณผ ๊ธฐ์กด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ๊ธฐ๋ฅ ๊ตฌํ ๋ฐฉ์์ ๋จผ์ ๋ถ์ํ๊ณ ๊ณํ์ ์์ธํ ์ธ์ ์ฝ๋ฉ ์์ด์ ํธ์๊ฒ ์์
์ ์์ํด์ ์งง์ ์๊ฐ ์์ ํํ๋ฅผ ์ก์ ์ ์์์ต๋๋ค.
์์
๋ฐฉ์์ ๋จ์ํ์ต๋๋ค.
- ์ค๊ณ์ ๋ํ ํ๋จ๊ณผ ๊ตฌ์กฐ์ ๋ํ ๊ฒฐ์ ์ ์ ๊ฐ ํฉ๋๋ค. ๊ทธ ๊ณผ์ ์์ ํ์ํ ๋ถ์ ์์
์๋ Claude Code, Codex ๋ฑ๊ณผ ๊ฐ์ ์ฝ๋ฉ ์์ด์ ํธ์ ๋์์ ๋ฐ์์ต๋๋ค.
- ๊ตฌํ์ ์ฝ๋ฉ ์์ด์ ํธ์๊ฒ ์ ์ ์ผ๋ก ์์ํฉ๋๋ค. ๊ตฌํ ์ ์ ํ๋์ ๋งค์ฐ ์์ธํ๊ฒ ์ธ์ด ๋ค, ๋ฉ์ธ ์์ด์ ํธ๋ ์ง์ ์์
์ ํ๋ ๊ฒ์ด ์๋๋ผ Orchestrator ์์ด์ ํธ๋ก์ ๊ฐ ๊ธฐ๋ฅ ํํธ๋ง๋ค ์๋ธ ์์ด์ ํธ๋ฅผ ๋ณ๋ ฌ๋ก ๋๋ฆฌ๊ณ ๊ด๋ฆฌํฉ๋๋ค.
- ๊ตฌํ์ด ๋๋๋ฉด ๋ณ๋์ ์์ด์ ํธ๋ฅผ ํตํด ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ๊ฑฐ์น ๋ค์, ์ ๊ฐ ์ง์ ํ์ธํฉ๋๋ค.
๋ค์ ๊ธ์์๋ ์ด ๋ฐฉ์์ด ์ค์ ๋ก ์ด๋ ์ง์ ์์ ์ ๋จนํ๊ณ , ์ด๋ ์ง์ ์์ ์ฌ๋์ด ๊ฐ์
ํด์ผ ํ๋์ง๋ฅผ ๋ ๊ตฌ์ฒด์ ์ผ๋ก ์ ์ ์์ ์
๋๋ค.
์์์ผ: ์ฑ๋ฅ์ ์๊ฐํ๊ธฐ ์์ํ๋ค (v0.2.0 ~ v0.3.0)
Buffer I/O (v0.2.0)
์ฒซ ๋ฆด๋ฆฌ์ฆ ์ดํ ์ผ๋ง ์ง๋์ง ์์ ์์์ผ(9์ผ) ์์นจ์ v0.2.0์ ์ฌ๋ ธ์ต๋๋ค.
ํต์ฌ์ Buffer I/O์์ต๋๋ค. ํ์ผ ๊ฒฝ๋ก ์์ด ๋ฉ๋ชจ๋ฆฌ ์์ ๋ฒํผ๋ก .xlsx๋ฅผ ์ฝ๊ณ ์ธ ์ ์๊ฒ ํ๋ ๊ธฐ๋ฅ์
๋๋ค. ์๋ฒ ํ๊ฒฝ์์๋ ํ์ผ ์์คํ
์ ๊ฑฐ์น์ง ์๊ณ HTTP ์์ฒญ์ ๋ฐ์ด๋๋ฆฌ๋ฅผ ๋ฐ๋ก ์ฒ๋ฆฌํ๊ฑฐ๋, ์์ฑ๋ ์์
์ ๋ฐ๋ก ์๋ต์ผ๋ก ๋ด๋ ค์ค์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. fill_formula ๊ฐ์ ์์ ํฌํผ๋ ์ด๋ ํจ๊ป ๋ฃ์์ต๋๋ค.
Buffer I/O๋ฅผ ๋ถ์ด๊ณ ๋์๋ถํฐ "์ค์ ์๋น์ค์์ ํ๋ ์ผ"๊ณผ ๋น์ทํ ์๋๋ฆฌ์ค ํ
์คํธ๋ฅผ ๋๋ฆฌ๊ธฐ ์์ํ๊ณ , ์ฌ๊ธฐ์ ์ง์ง ๋ณ๋ชฉ์ ๋ง๋ฌ์ต๋๋ค.
napi ๊ฒฝ๊ณ์ ์ค๋ฒํค๋๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด Raw Buffer๋ก ์ ํ (v0.3.0)
์ด๊ธฐ์๋ ์
๋จ์๋ก ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด Rust์ ์๋ฐ์คํฌ๋ฆฝํธ ๊ฒฝ๊ณ๋ฅผ ๋๊ธฐ๋ ํํ๋ก ์์ํ์ต๋๋ค. 50,000ํ x 20์ด ๊ฐ์ ํ์ผ์ "ํ ๋จ์ ๋ฐฐ์ด"๋ก ํ ๋ฒ์ ๊บผ๋ด์ค๋ฉด, ๋น์ฐํ์ง๋ง ์์ฃผ ๋ง์ ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ์ฒด๊ฐ ๋ง๋ค์ด์ง๋๋ค. ์ด ๊ตฌ์กฐ๋ GC ์๋ ฅ๊ณผ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ๋น ๋ฅด๊ฒ ์ฌ๋ฆฝ๋๋ค.
oxc ํ๋ก์ ํธ์์ ํจ์จ์ ์ผ๋ก Rust๋ก ๋น ๋ฅด๊ฒ AST ๋ฐ์ดํฐ๋ฅผ ์๋ฐ์คํฌ๋ฆฝํธ ์์ญ์ผ๋ก ์ ๋ฌํ๋ ๋ฐฉ์์์ ์๊ฐ์ ๋ฐ์์ ๋ค์๊ณผ ๊ฐ์ด ๋ฐฉํฅ์ ๋ฐ๊ฟจ์ต๋๋ค. (์ฐธ๊ณ ๋ฌธ์)
- ์
๋จ์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ง ์์ต๋๋ค.
- ์ ์ฒด ์ํธ๋ฅผ compact binary buffer๋ก ์ง๋ ฌํํฉ๋๋ค.
- FFI ๊ฒฝ๊ณ๋ฅผ "ํ ๋ฒ๋ง" ๋๊น๋๋ค.
๋ํ ์ด ๋ฐฉ์์๋ ์ํธ ๋ด ์
์ด ์ผ๋ง๋ ์๋์ง์ ๋ฐ๋ผ dense / sparse ๋ ์ด์์์ ์๋์ผ๋ก ์ ํํ๋ ๋ฐฉ์๋ ๊ฐ์ด ๋ค์ด๊ฐ์ต๋๋ค. Buffer๋ฅผ ๊ทธ๋๋ก ์ฃผ๊ณ ๋ฐ๊ธฐ ๋๋ฌธ์ TypeScript๋ก๋ ํ๋ฒ ๋ ๋ฒํผ ๊ท๊ฒฉ์ ๋ง๋ ํ์๋ฅผ ์์ฑํ์์ต๋๋ค.
v0.3.0์์ ์ฒซ ๋ฒ์งธ ๋ฒ์ ์ ๋ฒํผ ํฌ๋งท์ ๊ตฌํํ๊ณ , ์ดํ v0.5.0์์ ์ง์ฐ ๋ก๋ฉ๊ณผ ์ธ๋ผ์ธ string์ ์ง์ํ๋ ์๋ก์ด ํฌ๋งท์ผ๋ก ๊ฐ์ ํ์ต๋๋ค.
๋ํ Rust์ XML์ ์ฒ๋ฆฌํ๋ ๋ ์ด์ด์์๋ ํจ๊ป ์์ ํ ๊ฒ๋ค์ด ์์ต๋๋ค. "ํ ํ ๋น์ ์ค์ด๊ณ , ์์ฃผ ์ ๊ทผํ๋ ๊ฒฝ๋ก๋ฅผ ๋จ์ํ๊ฒ ๋ง๋ ๋ค"๊ฐ ๊ธฐ์ค์ด์์ต๋๋ค.
| ๋ณ๊ฒฝ |
์ด์ |
| ์
๋ ํผ๋ฐ์ค("A1")๋ฅผ String ๋์ ๊ณ ์ ๊ธธ์ด ์ธ๋ผ์ธ ๋ฐฐ์ด๋ก ์ ์ฅ |
์
๋ ํผ๋ฐ์ค๋ ์ต๋๊ฐ์ด ์ ํด์ ธ ์์ด์ ํ์ ์ฐ์ง ์์๋ ๋ฉ๋๋ค |
| ํ์
๋ฌธ์์ด์ 1๋ฐ์ดํธ ํ๊ทธ๋ก ์ ๊ทํ |
XML ์์ฑ ๋ฌธ์์ด์ ๊ทธ๋๋ก ๋ค๊ณ ๋ค๋์ง ์๊ฒ ํฉ๋๋ค |
| ํ ๋ด ์
๊ฒ์์ ์ ํ ํ์์์ ์ด์ง ํ์์ผ๋ก ์ ํ |
์ ๊ทผ ๋น์ฉ์ ์ค์
๋๋ค |
| ์งํ |
๋ณ๊ฒฝ ์ |
๋ณ๊ฒฝ ํ |
| 100kํ ๊ธฐ์ค ๋ฉ๋ชจ๋ฆฌ (RSS) |
361MB |
13.5MB |
| Node.js ์ฝ๊ธฐ ์ค๋ฒํค๋ (Rust ๋ค์ดํฐ๋ธ ๋๋น) |
โ |
~4% |
| GC ์๋ ฅ |
100๋ง+ ๊ฐ์ฒด ์์ฑ |
๋จ์ผ Buffer ์ ์ก |
๋ฒค์น๋งํฌ๋ฅผ ๋ง๋ค๊ณ ์ซ์๋ฅผ ๊ณ ์ ํ๊ธฐ
์ด ์์ ์ ๋ฒค์น๋งํฌ ์ค์ํธ๋ฅผ ๋ง๋ค์์ต๋๋ค. Node.js์ Rust ์ํ๊ณ์ ๊ธฐ์กด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค๊ณผ ์ฑ๋ฅ ์งํ๋ฅผ ๋น๊ตํ๋ ๋ฒค์น๋งํฌ์
๋๋ค. ๋ฒค์น๋งํฌ ํ๋ก๊ทธ๋จ์ ์คํํ๋ฉด ๊ฒฐ๊ณผ๋ฅผ ๋งํฌ๋ค์ด ๋ฌธ์๋ก ์๋์ผ๋ก ์ถ๋ ฅํด์ฃผ๋ฉฐ ์ฌ๊ธฐ์๋ ์คํ ํ๊ฒฝ, ๋ฐ๋ณต ํ์ ๋ฑ์ ๊ฐ์ด ์ ๋ฆฌํด๋์์ต๋๋ค.
- Node.js์ Rust์์์ ๋น๊ต(์ค์๊ฐ, 1 warmup + 5 runs): Apple M4 Pro, 24GB RAM / Node v25.3.0 / Rust 1.93.0
- ํ
์คํธ๋ฅผ ์ํ ์คํ๋ ๋์ํธ ํ์ผ์ ํฝ์ค์ฒ๋ ๊ฒฐ์ ๋ก ์ ์ผ๋ก ์์ฑ๋๊ณ ํ ์์๋ ํค๋ ํ์ด ํฌํจ๋ฉ๋๋ค
- RSS/heapUsed๋ ํผํฌ(peak) ๊ฐ์ด ์๋๋ผ ์์
์ ํ์ ์๋ฅ(residual) ๋ธํ ๊ฐ์
๋๋ค
50,000ํ x 20์ด ์คํ๋ ๋์ํธ ํ์ผ์ ๊ธฐ์ค์ผ๋ก Node.js ๋ฐ์ธ๋ฉ์์ ๊ธฐ๋ณธ ์ฝ๊ธฐ(getRows())๋ 541ms, ์ฐ๊ธฐ๋ 469ms๊ฐ ์์๋์์ต๋๋ค. ๊ฐ์ ์ํฌ๋ก๋์์ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์์ฑ๋ ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ฝ๊ธฐ์ 1.24 ~ 1.56์ด, ์ฐ๊ธฐ์ 1.09 ~ 2.62์ด๊ฐ ์์๋์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ฐ์ ์ ๊ฑฐ์น๋ฉด์ heapUsed ์ฆ๊ฐ๋ถ์ด 0MB๋ก ์ฐํ๋ ํํ๋ฅผ ๋ง๋ค ์ ์์ด์ ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ์ฒด๋ฅผ ์์ง ์๋๋ค๋ ๋ชฉํ๊ฐ ๊ฒฐ๊ณผ๋ก ํ์ธํ ์ ์์์ต๋๋ค.
๊ฒฐ๊ตญ ์ค์ํ ๊ฑด ์ฑ๋ฅ์ ์ธก์ ํ๊ณ , ์์น๋ฅผ ๋น๊ตํ๊ณ , ์ด์ํ ์ ์ด ๋ณด์ด๋ฉด ์์ธ์ ๋๊น์ง ์ถ์ ํ๋ ๊ณผ์ ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค. ๋ฒค์น๋งํฌ๋ฅผ ๋๋ ธ์ ๋ Rust ์ํ๊ณ์ ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ(edit-xlsx)๊ฐ ์ฝ๊ธฐ์์ ์ด์ํ๊ฒ ๋น ๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ์ฃผ์๋๋ฐ, ์ด ์์ ์์๋ ์์ธ์ ์ ์ ์์์ต๋๋ค. ๋์ค์ v0.5.0 ์์
์ค ์ ํํ ์์ธ์ ํ์
ํ๊ฒ ๋๋๋ฐ, ์ด ์ด์ผ๊ธฐ๋ ํด๋น ์น์
์์ ๋ค๋ฃจ๊ฒ ์ต๋๋ค.
ํ์์ผ: ๋น ๋ฅด๊ฒ ๊ธฐ๋ฅ ๊ฒฉ์ฐจ๋ฅผ ์ค์ด๋ค (v0.4.0)
ํ์์ผ(2์ 10์ผ)์๋ v0.4.0์ ์ฌ๋ ธ์ต๋๋ค. ์ด ๋ฆด๋ฆฌ์ฆ๋ ์ฑ๋ฅ๋ณด๋ค ๋ถ์กฑํ ๊ธฐ๋ฅ์ ์ฑ์ฐ๋ ๊ฒ์ ๋ชฉํํ์ต๋๋ค.
๋ค๋ฅธ ์์
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์๋ ์์ง๋ง SheetKit์ ์๋ ๊ธฐ๋ฅ์ด ๋ฌด์์ธ์ง ๋น๊ตํ๊ณ OOXML ์คํ๊ณผ ๊ธฐ๋ ๋์์ ๋ค์ ์ ๋ฆฌํ์ต๋๋ค. ๋ํ, ์ฌ๋ผ์ด์, ์์ ์ปจํธ๋กค, ๋ฉ๋ชจ, VBA ์ถ์ถ, CLI ๊ฐ์ ๊ธฐ๋ฅ์ ์ด๋ ํ๊บผ๋ฒ์ ๋ถ์์ต๋๋ค. ์์ ํจ์๋ ์ถ๊ฐ๋ก ๋๋ ธ์ต๋๋ค.
๋ฉ๋ชจ๋ฆฌ ์ต์ ํ๋ ๊ณ์๋์์ต๋๋ค. ์
๊ตฌ์กฐ์ฒด์ SST(Shared Strings Table)์ ๋ฉ๋ชจ๋ฆฌ ๋ ์ด์์์ ๊ฐ์ ํด์, Node.js์์ ๋๊ธฐ API๋ก ์ฝ์์ ๋ ๊ธฐ์ค RSS(Resident Set Size)๊ฐ 349MB์์ 195MB๋ก 44% ๊ฐ์ํ์ต๋๋ค. ๋น๋๊ธฐ ์ฝ๊ธฐ์์๋ RSS๊ฐ 17MB๊น์ง ๋ด๋ ค๊ฐ์ต๋๋ค.
์ด ์์ ์์ ๋ฌธ์๋ ์น ํ์ด์ง๋ก ๊ด๋ฆฌํ๊ณ ์ถ๋ค๋ ์๊ฐ์ด ๋ค์ด์ VitePress๋ฅผ ํ์ฉํด์ ๋ง๋ค๊ฒ ๋์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ค๋๊น์ง: ๊ตฌ์กฐ๋ฅผ ๋ค์ ์๊ฐํ๋ค (v0.5.0)
์ด ๊ธ์ ์ฐ๋ 2์ 14์ผ ์ค๋ ์ ๋
์ v0.5.0์ ๋ฆด๋ฆฌ์ฆํ์ต๋๋ค.
์ด์ ๊น์ง๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ API์ ํฐ breaking changes ์์ด ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ณ ์ต์ ํ๋ฅผ ํด์๋ค๋ฉด, v0.5.0์์ Node.js API ๊ตฌ์กฐ๋ฅผ ์ฌ์ค๊ณํ๊ณ Rust์์์ ์ฝ์ด๋ ๊ฐ์ด ๋ฐ๊พธ๋ ์์
์ด์์ต๋๋ค.
๋น๋๊ธฐ, ๊ทธ๋ฆฌ๊ณ ์ง์ฐ ๋ก๋ฉ์ ๊ธฐ๋ณธ์ผ๋ก
๊ธฐ์กด open()์ ํตํด ์ํธ๋ฅผ ์ด๋ฉด ํธ์ถ ์์ ์ ์คํ๋ ๋์ํธ ํ์ผ ๋ด XML ํํธ๋ฅผ ํ ๋ฒ์ ํ์ฑํ์ต๋๋ค. ๊ทธ๋งํผ ํฐ ํ์ผ์ ์ด๋ฉด ์ ๊ทผํ์ง ์๋ ์ํธ์ ๋ฐ์ดํฐ๊น์ง ๋ฉ๋ชจ๋ฆฌ์ ํ๋ฒ์ ์ฌ๋ผ๊ฐ๋๋ค. ๊ทธ๋์ v0.5.0์์๋ ์ฝ๊ธฐ ๋ชจ๋๋ฅผ ์ธ ๊ฐ์ง๋ก ๋๋๊ฒ ๋์์ต๋๋ค.
lazy(ReadMode::Lazy, ๊ธฐ๋ณธ๊ฐ): ZIP ์ธ๋ฑ์ค/๋ฉํ๋ฐ์ดํฐ๋ง ์ฝ๊ณ , ์ํธ๋ ์ฒ์ ์ ๊ทผํ ๋ ํ์ฑํฉ๋๋ค
eager(ReadMode::Eager): ๋ชจ๋ ์ํธ๋ฅผ ์ฆ์ ํ์ฑํฉ๋๋ค
stream(ReadMode::Lazy): ์ ํ๋ ๋ฉ๋ชจ๋ฆฌ ์์์ ์๋ฐฉํฅ์ผ๋ก๋ง ์ฝ์ต๋๋ค
์คํธ๋ฆฌ๋ฐ ๋ฆฌ๋
๋์ฉ๋ ํ์ผ์์ ์ ์ฒด๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ฆฌ์ง ์๊ณ ํ ๋จ์๋ก ์์ฐจ ์ฒ๋ฆฌํ ์ ์๋ forward-only ๋ฆฌ๋์
๋๋ค.
const wb = await Workbook.open("huge.xlsx", { readMode: "stream" });
const reader = await wb.openSheetReader("Sheet1", { batchSize: 1000 });
for await (const batch of reader) {
for (const row of batch) {
// ํ ๋ฒ์ ํ ๋ฐฐ์น๋ง ๋ฉ๋ชจ๋ฆฌ์ ์กด์ฌํฉ๋๋ค
}
}
copy-on-write ์ ์ฅ
์ง์ฐ ๋ก๋ฉ ๋ชจ๋๋ก ์ด๋ฆฐ ์ํฌ๋ถ์ ์ ์ฅํ ๋, ๋ณ๊ฒฝ๋์ง ์์ ์ํธ๋ ์๋ณธ ZIP ์ํธ๋ฆฌ์์ ์ง์ ์ ๋ฌํฉ๋๋ค. ํ์ฑ๊ณผ ์ง๋ ฌํ ์๋ณต์ ๊ฑฐ์น์ง ์๊ธฐ ๋๋ฌธ์, ํฐ ์ํฌ๋ถ์์ ์ผ๋ถ ์ํธ / ์ผ๋ถ ์
๋ง ์์ ํ๋ ์ํฌ๋ก๋์์ ์ ์ฅ ์๊ฐ์ด ์ค์ด๋ญ๋๋ค.
์ ๊ฐ ์ค์ ๋ก ๊ฒช๋ ํ
ํ๋ฆฟ ์์ฑ ์๋๋ฆฌ์ค("๋๋ถ๋ถ์ ๊ทธ๋๋ก ๋๊ณ ์ผ๋ถ ์
๋ง ์ฑ์์ ๋ด๋ ค์ฃผ๊ธฐ")๊ฐ ๋ฑ ์ด ์ผ์ด์ค์๊ณ , ์ด๊ฒ v0.5.0์์ ๊ฐ์ ํ ๋ฐฉํฅ์ด์์ต๋๋ค.
edit-xlsx ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ฝ๊ธฐ ์ด์์น์ ๋ฒค์น๋งํฌ ๋น๊ต ๊ท์น
v0.3.0 ์ดํ๋ก ๋ฒค์น๋งํฌ๋ฅผ ๋ง๋ค๊ณ ๊ด๋ฆฌํ๋ฉด์ ์คํํด๋ณด๋ฉด ์ด์์น๊ฐ ๋์ต๋๋ค. Rust ๋น๊ต ๋ฒค์น๋งํฌ์์ edit-xlsx๊ฐ ์ฝ๊ธฐ์์ ๋น์ ์์ ์ผ๋ก ์งง์ ์๊ฐ์ ์ฐ๋ ๊ฒฝ์ฐ๊ฐ ์์๊ณ , ์์ธํ ๋ค์ฌ๋ค๋ณด๋ rows/cells ์นด์ดํธ๊ฐ 0์ผ๋ก ๋จ์ด์ง๋ ์ผ์ด์ค๊ฐ ์์ฌ ์์์ต๋๋ค.
๊ทธ๋์ โ๋น๊ต ๊ฐ๋ฅ์ฑ ๊ท์น(comparability rules)โ์ ๋์
ํ์ต๋๋ค.
- rows / cells ์นด์ดํธ๊ฐ ๊ธฐ๋์น์ ๋ง๋์ง ํ์ธ
- ๋์ผ ์ขํ์ ๊ฐ ๊ฒ์ฆ(value probe)์ด ๋ง๋์ง ํ์ธ
- ํ๋๋ผ๋ ์ด๊ธ๋๋ฉด ๊ฒฐ๊ณผ๋ฅผ ๋น๊ตํ ์ ์๋ค๊ณ ํ์
๋ฒค์น๋งํฌ๋ ์ซ์๋ฅผ ๋ฝ๋ ๋๊ตฌ์ด๊ธฐ๋ ํ์ง๋ง, ์ด์์น๋ฅผ ์ก์๋ด๋ ๋๊ตฌ์ด๊ธฐ๋ ํฉ๋๋ค. ์ด ๊ท์น์ ๋ฃ๊ณ ๋์๋ถํฐ๋ โ๋น ๋ฅธ๋ฐ ๋ญ๊ฐ ์ด์ํ ๊ฒฐ๊ณผโ๋ฅผ ์๋์ผ๋ก ๊ฑธ๋ฌ๋ผ ์ ์๊ฒ ๋์ต๋๋ค.
์ด ์ดํ์ ์ ์ด๋ฐ ๊ฒฐ๊ณผ๊ฐ ๋์์๊น ๊ถ๊ธํด์ edit-xlsx ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ถ์ํ๊ฒ ๋์์ต๋๋ค. SpreadsheetML ๊ท๊ฒฉ์์ workbook.xml์ fileVersion, workbookPr, bookViews๋ ์ ํ ์์์
๋๋ค. ํ์ง๋ง ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์๋ ํ์ฑ ๊ณผ์ ์์ ์ด ์์๋ค์ ํ์๋ก ์๊ตฌํ๊ณ ์์์ต๋๋ค. ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ํ์ฑ๊ณผ ์ญ์ง๋ ฌํ์ ์คํจํ๋ฉด ๊ธฐ๋ณธ ๊ตฌ์กฐ์ฒด๋ก ๋์ฒด๋๋๋ฐ, ์ด ๊ณผ์ ์์ rows์ cells์ ์๊ฐ 0์ด ๋์ค๊ณ ๋งค์ฐ ์งง์ ์คํ ์๊ฐ์ ๊ธฐ๋กํ๊ฒ ๋ฉ๋๋ค. ์ฆ, ๋ฐ์ดํฐ๋ฅผ ์ค์ ๋ก ์ฝ์ง ์์๊ธฐ ๋๋ฌธ์ ๋น ๋ฅธ ๊ฒ์ด์์ต๋๋ค.
๊ทธ๋์ SheetKit์์๋ ํธํ์ ์ํด ํ์ผ์ ์ ์ฅํ ๋ workbook.xml์์ fileVersion, workbookPr ๊ฐ์ด ์์ ์์ ๊ฒฝ์ฐ์๋ ํด๋น ๊ฐ๋ค์ ๋ํด Microsoft Excel๋ฅผ ์ฐธ๊ณ ํด ์ ์ฌํ ๊ธฐ๋ณธ ๊ฐ์ ๋ฃ์ด์ฃผ๊ฒ ๋์์ต๋๋ค.
๋ฐ์ธ๋ฉ์ ๊ฑฐ์ณค๋๋ฐ ์คํ๋ ค ๋ ๋น ๋ฅด๋ค๊ณ ?
Rust ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ Node ๋ฐ์ธ๋ฉ์ ๊ฐ์ด ๋๋ ค๋ณด๋ฉด ํฅ๋ฏธ๋ก์ด ๊ฒฐ๊ณผ๊ฐ ๋์ค๋ ์ผ์ด์ค๊ฐ ์์ต๋๋ค. ์ผ๋ถ ์ฐ๊ธฐ ์๋๋ฆฌ์ค์์ Node.js ๋ฐ์ธ๋ฉ์ด Rust ๋ค์ดํฐ๋ธ๋ณด๋ค ์คํ๋ ค ๋น ๋ฅด๋ค๋ ์ ์
๋๋ค.
| ์๋๋ฆฌ์ค |
Rust |
Node.js |
์ค๋ฒํค๋ |
| 50kํ x 20์ด ์ฐ๊ธฐ |
544ms |
469ms |
-14% (Node.js๊ฐ ๋น ๋ฆ) |
| 20kํ ํ
์คํธ ์ฐ๊ธฐ |
108ms |
86ms |
-20% (Node.js๊ฐ ๋น ๋ฆ) |
์ ์ด๋ฐ ๊ฒฐ๊ณผ๊ฐ ๋์ฌ ์ ์์์๊น์? ๋ด๋ถ์ ์ผ๋ก SST ๋ฐ์ดํฐ๋ฅผ ๊ตฌ์ฑํ๋ ๊ณผ์ ์์ V8์ ๋ฌธ์์ด ์ธํฐ๋๊ณผ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ๊ฐ ํจ์จ์ ์ผ๋ก ์๋ํ ๊ฒฐ๊ณผ์
๋๋ค. napi ๊ฒฝ๊ณ๋ฅผ ๋๋ ์ค๋ฒํค๋๋ณด๋ค V8 ์์ง ์์ฒด์ ์ต์ ํ๊ฐ ๋ ํฐ ์ด๋์ ์ค ์
์
๋๋ค. Rust ์์ ๋ฐ์ธ๋ฉ์ ์ฌ๋ฆฌ๋ ์์
์ ํ๋ฉด์, JavaScript ์์ง์ ์ต์ ํ๊ฐ ์ผ๋ง๋ ์ ๊ตํ์ง ๋ค์ ํ ๋ฒ ๋๋ผ๊ฒ ๋์์ต๋๋ค.
SheetKit, ์ด์ฌํ ๊ฐ๋ฐฅ๋จน๊ธฐ ์ค
์ ๋ ํ์ฌ์์ SheetKit์ ๊ฐ๋ฐฅ๋จน๊ธฐ(dogfooding)ํ๊ณ ์์ต๋๋ค. ๊ธฐ์กด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฑท์ด๋ด๊ณ ๊ต์ฒดํ ๋ค์๋, ํ
ํ๋ฆฟ ์์ฑ๊ณผ ์
๋ก๋ ์ฒ๋ฆฌ ํ๋ก์ฐ์์ ํ์ํ ๊ธฐ๋ฅ๋ค์ ๋ฌด๋ฆฌ ์์ด ์ํํ๊ณ ์์ต๋๋ค.
SheetKit ํ๋ก์ ํธ๋ ๊ธ์ ์ฐ๊ณ ์๋ 2์ 14์ผ ์ค๋ ๊ธฐ์ค, ๋ค์๊ณผ ๊ฐ์ด ์ง์ํฉ๋๋ค.
- Node.js์ Rust์์ ์คํธ๋ฆฌ๋ฐ ์ฝ๊ธฐ / ์ฐ๊ธฐ
- 164๊ฐ์ ๋ค์ํ ์์ ํจ์ ์ง์
- 43๊ฐ์ ๋ค์ํ ์ฐจํธ ํ์
์ง์
- ๋ค์ํ ์ด๋ฏธ์ง ํฌ๋งท์ ์ง์
Node.js - Rust๊ฐ ์ค๋ฒํค๋๋ ์ฝ๊ธฐ ํญ๋ชฉ์์ ~ 4% ์ ๋์ด๋ฉฐ, ์ฐ๊ธฐ ์๋๋ฆฌ์ค์์๋ ์ผ์ด์ค์ ๋ฐ๋ผ ์คํ๋ ค Node.js๊ฐ ๋น ๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์จ ์ผ์ด์ค๋ ์์์ต๋๋ค.
์์ธํ ๋ด์ฉ์ ๋ฌธ์ ์ฌ์ดํธ์์ ํ์ธํ์ค ์ ์์ต๋๋ค.
SheetKit์ ์์ง ๊ฐ์ ํ ์ ๋ค์ด ์๊ณ API๋ ๋ณ๊ฒฝ๋ ์ ์์ต๋๋ค. ํ์ง๋ง ์ค์ ๋ก ์ ์ฉํด์ ์ฐ๋ฉด์ ๊ณ ์น๊ณ , ์ฑ๋ฅ์ ์ธก์ ํ๊ณ ๋ถ์ํด์ ๊ณ ์น๋ ๋ฐฉ์์ ๊ณ์ ์ ์งํ ์๊ฐ์
๋๋ค. ๊ถ๊ธํ ์ ์ด ์์ผ๋ฉด ํธํ๊ฒ ๋ฌผ์ด๋ด์ฃผ์๊ณ , ์ด์์ PR ๋ชจ๋ ํ์ํฉ๋๋ค.
๋ค์ ๊ธ์์๋...
์ด๋ฒ ๊ธ์์๋ ์ผ์ฃผ์ผ๋์ ์ด๋ป๊ฒ ๋ฌด์์ ๋ง๋ค์๋์ง๋ฅผ ์์ธํ ์ ์ด๋ณด์์ต๋๋ค. ๋ค์ ๊ธ์์๋ Claude Code์ Codex ๋ฑ ์ฝ๋ฉ ์์ด์ ํธ์ ํ์
ํ ๋ฐฉ์(์์
์ ์ํฌํ๋ก์ฐ, ์ ์ฝ์ฌํญ, ์๋ธ ์์ด์ ํธ ๊ตฌ์กฐ, ์ฌ๋์ ๋ฆฌ๋ทฐ ์ ๋ณ๋ ์์ด์ ํธ๋ก ๋ฆฌ๋ทฐ ํ ํผ๋๋ฐฑ ๋ฃจํ๋ฅผ ๋ง๋ ์ , ์ฌ๋์ด ์ด๋ป๊ฒ ๊ฐ์
ํ๋์ง)๊ณผ ๊ทธ ๊ณผ์ ์์ ๋๋ ์ , ๋ฐฐ์ด ์ ๋ค์ ๋ ๊ตฌ์ฒด์ ์ผ๋ก ์ ์ด๋ณด๋ ค๊ณ ํฉ๋๋ค.
Read more โ