Rust ๐Ÿฆ€ and WebAssembly ๐Ÿ•ธ

์ด ๊ฐ„๊ฒฐํ•œ ์ฑ…์€ ๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

๋ฒˆ์—ญ๋œ ๋ฒ„์ „์— ์ด์Šˆ๋‚˜ ํ’€ ๋ฆฌํ€˜์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ ์ž ํ•˜์‹œ๋‚˜์š”? ๋ฒˆ์—ญ ๋ฒ„์ „์˜ ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.

๋ˆ„๊ตฌ๋ฅผ ์œ„ํ•œ ์ฑ…์ธ๊ฐ€์š”?

์ด ์ฑ…์€ ๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜์—ฌ ์›น์—์„œ ๋™์ž‘ํ•˜๋Š” ๋น ๋ฅด๊ณ  ์•ˆ์ •๋œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๊ด€์‹ฌ์ด ์žˆ๋Š” ๋ถ„๋“ค์„ ์œ„ํ•ด ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ผญ ์ „๋ฌธ๊ฐ€๊ฐ€ ๋ผ์•ผํ•  ํ•„์š”๋Š” ์—†์ง€๋งŒ, Rust๋ฅผ ์กฐ๊ธˆ์ด๋ผ๋„ ์•Œ์•„์•ผ ํ•˜๊ณ  ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ HTML, CSS์— ์ต์ˆ™ํ•˜๋ฉด ๋” ์ข‹์Šต๋‹ˆ๋‹ค.

๋Ÿฌ์ŠคํŠธ๋ฅผ ์•„์ง ๋ชจ๋ฅด์‹œ๋‚˜์š”? The Rust Programming Language ์ฑ…์œผ๋กœ ์‹œ์ž‘ํ•ด ๋ณด์„ธ์š”.

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋‚˜ HTML, CSS๋ฅผ ๋ชจ๋ฅด์‹œ๋‚˜์š”? MDN์—์„œ ๋” ์•Œ์•„๋ณด์„ธ์š”.

์ด ์ฑ…์„ ์ฝ๋Š” ๋ฐฉ๋ฒ•

์™œ ๋Ÿฌ์ŠคํŠธ๋กœ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐœ๋ฐœ์„ ํ•ด์•ผ ํ•˜๋‚˜์š”? ์„น์…˜์„ ์ฝ์–ด๋ณด๋ฉด ์ข‹๊ณ , ๋ฐฐ๊ฒฝ์ง€์‹๊ณผ ๋จผ์ € ์นœ์ˆ™ํ•ด์ ธ ๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์Šต๋‹ˆ๋‹ค.

์ด ํŠœํ† ๋ฆฌ์–ผ์€ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋๊นŒ์ง€ ์ฝ๋„๋ก ์ž‘์„ฑ๋์Šต๋‹ˆ๋‹ค. ํŠœํ† ๋ฆฌ์–ผ์— ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑ, ์ปดํŒŒ์ผํ•˜๊ณ  ์ง์ ‘ ์‹คํ–‰ํ•ด ๋ณด์„ธ์š”. Rust์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ๊ฐ™์ด ์‚ฌ์šฉํ•ด ๋ณธ ์ ์ด ์—†๋‹ค๋ฉด, ํŠœํ† ๋ฆฌ์–ผ์„ ํ•œ๋ฒˆ ํ™œ์šฉํ•ด ๋ณด์„ธ์š”!

์ฐธ์กฐ ์„น์…˜ ์€ ์•„๋ฌด ์ˆœ์„œ๋กœ ์ •๋…ํ•ด๋„ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค.

๐Ÿ’ก ํŒ: ํŽ˜์ด์ง€ ์ตœ์ƒ๋‹จ์— ์žˆ๋Š” ๐Ÿ” ์•„์ด์ฝ˜์„ ๋ˆ„๋ฅด๊ฑฐ๋‚˜ s ํ‚ค๋ฅผ ๋ˆŒ๋Ÿฌ์„œ ์ฑ… ์ „์ฒด๋ฅผ ๊ฒ€์ƒ‰ํ•ด ๋ณผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฒˆ์—ญ๋ณธ

์ปค๋ฎค๋‹ˆํ‹ฐ ๋ฒˆ์—ญ๋ณธ๋„ ์žˆ์œผ๋‹ˆ ํ•œ๋ฒˆ ํ™•์ธํ•ด ๋ณด์„ธ์š”.

์ด ์ฑ…์— ๊ธฐ์—ฌํ•˜๊ธฐ

์ด ์ฑ…์€ ์˜คํ”ˆ์†Œ์Šค์ž…๋‹ˆ๋‹ค! ์˜คํƒ€๋ฅผ ์ฐพ์œผ์…จ๋‚˜์š”? ๋ˆ„๋ฝ๋œ ๋ถ€๋ถ„์ด ์žˆ๋‚˜์š”? ํ’€ ๋ฆฌํ€˜์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•ด ๋ณด์„ธ์š”!

์™œ ๋Ÿฌ์ŠคํŠธ๋กœ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๊ฐœ๋ฐœ์„ ํ•ด์•ผ ํ•˜๋‚˜์š”?

์ €๋ ˆ๋ฒจ ์ปจํŠธ๋กค๊ณผ ๊ณ ๋ ˆ๋ฒจ ๊ฐœ๋ฐœ์ž ๊ฒฝํ—˜

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์›น ์•ฑ๋“ค์€ ์•ˆ์ •๋œ ์„ฑ๋Šฅ์„ ํ™•๋ณดํ•˜๊ณ  ์œ ์ง€ํ•˜๋Š” ๋ฐ ์–ด๋ ค์›€์„ ๊ฒช์Šต๋‹ˆ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋™์  ํƒ€์ž… ์‹œ์Šคํ…œ๊ณผ ๊ฐ€๋น„์ง€ ์ฝœ๋ ‰์…˜์„ ์ž ์‹œ ์ค‘๋‹จํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋Š” ํฌ๊ฒŒ ๋„์›€์ด ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ณด๊ธฐ์—๋Š” ์ž‘์€ ๋‚ด์šฉ์˜ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๋”๋ผ๋„ JIT์— ์น˜๋ช…์ ์ธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค๋ฉด ๋“œ๋ผ๋งˆํ‹ฑํ•œ ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๋Ÿฌ์ŠคํŠธ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ณ‘๋“ค๊ฒŒ ๋งŒ๋“œ๋Š” ๋น„๊ฒฐ์ •์ ์ธ ๊ฐ€๋น„์ง€ ์ฝœ๋ ‰์…˜ ์ค‘๋‹จ์œผ๋กœ๋ถ€ํ„ฐ ์ž์œ ๋กœ์šธ ๋ฟ ์•„๋‹ˆ๋ผ, ํ”„๋กœ๊ทธ๋ž˜๋จธ๋“ค์ด ๊ฐ„์ ‘์ง€์ •(indirection)๊ณผ ๋‹จ์ผํ™”(monomorphization), ๋ฉ”๋ชจ๋ฆฌ ๋ ˆ์ด์•„์›ƒ์„ ์ปจํŠธ๋กคํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ์œผ๋กœ ์ €๋ ˆ๋ฒจ ์ปจํŠธ๋กค๊ณผ ์•ˆ์ •๋œ ์„ฑ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ž‘์€ .wasm ์‚ฌ์ด์ฆˆ

.wasm ํŒŒ์ผ์ด ๋„คํŠธ์›Œํฌ๋กœ ์ „์†ก๋ผ์•ผ ํ•˜๋Š” ์ ์„ ๊ณ ๋ คํ•˜๋ฉด ์ฝ”๋“œ์˜ ํŒŒ์ผ ์‚ฌ์ด์ฆˆ๋Š” ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ๋Ÿฌ์ŠคํŠธ๋Š” ๋Ÿฐํƒ€์ž„์ด ์ž‘๊ณ  ๊ฐ€๋น„์ง€ ์ฝœ๋ ‰ํ„ฐ์™€ ๊ฐ™์€ ๋ถˆํ•„์š”ํ•œ ์š”์†Œ๊ฐ€ ์—†์–ด .wasm ์‚ฌ์ด์ฆˆ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ์ค„์ผ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๊ณ , ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํ•จ์ˆ˜๋“ค์„ (๋นŒ๋“œํ•˜๋Š” ์ฝ”๋“œ ํŒŒ์ผ์—์„œ) ์ œ์™ธ์‹œ์ผœ์ค๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์ฝ”๋“œ ํŒŒ์ผ ์‚ฌ์ด์ฆˆ์˜ ๊ด€์ ์—์„œ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋Šฅ๋งŒ ์ถœ๋ ฅ๋˜๋Š” ์ฝ”๋“œ์— ํฌํ•จํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ๋‚ด์šฉ์„ ๋‹ค์‹œ ์ž‘์„ฑํ•˜์ง€ ๋ง์•„ ์ฃผ์„ธ์š”

๊ธฐ์กด์— ์กด์žฌํ•˜๋Š” ์ฝ”๋“œ ๋ฒ ์ด์Šค๋ฅผ ์ง€์šธ ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. ๋ฐ”๋กœ ํšจ๊ณผ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ์„ฑ๋Šฅ ๊ฐœ์„ ์ด ์ค‘์š”ํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ•จ์ˆ˜๋“ค์„ ๋จผ์ € ๋Ÿฌ์ŠคํŠธ๋กœ ์˜ฎ๊ธฐ๋Š” ๊ฒƒ์œผ๋กœ ์‹œ์ž‘ํ•ด ๋ณผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์›ํ•œ๋‹ค๋ฉด ๊ฑฐ๊ธฐ์„œ ๋ฉˆ์ถฐ๋„ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ํˆด๊ณผ ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค

๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋Š” ๊ธฐ์กด์— ์กด์žฌํ•˜๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํˆด๋“ค๊ณผ ํ•จ๊ป˜ ๊ฐ€์žฅ ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋Š” ECMAScript ๋ชจ๋“ˆ์„ ์ง€์›ํ•˜๊ณ , ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ npm๊ณผ webpack์ฒ˜๋Ÿผ ๊ธฐ์กด์— ์‚ฌ๋ž‘๋ฐ›๋˜ ํˆด๋“ค์„ ๊ณ„์† ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋Œ€ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ•์ ๋“ค

๋Ÿฌ์ŠคํŠธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฐœ๋ฐœ์ž๋“ค์ด ์ผ๋ฐ˜์ ์œผ๋กœ ๊ธฐ๋Œ€ํ•˜๊ฒŒ ๋˜๋Š” ํ˜„๋Œ€์ ์ธ ํŽธ์˜ ๊ธฐ๋Šฅ์„ ๊ฐ–์ถ”๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  • cargo๋กœ ๊ฐ•๋ ฅํ•œ ํŒจํ‚ค์ง€ ๊ด€๋ฆฌํ•˜๊ธฐ

  • (์ถ”๊ฐ€ ๋น„์šฉ ์—†์ด) ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์šด ์ฝ”๋“œ๋ฅผ ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์ถ”์ƒํ™”

  • ํ™˜์˜ํ•˜๋Š” ์ปค๋ฎค๋‹ˆํ‹ฐ! ๐Ÿ˜Š

๋ฐฐ๊ฒฝ์ง€์‹

์ด ์„น์…˜์€ ๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ•  ๋•Œ ํ•„์š”ํ•œ ๋ฐฐ๊ฒฝ์ง€์‹์„ ์„ค๋ช…ํ•ด์ค๋‹ˆ๋‹ค.

์›น์–ด์…ˆ๋ธ”๋ฆฌ๊ฐ€ ๋ญ”๊ฐ€์š”?

์›น์–ด์…ˆ๋ธ”๋ฆฌ๋Š” ํฌ๊ด„์ ์ธ ์‚ฌ์–‘์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฐ„๋‹จํ•œ ๊ธฐ๊ณ„ ๋ชจ๋ธ์ด์ž ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ํฌ๋งท์ž…๋‹ˆ๋‹ค. ํœด๋Œ€ ๊ฐ€๋Šฅํ•˜๊ณ , ๊ฐ€๋ฒผ์šฐ๋ฉฐ ๊ฑฐ์˜ ๋„ค์ดํ‹ฐ๋ธŒ ํ”„๋กœ๊ทธ๋žจ๊ณผ ๊ฐ™์€ ์†๋„๋กœ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋์Šต๋‹ˆ๋‹ค.

ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋กœ์จ, ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋Š” ๊ฐ™์€ ๊ตฌ์กฐ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š”๋ฐ, ๋‘ ๊ฐ€์ง€ ๋‹ค๋ฅธ ํฌ๋งท์œผ๋กœ ๊ตฌ์„ฑ๋ผ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ("WebAssembly Text" ์—์„œ ์ด๋ฆ„์ด ์œ ๋ž˜๋œ) .wat ํ…์ŠคํŠธ ํฌ๋งท์€ S-expressions ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  Scheme์ด๋‚˜ Clojure์™€ ๊ฐ™์€ Lisp ๊ณ„์—ด ์–ธ์–ด์™€ ์œ ์‚ฌ์ ๋“ค์„ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค.

  2. .wasm ๋ฐ”์ด๋„ˆ๋ฆฌ ํฌ๋งท์€ ๋” ์ €๋ ˆ๋ฒจ์ด๋ฉด์„œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๊ฐ€์ƒ ๋จธ์‹ ์—์„œ ๋ฐ”๋กœ ์‚ฌ์šฉ๋˜๋„๋ก ์˜๋„๋์Šต๋‹ˆ๋‹ค. ๊ฐœ๋…์ ์œผ๋กœ ELF ์™€ Mach-0์™€ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ  ์ž๋ฃŒ๋กœ, wat ์–ธ์–ด๋กœ ์ž‘์„ฑ๋œ ํŒฉํ† ๋ฆฌ์–ผ ํ•จ์ˆ˜๋ฅผ ํ™•์ธํ•ด ๋ณด์„ธ์š”.

(module
  (func $fac (param f64) (result f64)
    local.get 0
    f64.const 1
    f64.lt
    if (result f64)
      f64.const 1
    else
      local.get 0
      local.get 0
      f64.const 1
      f64.sub
      call $fac
      f64.mul
    end)
  (export "fac" (func $fac)))

์œ„ ์˜ˆ์ œ๊ฐ€ .wasm ํŒŒ์ผ๋กœ๋Š” ์–ด๋–ป๊ฒŒ ๋ณด์ผ์ง€ ๊ถ๊ธˆํ•˜๋‹ค๋ฉด, wat2wasm ๋ฐ๋ชจ ์›น์‚ฌ์ดํŠธ๋ฅผ ์ด์šฉํ•ด ๋ณด์„ธ์š”.

์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ

์›น์–ด์…ˆ๋ธ”๋ฆฌ๋Š” ๋งค์šฐ ๊ฐ„๋‹จํ•œ ๋ฉ”๋ชจ๋ฆฌ ๋ชจ๋ธ์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ , ํ•œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ชจ๋“ˆ์€ ํ•˜๋‚˜์˜ "์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ" ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฉ”๋ชจ๋ฆฌ๋Š” ํŽ˜์ด์ง€ ์‚ฌ์ด์ฆˆ (64K)์˜ ๊ณฑ๋งŒํผ ์ปค์งˆ ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ด ์‚ฌ์ด์ฆˆ๋Š” ์ค„์–ด๋“ค ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์›น์—์„œ๋งŒ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‚˜์š”?

ํ˜„์žฌ๋กœ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์›น ์ปค๋ฎค๋‹ˆํ‹ฐ์—์„œ ์ฃผ๋กœ ์ฃผ๋ชฉ์„ ๋ฐ›๊ณ  ์žˆ์ง€๋งŒ, ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋Š” ํŠน์ • ์‹คํ–‰ ํ™˜๊ฒฝ์„ ํ•„์š”๋กœ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ, ์›น์–ด์…ˆ๋ธ”๋ฆฌ๊ฐ€ ๋ฏธ๋ž˜์— ๋‹ค์–‘ํ•œ ๋งฅ๋ฝ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” "ํœด๋Œ€ ๊ฐ€๋Šฅํ•œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š”" ํฌ๋งท์ด๋ผ๊ณ  ์—ฌ๊ฒจ์งˆ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์˜ค๋Š˜๋‚  ํ˜„์žฌ ์‹œ์ ์—์„œ๋Š” ์›น์–ด์…ˆ๋ธ”๋ฆฌ๊ฐ€ (์›น๊ณผ Node.js์„ ํฌํ•จํ•œ) ๋‹ค์–‘ํ•œ ํ˜•ํƒœ๋กœ ์กด์žฌํ•˜๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ ํ•จ๊ป˜ ์ฃผ๋กœ ์–ธ๊ธ‰๋ฉ๋‹ˆ๋‹ค.

ํŠœํ† ๋ฆฌ์–ผ: Conway's Game of Life

์ด ํŠœํ† ๋ฆฌ์–ผ์€ Conway's Game of Life๋ฅผ ๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๋‚ด์šฉ์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

๋ˆ„๊ตฌ๋ฅผ ์œ„ํ•œ ํŠœํ† ๋ฆฌ์–ผ์ธ๊ฐ€์š”?

์ด ํŠœํ† ๋ฆฌ์–ผ์€ ์ด๋ฏธ ๊ธฐ์ดˆ์ ์ธ ๋Ÿฌ์ŠคํŠธ์™€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ฐฐ์› ๊ณ , ๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์–ด ํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•ด ์ž‘์„ฑ๋์Šต๋‹ˆ๋‹ค.

์›ํ™œํ•œ ์ง„ํ–‰์„ ์œ„ํ•ด ๊ธฐ์ดˆ์ ์ธ ๋Ÿฌ์ŠคํŠธ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ, HTML ์ฝ”๋“œ๋ฅผ ๋ฌธ์ œ์—†์ด ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ „๋ฌธ๊ฐ€๊ฐ€ ๋ผ์•ผ ํ•  ํ•„์š”๋Š” ์ „ํ˜€ ์—†์Šต๋‹ˆ๋‹ค.

๋ฌด์—‡์„ ๋ฐฐ์šฐ๊ฒŒ ๋˜๋‚˜์š”?

  • ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์ปดํŒŒ์ผํ•  ์ˆ˜ ์žˆ๋„๋ก ๋Ÿฌ์ŠคํŠธ ํˆด์ฒด์ธ์„ ์„ค์ •ํ•˜๋Š” ๋ฒ•.

  • ๋Ÿฌ์ŠคํŠธ, ์›น์–ด์…ˆ๋ธ”๋ฆฌ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ, HTML, CSS์œผ๋กœ ๋‹ค์–ธ์–ด ํ”„๋กœ๊ทธ๋žจ์„ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋Š” ์›Œํฌํ”Œ๋กœ์šฐ.

  • ๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ, ๊ทธ๋ฆฌ๊ณ  ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ๊ฐ•์ ์„ ๋ชจ๋‘ ์‚ด๋ฆฌ๋„๋ก API๋ฅผ ์„ค๊ณ„ํ•˜๋Š” ๋ฐฉ๋ฒ•.

  • ๋Ÿฌ์ŠคํŠธ ์ฝ”๋“œ์—์„œ ์ปดํŒŒ์ผ๋œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ชจ๋“ˆ์„ ๋””๋ฒ„๊น…ํ•˜๋Š” ๋ฐฉ๋ฒ•.

  • ๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํ”„๋กœ๊ทธ๋žจ์„ ๋” ๋น ๋ฅด๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ํƒ€์ž„ ํ”„๋กœํŒŒ์ผ๋งํ•˜๋Š” ๋ฐฉ๋ฒ•.

  • .wasm ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๋” ์ž‘๊ณ  ๋น ๋ฅด๊ฒŒ ๋งŒ๋“ค์–ด ๋„คํŠธ์›Œํฌ๋ฅผ ํ†ตํ•œ ๋‹ค์šด๋กœ๋“œ๊ฐ€ ๋” ์›ํ™œํ•  ์ˆ˜ ์žˆ๋„๋ก ๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํ”„๋กœ๊ทธ๋žจ์„ ์‚ฌ์ด์ฆˆ ํ”„๋กœํŒŒ์ผ๋งํ•˜๋Š” ๋ฐฉ๋ฒ•.

์…‹์—…

์ด ์„น์…˜์—์„œ๋Š” Rust ํ”„๋กœ๊ทธ๋žจ๋“ค์„ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋กœ ์ปดํŒŒ์ผํ•˜๊ณ  ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ๊ณผ ํ†ตํ•ฉ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋‹ค๋ฃจ์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋Ÿฌ์ŠคํŠธ ํˆด์ฒด์ธ

์ง„ํ–‰์„ ์œ„ํ•ด rustup, rustc, cargo๋ฅผ ํฌํ•จํ•œ ์Šคํƒ ๋‹ค๋“œ Rust ํˆด์ฒด์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋Ÿฌ์ŠคํŠธ ํˆด์ฒด์ธ์„ ์„ค์น˜ํ•˜๋ ค๋ฉด ์ด ์ง€์นจ์„ ๋”ฐ๋ผ์ฃผ์„ธ์š”.

๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์ด stable ๋ฒ„์ „์˜ ๋Ÿฌ์ŠคํŠธ์— ํฌํ•จ๋  ๋งŒํผ ์•ˆ์ •ํ™”๋˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋–ค ์‹คํ—˜์  ๊ธฐ๋Šฅ flag๋„ ์š”๊ตฌ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ Rust 1.30์ด๋‚˜ ๊ทธ ์ดํ›„ ๋ฒ„์ „์ด ์š”๊ตฌ๋ฉ๋‹ˆ๋‹ค.

wasm-pack

wasm-pack์€ ๋Ÿฌ์ŠคํŠธ๋กœ ์ƒ์„ฑํ•œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ๊ฐœ๋ฐœ, ํ…Œ์ŠคํŒ…, ๋ฐฐํฌํ•˜๋„๋ก ๋„์™€์ฃผ๋Š” ๋งŒ๋Šฅ ํˆด์ž…๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ wasm-pack ๋‹ค์šด๋กœ๋“œ ํ•ด๋ณด์„ธ์š”!

cargo-generate

cargo-generate๋ฅผ ํ†ตํ•ด ๊ธฐ์กด์— ์กด์žฌํ•˜๋Š” git ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ํ…œํ”Œ๋ฆฟ์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด์„œ ์ƒˆ ๋Ÿฌ์ŠคํŠธ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•˜๊ณ  ๋น ๋ฅด๊ฒŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ช…๋ น์–ด๋กœ cargo-generate๋ฅผ ์„ค์น˜ํ•ด ๋ณด์„ธ์š”:

cargo install cargo-generate

npm

npm์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜๋Š” ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ €์ž…๋‹ˆ๋‹ค. ์ด ์ฑ…์„ ์ง„ํ–‰ํ•˜๋ฉด์„œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ฒˆ๋“ค๋Ÿฌ์™€ ๊ฐœ๋ฐœ ์„œ๋ฒ„๋ฅผ ์„ค์น˜ํ•˜๊ณ  ๊ตฌ๋™ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ์ด ํŠœํ† ๋ฆฌ์–ผ ๋์—์„œ๋Š” ์ปดํŒŒ์ผ๋œ .wasm์„ npm ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ๋ฐฐํฌํ•ด ๋ด…๋‹ˆ๋‹ค.

npm ์„ ์„ค์น˜ํ•˜๋ ค๋ฉด ์ด ์ง€์นจ์„ ๋”ฐ๋ผ์ฃผ์„ธ์š”.

์ด๋ฏธ npm์ด ์„ค์น˜๋ผ ์žˆ๋‹ค๋ฉด, ์ด ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ์ตœ์‹  ๋ฒ„์ „์œผ๋กœ ์—…๋ฐ์ดํŠธ๋ผ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด ์ฃผ์„ธ์š”:

npm install npm@latest -g

Hello, World!

์ด ์„น์…˜์—์„œ๋Š” ๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํ”„๋กœ๊ทธ๋žจ์„ ์ฒ˜์Œ ์‹คํ–‰ํ•˜๋Š” ๊ณผ์ •์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค. "Hello, World!" ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์›นํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค์–ด๋ด…์‹œ๋‹ค.

์‹œ์ž‘ํ•˜๊ธฐ ์ „์— ์…‹์—… ๊ฐ€์ด๋“œ๋ฅผ ์ฝ๊ณ  ์ž˜ ๋”ฐ๋ผ์™”๋Š”์ง€ ํ•œ ๋ฒˆ ๋” ํ™•์ธํ•ด ์ฃผ์„ธ์š”.

ํ”„๋กœ์ ํŠธ ํ…œํ”Œ๋ฆฟ ํด๋ก ํ•˜๊ธฐ

์ด ํ”„๋กœ์ ํŠธ ํ…œํ”Œ๋ฆฟ์€ ํ•ฉ๋ฆฌ์ ์ธ ๊ธฐ๋ณธ ์„ค์ •์œผ๋กœ ๊ตฌ์„ฑ๋ผ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ…œํ”Œ๋ฆฟ์„ ํ™œ์šฉํ•ด์„œ ์›น์œผ๋กœ ์ฝ”๋“œ๋ฅผ ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœ, ํ†ตํ•ฉ ๋ฐ ํŒจํ‚ค์ง•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ช…๋ น์–ด๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ํด๋ก ํ•ด๋ณด์„ธ์š”:

cargo generate --git https://github.com/rustwasm/wasm-pack-template

์ƒˆ ํ”„๋กœ์ ํŠธ ์ด๋ฆ„์„ ์–ด๋–ป๊ฒŒ ์ง€์„์ง€ ์ž…๋ ฅํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. "wasm-game-of-life" ์ด๋ผ๋Š” ์ด๋ฆ„์„ ์‚ฌ์šฉํ•ฉ์‹œ๋‹ค.

wasm-game-of-life

์–ด๋–ป๊ฒŒ ๊ตฌ์„ฑ๋ผ ์žˆ๋‚˜์š”?

์ƒˆ๋กœ ์ƒ์„ฑํ•œ wasm-game-of-life ํ”„๋กœ์ ํŠธ๋ฅผ ์—ด์–ด๋ณด๋„๋ก ํ•ฉ์‹œ๋‹ค.

cd wasm-game-of-life

๊ทธ๋‹ค์Œ ์•ˆ์— ์–ด๋–ค ํŒŒ์ผ์ด ๋‹ด๊ฒจ์žˆ๋Š”์ง€ ํ™•์ธํ•ด ๋ด…์‹œ๋‹ค:

wasm-game-of-life/
โ”œโ”€โ”€ Cargo.toml
โ”œโ”€โ”€ LICENSE_APACHE
โ”œโ”€โ”€ LICENSE_MIT
โ”œโ”€โ”€ README.md
โ””โ”€โ”€ src
    โ”œโ”€โ”€ lib.rs
    โ””โ”€โ”€ utils.rs

๋ช‡ ๊ฐ€์ง€ ํŒŒ์ผ์„ ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ณผ๊นŒ์š”?

wasm-game-of-life/Cargo.toml

Cargo.toml ํŒŒ์ผ์€ ๋Ÿฌ์ŠคํŠธ์˜ ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ €์ด์ž ๋นŒ๋“œ ํˆด์ธ cargo์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜๋Š”๋ฐ, ์˜์กด์„ฑ๊ณผ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ง€์ •ํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ์ด ํŒŒ์ผ์€ wasm-bindgen ์˜์กด์„ฑ๊ณผ ํ•จ๊ป˜ wasm ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ƒ์„ฑ์— ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋„๋ก ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์„ค์ •๋œ crate-type์ด ํ•จ๊ป˜ ์‚ฌ์ „์— ๋ฏธ๋ฆฌ ํฌํ•จ๋ผ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ช‡ ๊ฐ€์ง€ ํ•„์ˆ˜๊ฐ€ ์•„๋‹Œ ์˜์กด์„ฑ์€ ๋‚˜์ค‘์— ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

wasm-game-of-life/src/lib.rs

src/lib.rs ํŒŒ์ผ์€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋กœ ์ปดํŒŒ์ผํ•˜๋Š” ๋Ÿฌ์ŠคํŠธ ํฌ๋ ˆ์ดํŠธ์˜ ํ•ต์‹ฌ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. wasm-bindgen์„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ์กฐ์ž‘ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๊ณ , window.alert ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ•จ์ˆ˜๋ฅผ ๋ถˆ๋Ÿฌ์˜จ ๋‹ค์Œ์— greet ๋Ÿฌ์ŠคํŠธ ํ•จ์ˆ˜๋ฅผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ๋ณด๋ƒ…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ "Hello World" alert ๋ฉ”์„ธ์ง€๋ฅผ ํ‘œ์‹œ์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

#![allow(unused)]
fn main() {
mod utils;

use wasm_bindgen::prelude::*;

// `wee_alloc` ๊ธฐ๋Šฅ์ด ํ™œ์„ฑํ™”๋ผ ์žˆ์œผ๋ฉด, `wee-alloc`๋ฅผ ์ „์—ญ ํ• ๋‹น์ž๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

#[wasm_bindgen]
extern {
    fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet() {
    alert("Hello, wasm-game-of-life!");
}
}

wasm-game-of-life/src/utils.rs

src/utils.rs ๋ชจ๋“ˆ์€ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ์ œ๊ณตํ•˜๋Š”๋ฐ, ์ด ์œ ํ‹ธ๋ฆฌํ‹ฐ๊ฐ€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋กœ ์ปดํŒŒ์ผ๋œ ๋Ÿฌ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์—…์„ ๋” ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค. wasm ์ฝ”๋“œ ๋””๋ฒ„๊น…ํ•˜๊ธฐ์™€ ๊ฐ™์€ ํŠœํ† ๋ฆฌ์–ผ ํ›„๋ฐ˜ ์„น์…˜์—์„œ ์ด๋Ÿฌํ•œ ์œ ํ‹ธ๋ฆฌํ‹ฐ๋“ค์„ ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ์‹œ์ ์—์„œ๋Š” ์ด ํŒŒ์ผ์„ ๋ฌด์‹œํ•ด๋„ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ ๋นŒ๋“œํ•˜๊ธฐ

wasm-pack์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์Œ ๋นŒ๋“œ ๊ณผ์ •์„ ์ž๋™ํ™”ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค:

  • ๋Ÿฌ์ŠคํŠธ 1.30๋‚˜ ๊ทธ ์ดํ›„ ๋ฒ„์ „์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”์ง€, wasm32-unknown-unknown ํƒ€๊ฒŸ์ด rustup์„ ํ†ตํ•ด ์„ค์น˜๋ผ ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • cargo๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋Ÿฌ์ŠคํŠธ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์›น์–ด์…ˆ๋ธ”๋ฆฌ .wasm ๋ฐ”์ด๋„ˆ๋ฆฌ๋กœ ์ปดํŒŒ์ผํ•ฉ๋‹ˆ๋‹ค.
  • wasm-bindgen์„ ์‚ฌ์šฉํ•˜์—ฌ ๋Ÿฌ์ŠคํŠธ๋กœ ์ƒ์„ฑํ•œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ API๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

์œ„ ์ž‘์—…์„ ์‹œ์ž‘ํ•˜๋ ค๋ฉด, ํ”„๋กœ์ ํŠธ ๊ฒฝ๋กœ์—์„œ ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ด ์ฃผ์„ธ์š”:

wasm-pack build

๋นŒ๋“œ๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด ๊ฒฐ๊ณผ๋ฌผ์„ pkg ๊ฒฝ๋กœ์—์„œ ํ™•์ธํ•ด ๋ณด์„ธ์š”. ๋‹ค์Œ ๋‚ด์šฉ๋ฌผ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

pkg/
โ”œโ”€โ”€ package.json
โ”œโ”€โ”€ README.md
โ”œโ”€โ”€ wasm_game_of_life_bg.wasm
โ”œโ”€โ”€ wasm_game_of_life.d.ts
โ””โ”€โ”€ wasm_game_of_life.js

README.md ํŒŒ์ผ์ด ๋ฉ”์ธ ํ”„๋กœ์ ํŠธ์—์„œ ๋ณต์‚ฌ๋์ง€๋งŒ ๋‚˜๋จธ์ง€ ํŒŒ์ผ๋“ค์€ ์™„์ „ํžˆ ์ƒˆ๋กœ ์ƒ์„ฑ๋œ ๋ถ€๋ถ„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

wasm-game-of-life/pkg/wasm_game_of_life_bg.wasm

์ด๋Ÿฌํ•œ .wasm ํŒŒ์ผ์€ ๋Ÿฌ์ŠคํŠธ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋Ÿฌ์ŠคํŠธ ์†Œ์Šค ์ฝ”๋“œ๋กœ ์ƒ์„ฑํ•œ ๋ฐ”์ด๋„ˆ๋ฆฌ ํŒŒ์ผ์ž…๋‹ˆ๋‹ค. ์ด ํŒŒ์ผ์€ wasm ํ˜•์‹์œผ๋กœ ์ปดํŒŒ์ผ๋œ ๋ชจ๋“  ๋Ÿฌ์ŠคํŠธ ํ•จ์ˆ˜๋“ค๊ณผ ๋ฐ์ดํ„ฐ๋กœ ๊ตฌ์„ฑ๋ผ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณ€ํ™˜๋œ "greet" ํ•จ์ˆ˜๊ฐ€ ์˜ˆ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

wasm-game-of-life/pkg/wasm_game_of_life.js

์ด๋Ÿฌํ•œ .js ํŒŒ์ผ์€ wasm-bindgen์— ์˜ํ•ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. DOM๊ณผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ•จ์ˆ˜๋ฅผ ๋Ÿฌ์ŠคํŠธ์—์„œ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๊ณ , ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์—์„œ๋„ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ์œ ์šฉํ•œ API๋ฅผ ๋…ธ์ถœ์‹œ์ผœ์ค๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด์„œ, greet ์ด๋ผ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์›น์–ด์…ˆ๋ธ”๋ฆฌ์˜ ํ•ด๋‹นํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ์‹œ์ ์—์„œ๋Š” ์ด๋Ÿฌํ•œ ๋ฐ”์ธ๋”ฉ(bindings glue)๋“ค์ด ํฐ ์—ญํ• ์„ ํ•˜์ง€๋Š” ์•Š์ง€๋งŒ, ๋” ๋ณต์žกํ•œ ๊ฐ’๋“ค์„ ์›น์–ด์…ˆ๋ธ”๋ฆฌ์™€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์‚ฌ์ด์—์„œ ์ฃผ๊ณ ๋ฐ›์„ ๋•Œ ์ •๋ง ๋„์›€์ด ๋งŽ์ด ๋ฉ๋‹ˆ๋‹ค.

import * as wasm from './wasm_game_of_life_bg';

// ...

export function greet() {
    return wasm.greet();
}

wasm-game-of-life/pkg/wasm_game_of_life.d.ts

.d.ts ํŒŒ์ผ์€ ์ƒ์„ฑํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” TypeScript ํƒ€์ž… ์ •์˜๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. TypeScript๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์ •์  ํƒ€์ž… ์ •์˜๋ฅผ ํ†ตํ•ด IDE๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ž๋™ ์™„์„ฑ๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด์„œ ๋” ์‰ฝ๊ฒŒ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. TypeScript๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, ์ด ํŒŒ์ผ์€ ๋ฌด์‹œํ•ด๋„ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค.

export function greet(): void;

wasm-game-of-life/pkg/package.json

package.json ํŒŒ์ผ์€ ์ƒ์„ฑ๋œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํŒจํ‚ค์ง€์— ๋Œ€ํ•œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋Š” npm๊ณผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ฒˆ๋“ค๋Ÿฌ๊ฐ€ ์—ฌ๋Ÿฌ ํŒจํ‚ค์ง€๋“ค์˜ ์ข…์†์„ฑ, ํŒจํ‚ค์ง€ ์ด๋ฆ„, ๋ฒ„์ „ ๋“ฑ์„ ๊ฒฐ์ •ํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํˆด๋ง๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ณ  npm์— ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํŒจํ‚ค์ง€๋ฅผ ๋ฐฐํฌํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

{
  "name": "wasm-game-of-life",
  "collaborators": [
    "Your Name <your.email@example.com>"
  ],
  "description": null,
  "version": "0.1.0",
  "license": null,
  "repository": null,
  "files": [
    "wasm_game_of_life_bg.wasm",
    "wasm_game_of_life.d.ts"
  ],
  "main": "wasm_game_of_life.js",
  "types": "wasm_game_of_life.d.ts"
}

์›น ํŽ˜์ด์ง€์— ํฌํ•จ์‹œํ‚ค๊ธฐ

wasm-game-of-life ํŒจํ‚ค์ง€๋ฅผ ์›นํŽ˜์ด์ง€์— ํฌํ•จ์‹œํ‚ค๊ณ  ์›น ํ™˜๊ฒฝ์—์„œ ์ž‘๋™์‹œํ‚ค๊ธฐ ์œ„ํ•ด, create-wasm-app ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ”„๋กœ์ ํŠธ ํ…œํ”Œ๋ฆฟ ์„ ์‚ฌ์šฉํ•ด ๋ด…์‹œ๋‹ค.

๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ wasm-game-of-life ๊ฒฝ๋กœ ๋‚ด๋ถ€์—์„œ ์‹คํ–‰ํ•ด ์ฃผ์„ธ์š”:

npm init wasm-app www

์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑ๋œ ๋‚ด๋ถ€ ๊ฒฝ๋กœ์ธ wasm-game-of-life/www ๋Š” ๋‹ค์Œ ํŒŒ์ผ๋“ค์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค:

wasm-game-of-life/www/
โ”œโ”€โ”€ bootstrap.js
โ”œโ”€โ”€ index.html
โ”œโ”€โ”€ index.js
โ”œโ”€โ”€ LICENSE-APACHE
โ”œโ”€โ”€ LICENSE-MIT
โ”œโ”€โ”€ package.json
โ”œโ”€โ”€ README.md
โ””โ”€โ”€ webpack.config.js

ํ•œ ๋ฒˆ ๋” ์ƒ์„ฑ๋œ ํŒŒ์ผ๋“ค์„ ์ž์„ธํžˆ ์‚ดํŽด๋ด…์‹œ๋‹ค.

wasm-game-of-life/www/package.json

์ด package.json ํŒŒ์ผ์€ webpack๊ณผ webpack-dev-server ์ข…์†์„ฑ๋“ค๋กœ ๋ฏธ๋ฆฌ ์…‹์—…์ด ๋˜์–ด ์žˆ๊ณ , npm ์— ๋ฐฐํฌ๋ผ ์žˆ๋Š” wasm-pack-template์˜ ์ดˆ๊ธฐ ๋ฒ„์ „์ธ hello-wasm-pack ํŒจํ‚ค์ง€ ๋˜ํ•œ ์ข…์†์„ฑ์œผ๋กœ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

wasm-game-of-life/www/webpack.config.js

์ด ํŒŒ์ผ์€ webpack๊ณผ ๋กœ์ปฌ ๊ฐœ๋ฐœ ์„œ๋ฒ„๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๋ฏธ๋ฆฌ ์…‹์—… ์ž‘์—…์ด ๋ผ ์žˆ๋Š”๋ฐ, webpack๊ณผ ๋กœ์ปฌ ๊ฐœ๋ฐœ ์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ์—๋„ ์ „ํ˜€ ๋”ฐ๋กœ ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

wasm-game-of-life/www/index.html

์›น์‚ฌ์ดํŠธ์— ์‚ฌ์šฉ๋˜๋Š” ์ตœ์ƒ๋‹จ HTML ํŒŒ์ผ์ž…๋‹ˆ๋‹ค. index.js ๋ฅผ ๊ฐ์‹ธ์ฃผ๋Š” bootstrap.js๋ฅผ ๋ถ€๋ฅด๋Š” ๊ฒƒ ์™ธ์—๋Š” ํŠน๋ณ„ํ•œ ๋™์ž‘์„ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Hello wasm-pack!</title>
  </head>
  <body>
    <script src="./bootstrap.js"></script>
  </body>
</html>

wasm-game-of-life/www/index.js

์ด index.js๋Š” ์ž‘์—…์„ ํ•˜๊ฒŒ ๋  ์›น์‚ฌ์ดํŠธ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ์ง„์ž…์  (entry point) ์ž…๋‹ˆ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ์™€ wasm-pack-template์˜ ์ปดํŒŒ์ผ ๊ฒฐ๊ณผ๋ฌผ์ธ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๊ฐ€ ํฌํ•จ๋ผ ์žˆ๋Š”๋ฐ, hello-wasm-pack npm ํŒจํ‚ค์ง€๋ฅผ ๋กœ๋“œํ•˜๊ณ  ํŒจํ‚ค์ง€ ๋‚ด๋ถ€์˜ greet ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด ์ค๋‹ˆ๋‹ค.

import * as wasm from "hello-wasm-pack";

wasm.greet();

์ข…์†์„ฑ ์„ค์น˜ํ•˜๊ธฐ

์šฐ์„ , wasm-game-of-life/www ๋‚ด๋ถ€ ๊ฒฝ๋กœ์—์„œ npm install ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ๋กœ์ปฌ ๊ฐœ๋ฐœ ์„œ๋ฒ„์™€ ์ข…์†์„ฑ๋“ค์ด ์„ค์น˜๋ผ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด ์ฃผ์„ธ์š”:

npm install

์ด ์ปค๋งจ๋“œ๋Š” ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์‹คํ–‰ํ•˜๋ฉด webpack ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ฒˆ๋“ค๋Ÿฌ์™€ ๊ฐœ๋ฐœ ์„œ๋ฒ„๋ฅผ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹จ์ˆœํžˆ ๊ฐ„ํŽธํ•˜๊ฒŒ ์ฑ…์„ ์ง„ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์ด ๋ฒˆ๋“ค๋Ÿฌ์™€ ๊ฐœ๋ฐœ ์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•  ์˜ˆ์ •์ด์ง€๋งŒ webpack์ด ๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์ž‘์—…์— ํ•„์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ๋Š” ์ ์„ ๊ธฐ์–ตํ•ด ์ฃผ์„ธ์š”. Parcel๊ณผ Rollup๋„ ์›น์–ด์…ˆ๋ธ”๋ฆฌ์™€ ECMAScript ๋ชจ๋“ˆ์„ ๋ถ€๋ฅด๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์›ํ•œ๋‹ค๋ฉด ๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ๋ฒˆ๋“ค๋Ÿฌ ์—†์ด ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

www ํŒจํ‚ค์ง€ ๋‚ด๋ถ€์—์„œ ๋กœ์ปฌ wasm-game-of-life ํŒจํ‚ค์ง€ ์‚ฌ์šฉํ•˜๊ธฐ

npm์—์„œ ๋‹ค์šด๋กœ๋“œํ•œ hello-wasm-pack ๋Œ€์‹ ์— ๋กœ์ปฌ ํ™˜๊ฒฝ์— ์žˆ๋Š” wasm-game-of-life๋ฅผ ์‚ฌ์šฉํ•ด ๋ด…์‹œ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•จ์œผ๋กœ์จ Game of Life ํ”„๋กœ๊ทธ๋žจ์„ ๋” ์ ์ง„์ ์œผ๋กœ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

wasm-game-of-life/www/package.json ํŒŒ์ผ์„ ์—ด๊ณ  devDependencies ๋‹ค์Œ์— dependencies ํ•„๋“œ๋ฅผ ์ƒ์„ฑํ•ด ์ฃผ์„ธ์š”. ๊ทธ๋‹ค์Œ์—, "wasm-game-of-life": "file:../pkg" ์—”ํŠธ๋ฆฌ๋ฅผ ํฌํ•จ์‹œ์ผœ์ฃผ์„ธ์š”.

{
  // ...
  "dependencies": {                     // ์ด 3์ค„ ๊ธธ์ด์˜ ๋ธ”๋Ÿญ์„ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”!
    "wasm-game-of-life": "file:../pkg"
  },
  "devDependencies": {
    //...
  }
}

๋‹ค์Œ์œผ๋กœ, hello-wasm-pack ๋Œ€์‹ ์— wasm-game-of-life๋ฅผ ๋ถ€๋ฅผ ์ˆ˜ ์žˆ๋„๋ก wasm-game-of-life/www/index.js ํŒŒ์ผ์„ ์ˆ˜์ •ํ•ด ์ฃผ์„ธ์š”:

import * as wasm from "wasm-game-of-life";

wasm.greet();

์ƒˆ ์ข…์†์„ฑ์„ ๋งŒ๋“ค์—ˆ๋‹ค๋ฉด, ๋‹ค์Œ ๋ช…๋ น์–ด๋กœ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:

npm install

์ด์ œ ์›น์‚ฌ์ดํŠธ๋ฅผ ๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ ๊ตฌ๋™ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋์Šต๋‹ˆ๋‹ค!

๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ ๊ตฌ๋™ํ•˜๊ธฐ

์ด์ œ ๊ฐœ๋ฐœ ์„œ๋ฒ„๋ฅผ ๊ตฌ๋™ํ•  ์ƒˆ ํ„ฐ๋ฏธ๋„์„ ์—ด์–ด์ฃผ์„ธ์š”. ์ƒˆ๋กœ ์—ฐ ํ„ฐ๋ฏธ๋„์—์„œ ์„œ๋ฒ„๋ฅผ ๊ตฌ๋™ํ•˜๋ฉด ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๊ณ„์† ์„œ๋ฒ„๋ฅผ ๊ตฌ๋™ํ•˜๋ฉด์„œ ๋‹ค๋ฅธ ๋ช…๋ น์–ด๋ฅผ ๊ณ„์† ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ƒˆ ํ„ฐ๋ฏธ๋„์—์„œ wasm-game-of-life/www ๊ฒฝ๋กœ๋กœ ๋“ค์–ด๊ฐ„ ๋‹ค์Œ ์ด ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ด ์ฃผ์„ธ์š”:

npm run start

์›น ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์—ด๊ณ  http://localhost:8080/ ๋ฅผ ์—ด๋ฉด ํ‘œ์‹œ๋˜๋Š” "Hello World" alert ๋ฉ”์„ธ์ง€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

"Hello, wasm-game-of-life!" ์›น ํŽ˜์ด์ง€ alert ๋ฉ”์„ธ์ง€ ์Šคํฌ๋ฆฐ์ƒท

ํŒŒ์ผ์„ ์ €์žฅํ•  ๋•Œ๋งˆ๋‹ค http://localhost:8080/ ํŽ˜์ด์ง€์— ๋ฐ˜์˜๋˜๋„๋ก ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, wasm-pack build ๋ช…๋ น์–ด๋ฅผ wasm-game-of-life ๊ฒฝ๋กœ์—์„œ ๋‹ค์‹œ ์‹คํ–‰ํ•ด ์ฃผ์„ธ์š”.

์—ฐ์Šตํ•ด ๋ณด๊ธฐ

  • wasm-game-of-life/src/lib.rs ๊ฒฝ๋กœ์— ์žˆ๋Š” greet ํ•จ์ˆ˜๋ฅผ ์ˆ˜์ •ํ•ด์„œ ํ‘œ์‹œ๋˜๋Š” ๋ฉ”์„ธ์ง€๋ฅผ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•  ์ˆ˜ ์žˆ๋„๋ก name: &str ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ด ๋ณด๊ณ , wasm-game-of-life/www/index.js ํŒŒ์ผ์—์„œ greet ํ•จ์ˆ˜๋ฅผ ์ด๋ฆ„๊ณผ ํ•จ๊ป˜ ํ˜ธ์ถœํ•ด ๋ณด์„ธ์š”. wasm-pack build ๋ช…๋ น์–ด๋กœ .wasm ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๋‹ค์‹œ ๋นŒ๋“œํ•˜๊ณ , http://localhost:8080/ ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ˆ˜์ •๋œ ์•Œ๋ฆผ ๋ฉ”์„ธ์ง€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    ์ •๋‹ต

    wasm-game-of-life/src/lib.rs ํŒŒ์ผ์—์„œ ์ƒˆ๋กญ๊ฒŒ ์ˆ˜์ •๋œ greet ํ•จ์ˆ˜:

    #![allow(unused)]
    fn main() {
    #[wasm_bindgen]
    pub fn greet(name: &str) {
        alert(&format!("Hello, {}!", name));
    }
    }

    wasm-game-of-life/www/index.js ํŒŒ์ผ์—์„œ ์ˆ˜์ •๋œ greet ํ•จ์ˆ˜ ํ˜ธ์ถœํ•˜๊ธฐ:

    wasm.greet("Your Name");
    

Conway's Game of Life์˜ ๊ทœ์น™

Note: ์ด๋ฏธ Conway's Game of Life ์™€ ์ด ๊ฒŒ์ž„์˜ ๊ทœ์น™์„ ์ž˜ ์•Œ๊ณ  ์žˆ๋‹ค๋ฉด ๋‹ค์Œ ์„น์…˜์œผ๋กœ ๋„˜์–ด๊ฐ€๋„ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค.

Wikipedia์— Conway's Game of Life์˜ ๊ทœ์น™์ด ์•„์ฃผ ์ž˜ ์„ค๋ช…๋ผ ์žˆ์Šต๋‹ˆ๋‹ค.

Game of Life์˜ ์„ธ์ƒ(universe)์€ ์‚ฌ๊ฐํ˜• ์„ธํฌ๋กœ ์ด๋ฃจ์–ด์ง„ ๋ฌดํ•œํ•œ ์‚ฌ์ด์ฆˆ์˜ 2์ฐจ์› ์ •์‚ฌ๊ฐํ˜•_ํ…Œ์…€๋ ˆ์ด์…˜์ธ๋ฐ, ๊ฐ๊ฐ์˜ ์„ธํฌ๋Š” ์‚ด์•„์žˆ๊ฑฐ๋‚˜ ์ฃฝ์–ด์žˆ๊ฑฐ๋‚˜, ํ˜น์€ "์ฃผ๊ฑฐ"๋‚˜ "๋ฌด์ฃผ๊ฑฐ" ์ค‘ ํ•œ ์ƒํƒœ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ์„ธํฌ์€ ์ˆ˜ํ‰, ์ˆ˜์ง, ํ˜น์€ ๋Œ€๊ฐ์„ ์œผ๋กœ ์ด์›ƒํ•˜๋Š” ์—ฌ๋Ÿ๊ฐœ์˜ ์ด์›ƒ ์„ธํฌ๊ณผ ์ƒํ˜ธ์ž‘์šฉํ•ฉ๋‹ˆ๋‹ค. ๋งค ๋‹จ๊ณ„๋งˆ๋‹ค, ๋‹ค์Œ ์ „์ด๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

  1. ์ธ๊ตฌ ๋ถ€์กฑ์œผ๋กœ 2๊ฐœ ๋ฏธ๋งŒ์˜ ์ด์›ƒ์„ ๊ฐ€์ง„ ์„ธํฌ๋Š” ์ฃฝ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  2. 2๊ฐœ ํ˜น์€ 3๊ฐœ์˜ ์ด์›ƒ์„ ๊ฐ€์ง„ ์„ธํฌ๋Š” ๋‹ค์Œ ์„ธ๋Œ€์—์„œ ๊ณ„์† ์‚ด์•„์žˆ์Šต๋‹ˆ๋‹ค.
  3. ๊ณผ์ž‰ ์ธ๊ตฌ๋กœ 3๊ฐœ ์ดˆ๊ณผ์˜ ์ด์›ƒ์„ ๊ฐ€์ง„ ๋ชจ๋“  ์„ธํฌ๋Š” ์ฃฝ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  4. ์„ธํฌ ์ฆ์‹์œผ๋กœ ์ •ํ™•ํžˆ 3๊ฐœ์˜ ์ด์›ƒ์„ ๊ฐ€์ง„ ์„ธํฌ๋Š” ์‚ด์•„๋‚˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด ์ดˆ๊ธฐ ํŒจํ„ด์€ ๊ฒŒ์ž„ ์‹œ์Šคํ…œ์˜ ์‹œ์ž‘์ (seed)์„ ๋งŒ๋“ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์œ„ ๊ทœ์น™๋“ค์„ ์‹œ์ž‘์ (seed)์˜ ๋ชจ๋“  ์„ธํฌ์— ์ ์šฉํ•˜๋ฉด์„œ ์ฒซ ๋ฒˆ์งธ ์„ธ๋Œ€๊ฐ€ ์ƒ์„ฑ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ถœ์ƒ๊ณผ ์‚ฌ๋ง์€ ๋™์‹œ์— ์ผ์–ด๋‚˜๊ณ , ์ด๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฐ๊ฐ์˜ ์ˆœ๊ฐ„์„ ํ‹ฑ(tick) ์ด๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค. (๋‹ค์‹œ ๋งํ•ด, ๊ฐ ์„ธ๋Œ€๋Š” ์ง์ „ ์„ธ๋Œ€์˜ ์ˆœ์ˆ˜ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.) ์ด ๊ทœ์น™์€ ๊ณ„์† ์ ์šฉ๋˜์–ด ๋ฐ˜๋ณต์ ์œผ๋กœ ์ถ”๊ฐ€ ์„ธ๋Œ€๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

๋‹ค์Œ ์ด๋ฏธ์ง€๋ฅผ ์ดˆ๊ธฐ ์„ธ์ƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ด ๋ด…์‹œ๋‹ค:

Initial Universe

๋‹ค์Œ ์„ธ๋Œ€๋ฅผ ๊ณ„์‚ฐํ•  ๋•Œ ํ•˜๋‚˜์”ฉ ๊ฐ ์„ธํฌ๋ฅผ ๊ณ ๋ คํ•ด ๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ, ํ•œ๋ฒˆ ์‚ดํŽด๋ณด๋„๋ก ํ•ฉ์‹œ๋‹ค. ์ด ์ดˆ๊ธฐ ์„ธ์ƒ์—์„œ ์™ผ์ชฝ ์ตœ์ƒ๋‹จ ์„ธํฌ๋Š” ์ฃฝ์–ด์žˆ์Šต๋‹ˆ๋‹ค. ๊ทœ์น™ (4)๋Š” ์ฃฝ์–ด์žˆ๋Š” ์„ธํฌ์—๋งŒ ์ ์šฉ๋˜๋Š” ์œ ์ผํ•œ ์ „์ด ์„ธํฌ์ด์ง€๋งŒ, ์ตœ์ƒ๋‹จ ์™ผ์ชฝ ์„ธํฌ๋Š” ์ •ํ™•ํžˆ 3๊ฐœ์˜ ์‚ด์•„์žˆ๋Š” ์ด์›ƒ์„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ „์ด ๊ทœ์น™์ด ์ ์šฉ๋˜์ง€ ์•Š๊ณ  ๋‹ค์Œ ์„ธ๋Œ€์—์„œ๋„ ์ฃฝ์–ด ์žˆ๋Š” ์ƒํƒœ๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. ๋™์ผํ•œ ์ด์œ ๋กœ ์ฒซ ๋ฒˆ์งธ ํ–‰์˜ ๋‹ค๋ฅธ ์„ธํฌ๋“ค๋„ ๊ทธ๋Œ€๋กœ ์ฃฝ์–ด์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋‘ ๋ฒˆ์งธ ํ–‰, ์„ธ๋ฒˆ์งธ ์—ด์˜ ์‚ด์•„์žˆ๋Š” ์„ธํฌ๋ฅผ ๋ณด๋ฉด ๋งค์šฐ ํฅ๋ฏธ๋กœ์šด ๋‚ด์šฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์„ธํฌ๊ฐ€ ์‚ด์•„์žˆ๋‹ค๋ฉด ์ฒซ๋ฒˆ์งธ ์„ธ ๊ทœ์น™์ด ์ ์šฉ๋  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์„ธํฌ๋Š” ๋‹จ ํ•œ ๊ฐœ์˜ ์‚ด์•„์žˆ๋Š” ์ด์›ƒ์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ทœ์น™ (1)์ด ์ ์šฉ๋˜๊ฒŒ ๋˜์–ด ๋‹ค์Œ ์„ธ๋Œ€์—์„œ ์ฃฝ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ตœํ•˜๋‹จ์˜ ์‚ด์•„์žˆ๋Š” ์„ธํฌ๋„ ๋™์ผํ•˜๊ฒŒ ์ฃฝ์Šต๋‹ˆ๋‹ค.

๊ฐ€์šด๋ฐ์— ์œ„์น˜ํ•œ ์‚ด์•„์žˆ๋Š” ์„ธํฌ๋Š” ์œ„์•„๋ž˜๋กœ ๋‘ ๊ฐœ์˜ ์‚ด์•„์žˆ๋Š” ์ด์›ƒ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ๊ทœ์น™ (2)๊ฐ€ ์ ์šฉ์ด ๋˜์–ด ๋‹ค์Œ ์„ธ๋Œ€์—์„œ๋„ ์‚ด์•„์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ ๊ฐ€์šด๋ฐ์— ์‚ด์•„์žˆ๋Š” ์„ธํฌ๋“ค์˜ ์™ผ์ชฝ๊ณผ ์˜ค๋ฅธ์ชฝ ์ด์›ƒ๋“ค์„ ๋ณด๋ฉด ์ •๋ง ํฅ๋ฏธ๋กœ์šด ๋ถ€๋ถ„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด 3๊ฐœ์˜ ์‚ด์•„์žˆ๋Š” ์„ธํฌ๋“ค์€ ์–‘์ชฝ ๋ฐฉํ–ฅ์œผ๋กœ ๋‘ ์„ธํฌ๋“ค๊ณผ ์ด์›ƒํ•ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ทœ์น™ (4)๊ฐ€ ์ ์šฉ์ด ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ์ด ์„ธํฌ๋“ค์€ ๋‹ค์Œ ์„ธ๋Œ€์—์„œ ์‚ด์•„์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์œ„์—์„œ ๋‹ค๋ฃฌ ๋‚ด์šฉ์„ ํ† ๋Œ€๋กœ, ๋‹ค์Œ ํ‹ฑ์˜ ์„ธ์ƒ์€ ์ด๋ ‡๊ฒŒ ๋ณด์ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค:

๋‹ค์Œ ์„ธ์ƒ

์ด๋Ÿฌํ•œ ๊ฐ„๋‹จํ•˜๊ณ  ๊ฒฐ์ •์ ์ธ ๊ทœ์น™์„ ์ ์šฉํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์ƒํ•˜์ง€๋งŒ ํฅ๋ฏธ๋กœ์šด ๋™์ž‘์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค:

Gosper's glider gunPulsarSpace ship
Gosper's glider gunPulsarLighweight space ship

์—ฐ์Šตํ•ด ๋ณด๊ธฐ

  • ๋ฐฉ๊ธˆ ๋ณด์—ฌ๋“œ๋ฆฐ ๋‹ค์Œ ํ‹ฑ ์˜ˆ์‹œ ์ดํ›„, ๊ทธ ๋‹ค์Œ ํ‹ฑ์„ ์Šค์Šค๋กœ ๊ณ„์‚ฐํ•ด ๋ณด์„ธ์š”. ์–ด๋–ป๊ฒŒ ๊ฐ์ด ์˜ค์‹œ๋Š” ๊ฒƒ ๊ฐ™๋‚˜์š”?

    ์ •๋‹ต

    ์ดˆ๊ธฐ ์ƒํƒœ์˜ ์šฐ์ฃผ๋กœ ๋‹ค์‹œ ๋Œ์•„๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

    ์ดˆ๊ธฐ ์ƒํƒœ์˜ ์šฐ์ฃผ

    ์ด ํŒจํ„ด์€ ์ฃผ๊ธฐ์ ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ๋‘ ํ‹ฑ๋งˆ๋‹ค ์ฒ˜์Œ ์ƒํƒœ๋กœ ๋Œ์•„๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

  • "์•ˆ์ •๋œ" ์ดˆ๊ธฐ ์„ธ์ƒ์„ ์ฐพ์•„๋ณด์‹ค ์ˆ˜ ์žˆ์œผ์‹ ๊ฐ€์š”? ์„ธ๋Œ€๊ฐ€ ๋ฐ”๋€Œ์–ด๋„ ๋‚ด์šฉ์ด ๋ฐ”๋€Œ์ง€ ์•Š๋Š” ์„ธ์ƒ์„ ์ฐพ์•„๋ณด์„ธ์š”.

    ์ •๋‹ต

    ์•ˆ์ •๋œ ์„ธ์ƒ์€ ๋ฌดํ•œํ•˜๊ฒŒ ๋งŽ์Šต๋‹ˆ๋‹ค. ์ง€๋ฃจํ•˜๊ฒŒ๋„ ํ…… ๋น„์–ด์žˆ๋Š” ์„ธ์ƒ๋„ ์•ˆ์ •๋œ ์„ธ์ƒ์ด๊ณ , ์‚ด์•„์žˆ๋Š” ์„ธํฌ๋“ค์ด 2 x 2 ์‚ฌ์ด์ฆˆ์˜ ์‚ฌ๊ฐํ˜• ๋ชจ์–‘์„ ํ˜•์„ฑํ•  ๋•Œ๋„ ์•ˆ์ •๋œ ์„ธ์ƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Conway's Game of Life ๊ตฌํ˜„ํ•˜๊ธฐ

์„ค๊ณ„

์‹œ์ž‘ํ•˜๊ธฐ ์ „์—, ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ Game of Life๋ฅผ ์„ค๊ณ„ํ• ์ง€ ์‚ดํŽด๋ด…์‹œ๋‹ค.

๋ฌดํ•œํ•œ ์„ธ์ƒ

Game of Life๋Š” ๋ฌดํ•œํ•œ ์„ธ์ƒ์—์„œ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ณดํ†ต์€ ์šฐ๋ฆฌ๊ฐ€ ๋ฌดํ•œํ•œ ๋ฉ”๋ชจ๋ฆฌ์™€ ์ปดํ“จํ„ฐ ํŒŒ์›Œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์Œ ์„ธ ๊ฐ€์ง€ ๋ฐฉ๋ฒ• ์ค‘ ํ•œ ๋ฐฉ๋ฒ•์„ ํ†ตํ•ด ์ด ๊ท€์ฐฎ์€ ์ œํ•œ์„ ์šฐํšŒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค:

  1. ์„ธ์ƒ์˜ ์–ด๋–ค ๋ถ€๋ถ„์ด ๋งŽ์€ ์ปดํ“จํ„ฐ ์ž์›์„ ํ•„์š”๋กœ ํ•˜๋Š”์ง€ ์ถ”์ ํ•˜๊ณ  ์ด๋Ÿฌํ•œ ๋ถ€๋ถ„์„ ํ•„์š”ํ•  ๋•Œ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค. ์ตœ์•…์˜ ๊ฒฝ์šฐ์—๋Š”, ์ด ํ™•์žฅ์ด ์ œํ•œ ์—†์ด ์ง„ํ–‰๋˜๊ณ  ์ฝ”๋“œ๊ฐ€ ๊ณ„์†ํ•ด์„œ ๋Š๋ ค์ง€๋ฉด์„œ ๊ฒฐ๊ตญ์—๋Š” ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋‹ค ์ฐจ์ง€ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

  2. ๋ชจ์„œ๋ฆฌ์— ์œ„์น˜ํ•œ ์„ธํฌ๋“ค์ด ๊ฐ€์šด๋ฐ์— ์œ„์น˜ํ•œ ์„ธํฌ๋“ค๊ณผ ๋น„๊ตํ•ด์„œ ๋” ์ ์€ ์ด์›ƒ์„ ๊ฐ€์ง€๊ฒŒ ๋˜๋Š” ์‚ฌ์ด์ฆˆ๊ฐ€ ์ •ํ•ด์ ธ ์žˆ๋Š” ์„ธ์ƒ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์—๋Š” gliders์™€ ๊ฐ™์€ ๋ฌดํ•œํ•œ ํŒจํ„ด์ด ๋ชจ์„œ๋ฆฌ์—์„œ ๋๋‚˜๋ฒ„๋ฆฌ๊ฒŒ ๋œ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  3. ์‚ฌ์ด์ฆˆ๊ฐ€ ์ •ํ•ด์กŒ์ง€๋งŒ ๊ณ„์†ํ•ด์„œ ์—ฐ๊ฒฐ๋˜๋Š” ์šฐ์ฃผ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์„ธ์ƒ์˜ ๋์„ ๋ฐ˜๋Œ€์ชฝ ์„ธ์ƒ์˜ ๋์œผ๋กœ ์—ฐ๊ฒฐ์‹œ์ผœ ์„ธํฌ๋“ค์ด ๊ณ„์†ํ•ด์„œ ์ด์›ƒ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ gliders ํŒจํ„ด์ด ๊ณ„์† ์›€์ง์ผ ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋ฉด ์„ธ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์œผ๋กœ ๊ตฌํ˜„์„ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋Ÿฌ์ŠคํŠธ์™€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋ผ๋ฆฌ ์—ฐ๊ฒฐํ•˜๊ธฐ

โšก ๋‹ค์Œ ๋‚ด์šฉ์€ ์ด ํŠœํ† ๋ฆฌ์–ผ์—์„œ ๋‹ค๋ฃจ๋Š” ๋‚ด์šฉ ์ค‘์—์„œ๋„ ์•„์ฃผ ์ค‘์š”ํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค. ์ด ๋‚ด์šฉ์„ ์ดํ•ดํ•˜๋ฉด์„œ ์–ป์–ด๊ฐˆ ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์ด ์•„์ฃผ ๋งŽ์Šต๋‹ˆ๋‹ค!

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” Object, Array ๊ทธ๋ฆฌ๊ณ  DOM ๋…ธ๋“œ (node) ๋“ค์ด ํ• ๋‹น๋˜๋Š” ๊ฐ€๋น„์ง€ ์ฝœ๋ ‰ํ„ฐ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ํž™์„ ์‚ฌ์šฉํ•˜์ง€๋งŒ, ์ž‘์„ฑํ•˜๊ฒŒ ๋  ๋Ÿฌ์ŠคํŠธ ์ฝ”๋“œ์˜ ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ๋Š” ๋ณ„๊ฐœ์˜ ๊ณต๊ฐ„์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋Š” ํ˜„์žฌ๋กœ์จ๋Š” ๊ฐ€๋น„์ง€ ์ฝœ๋ ‰ํ„ฐ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ํž™์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. (2018๋…„ 4์›” ๊ธฐ์ค€์œผ๋กœ, "์ธํ„ฐํŽ˜์ด์Šค ํƒ€์ž…" ์ œ์•ˆ ๊ณผ ํ•จ๊ป˜ ๋ณ€๊ฒฝ๋  ์ „๋ง์ด๊ธด ํ•ฉ๋‹ˆ๋‹ค.) ๋ฐ˜๋ฉด์— ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ArrayBuffer๋‚˜ ์Šค์นผ๋ผ ๊ฐ’ (scalar values / u8, i32, f64, ๋“ฑ...) ๋งŒ์œผ๋กœ๋ผ๋„ ์ด ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ฝ๊ณ  ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๋‚ด์šฉ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ชจ๋“  ์›น์–ด์…ˆ๋ธ”๋ฆฌ์™€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์‚ฌ์ด์˜ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜์ด ๊ตฌ์„ฑ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

wasm_bindgen๋Š” ์ด ๊ฒฝ๊ณ„๋ฅผ ์‚ฌ์ด๋กœ ์–ด๋–ป๊ฒŒ ๊ตฌ์กฐ์ฒด (compound structure) ๋“ค์„ ์ฃผ๊ณ ๋ฐ›์•„์•ผ ํ•˜๋Š”์ง€ ์ •ํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ž‘์—…์€ ๋Ÿฌ์ŠคํŠธ ๊ตฌ์กฐ์ฒด๋ฅผ ๋ฐ•์‹ฑ(boxing)ํ•˜๊ณ , ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํด๋ž˜์Šค์— ํฌ์ธํ„ฐ๋ฅผ ๋žฉํ•‘(wrapping)ํ•˜๊ณ , ๋Ÿฌ์ŠคํŠธ ์ฝ”๋“œ์—์„œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด ํ…Œ์ด๋ธ”์„ ์ธ๋ฑ์‹ฑ(indexing)ํ•˜๋Š” ๊ณผ์ •์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. wasm_bindgen์€ ๋งค์šฐ ๊ฐ„ํŽธํ•˜์ง€๋งŒ, ๋ฐ์ดํ„ฐ ํ‘œํ˜„ ์„ค๊ณ„๋ฅผ ๋ชจ๋‘ ๋Œ€์‹  ํ•ด์ฃผ์ง„ ์•Š์Šต๋‹ˆ๋‹ค. ์›ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ธํ„ฐํŽ˜์ด์Šค ์„ค๊ณ„๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๋„๊ตฌ ์ •๋„๋กœ ์ƒ๊ฐํ•˜๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค.

WebAssembly์™€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์‚ฌ์ด์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์„ค๊ณ„ํ•  ๋•Œ, ๋‹ค์Œ ๋‚ด์šฉ๋“ค์„ ์ตœ์ ํ™” ์ž‘์—… ์‹œ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:

  1. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์ด๋ฅผ ์˜ค๊ฐ€๋Š” ๋ณต์‚ฌ(copy) ์ตœ์†Œํ™”ํ•˜๊ธฐ. ๋ถˆํ•„์š”ํ•œ ๋ณต์‚ฌ๋Š” ๋ถˆํ•„์š”ํ•œ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

  2. ์ง๋ ฌํ™”(serializing)์™€ ์—ญ์ง๋ ฌํ™”(deserializing) ์ตœ์†Œํ™”ํ•˜๊ธฐ. ๋ณต์‚ฌ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ์ง๋ ฌํ™”์™€ ์—ญ์ง๋ ฌํ™”๋„ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ณ , ์ด๋Ÿฌํ•œ ์ž‘์—…์ด ๋ณต์‚ฌ๋„ ์ž์ฃผ ๋ฐœ์ƒ์‹œํ‚ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ํ•œ ๊ณณ์—์„œ ๋ชจ๋“  ์ง๋ ฌํ™” ์ž‘์—…์„ ํ•˜๋Š” ๋Œ€์‹  ์ผ๋ฐ˜์ ์œผ๋กœ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ์˜ ์•Œ๋ ค์ง„ ์œ„์น˜๋กœ opaque handle๋“ค์„ ๋„˜๊ธฐ๋Š” ๋ฐฉ์‹์œผ๋กœ ๋งŽ์€ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์ค„์ผ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  wasm_bindgen์„ ํ†ตํ•ด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ Object๋‚˜ ๋ฐ•์‹ฑ๋œ ๋Ÿฌ์ŠคํŠธ struct๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” opaque handle๋“ค์„ ๋” ์‰ฝ๊ฒŒ ์ •์˜ํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ์—๋Š”, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์˜ค๊ฐˆ ๋•Œ ์‚ฌ์ด์ฆˆ๊ฐ€ ํฌ๊ณ  ์˜ค๋ž˜ ์‚ด์•„์žˆ์–ด์•ผ ํ•˜๋Š” ์ž๋ฃŒ ๊ตฌ์กฐ๋ฅผ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ์— ๋‘๊ณ , ์ด๋Ÿฌํ•œ ๊ฐ’๋“ค์„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ opaque handle๋กœ์จ ๋…ธ์ถœ์‹œํ‚ค๋Š” ๊ฒƒ์ด ์ข‹์€ ์ธํ„ฐํŽ˜์ด์Šค ์„ค๊ณ„์ž…๋‹ˆ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ด๋Ÿฌํ•œ opaque handle๋ฅผ ํ†ตํ•ด ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€ํ˜•์‹œํ‚ค๊ณ , ๋ฌด๊ฑฐ์šด ์ปดํ“จํŒ… ์ž‘์—…์„ ํ•˜๊ณ , ๊ฐ’์„ ๊ฒ€์ƒ‰ํ•˜๊ณ , ์ตœ์ข…์ ์œผ๋กœ ์ž‘์€ ์‚ฌ์ด์ฆˆ์˜ ๋ณต์‚ฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ž‘์€ ๊ฐ’๋งŒ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋˜๋ฉด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ€๋น„์ง€ ์ฝœ๋ ‰ํ„ฐ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ํž™๊ณผ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์ด์˜ ๋ชจ๋“  ๊ฐ’๋“ค์„ ์•ž๋’ค๋กœ ๋ณต์‚ฌํ•˜๊ณ  ์ง๋ ฌํ™”ํ•  ํ•„์š”๊ฐ€ ์—†์–ด์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋Ÿฌ์ŠคํŠธ์™€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ์—์„œ ์กฐ์ž‘ํ•˜๊ธฐ

์œ„ํ—˜ํ•œ ์‚ฌ๋ก€๋ฅผ ์‚ดํŽด๋ณด๋Š” ๊ฒƒ์œผ๋กœ ์‹œ์ž‘ํ•ด๋ด…์‹œ๋‹ค. ๋งค ํ‹ฑ๋งˆ๋‹ค ์„ธ์ƒ์„ ์›น์–ด์…ˆ๋ธ”๋ฆฌ์—์„œ ๋ถˆ๋Ÿฌ์˜ค๊ฑฐ๋‚˜ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด ๋ณต์‚ฌํ•˜์ง€ ์•Š์•„์•ผ ํ•˜๊ณ , ์„ธํฌ ํ•˜๋‚˜์”ฉ ๊ฐ์ฒด๋ฅผ ๋ชจ๋‘ ํ• ๋‹นํ•˜๊ฑฐ๋‚˜ ๊ฒฝ๊ณ„๋ฅผ ์˜ค๊ฐ€๋ฉด์„œ ์ฝ๊ณ  ์“ฐ๋Š” ๊ฒƒ๋„ ์ข‹์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒŒ ์ข‹์„๊นŒ์š”? ์ฃฝ์€ ์„ธํฌ๋ฅผ 0๋กœ ๋‚˜ํƒ€๋‚ด๊ณ  ์‚ด์•„์žˆ๋Š” ์„ธํฌ๋ฅผ 1๋กœ ๋‚˜ํƒ€๋‚ด๋Š” ์‹์œผ๋กœ ์„ธํฌ๋“ค์„ ๊ฐ๊ฐ 1 byte ๊ฐ’์œผ๋กœ ๋‚˜ํƒ€๋‚ด๋ณผ ์ˆ˜๋„ ์žˆ๋Š”๋ฐ, ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ์— 1์ฐจ์› ๋ฐฐ์—ด๋กœ ๋‚˜ํƒ€๋‚ด๋ด…์‹œ๋‹ค.

4 x 4 ์‚ฌ์ด์ฆˆ์˜ ์šฐ์ฃผ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ ์ด๋ฏธ์ง€๋กœ ํ‘œํ˜„ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

4 x 4 ์‚ฌ์ด์ฆˆ ์„ธ์ƒ์˜ ์Šคํฌ๋ฆฐ์ƒท

์ด ๊ณต์‹์„ ์‚ฌ์šฉํ•ด์„œ ์ฃผ์–ด์ง„ ์—ด๊ณผ ํ–‰์— ํ•ด๋‹นํ•˜๋Š” ๋ฐฐ์—ด ์ธ๋ฑ์Šค๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

index(row, column, universe) = row * width(universe) + column

์„ธํฌ๋“ค์„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์— ๋…ธ์ถœ์‹œํ‚ฌ๋•Œ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•ด๋ณผ์ˆ˜ ์žˆ๋Š”๋ฐ, ์šฐ์„ ์€ ๋Ÿฌ์ŠคํŠธ String ํƒ€์ž…์˜ ๊ฐ’์œผ๋กœ ์„ธํฌ๋“ค์„ ๋ฌธ์ž๋กœ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋„๋ก Universe ํƒ€์ž…์— std::fmt::Display ํŠธ๋ ˆ์ดํŠธ๋ฅผ ๊ตฌํ˜„ํ•ด ์ฃผ๋„๋ก ํ•ฉ์‹œ๋‹ค. ์ด ํŠธ๋ ˆ์ดํŠธ๋ฅผ ํ†ตํ•ด ๋Ÿฌ์ŠคํŠธ String ํƒ€์ž…์˜ ๊ฐ’์„ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ€๋น„์ง€ ์ฝœ๋ ‰ํ„ฐ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ํž™์œผ๋กœ ๋ณต์‚ฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ ๋‹ค์Œ, ๋ณต์‚ฌ๋œ ๊ฐ’์„ HTML textConent์— ํ‘œ์‹œํ•ด ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ์ฑ•ํ„ฐ ํ›„๋ฐ˜์—์„œ๋Š” ์ด ๊ตฌํ˜„์— ๋ง๋ถ™์—ฌ์„œ ์„ธํฌ๋“ค์„ ํž™์— ๋ณต์‚ฌํ•˜์ง€ ์•Š๋„๋ก ํ•ด๋ณด๊ณ  ์„ธํฌ๋“ค์„ <canvas>์— ํ‘œ์‹œํ•ด๋ณผ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

ํ•˜๋‚˜ ๋” ๋Œ€์‹  ํ•ด๋ณผ ๋ฒ•ํ•œ ์„ค๊ณ„๊ฐ€ ์žˆ๋Š”๋ฐ, ์„ธ์ƒ ์ „์ฒด๋ฅผ ๋…ธ์ถœ์‹œํ‚ค์ง€ ์•Š๊ณ  ๋งค ํ‹ฑ๋งˆ๋‹ค ์ƒํƒœ๊ฐ€ ๋ฐ”๋€Œ๊ฒŒ ๋˜๋Š” ์„ธํฌ๋“ค์„ ๋ชฉ๋ก์œผ๋กœ ๋งŒ๋“ค์–ด์„œ ๋Ÿฌ์ŠคํŠธ ์ฝ”๋“œ์—์„œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ๋ฐ˜ํ™˜ํ•ด ๋ณผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์œผ๋กœ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ์—์„œ ์„ธ์ƒ ์ „์ฒด๋ฅผ ์ˆœํšŒํ•  ํ•„์š” ์—†์ด ์ผ๋ถ€๋งŒ ์ˆœํšŒํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋‹จ์ ์œผ๋กœ๋Š”, ์ด ๋ธํƒ€ ๊ธฐ๋ฐ˜ (delta-based)์˜ ์„ค๊ณ„๋Š” ๊ตฌํ˜„ํ•˜๊ธฐ๊ฐ€ ์กฐ๊ธˆ ๋” ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

๋Ÿฌ์ŠคํŠธ ์ฝ”๋“œ ๊ตฌํ˜„ํ•˜๊ธฐ

์ง์ „ ์ฑ•ํ„ฐ์—์„œ ์ดˆ๊ธฐ ํ”„๋กœ์ ํŠธ ํ…œํ”Œ๋ฆฟ์„ ํด๋ก ํ–ˆ๋Š”๋ฐ, ์ด ํ…œํ”Œ๋ฆฟ์„ ํ•œ๋ฒˆ ์ˆ˜์ •ํ•ด ๋ณด๋„๋ก ํ•ฉ์‹œ๋‹ค.

alert๋ฅผ ์ž„ํฌํŠธํ•˜๋Š” ์ค„๊ณผ greet ํ•จ์ˆ˜๋ฅผ wasm-game-of-life/src/lib.rs ํŒŒ์ผ์—์„œ ์ง€์›Œ๋ณด๊ณ , ์„ธํฌ์˜ ํƒ€์ž… ์ •์˜๋ฅผ ๋Œ€์‹  ์ถ”๊ฐ€ํ•ด ์ฃผ๋Š” ๊ฒƒ์œผ๋กœ ์‹œ์ž‘ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

#![allow(unused)]
fn main() {
#[wasm_bindgen]
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Cell {
    Dead = 0,
    Alive = 1,
}
}

๊ฐ ์„ธํฌ๊ฐ€ 1 byte ์‚ฌ์ด์ฆˆ๋กœ ํ‘œํ˜„๋ผ์•ผ ํ•˜๋ฏ€๋กœ #[repr(u8)]์„ ์žŠ์ง€ ์•Š๊ณ  ๋ถ™์—ฌ์ฃผ๋„๋ก ํ•˜๊ณ , ์„ธํฌ ์ฃผ๋ณ€์— ์‚ด์•„์žˆ๋Š” ์ด์›ƒ๋“ค์„ ์‰ฝ๊ฒŒ ์…€ ์ˆ˜ ์žˆ๋„๋ก, 0๋ฅผ Dead๋กœ, 1์„ Alive๋กœ ์ •ํ•ด์ฃผ๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋‹ค์Œ, ์„ธ์ƒ์„ ์ •์˜ํ•ด๋ด…์‹œ๋‹ค. ์„ธ์ƒ์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ตฌ์กฐ์ฒด๋Š” ๋„ˆ๋น„์™€ ๋†’์ด, ์„ธํฌ๋“ค์„ ๋‚˜ํƒ€๋‚ด๋Š” width * height ํฌ๊ธฐ์˜ ๋ฒกํ„ฐ(vector)๋ฅผ ํ•„๋“œ๋กœ ๊ฐ€์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

#![allow(unused)]
fn main() {
#[wasm_bindgen]
pub struct Universe {
    width: u32,
    height: u32,
    cells: Vec<Cell>,
}
}

์ด์ „์— ์„ค๋ช…ํ•œ ๋‚ด์šฉ๋Œ€๋กœ ์ฃผ์–ด์ง„ ํ–‰๊ณผ ์—ด์„ ์„ธํฌ ๋ฒกํ„ฐ์˜ ์ธ๋ฑ์Šค๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

#![allow(unused)]
fn main() {
impl Universe {
    fn get_index(&self, row: u32, column: u32) -> usize {
        (row * self.width + column) as usize
    }

    // ...
}
}

์„ธํฌ์˜ ๋‹ค์Œ ์ƒํƒœ๋ฅผ ๊ณ„์‚ฐํ•˜๋ ค๋ฉด ๋ช‡ ๊ฐœ์˜ ์ด์›ƒ์ด ์‚ด์•„์žˆ๋Š”์ง€ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋‚ด์šฉ์„ ํ† ๋Œ€๋กœ live_neighbor_count ๋ฉ”์†Œ๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ด…์‹œ๋‹ค!

#![allow(unused)]
fn main() {
impl Universe {
    // ...

    fn live_neighbor_count(&self, row: u32, column: u32) -> u8 {
        let mut count = 0;
        for delta_row in [self.height - 1, 0, 1].iter().cloned() {
            for delta_col in [self.width - 1, 0, 1].iter().cloned() {
                if delta_row == 0 && delta_col == 0 {
                    continue;
                }

                let neighbor_row = (row + delta_row) % self.height;
                let neighbor_col = (column + delta_col) % self.width;
                let idx = self.get_index(neighbor_row, neighbor_col);
                count += self.cells[idx] as u8;
            }
        }
        count
    }
}
}

live_neighbor_count ๋ฉ”์†Œ๋“œ๋Š” ๋ธํƒ€๊ฐ’๊ณผ ๋‚˜๋จธ์ง€ ๊ฐ’์„ ๊ฐ๊ฐ ํ™•์ธํ•ด์„œ ์„ธ์ƒ์˜ ๊ฐ€์žฅ์ž๋ฆฌ์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ˆ์™ธ๋ฅผ if ๋ฌธ์œผ๋กœ ์ฒ˜๋ฆฌํ•ด ์ค๋‹ˆ๋‹ค. -1์˜ ๋ธํƒ€๋ฅผ ์ ์šฉํ•  ๋•Œ, self.height - 1์„ ์ถ”๊ฐ€ํ•ด์„œ 1์„ ๋นผ๋Š” ๋Œ€์‹  ๋‚˜๋จธ์ง€ ๊ฐ’์„ ๊ณ„์† ์ฒ˜๋ฆฌํ•˜๊ณ , row์™€ column์€ ๊ฐ๊ฐ 0์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด row์™€ column ๊ฐ’์—์„œ 1์„ ๋นผ๋ ค๊ณ  ์‹œ๋„ํ•  ๋•Œ, unsigned integer underflow ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด์ œ ํ˜„์žฌ ์„ธ๋Œ€๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹ค์Œ ์„ธ๋Œ€๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ์ค€๋น„๊ฐ€ ์™„๋ฃŒ๋์Šต๋‹ˆ๋‹ค! match ๋ฌธ์„ ์‚ฌ์šฉํ•ด์„œ ๊ฒŒ์ž„์˜ ๊ทœ์น™์„ ๋ณด๊ธฐ ๋ช…ํ™•ํ•˜๊ฒŒ ๋‚˜ํƒ€๋‚ด๋ด…์‹œ๋‹ค. ์ถ”๊ฐ€๋กœ ํ‹ฑ์ด ์ผ์–ด๋‚  ๋•Œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ปจํŠธ๋กคํ•˜๋„๋ก ํ•  ์˜ˆ์ •์ด๊ธฐ ๋•Œ๋ฌธ์—, #[wasm_bindgen] ๋ธ”๋Ÿญ์„ ์ถ”๊ฐ€ํ•ด์„œ ์ด ๋ฉ”์†Œ๋“œ๋ฅผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ์— ๋…ธ์ถœ์‹œ์ผœ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

#![allow(unused)]
fn main() {
/// Public ๋ฉ”์†Œ๋“œ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ์ต์ŠคํฌํŠธ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ.
#[wasm_bindgen]
impl Universe {
    pub fn tick(&mut self) {
        let mut next = self.cells.clone();

        for row in 0..self.height {
            for col in 0..self.width {
                let idx = self.get_index(row, col);
                let cell = self.cells[idx];
                let live_neighbors = self.live_neighbor_count(row, col);

                let next_cell = match (cell, live_neighbors) {
                    // ๊ทœ์น™ 1: ์ธ๊ตฌ ๋ถ€์กฑ์œผ๋กœ 2๊ฐœ ๋ฏธ๋งŒ์˜ ์ด์›ƒ์„ ๊ฐ€์ง„ ์„ธํฌ๋Š” ์ฃฝ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
                    (Cell::Alive, x) if x < 2 => Cell::Dead,
                    // ๊ทœ์น™ 2: 2๊ฐœ ํ˜น์€ 3๊ฐœ์˜ ์ด์›ƒ์„ ๊ฐ€์ง„ ์„ธํฌ๋Š” ๋‹ค์Œ ์„ธ๋Œ€์—์„œ ๊ณ„์† ์‚ด์•„์žˆ์Šต๋‹ˆ๋‹ค.
                    (Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive,
                    // Rule 3: ๊ณผ์ž‰ ์ธ๊ตฌ๋กœ 3๊ฐœ ์ดˆ๊ณผ์˜ ์ด์›ƒ์„ ๊ฐ€์ง„ ๋ชจ๋“  ์„ธํฌ๋Š” ์ฃฝ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
                    (Cell::Alive, x) if x > 3 => Cell::Dead,
                    // ๊ทœ์น™ 4: ์„ธํฌ ์ฆ์‹์œผ๋กœ ์ •ํ™•ํžˆ 3๊ฐœ์˜ ์ด์›ƒ์„ ๊ฐ€์ง„ ์„ธํฌ๋Š” ์‚ด์•„๋‚˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
                    (Cell::Dead, 3) => Cell::Alive,
                    // ๊ทœ์น™์ด ์ ์šฉ๋˜์ง€ ์•Š๋Š” ์„ธํฌ๋“ค์˜ ์ƒํƒœ๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
                    (otherwise, _) => otherwise,
                };

                next[idx] = next_cell;
            }
        }

        self.cells = next;
    }

    // ...
}
}

์ง€๊ธˆ๊นŒ์ง€๋Š” ์„ธ์ƒ์˜ ์ƒํƒœ๋ฅผ ์„ธํฌ๋“ค์˜ ๋ฒกํ„ฐ๋กœ ๋‚˜ํƒ€๋ƒˆ์Šต๋‹ˆ๋‹ค. ์กฐ๊ธˆ ๋” ์‚ฌ๋žŒ์ด ์ฝ๊ธฐ ์‰ฝ๋„๋ก ํ…์ŠคํŠธ ๋ Œ๋”๋Ÿฌ (text renderer) ๋ฅผ ๊ตฌํ˜„ํ•ด ๋ณด๋„๋ก ํ•ฉ์‹œ๋‹ค. ์„ธ์ƒ์„ ํ•œ์ค„ ํ•œ์ค„์”ฉ ํ…์ŠคํŠธ๋กœ ํ‘œํ˜„์„ ํ•ด๋ณด๋„๋ก ํ•˜๋Š”๋ฐ, ์‚ด์•„์žˆ๋Š” ์„ธํฌ๋“ค์„ ์œ ๋‹ˆ์ฝ”๋“œ ๋ฌธ์ž โ—ผ ("black medium square") ๋กœ ๋‚˜ํƒ€๋‚ด๊ณ  ์ฃฝ์€ ์„ธํฌ๋“ค์„ โ—ป ("white medium square") ๋กœ ํ‘œํ˜„ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ, ๋Ÿฌ์ŠคํŠธ ์Šคํƒ ๋‹ค๋“œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ Display ํŠธ๋ ˆ์ดํŠธ๋ฅผ ๊ตฌํ˜„ํ•ด์„œ ์‚ฌ๋žŒ์ด ์ฝ๊ธฐ ์‰ฌ์šด ๋ฐฉ์‹์œผ๋กœ ํฌ๋งทํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฉ”์†Œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ํŠธ๋ ˆ์ดํŠธ๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด ์ž๋™์ ์œผ๋กœ Universe์˜ ์ธ์Šคํ„ด์Šค(instance)๋“ค์ด to_string ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

#![allow(unused)]
fn main() {
use std::fmt;

impl fmt::Display for Universe {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for line in self.cells.as_slice().chunks(self.width as usize) {
            for &cell in line {
                let symbol = if cell == Cell::Dead { 'โ—ป' } else { 'โ—ผ' };
                write!(f, "{}", symbol)?;
            }
            write!(f, "\n")?;
        }

        Ok(())
    }
}
}

๋งˆ์ง€๋ง‰์œผ๋กœ render ๋ฉ”์†Œ๋“œ์™€ ํ•จ๊ป˜ ์ƒ์„ฑ์ž๋ฅผ ์ •์˜ํ•ด์„œ ์„ธํฌ๋“ค์ด ์‚ด์•„๋‚˜๊ณ  ์ฃฝ์–ด๊ฐ€๋Š” ์‹ ๊ธฐํ•œ ์„ธ์ƒ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

#![allow(unused)]
fn main() {
/// Public ๋ฉ”์†Œ๋“œ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ์ต์ŠคํฌํŠธ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ.
#[wasm_bindgen]
impl Universe {
    // ...

    pub fn new() -> Universe {
        let width = 64;
        let height = 64;

        let cells = (0..width * height)
            .map(|i| {
                if i % 2 == 0 || i % 7 == 0 {
                    Cell::Alive
                } else {
                    Cell::Dead
                }
            })
            .collect();

        Universe {
            width,
            height,
            cells,
        }
    }

    pub fn render(&self) -> String {
        self.to_string()
    }
}
}

๋“œ๋””์–ด Game of Life์˜ ๋Ÿฌ์ŠคํŠธ ์ฝ”๋“œ ๊ตฌํ˜„์ด ๋๋‚ฌ์Šต๋‹ˆ๋‹ค!

์ด์ œ wasm-game-of-life ๊ฒฝ๋กœ์—์„œ wasm-pack build๋ฅผ ์‹คํ–‰ํ•ด์„œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํŒŒ์ผ์„ ๋‹ค์‹œ ์ปดํŒŒ์ผํ•ด ์ฃผ์„ธ์š”.

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ํŽ˜์ด์ง€ ๋ Œ๋”๋งํ•˜๊ธฐ

wasm-game-of-life/www/index.html์— <pre> ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ ์„ธ์ƒ์„ ๋ Œ๋”๋งํ•ด ๋ด…์‹œ๋‹ค. <pre> ์š”์†Œ๋ฅผ <script> ํƒœ๊ทธ ๋ฐ”๋กœ ์œ„์— ์ถ”๊ฐ€ํ•ด ์ฃผ์„ธ์š”:

<body>
  <pre id="game-of-life-canvas"></pre>
  <script src="./bootstrap.js"></script>
</body>

์ถ”๊ฐ€๋กœ, <pre> ๊ฐ€ ์›น์‚ฌ์ดํŠธ ์ค‘๊ฐ„์— ํ‘œ์‹œ๋  ์ˆ˜ ์žˆ๋„๋ก CSS flex box๋ฅผ ์‚ฌ์šฉํ•ด ๋ด…์‹œ๋‹ค. wasm-game-of-life/www/index.html ํŒŒ์ผ์„ ์—ด๊ณ  <head> ๋‚ด์— <style> ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•ด ์ฃผ์„ธ์š”:

<style>
  body {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }
</style>

์ด์ œ wasm-game-of-life/www/index.js ํŒŒ์ผ ์ตœ์ƒ๋‹จ ์œ„์น˜ํ•œ ๊ธฐ์กด greet ํ•จ์ˆ˜๋ฅผ ์ง€์šฐ๊ณ  Universe๋ฅผ ์ž„ํฌํŠธํ•˜๋Š” ์ค„์„ ์ถ”๊ฐ€ํ•ด ์ฃผ์„ธ์š”.

import { Universe } from "wasm-game-of-life";

<pre> ์š”์†Œ๋ฅผ pre ์ƒ์ˆ˜์— ๋‹ด์€ ๋‹ค์Œ ์ƒˆ ์šฐ์ฃผ๋ฅผ ์‹œ์ž‘ํ•ด ๋ด…์‹œ๋‹ค.

const pre = document.getElementById("game-of-life-canvas");
const universe = Universe.new();

์ด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ•จ์ˆ˜๋Š” requestAnimationFrame ๋ฃจํ”„๋กœ ์‹คํ–‰ํ•ด์„œ ๋งค ๋ฐ˜๋ณต๋งˆ๋‹ค ์—…๋ฐ์ดํŠธ๋œ ์„ธ์ƒ์„ <pre>์— ๋ฐ˜์˜ํ•˜๊ณ  Universe::tick์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

const renderLoop = () => {
  pre.textContent = universe.render();
  universe.tick();

  requestAnimationFrame(renderLoop);
};

๋ Œ๋”๋ง ์ฒ˜๋ฆฌ๋ฅผ ์‹œ์ž‘ํ•˜๋ ค๋ฉด ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ renderLoop ํ•จ์ˆ˜ ๋ฐ–์— ์ถ”๊ฐ€ํ•ด์„œ ๋ Œ๋”๋ง ๋ฃจํ”„๋ฅผ ์‹œ์ž‘ํ•ด ์ฃผ์„ธ์š”:

requestAnimationFrame(renderLoop);

๋‹ค์‹œ ํ•œ๋ฒˆ (npm run์„ wasm-game-of-life/www ๊ฒฝ๋กœ์—์„œ ์‹คํ–‰ํ•œ) ๊ฐœ๋ฐœ ์„œ๋ฒ„๊ฐ€ ์•„์ง ๊ตฌ๋™ ์ค‘์ธ์ง€ ํ™•์ธํ•ด ์ฃผ์„ธ์š”. http://localhost:8080/ ํŽ˜์ด์ง€๋ฅผ ์—ด๋ฉด ๋‹ค์Œ ๋‚ด์šฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค:

ํ…์ŠคํŠธ๋ฅผ ๋ Œ๋”ํ•˜๋Š” Game of Life ๊ตฌํ˜„ ์Šคํฌ๋ฆฐ์ƒท

๋ฉ”๋ชจ๋ฆฌ์—์„œ ๋ฐ”๋กœ ์บ”๋ฒ„์Šค๋กœ ๋ Œ๋”๋งํ•˜๊ธฐ

๋Ÿฌ์ŠคํŠธ ์ฝ”๋“œ์—์„œ String์„ ์ƒ์„ฑ (๋ฐ ํ• ๋‹น) ํ•˜๊ณ  wasm-bindgen๋กœ ์ด ์ƒ์„ฑํ•œ ๊ฐ’์„ ์œ ํšจํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด ์„ธํฌ๋“ค์„ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๋ณต์‚ฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ์—์„œ ์„ธ์ƒ์˜ ๋„ˆ๋น„์™€ ๋†’์ด๋ฅผ ์ด๋ฏธ ์•Œ๊ณ  ์žˆ๊ณ , ์„ธํฌ๋ฅผ ๋งŒ๋“œ๋Š” ์ฒ˜๋ฆฌ๊ฐ€ ์ด๋ฃจ์–ด์ง€๋Š” ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, render ๋ฉ”์†Œ๋“œ๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ cells ๋ฐฐ์—ด์˜ ์‹œ์ž‘์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ํฌ์ธํ„ฐ๋ฅผ ๋Œ€์‹  ๋ฐ˜ํ™˜ํ•ด ๋ณด๋„๋ก ํ•ฉ์‹œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์œ ๋‹ˆ์ฝ”๋“œ ๋ฌธ์ž๋ฅผ ๋ Œ๋”๋งํ•˜์ง€ ์•Š๊ณ  Canvas API ๋ฅผ ๋Œ€์‹  ์‚ฌ์šฉํ•ด ๋ด…์‹œ๋‹ค. ์ด API๋ฅผ ์ด ๋ถ€๋ถ„ ์ดํ›„๋ถ€ํ„ฐ ๊ณ„์† ์‚ฌ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

wasm-game-of-life/www/index.html ํŒŒ์ผ์—์„œ ์ด์ „์— ์ถ”๊ฐ€ํ•œ <pre>๋ฅผ ์ง€์šฐ๊ณ  ๋ Œ๋”๋ง์— ์‚ฌ์šฉํ•  <canvas>๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”. (<body> ํƒœ๊ทธ ๋‚ด์—์„œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋ฅผ ์‹œ์ž‘์‹œํ‚ค๋Š” <script> ํƒœ๊ทธ ์œ„์— ์ถ”๊ฐ€๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค.)

<body>
  <canvas id="game-of-life-canvas"></canvas>
  <script src='./bootstrap.js'></script>
</body>

๋Ÿฌ์ŠคํŠธ๋กœ ๊ตฌํ˜„ํ•œ ์ฝ”๋“œ์—์„œ ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ์–ป์–ด์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ์„ธ์ƒ์˜ ๋„“์ด, ๋„ˆ๋น„, ์„ธํฌ ๋ฐฐ์—ด์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ํฌ์ธํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” getter ํ•จ์ˆ˜๋“ค์„ ์กฐ๊ธˆ ๋” ์ž‘์„ฑํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋“ค๋„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋กœ ๋…ธ์ถœ์‹œ์ผœ์•ผ ํ•˜๋‹ˆ ์ž˜ ํ™•์ธํ•ด ์ฃผ๊ณ , wasm-game-of-life/src/lib.rs ํŒŒ์ผ์— ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด ์ฃผ์„ธ์š”:

#![allow(unused)]
fn main() {
/// Public ๋ฉ”์†Œ๋“œ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ์ต์ŠคํฌํŠธ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ.
#[wasm_bindgen]
impl Universe {
    // ...

    pub fn width(&self) -> u32 {
        self.width
    }

    pub fn height(&self) -> u32 {
        self.height
    }

    pub fn cells(&self) -> *const Cell {
        self.cells.as_ptr()
    }
}
}

๊ทธ ๋‹ค์Œ, wasm-game-of-life/www/index.js ํŒŒ์ผ์˜ wasm-game-of-life ๋ชจ๋“ˆ์—์„œ Cell์„ ์ž„ํฌํŠธํ•˜๋Š” ์ค„์„ ์ถ”๊ฐ€ํ•ด ์ค€ ๋‹ค์Œ ์บ”๋ฒ„์Šค๋ฅผ ๋ Œ๋”๋งํ•  ๋•Œ ์‚ฌ์šฉํ•  ์ƒ์ˆ˜ ๋ช‡ ๊ฐ€์ง€๋ฅผ ์ •์˜ํ•ด์ฃผ์„ธ์š”:

import { Universe, Cell } from "wasm-game-of-life";

const CELL_SIZE = 5; // px
const GRID_COLOR = "#CCCCCC";
const DEAD_COLOR = "#FFFFFF";
const ALIVE_COLOR = "#000000";

์ด์ œ <pre> ํƒœ๊ทธ์˜ textContent ๋Œ€์‹ ์— <canvas>๋ฅผ ์—…๋ฐ์ดํŠธ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ๋‚˜๋จธ์ง€ ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”:

// Universe๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๋„ˆ๋น„์™€ ๋†’์ด๋ฅผ ๋ฐ˜ํ™˜๋ฐ›์Šต๋‹ˆ๋‹ค.
const universe = Universe.new();
const width = universe.width();
const height = universe.height();

// ์บ”๋ฒ„์Šค์— ์„ธํฌ๋“ค์„ ํ‘œ์‹œํ•  ๊ณต๊ฐ„์„ ๋งŒ๋“ค์–ด์ฃผ๊ณ , ๊ฐ ์„ธํฌ๋“ค์ด 1px ๋‘๊ป˜์˜ ํ…Œ๋‘๋ฆฌ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค๋‹ˆ๋‹ค.
const canvas = document.getElementById("game-of-life-canvas");
canvas.height = (CELL_SIZE + 1) * height + 1;
canvas.width = (CELL_SIZE + 1) * width + 1;

const ctx = canvas.getContext('2d');

const renderLoop = () => {
  universe.tick();

  drawGrid();
  drawCells();

  requestAnimationFrame(renderLoop);
};

์„ธํฌ๋“ค ์‚ฌ์ด์— ๊ฒฉ์ž๋ฅผ ๊ทธ๋ฆฌ๋ ค๋ฉด ์ผ์ •ํ•œ ๊ฐ„๊ฒฉ์œผ๋กœ ๋‚˜๋ž€ํžˆ ๋†“์ธ ์ˆ˜ํ‰์„ ๊ณผ ์ˆ˜์ง์„ ์„ ๊ทธ๋ ค์ค๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์„ ๋“ค์€ ๊ต์ฐจํ•˜์—ฌ ๊ฒฉ์ž๋ฅผ ํ˜•์„ฑํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

const drawGrid = () => {
  ctx.beginPath();
  ctx.strokeStyle = GRID_COLOR;

  // ์ˆ˜์ง์ค„
  for (let i = 0; i <= width; i++) {
    ctx.moveTo(i * (CELL_SIZE + 1) + 1, 0);
    ctx.lineTo(i * (CELL_SIZE + 1) + 1, (CELL_SIZE + 1) * height + 1);
  }

  // ์ˆ˜ํ‰์ค„
  for (let j = 0; j <= height; j++) {
    ctx.moveTo(0,                           j * (CELL_SIZE + 1) + 1);
    ctx.lineTo((CELL_SIZE + 1) * width + 1, j * (CELL_SIZE + 1) + 1);
  }

  ctx.stroke();
};

raw wasm ๋ชจ๋“ˆ์ธ wasm_game_of_life_bg์— ์ •์˜๋œ memory๋ฅผ ์ž„ํฌํŠธํ•ด์„œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ์˜ ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„ธํฌ๋ฅผ ๊ทธ๋ฆฌ๋ ค๋ฉด ์šฐ์„  ์„ธ์ƒ์— ์žˆ๋Š” ์„ธํฌ๋“ค์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ํฌ์ธํ„ฐ๋ฅผ ๋ฐ˜ํ™˜๋ฐ›๊ณ  ์„ธํฌ ๋ฒ„ํผ(buffer)๋ฅผ ์˜ค๋ฒ„๋ ˆ์ด(overlay)ํ•˜๋Š” Uint8Array ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ๋‹ค์Œ ๊ฐ ์„ธํฌ๋ฅผ ์ˆœํšŒํ•˜์—ฌ ์ƒ์กด ์—ฌ๋ถ€์— ๋”ฐ๋ผ ํฐ์ƒ‰ ๋˜๋Š” ๊ฒ€์€์ƒ‰ ์‚ฌ๊ฐํ˜•์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค. ํฌ์ธํ„ฐ๋ฅผ ์˜ค๋ฒ„๋ ˆ์ด ํ•˜๊ฒŒ ๋˜๋ฉด์„œ, ๋ชจ๋“  ํ‹ฑ๋งˆ๋‹ค ์„ธํฌ๋“ค์„ ๋ณต์‚ฌํ•˜์ง€ ์•Š๋„๋ก ์ตœ์ ํ™” ์ž‘์—…์„ ํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํŒŒ์ผ ์ตœ์ƒ๋‹จ์— ์ž„ํฌํŠธ ํ•ด์ค๋‹ˆ๋‹ค.
import { memory } from "wasm-game-of-life/wasm_game_of_life_bg";

// ...

const getIndex = (row, column) => {
  return row * width + column;
};

const drawCells = () => {
  const cellsPtr = universe.cells();
  const cells = new Uint8Array(memory.buffer, cellsPtr, width * height);

  ctx.beginPath();

  for (let row = 0; row < height; row++) {
    for (let col = 0; col < width; col++) {
      const idx = getIndex(row, col);

      ctx.fillStyle = cells[idx] === Cell.Dead
        ? DEAD_COLOR
        : ALIVE_COLOR;

      ctx.fillRect(
        col * (CELL_SIZE + 1) + 1,
        row * (CELL_SIZE + 1) + 1,
        CELL_SIZE,
        CELL_SIZE
      );
    }
  }

  ctx.stroke();
};

์ด์ „์— ๋ณด์—ฌ๋“œ๋ฆฐ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ Œ๋”๋ง ์ฒ˜๋ฆฌ๋ฅผ ์‹œ์ž‘ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

drawGrid();
drawCells();
requestAnimationFrame(renderLoop);

drawGrid()์™€ drawCells() ๋‘ ํ•จ์ˆ˜๋ฅผ requestAnimationFrame() ํ•จ์ˆ˜ ํ˜ธ์ถœ ์ด์ „์— ํ˜ธ์ถœํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ ์„ ๊ผญ ๊ธฐ์–ตํ•ด ์ฃผ์„ธ์š”. ์ดˆ๊ธฐ ์ƒํƒœ์˜ ์„ธ์ƒ์ด ๊ทธ๋ ค์ง„ ์ดํ›„์— ์ˆ˜์ • ์‚ฌํ•ญ์„ ์ ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. requestAnimationFrame(renderLoop) ํ•จ์ˆ˜๋งŒ ํ˜ธ์ถœํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด universe.tick()์ด ํ˜ธ์ถœ๋œ ์ดํ›„ ์‹œ์ ์˜ ๋‘ ๋ฒˆ์งธ ํ‹ฑ์ด ๋Œ€์‹  ๊ทธ๋ ค์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋‹ค ๋์–ด์š”!

์ตœ์ƒ๋‹จ wasm-game-of-life ๊ฒฝ๋กœ์—์„œ ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ์›น์–ด์…ˆ๋ธ”๋ฆฌ์™€ ๋ฐ”์ธ๋”ฉ ํŒŒ์ผ (bindings glue) ๋“ค์„ ๋‹ค์‹œ ๋นŒ๋“œํ•ด์ค์‹œ๋‹ค:

wasm-pack build

๋‹ค์‹œ ํ•œ๋ฒˆ ๊ฐœ๋ฐœ ์„œ๋ฒ„๊ฐ€ ์•„์ง ๊ตฌ๋™ ์ค‘์ธ์ง€ ํ™•์ธํ•ด์ฃผ์„ธ์š”. ๊ตฌ๋™ ์ค‘์ด์ง€ ์•Š๋‹ค๋ฉด wasm-game-of-life/www ๊ฒฝ๋กœ์—์„œ ๋‹ค์‹œ ์‹œ์ž‘ํ•ด์ฃผ์„ธ์š”:

npm run start

http://localhost:8080/ ํŽ˜์ด์ง€๋ฅผ ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ๊ตฌํ˜„๋œ ํฅ๋ฏธ์ง„์ง„ํ•œ Game of Life๊ฐ€ ์‹œ์ž‘๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Game of Life ๊ตฌํ˜„ ์Šคํฌ๋ฆฐ์ƒท

์ถ”๊ฐ€๋กœ ๊ด€์‹ฌ์ด ์žˆ๋‹ค๋ฉด, hashlife ๋ผ๋Š” ์—„์ฒญ ๋ฉ‹์ง„ Game of Life ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ตฌํ˜„๋„ ์žˆ์œผ๋‹ˆ ํ•œ๋ฒˆ ํ™•์ธํ•ด ๋ณด์„ธ์š”. ์ด ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ๊ณต๊ฒฉ์ ์ธ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ (aggressive memoizing) ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋Š”๋ฐ, ๋•๋ถ„์— ์ฝ”๋“œ๊ฐ€ ๋” ์˜ค๋ž˜ ๊ตฌ๋™๋˜๋Š” ๋งŒํผ ๋ฏธ๋ž˜ ์„ธ๋Œ€๋“ค์„ ๊ธฐํ•˜๊ธ‰์ˆ˜์ ์œผ๋กœ ๋” ๋น ๋ฅด๊ฒŒ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. hashlife๋ฅผ ์ด ํŠœํ† ๋ฆฌ์–ผ์—์„œ ๊ตฌํ˜„ํ•ด ๋ณด๋ฉด ์ •๋ง ์žฌ๋ฐŒ๊ฒ ์ง€๋งŒ, ์ด ์ฑ…์€ ๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์‚ฌ์šฉ์— ์ค‘์ ์„ ๋‘๊ณ  ์žˆ์œผ๋ฏ€๋กœ ๋‹ค๋ฃจ์ง€ ์•Š๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ hashlife์— ๋Œ€ํ•ด ๋”ฐ๋กœ ๋ฐฐ์›Œ๋ณด๊ธธ ์ ๊ทน์ ์œผ๋กœ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

์—ฐ์Šตํ•ด ๋ณด๊ธฐ

  • space ship ํŒจํ„ด ํ•˜๋‚˜๋ฅผ ํ‘œ์‹œํ•˜๋Š” ์„ธ์ƒ์„ ๋งŒ๋“ค์–ด๋ณด์„ธ์š”.

  • ์ดˆ๊ธฐ ์„ธ์ƒ์„ ํ•˜๋“œ์ฝ”๋”ฉ ํ•˜๋Š” ๋Œ€์‹ , ๊ฐ ์„ธํฌ๊ฐ€ 50% ํ™•๋ฅ ๋กœ ์‚ด์•„์žˆ๊ฑฐ๋‚˜ ์ฃฝ์–ด์žˆ๋Š” ์ƒํƒœ๋กœ ๋žœ๋คํ•˜๊ฒŒ ์ƒ์„ฑ๋  ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋ณด์„ธ์š”.

    ํžŒํŠธ: the js-sys crate ํฌ๋ ˆ์ดํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Math.random ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ•จ์ˆ˜๋ฅผ ์ž„ํฌํŠธํ•ด๋ณด์„ธ์š”.

    ์ •๋‹ต

    ๋จผ์ €, wasm-game-of-life/Cargo.toml ํŒŒ์ผ์„ ์—ด๊ณ  js-sys๋ฅผ ์ข…์†์„ฑ์œผ๋กœ ์ถ”๊ฐ€ํ•ด ์ฃผ์„ธ์š”:

    # ...
    [dependencies]
    js-sys = "0.3"
    # ...
    

    ๊ทธ ๋‹ค์Œ js_sys::Math::random ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์„œ 50% ํ™•๋ฅ ๋กœ ๊ฐ’์„ ๊ฒฐ์ •ํ•ด ์ฃผ์„ธ์š”:

    #![allow(unused)]
    fn main() {
    extern crate js_sys;
    
    // ...
    
    if js_sys::Math::random() < 0.5 {
        // ์„ธํฌ๊ฐ€ ์‚ด์•„์žˆ๊ฒŒ ํ•จ
    } else {
        // ์„ธํฌ๊ฐ€ ์ฃฝ์–ด์žˆ๊ฒŒ ํ•จ
    }
    }
  • ๊ฐ ์„ธํฌ๋ฅผ byte ๊ฐ’์œผ๋กœ ํ‘œํ˜„ํ•˜๋ฉด์„œ ์ˆœํšŒ๋ฅผ ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋ฉ”๋ชจ๋ฆฌ ์ž์›์„ ๋‚ญ๋น„ํ•œ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. 1 byte๋Š” 8 bit์ธ๋ฐ, ์‹ค์ œ๋กœ ์„ธํฌ ์ƒ์กด ์—ฌ๋ถ€๋ฅผ ํ‘œ์‹œํ•  ๋•Œ๋Š” 1 bit๋งŒ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฐ์ดํ„ฐ ํ‘œํ˜„๋“ค์„ ๋ฆฌํŒฉํ† ๋งํ•˜์—ฌ ๊ฐ ์„ธํฌ๊ฐ€ 1 bit์˜ ์‚ฌ์ด์ฆˆ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ณด์„ธ์š”.

    ์ •๋‹ต

    ๋Ÿฌ์ŠคํŠธ ์–ธ์–ด์—์„œ Vec<Cell> ํƒ€์ž… ๋Œ€์‹  (์ถ”๊ฐ€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ) fixedbitset ํฌ๋ ˆ์ดํŠธ์˜ FixedBitSet ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜์—ฌ ์„ธํฌ๋“ค์„ ๋‚˜ํƒ€๋‚ด๋ณผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

    #![allow(unused)]
    fn main() {
    // Cargo.toml์— ์ข…์†์„ฑ์„ ์ถ”๊ฐ€ํ–ˆ๋Š”์ง€ ํ™•์ธํ•ด ์ฃผ์„ธ์š”!
    extern crate fixedbitset;
    use fixedbitset::FixedBitSet;
    
    // ...
    
    #[wasm_bindgen]
    pub struct Universe {
        width: u32,
        height: u32,
        cells: FixedBitSet,
    }
    }

    Universe์˜ ์ƒ์„ฑ์ž๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

    #![allow(unused)]
    fn main() {
    pub fn new() -> Universe {
        let width = 64;
        let height = 64;
    
        let size = (width * height) as usize;
        let mut cells = FixedBitSet::with_capacity(size);
    
        for i in 0..size {
            cells.set(i, i % 2 == 0 || i % 7 == 0);
        }
    
        Universe {
            width,
            height,
            cells,
        }
    }
    }

    ๋‹ค์Œ ํ‹ฑ์—์„œ ์„ธํฌ๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋„๋ก FixedBitSet ํƒ€์ž…์˜ set ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ๋ด…์‹œ๋‹ค:

    #![allow(unused)]
    fn main() {
    next.set(idx, match (cell, live_neighbors) {
        (true, x) if x < 2 => false,
        (true, 2) | (true, 3) => true,
        (true, x) if x > 3 => false,
        (false, 3) => true,
        (otherwise, _) => otherwise
    });
    }

    ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ์‹œ์ž‘ํ•˜๋Š” bit๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ํฌ์ธํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•˜๋ฏ€๋กœ, FixedBitSet ํƒ€์ž…์„ ์Šฌ๋ผ์ด์Šค(slice)๋กœ ๋ณ€ํ™˜ํ•œ ๋‹ค์Œ ๋ณ€ํ™˜๋œ ์Šฌ๋ผ์ด์Šค๋ฅผ ํฌ์ธํ„ฐ๋กœ ๋‹ค์‹œ ๋ณ€ํ™˜ํ•ด ์ฃผ์„ธ์š”:

    #![allow(unused)]
    fn main() {
    #[wasm_bindgen]
    impl Universe {
        // ...
    
        pub fn cells(&self) -> *const u32 {
            self.cells.as_slice().as_ptr()
        }
    }
    }

    ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ์—์„œ Uint8Array๋ฅผ ์ƒ์„ฑํ•ด์˜ค๋Š” ๋ฐฉ๋ฒ•์€ ์ด์ „๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋ฒˆ์—๋Š” ๊ฐ ์„ธํฌ๋ฅผ ๋‚˜ํƒ€๋‚ด๊ธฐ ์œ„ํ•ด byte ๋Œ€์‹  bit์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ๋ฐฐ์—ด์˜ ๊ธธ์ด๊ฐ€ ๋” ์ด์ƒ width * height๊ฐ€ ์•„๋‹ˆ๊ณ  width * height / 8์ด ๋˜์–ด์•ผ ํ•˜๋‹ˆ ๋‹ค์‹œ ์ž˜ ํ™•์ธํ•ด ์ฃผ์„ธ์š”:

    const cells = new Uint8Array(memory.buffer, cellsPtr, width * height / 8);
    

    ๋‹ค์Œ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ธ๋ฑ์Šค์™€ Uint8Array๊ฐ€ ์ฃผ์–ด์งˆ ๋•Œ n๋ฒˆ์งธ bit์˜ ๊ฐ’์ด 0์ธ์ง€ 1์ธ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

    const bitIsSet = (n, arr) => {
      const byte = Math.floor(n / 8);
      const mask = 1 << (n % 8);
      return (arr[byte] & mask) === mask;
    };
    

    ์ด์ œ ์ค€๋น„๊ฐ€ ๋์œผ๋‹ˆ drawCells ํ•จ์ˆ˜๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์—…๋ฐ์ดํŠธํ•ด์ค์‹œ๋‹ค:

    const drawCells = () => {
      const cellsPtr = universe.cells();
    
      // ์ˆ˜์ •๋œ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค!
      const cells = new Uint8Array(memory.buffer, cellsPtr, width * height / 8);
    
      ctx.beginPath();
    
      for (let row = 0; row < height; row++) {
        for (let col = 0; col < width; col++) {
          const idx = getIndex(row, col);
    
          // ์ˆ˜์ •๋œ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค!
          ctx.fillStyle = bitIsSet(idx, cells)
            ? ALIVE_COLOR
            : DEAD_COLOR;
    
          ctx.fillRect(
            col * (CELL_SIZE + 1) + 1,
            row * (CELL_SIZE + 1) + 1,
            CELL_SIZE,
            CELL_SIZE
          );
        }
      }
    
      ctx.stroke();
    };
    

Game of Life ํ…Œ์ŠคํŒ…ํ•˜๊ธฐ

๋ธŒ๋ผ์šฐ์ € ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก Game of Life๋ฅผ ๊ตฌํ˜„ํ–ˆ์œผ๋‹ˆ ์ด์ œ ๋Ÿฌ์ŠคํŠธ ์ฝ”๋“œ์—์„œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํ•จ์ˆ˜๋ฅผ ํ…Œ์ŠคํŒ…ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ด…์‹œ๋‹ค.

tick ํ•จ์ˆ˜๋กœ ์˜ˆ์ƒ๊ฐ’๊ณผ ์ผ์น˜ํ•˜๋Š” ์˜ฌ๋ฐ”๋ฅธ ๊ฐ’์„ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ๋Š”์ง€ ํ…Œ์ŠคํŒ… ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์šฐ์„  ํ…Œ์ŠคํŒ…์„ ์ž‘์„ฑํ•˜๊ธฐ ์ „์— wasm_game_of_life/src/lib.rs ํŒŒ์ผ์— ์ž‘์„ฑํ•œ impl Universe ๋ธ”๋Ÿญ ๋‚ด๋ถ€์— setter ํ•จ์ˆ˜์™€ getter ํ•จ์ˆ˜๋“ค์„ ์กฐ๊ธˆ ๋” ์ž‘์„ฑํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. set_width์™€ set_height ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•ด์„œ ๋‹ค๋ฅธ ์‚ฌ์ด์ฆˆ์˜ Universe๋“ค์„ ๋งŒ๋“ค์–ด๋ณผ ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋ด…์‹œ๋‹ค.

#![allow(unused)]
fn main() {
#[wasm_bindgen]
impl Universe { 
    // ...

    /// ์„ธ์ƒ์˜ ๋„ˆ๋น„๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    ///
    /// ๋ชจ๋“  ์„ธํฌ๋ฅผ ์ฃฝ์€ ์ƒํƒœ๋กœ ๋ฆฌ์…‹ํ•ฉ๋‹ˆ๋‹ค.
    pub fn set_width(&mut self, width: u32) {
        self.width = width;
        self.cells = (0..width * self.height).map(|_i| Cell::Dead).collect();
    }

    /// ์„ธ์ƒ์˜ ๋„“์ด๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    ///
    /// ๋ชจ๋“  ์„ธํฌ๋ฅผ ์ฃฝ์€ ์ƒํƒœ๋กœ ๋ฆฌ์…‹ํ•ฉ๋‹ˆ๋‹ค.
    pub fn set_height(&mut self, height: u32) {
        self.height = height;
        self.cells = (0..self.width * height).map(|_i| Cell::Dead).collect();
    }

}
}

wasm_game_of_life/src/lib.rs ํŒŒ์ผ์— #[wasm_bindgen] ์†์„ฑ ์—†์ด impl Universe ๋ธ”๋Ÿญ์„ ํ•˜๋‚˜ ๋” ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ถ”๊ฐ€๋กœ ํ…Œ์ŠคํŒ…์— ์‚ฌ์šฉํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ํ•จ์ˆ˜๊ฐ€ ๋ช‡ ๊ฐœ ์žˆ๋Š”๋ฐ, ์ด ํ•จ์ˆ˜๋“ค์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ๋…ธ์ถœ์‹œํ‚ค์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋Ÿฌ์ŠคํŠธ๋กœ ์ƒ์„ฑํ•œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํ•จ์ˆ˜๋Š” ๋Œ€์—ฌํ•œ ์ฐธ์กฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜์ง€ ๋ชปํ•˜๋Š”๋ฐ, ์ด ํ•จ์ˆ˜๋“ค ์œ„์— #[wasm_bindgen] ์†์„ฑ์„ ์ถ”๊ฐ€ํ•ด ๋ณด๊ณ  ์–ด๋–ค ์—๋Ÿฌ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋Š”์ง€ ์‚ดํŽด๋ด…์‹œ๋‹ค.

get_cells ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•ด์„œ Universe์˜ cells ํ•„๋“œ ๊ฐ’์„ ๊ฐ€์ ธ์™€ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. set_cells ํ•จ์ˆ˜๋„ ์ž‘์„ฑํ•ด์„œ ์ฃผ์–ด์ง„ ํ–‰๊ณผ ์—ด์— ์œ„์น˜ํ•œ Universe์˜ ์„ธํฌ๋ฅผ Alive ์ƒํƒœ๋กœ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

#![allow(unused)]
fn main() {
impl Universe {
    /// ์„ธ์ƒ์— ์กด์žฌํ•˜๋Š” ๋ชจ๋“  ์ฃฝ์–ด์žˆ๋Š” ์„ธํฌ์™€ ์‚ด์•„์žˆ๋Š” ์„ธํฌ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
    pub fn get_cells(&self) -> &[Cell] {
        &self.cells
    }

    /// ๋ฐฐ์—ด๋กœ ์ฃผ์–ด์ง„ ํ–‰๊ณผ ์—ด๋“ค์„ ํ™•์ธํ•˜๊ณ  ์„ธํฌ๋“ค์„ ์‚ด์•„์žˆ๋Š” ์ƒํƒœ๋กœ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.
    pub fn set_cells(&mut self, cells: &[(u32, u32)]) {
        for (row, col) in cells.iter().cloned() {
            let idx = self.get_index(row, col);
            self.cells[idx] = Cell::Alive;
        }
    }

}
}

์ด์ œ wasm_game_of_life/tests/web.rs ํŒŒ์ผ์— ํ…Œ์ŠคํŒ… ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์ง„ํ–‰ํ•˜๊ธฐ ์ „์—, ์ด๋ฏธ ์™„์„ฑ๋œ ํ…Œ์ŠคํŒ… ์ฝ”๋“œ๊ฐ€ ์žˆ์œผ๋‹ˆ ํ•œ๋ฒˆ ์‚ดํŽด๋ด…์‹œ๋‹ค. wasm-game-of-life ๊ฒฝ๋กœ์—์„œ wasm-pack test --chrome --headless ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ๋Ÿฌ์ŠคํŠธ๋กœ ์ƒ์„ฑํ•œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํ…Œ์ŠคํŒ… ์ฝ”๋“œ๊ฐ€ ์ž˜ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. --firefox, --safari, --node ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ • ๋ธŒ๋ผ์šฐ์ € ํ™˜๊ฒฝ์—์„œ ์ฝ”๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์šฐ์„  wasm_game_of_life/tests/web.rs ํŒŒ์ผ์—์„œ, wasm_game_of_life ํฌ๋ ˆ์ดํŠธ์™€ Universe๋ฅผ ์ต์ŠคํฌํŠธ ํ•ด์ค์‹œ๋‹ค.

#![allow(unused)]
fn main() {
extern crate wasm_game_of_life;
use wasm_game_of_life::Universe;
}

wasm_game_of_life/tests/web.rs ํŒŒ์ผ์—์„œ spaceship ์˜ˆ์‹œ ํŒจํ„ด์„ ๋งŒ๋“œ๋Š” ํ•จ์ˆ˜๋“ค์„ ์ž‘์„ฑํ•ด ๋ด…์‹œ๋‹ค.

tick ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์‚ฌ์šฉํ•  ํŒจํ„ด๊ณผ, ํ•œ ํ‹ฑ ์ดํ›„์˜ ๊ฒฐ๊ด๊ฐ’์„ ๋น„๊ตํ•  ๋•Œ ์‚ฌ์šฉํ•  ์˜ˆ์ƒ๊ฐ’์ด ํ•„์š”ํ•œ๋ฐ, ์šฐ์„ ์€ input_spaceship ํ•จ์ˆ˜์—์„œ ์–ด๋–ค ์„ธํฌ๋“ค์„ Alive ์ƒํƒœ๋กœ ์ƒ์„ฑํ• ์ง€ ์ •ํ•ด์ค์‹œ๋‹ค. expected_spaceship ํ•จ์ˆ˜๋ฅผ ํ…Œ์ŠคํŠธํ•ด ๋ณด๋Š”๋ฐ, input_spaceship ํ˜ธ์ถœ ์ดํ›„ ์‹œ์  ํ‹ฑ์˜ spaceship ํŒจํ„ด์„ ์ง์ ‘ ๊ณ„์‚ฐํ•ด์„œ ๊ฐ’์„ ์ฑ„์›Œ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

#![allow(unused)]
fn main() {
#[cfg(test)]
pub fn input_spaceship() -> Universe {
    let mut universe = Universe::new();
    universe.set_width(6);
    universe.set_height(6);
    universe.set_cells(&[(1,2), (2,3), (3,1), (3,2), (3,3)]);
    universe
}

#[cfg(test)]
pub fn expected_spaceship() -> Universe {
    let mut universe = Universe::new();
    universe.set_width(6);
    universe.set_height(6);
    universe.set_cells(&[(2,1), (2,3), (3,2), (3,3), (4,2)]);
    universe
}
}

๋งˆ์ง€๋ง‰์œผ๋กœ test_tick ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•ด ์ฃผ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋จผ์ € input_spaceship()์™€ expected_spaceship()๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์ธ์Šคํ„ด์Šค๋“ค์„ ๋งŒ๋“ค์–ด์ค์‹œ๋‹ค. ๊ทธ๋‹ค์Œ์— input_universe ์ธ์Šคํ„ด์Šค์˜ tick ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด ์ฃผ๋„๋ก ํ•ฉ์‹œ๋‹ค. ์ถ”๊ฐ€๋กœ, assert_eq! ๋งคํฌ๋กœ๋ฅผ ์‚ฌ์šฉํ•ด์„œ get_cells()๋ฅผ ํ˜ธ์ถœํ•œ ๋‹ค์Œ input_universe์™€ expected_universe ๊ฐ€ ๋™์ผํ•œ Cell ํƒ€์ž…์˜ ๋ฐฐ์—ด์„ ๊ฐ’์œผ๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋งˆ๋ฌด๋ฆฌ๋กœ๋Š” ์ด ์ฝ”๋“œ ๋ธ”๋Ÿญ ์œ„์— #[wasm_bindgen_test] ์†์„ฑ์„ ์ถ”๊ฐ€ํ•ด์„œ wasm-pack test ๋ช…๋ น์–ด๋กœ ๋Ÿฌ์ŠคํŠธ๋กœ ์ƒ์„ฑํ•œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์ฝ”๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค์‹œ๋‹ค.

#![allow(unused)]
fn main() {
#[wasm_bindgen_test]
pub fn test_tick() {
    // ์ž‘์€ ์‚ฌ์ด์ฆˆ์˜ Universe๊ณผ spaceship ํŒจํ„ด์„ ์ƒ์„ฑํ•ด ๋ด…์‹œ๋‹ค.
    let mut input_universe = input_spaceship();

    // `input_universe` ์ธ์Šคํ„ด์Šค์˜ ๋‹ค์Œ ํ‹ฑ ๊ฒฐ๊ด๊ฐ’๊ณผ ๋น„๊ตํ•˜๊ฒŒ ๋  spaceship ํŒจํ„ด์ž…๋‹ˆ๋‹ค.
    let expected_universe = expected_spaceship();

    // `tick` ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๋‘ `Universe`์˜ ์„ธํฌ๋“ค์ด ๋™์ผํ•œ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    input_universe.tick();
    assert_eq!(&input_universe.get_cells(), &expected_universe.get_cells());
}
}

์ด์ œ wasm-game-of-life ๊ฒฝ๋กœ์—์„œ wasm-pack test --firefox --headless ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ํ…Œ์ŠคํŒ… ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ด ์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๋””๋ฒ„๊น…

์ฝ”๋“œ๋ฅผ ๋” ์ž‘์„ฑํ•˜๊ธฐ ์ „์—, ๋””๋ฒ„๊น… ํˆด๋“ค์„ ์กฐ๊ธˆ ์‚ดํŽด๋ณด๊ณ  ์žฅ์ „ํ•ด ๋ณผ๊นŒ์š”? ๊ด€์‹ฌ์ด ์žˆ๋‹ค๋ฉด ๋Ÿฌ์ŠคํŠธ๋กœ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•ด ๋ณผ ์ˆ˜ ์žˆ๋Š” ์ ‘๊ทผ ๋ฐฉ๋ฒ•๋“ค๊ณผ ํˆด๋“ค์— ๋Œ€ํ•ด ๋‹ค๋ฃจ๋Š” ์ฐธ์กฐ ํŽ˜์ด์ง€๋„ ํ™•์ธํ•ด ๋ณด์„ธ์š”!

ํŒจ๋‹‰ ๋กœ๊ทธ ํ™œ์„ฑํ™”ํ•˜๊ธฐ

์ฝ”๋“œ๊ฐ€ ํŒจ๋‹‰ ํ•  ๋•Œ ๊ฐœ๋ฐœ์ž ์ฝ˜์†”์— ๋„์›€์ด ๋˜๋Š” ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋ฅผ ํ‘œ์‹œํ•˜๋ฉด ๋””๋ฒ„๊น…์ด ๋” ์‰ฌ์›Œ์ง‘๋‹ˆ๋‹ค.

ํ•„์ˆ˜๋กœ ์‚ฌ์šฉํ•  ํ•„์š”๋Š” ์—†์ง€๋งŒ wasm-pack-template์€ wasm-game-of-life/src/utils.rs ํŒŒ์ผ์— ๊ธฐ๋ณธ์œผ๋กœ ํ™œ์„ฑํ™”๋ผ ์žˆ๋Š” ์ข…์†์„ฑ์ธ the console_error_panic_hook ํฌ๋ ˆ์ดํŠธ๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ์ด ํ›…(hook)์„ ์ƒ์„ฑ์ž์— ํฌํ•จ์‹œ์ผœ์ฃผ๋ฉด ์ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. wasm-game-of-life/src/lib.rs ํŒŒ์ผ ๋‚ด์˜ Universe::new ์ƒ์„ฑ์ž์—์„œ ๋ถˆ๋Ÿฌ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

#![allow(unused)]
fn main() {
pub fn new() -> Universe {
    utils::set_panic_hook();

    // ...
}
}

๊ตฌํ˜„ํ•œ Game of Life์— ๋กœ๊ทธ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ํ•˜๊ธฐ

web-sys ํฌ๋ ˆ์ดํŠธ๋ฅผ ํ†ตํ•ด console.log ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด ๋ณด๊ณ , ๋งค ์„ธํฌ๋“ค์˜ ์ •๋ณด๋ฅผ ๋กœ๊ทธ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์šฐ์„ , wasm-game-of-life/Cargo.toml ํŒŒ์ผ์„ ์—ด๊ณ  web-sys๋ฅผ ์ข…์†์„ฑ์œผ๋กœ ์ถ”๊ฐ€ํ•œ ๋‹ค์Œ "console" ๊ธฐ๋Šฅ์„ ํ™œ์„ฑํ™”ํ•ด ์ฃผ์„ธ์š”:

[dependencies]

# ...

[dependencies.web-sys]
version = "0.3"
features = [
  "console",
]

๋” ๋‚˜์€ ๊ฐœ๋ฐœ์ž ๊ฒฝํ—˜์„ ์œ„ํ•ด console.log ํ•จ์ˆ˜๋ฅผ println! ์Šคํƒ€์ผ์˜ ๋งคํฌ๋กœ๋กœ ๊ฐ์‹ธ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

#![allow(unused)]
fn main() {
extern crate web_sys;

// `println!(...)` ์Šคํƒ€์ผ์˜ ๋ฌธ๋ฒ•์„ `console.log`์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋งคํฌ๋กœ.
macro_rules! log {
    ( $( $t:tt )* ) => {
        web_sys::console::log_1(&format!( $( $t )* ).into());
    }
}
}

์ด์ œ ๋Ÿฌ์ŠคํŠธ ์ฝ”๋“œ์—์„œ log ๋งคํฌ๋กœ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์ฝ˜์†”์— ๋ฉ”์„ธ์ง€๋ฅผ ๋กœ๊ทธ ํ•ด๋ด…์‹œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด์„œ, ์„ธํฌ์˜ ์ƒํƒœ, ์ด์›ƒ ์ˆ˜, ๋‹ค์Œ ํ‹ฑ ์ƒํƒœ๋ฅผ ๋กœ๊ทธ ํ•  ์ˆ˜ ์žˆ๋„๋ก wasm-game-of-life/src/lib.rs ํŒŒ์ผ์„ ์ˆ˜์ •ํ•ด ์ฃผ์„ธ์š”:

diff --git a/src/lib.rs b/src/lib.rs
index f757641..a30e107 100755
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -123,6 +122,14 @@ impl Universe {
                 let cell = self.cells[idx];
                 let live_neighbors = self.live_neighbor_count(row, col);

+                log!(
+                    "cell[{}, {}] is initially {:?} and has {} live neighbors",
+                    row,
+                    col,
+                    cell,
+                    live_neighbors
+                );
+
                 let next_cell = match (cell, live_neighbors) {
                     // ๊ทœ์น™ 1: ์ธ๊ตฌ ๋ถ€์กฑ์œผ๋กœ 2๊ฐœ ๋ฏธ๋งŒ์˜ ์ด์›ƒ์„ ๊ฐ€์ง„ ์„ธํฌ๋Š” ์ฃฝ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. 
@@ -140,6 +147,8 @@ impl Universe {
                     (otherwise, _) => otherwise,
                 };

+                log!("    it becomes {:?}", next_cell);
+
                 next[idx] = next_cell;
             }
         }

๋””๋ฒ„๊ฑฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋งค ํ‹ฑ๋งˆ๋‹ค ์ผ์‹œ์ •์ง€ ์‹œํ‚ค๊ธฐ

๋ธŒ๋ผ์šฐ์ €์˜ ์Šคํƒญํ•‘ ๋””๋ฒ„๊ฑฐ (stepping debugger) ๋Š” Rust๋กœ ์ž‘์„ฑํ•œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณผ ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด์„œ, universe.tick() ํ˜ธ์ถœ ์ด์ „์— ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ์— debugger; ์ค„์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋””๋ฒ„๊น… ํˆด์„ ์‚ฌ์šฉํ•˜์—ฌ renderLoop์˜ ๋งค ์ˆœํšŒ๋งˆ๋‹ค ์ฝ”๋“œ ์‹คํ–‰์„ ์ผ์‹œ์ •์ง€์‹œํ‚ฌ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

const renderLoop = () => {
  debugger;
  universe.tick();

  drawGrid();
  drawCells();

  requestAnimationFrame(renderLoop);
};

์ด์ œ ๋กœ๊ทธ ๋ฉ”์„ธ์ง€๋ฅผ ์‰ฝ๊ฒŒ ์‚ดํŽด๋ณผ ์ˆ˜ ์žˆ๋„๋ก ๊ฐ„ํŽธํ•œ ์ฒดํฌํฌ์ธํŠธ(checkpoint) ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋์Šต๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ํ˜„์žฌ ๋ Œ๋”๋œ ํ”„๋ ˆ์ž„๊ณผ ์ด์ „ ํ”„๋ ˆ์ž„์„ ๋น„๊ตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Game of Life ๋””๋ฒ„๊น… ํ™”๋ฉด ์Šคํฌ๋ฆฐ์ƒท

์—ฐ์Šตํ•ด ๋ณด๊ธฐ

  • ์ฃฝ๊ฒŒ ๋˜๊ฑฐ๋‚˜ ์‚ด์•„๋‚˜๊ฒŒ ๋˜๋Š” ์‹์œผ๋กœ ์ƒํƒœ๊ฐ€ ์ „ํ™˜๋˜๋Š” ๊ฐ๊ฐ ์„ธํฌ์˜ ํ–‰๊ณผ ์—ด์„ ๊ธฐ๋กํ•  ์ˆ˜ ์žˆ๋„๋ก tick ํ•จ์ˆ˜๋ฅผ ๋กœ๊ทธ ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด ๋ณด์„ธ์š”.

  • Universe::new ๋ฉ”์†Œ๋“œ์— panic!() ๋งคํฌ๋กœ๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ ์›น ๋ธŒ๋ผ์šฐ์ €์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋””๋ฒ„๊น… ํˆด์—์„œ ํŒจ๋‹‰ํ•œ ์ฝ”๋“œ๋ฅผ ํ‡ด๊ฐ๊ฒ€์ƒ‰(backtrace) ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ˆ˜์ •ํ•ด ๋ณด์„ธ์š”. ์ถ”๊ฐ€๋กœ debug ์‹ฌ๋ณผ์„ ๋น„ํ™œ์„ฑํ™”ํ•œ ๋‹ค์Œ console_error_panic_hook ์„ ํƒ์  ์ข…์†์„ฑ์„ ๋‹ค์‹œ ๋นŒ๋“œํ–ˆ์„ ๋•Œ ์Šคํƒ ์ถ”์ ๋„ ๋‹ค์‹œ ํ™•์ธํ•ด ๋ณด์„ธ์š”. ๊ทธ๋ ‡๊ฒŒ ์œ ์šฉํ•˜์ง„ ์•Š์€ ๊ฒƒ ๊ฐ™์€๋ฐ, ์•ˆ ๊ทธ๋Ÿฐ๊ฐ€์š”?

์ƒํ˜ธ์ž‘์šฉ ์ถ”๊ฐ€ํ•˜๊ธฐ

๊ตฌํ˜„ํ•œ Game of Life์— ์ƒํ˜ธ์ž‘์šฉ์„ ์กฐ๊ธˆ ๋” ์ถ”๊ฐ€ํ•ด ๋ณด๋ฉด์„œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์กฐ๊ธˆ ๋” ํƒํ—˜ํ•ด ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ์„น์…˜์—์„œ๋Š” ์œ ์ €๋“ค์ด ์„ธํฌ๋ฅผ ํด๋ฆญํ•ด์„œ ์ƒ์กด ์—ฌ๋ถ€๋ฅผ ์ „ํ™˜์‹œํ‚ฌ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋ณด๊ณ , ์ผ์‹œ์ •์ง€ ๊ธฐ๋Šฅ๋„ ๊ตฌํ˜„ํ•ด์„œ ์„ธํฌ ํŒจํ„ด์„ ์œ ์ €๋“ค์ด ์†์‰ฝ๊ฒŒ ๊ทธ๋ ค๋ณผ ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋ณผ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

์ผ์‹œ์ •์ง€ ๋ฒ„ํŠผ

๊ฒŒ์ž„์„ ์ผ์‹œ์ •์ง€ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋„๋ก ๋ฒ„ํŠผ์„ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด ๋ด…์‹œ๋‹ค. wasm-game-of-life/www/index.html ํŒŒ์ผ ๋‚ด์˜ <canvas> ๋ฐ”๋กœ ์œ„์— ์ด ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”:

<button id="play-pause"></button>

wasm-game-of-life/www/index.js ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ์— ๋‹ค์Œ ๋ณ€๊ฒฝ์‚ฌํ•ญ๋“ค์„ ์ ์šฉํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

  • ์ทจ์†Œ์‹œํ‚ค๊ณ  ์‹ถ์€ ์‹๋ณ„์ž๋ฅผ cancelAnimationFrame ํ•จ์ˆ˜๋กœ ์ „๋‹ฌ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋„๋ก requestAnimationFrame ํ•จ์ˆ˜๊ฐ€ ์ œ์ผ ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฐ˜ํ™˜ํ•œ ์‹๋ณ„์ž๋ฅผ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค.

  • ์žฌ์ƒ/์ผ์‹œ์ •์ง€ ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ, ๋Œ€๊ธฐ ์ค‘์ธ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํ”„๋ ˆ์ž„์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ์‹๋ณ„์ž๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ์ด ์‹๋ณ„์ž๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์‹œ์ ์—์„œ๋Š” ๊ฒŒ์ž„์ด ์‹คํ–‰ ์ค‘์ผ ๊ฒƒ์ด๋ฏ€๋กœ renderLoop๊ฐ€ ๋‹ค์‹œ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋„๋ก ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ผ์‹œ์ •์ง€ ์‹œํ‚ต๋‹ˆ๋‹ค. ์‹๋ณ„์ž๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๋‹ค๋ฉด ๊ฒŒ์ž„์ด ๋ฉˆ์ถฐ์žˆ๋‹ค๋Š” ์˜๋ฏธ์ด๋ฏ€๋กœ requestAnimationFrame ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒŒ์ž„์„ ๋‹ค์‹œ ์‹œ์ž‘ํ•ด์ฃผ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

์ด ์ž‘์—…์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์—์„œ ์ง„ํ–‰ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋”ฐ๋กœ ๋Ÿฌ์ŠคํŠธ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

animationId ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ requestAnimationFrame ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ์‹๋ณ„์ž๋ฅผ ์ถ”์ ํ•ด ๋ด…์‹œ๋‹ค. ๋Œ€๊ธฐ ์ค‘์ธ ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์—†์„ ๋•Œ๋Š” ์ด ๊ฐ’์„ null๋กœ ์„ค์ •ํ•ด ์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

let animationId = null;

// `requestAnimationFrame`์˜ ๋ฐ˜ํ™˜๊ฐ’์ด `animationId`์—
// ํ• ๋‹น๋œ๋‹ค๋Š” ์  ์™ธ์—๋Š” ๊ธฐ์กด๊ณผ ๋™์ผํ•œ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
const renderLoop = () => {
  drawGrid();
  drawCells();

  universe.tick();

  animationId = requestAnimationFrame(renderLoop);
};

์–ธ์ œ๋“  animationId์˜ ๊ฐ’์„ ํ™•์ธํ•ด์„œ ๊ฒŒ์ž„์ด ๋ฉˆ์ถฐ์žˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

const isPaused = () => {
  return animationId === null;
};

์ด์ œ ์žฌ์ƒ/์ผ์‹œ์ •์ง€ ๋ฒ„ํŠผ์ด ํด๋ฆญ ๋์„ ๋•Œ ๊ฒŒ์ž„์˜ ์žฌ์ƒ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋์Šต๋‹ˆ๋‹ค. ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ renderLoop ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋‹ค์‹œ ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜ ์ผ์‹œ์ •์ง€ ์‹œํ‚ฌ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ถ”๊ฐ€๋กœ, ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ ํ‘œ์‹œ๋˜๋Š” ํ…์ŠคํŠธ์˜ ์•„์ด์ฝ˜์„ ์—…๋ฐ์ดํŠธํ•ด ์ฃผ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

const playPauseButton = document.getElementById("play-pause");

const play = () => {
  playPauseButton.textContent = "โธ";
  renderLoop();
};

const pause = () => {
  playPauseButton.textContent = "โ–ถ";
  cancelAnimationFrame(animationId);
  animationId = null;
};

playPauseButton.addEventListener("click", event => {
  if (isPaused()) {
    play();
  } else {
    pause();
  }
});

๋งˆ์ง€๋ง‰์œผ๋กœ, ์ถ”๊ฐ€ํ•œ ๋ฒ„ํŠผ์ด ์˜ฌ๋ฐ”๋ฅธ ์ดˆ๊ธฐ ํ…์ŠคํŠธ ์•„์ด์ฝ˜์„ ํ‘œ์‹œํ•˜๋„๋ก ์ด์ „์— requestAnimationFrame(renderLoop)์„ ์ง์ ‘ ๋ถˆ๋Ÿฌ์„œ ๋น ๋ฅด๊ฒŒ ์‹œ์ž‘ํ–ˆ๋˜ ๋ถ€๋ถ„์„ play ํ•จ์ˆ˜๋ฅผ ๋Œ€์‹  ํ˜ธ์ถœํ•˜๋„๋ก ๋ณ€๊ฒฝํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

// ๊ธฐ์กด์—๋Š” `requestAnimationFrame(renderLoop)`๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ค„์ด์—ˆ์Šต๋‹ˆ๋‹ค.
play();

http://localhost:8080/ ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ์ผ์‹œ์ •์ง€/์žฌ์ƒ ๋ฒ„ํŠผ์ด ์ถ”๊ฐ€๋œ ๋ถ€๋ถ„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

onclick ์ด๋ฒคํŠธ๋กœ ์„ธํฌ ์ƒํƒœ ์ „ํ™˜ํ•˜๊ธฐ

์ด์ œ ๊ฒŒ์ž„์„ ์ผ์‹œ์ •์ง€ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋์œผ๋‹ˆ ์„ธํฌ๋“ค์„ ํด๋ฆญํ•ด์„œ ์ƒํƒœ๋ฅผ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋„๋ก ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์„ธํฌ๋ฅผ ํด๋ฆญํ•  ๋•Œ, ํด๋ฆญํ•œ ์„ธํฌ์˜ ์ƒ์กด ์—ฌ๋ถ€๋ฅผ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. toggle ๋ฉ”์†Œ๋“œ๋ฅผ wasm-game-of-life/src/lib.rs ํŒŒ์ผ ๋‚ด์˜ Cell์— ์ถ”๊ฐ€ํ•ด ์ค์‹œ๋‹ค.

#![allow(unused)]
fn main() {
impl Cell {
    fn toggle(&mut self) {
        *self = match *self {
            Cell::Dead => Cell::Alive,
            Cell::Alive => Cell::Dead,
        };
    }
}
}

์ฃผ์–ด์ง„ ํ–‰๊ณผ ์—ด์— ์œ„์น˜ํ•œ ์„ธํฌ์˜ ์ƒํƒœ๋ฅผ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ๋„๋ก, ํ–‰๊ณผ ์—ด์„ ๋ฒกํ„ฐ์˜ ์ธ๋ฑ์Šค๋กœ ๋ณ€ํ™˜ํ•˜๊ณ , ๋ณ€ํ™˜ํ•œ ์ธ๋ฑ์Šค๋ฅผ ์„ธํฌ ๋ฒกํ„ฐ๋กœ ๋ณ€ํ™˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์„ธํฌ ๋ฒกํ„ฐ๊ฐ€ ์ค€๋น„๋๋‹ค๋ฉด ๋ณ€ํ™˜๋œ ์ธ๋ฑ์Šค์— ์œ„์น˜ํ•˜๋Š” ์„ธํฌ๋ฅผ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด ๋ด…์‹œ๋‹ค:

#![allow(unused)]
fn main() {
/// Public ๋ฉ”์†Œ๋“œ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ์ต์ŠคํฌํŠธ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ.
#[wasm_bindgen]
impl Universe {
    // ...

    pub fn toggle_cell(&mut self, row: u32, column: u32) {
        let idx = self.get_index(row, column);
        self.cells[idx].toggle();
    }
}
}

์ด impl ๋ธ”๋Ÿญ ๋‚ด์— ์ •์˜๋œ ๋ฉ”์†Œ๋“œ์— #[wasm_bindgen] ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ์—์„œ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

wasm-game-of-life/www/index.js ํŒŒ์ผ์—์„œ <canvas> ์š”์†Œ์˜ ํด๋ฆญ ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ (listening)ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ๋‚ด๋ถ€์—๋Š” ํŽ˜์ด์ง€์˜ ์ƒ๋Œ€ ์ขŒํ‘œ๋ฅผ ์บ”๋ฒ„์Šค ์ƒ๋Œ€ ์ขŒํ‘œ๋กœ ๋ณ€ํ™˜ํ•œ ๋‹ค์Œ toggle_cell ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์žฅ๋ฉด์„ ๋‹ค์‹œ ๊ทธ๋ ค๋ณผ์ˆ˜ ์žˆ๋„๋ก ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

canvas.addEventListener("click", event => {
  const boundingRect = canvas.getBoundingClientRect();

  const scaleX = canvas.width / boundingRect.width;
  const scaleY = canvas.height / boundingRect.height;

  const canvasLeft = (event.clientX - boundingRect.left) * scaleX;
  const canvasTop = (event.clientY - boundingRect.top) * scaleY;

  const row = Math.min(Math.floor(canvasTop / (CELL_SIZE + 1)), height - 1);
  const col = Math.min(Math.floor(canvasLeft / (CELL_SIZE + 1)), width - 1);

  universe.toggle_cell(row, col);

  drawGrid();
  drawCells();
});

wasm-game-of-life ๊ฒฝ๋กœ์—์„œ wasm-pack build ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ๋‹ค์‹œ ๋นŒ๋“œํ•œ ๋‹ค์Œ, http://localhost:8080/ ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•ด ๋ณด์„ธ์š”. ์ด์ œ ์„ธํฌ๋ฅผ ์ง์ ‘ ํด๋ฆญํ•ด์„œ ์ƒํƒœ๋ฅผ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋์Šต๋‹ˆ๋‹ค. ํŒจํ„ด์„ ํ•œ๋ฒˆ ๊ทธ๋ ค๋ณด์„ธ์š”!

์—ฐ์Šตํ•ด ๋ณด๊ธฐ

  • <input type="range"> ์œ„์ ฏ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ๋ฐœ์ƒํ•˜๋Š” ํ‹ฑ์˜ ์ˆ˜๋ฅผ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ณด์„ธ์š”.

  • ์„ธ์ƒ์„ ๋žœ๋คํ•œ ์ดˆ๊ธฐ ์ƒํƒœ๋กœ ๋ฆฌ์…‹ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฒ„ํŠผ์„ ํ•˜๋‚˜ ๋” ์ถ”๊ฐ€ํ•ด ๋ณด์„ธ์š”. ๋˜ ๋‹ค๋ฅธ ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ•ด์„œ ๋ชจ๋“  ์„ธํฌ๊ฐ€ ์ฃฝ์€ ์ƒํƒœ๋กœ ์ƒˆ๋กœ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ•ด ๋ด๋„ ์ข‹์Šต๋‹ˆ๋‹ค.

  • Ctrl์„ ๋ˆ„๋ฅธ ์ƒํƒœ๋กœ ์„ธํฌ๋ฅผ ํด๋ฆญํ•˜๋ฉด (Ctrl + ํด๋ฆญ) ํด๋ฆญํ•œ ์„ธํฌ๋ฅผ ์ค‘์‹ฌ์œผ๋กœ glider ํŒจํ„ด์ด ์ถ”๊ฐ€๋˜๋„๋ก ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ณด์„ธ์š”. Shift๋ฅผ ๋ˆ„๋ฅธ ์ƒํƒœ๋กœ ํด๋ฆญํ•  ๋•Œ๋Š” (Shift + Click) pulsar ํŒจํ„ด์„ ๊ทธ๋ฆฌ๋„๋ก ์ž‘์„ฑํ•ด ๋ด๋„ ์ข‹์Šต๋‹ˆ๋‹ค.

ํƒ€์ž„ ํ”„๋กœํŒŒ์ผ๋ง

์ด ์ฑ•ํ„ฐ์—์„œ๋Š” ํƒ€์ž„ ํ”„๋กœํŒŒ์ผ๋ง ์ž‘์—…์„ ํ•ด๋ณด๋ฉด์„œ ์ด์ „์— ๊ตฌํ˜„ํ•œ Game of Life์˜ ์„ฑ๋Šฅ์„ ๊ฐœ์„ ์‹œ์ผœ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์‹œ์ž‘ํ•˜๊ธฐ ์ „์—, ๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์ฝ”๋“œ์— ์‚ฌ์šฉํ•ด ๋ณผ ์ˆ˜ ์žˆ๋Š” ํƒ€์ž„ ํ”„๋กœํŒŒ์ผ๋ง ํˆด๋“ค์„ ์‚ดํŽด๋ณด์…”๋„ ์ข‹์Šต๋‹ˆ๋‹ค.

window.performance.now ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ดˆ๋‹น ํ”„๋ ˆ์ž„ (FPS, Frames Per Second) ํƒ€์ด๋จธ ๋งŒ๋“ค๊ธฐ

์ด FPS ํƒ€์ด๋จธ๋Š” ๊ตฌํ˜„ํ•œ ๊ฒŒ์ž„์˜ ๋ Œ๋”๋ง ์†๋„๋ฅผ ์–ด๋–ป๊ฒŒ ๊ฐœ์„ ์‹œํ‚ฌ์ง€ ์‚ดํŽด๋ณผ ๋•Œ ๋งค์šฐ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

wasm-game-of-life/www/index.js ํŒŒ์ผ์— fps ๊ฐ์ฒด๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์‹œ์ž‘ํ•ด ๋ด…์‹œ๋‹ค:

const fps = new class {
  constructor() {
    this.fps = document.getElementById("fps");
    this.frames = [];
    this.lastFrameTimeStamp = performance.now();
  }

  render() {
    // ๋งˆ์ง€๋ง‰ ํ”„๋ ˆ์ž„ ๋ Œ๋”๋ถ€ํ„ฐ์˜ ๋ธํƒ€ ์‹œ๊ฐ„์„ fps ๋‹จ์œ„๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
    const now = performance.now();
    const delta = now - this.lastFrameTimeStamp;
    this.lastFrameTimeStamp = now;
    const fps = 1 / delta * 1000;

    // ๋งˆ์ง€๋ง‰ 100๊ฐœ์˜ ํƒ€์ด๋ฐ๋งŒ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
    this.frames.push(fps);
    if (this.frames.length > 100) {
      this.frames.shift();
    }

    // ์ตœ๋Œ€, ์ตœ์†Œ ํƒ€์ด๋ฐ๊ณผ ๋งˆ์ง€๋ง‰ 100๊ฐœ ํƒ€์ด๋ฐ์˜ ํ‰๊ท ์„ ์ฐพ์Šต๋‹ˆ๋‹ค.
    let min = Infinity;
    let max = -Infinity;
    let sum = 0;
    for (let i = 0; i < this.frames.length; i++) {
      sum += this.frames[i];
      min = Math.min(this.frames[i], min);
      max = Math.max(this.frames[i], max);
    }
    let mean = sum / this.frames.length;

    // ํ†ต๊ณ„๋ฅผ ๋ Œ๋”ํ•ฉ๋‹ˆ๋‹ค.
    this.fps.textContent = `
Frames per Second:
         latest = ${Math.round(fps)}
avg of last 100 = ${Math.round(mean)}
min of last 100 = ${Math.round(min)}
max of last 100 = ${Math.round(max)}
`.trim();
  }
};

fps ๊ฐ์ฒด์˜ render ํ•จ์ˆ˜๋ฅผ renderLoop ํ•จ์ˆ˜์˜ ๋งค ๋ฐ˜๋ณต๋งˆ๋‹ค ํ˜ธ์ถœํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

const renderLoop = () => {
    fps.render(); //new

    universe.tick();
    drawGrid();
    drawCells();

    animationId = requestAnimationFrame(renderLoop);
};

๋งˆ์ง€๋ง‰์œผ๋กœ, fps ์š”์†Œ๋ฅผ wasm-game-of-life/www/index.html ํŒŒ์ผ์— ์žŠ์ง€ ์•Š๊ณ  ์ถ”๊ฐ€ํ•ด ์ค์‹œ๋‹ค. <canvas> ๋ฐ”๋กœ ์œ„์— ์ถ”๊ฐ€ํ•ด ์ฃผ์„ธ์š”:

<div id="fps"></div>

CSS ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ ๊น”๋”ํ•˜๊ฒŒ ํฌ๋งทํ•ด ์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค:

#fps {
  white-space: pre;
  font-family: monospace;
}

์งœ์ž”! ์ด์ œ http://localhost:8080 ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจ ํ•˜๋ฉด FPS ์นด์šดํ„ฐ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋์Šต๋‹ˆ๋‹ค!

Universe::tick์˜ ๋งค ํ‹ฑ์„ console.time, console.timeEnd ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ธก์ •ํ•˜๊ธฐ

web-sys ํฌ๋ ˆ์ดํŠธ์˜ console.time, console.timeend ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๊ฐ Universe::tick์ด ํ˜ธ์ถœ๋˜๋Š”๋ฐ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์„ ํ™•์ธํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋จผ์ €, wasm-game-of-life/Cargo.toml ํŒŒ์ผ์„ ์—ด๊ณ  web-sys ๋ฅผ ์ข…์†์„ฑ์œผ๋กœ ์ถ”๊ฐ€ํ•ด ์ฃผ์„ธ์š”:

[dependencies.web-sys]
version = "0.3"
features = [
  "console",
]

console.time๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค console.timeEnd๋„ ๊ฐ™์ด ํ˜ธ์ถœ๋  ์˜ˆ์ •์ด๊ธฐ ๋•Œ๋ฌธ์—, RAII ํƒ€์ž…์œผ๋กœ ๋ฌถ์–ด์„œ ๊ฐ„ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค:

#![allow(unused)]
fn main() {
extern crate web_sys;
use web_sys::console;

pub struct Timer<'a> {
    name: &'a str,
}

impl<'a> Timer<'a> {
    pub fn new(name: &'a str) -> Timer<'a> {
        console::time_with_label(name);
        Timer { name }
    }
}

impl<'a> Drop for Timer<'a> {
    fn drop(&mut self) {
        console::time_end_with_label(self.name);
    }
}
}

๊ทธ๋‹ค์Œ, ๋ฉ”์†Œ๋“œ ์ตœ์ƒ๋‹จ์— ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ ๊ฐ Universe::tick ํ˜ธ์ถœ์ด ์–ผ๋งˆ๋‚˜ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š”์ง€ ์ธก์ •ํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

#![allow(unused)]
fn main() {
let _timer = Timer::new("Universe::tick");
}

์ฝ˜์†”์— Universe::tick๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฐ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์„ ๋กœ๊ทธ๋กœ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค:

console.time ๋กœ๊ทธ ์Šคํฌ๋ฆฐ์ƒท

์ถ”๊ฐ€๋กœ, ๋ธŒ๋ผ์šฐ์ € ํ”„๋กœํŒŒ์ผ๋Ÿฌ(profiler)์˜ timeline ํ˜น์€ waterfall ๋ทฐ์—์„œ console.time๊ณผ console.timeEnd๊ฐ€ ๊ฐ™์ด ์‹คํ–‰๋œ ๋ถ€๋ถ„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

console.time ๋กœ๊ทธ ์Šคํฌ๋ฆฐ์ƒท

Game of Life ์„ธ์ƒ์˜ ์‚ฌ์ด์ฆˆ ๋Š˜๋ ค๋ณด๊ธฐ

โš ๏ธ ์ด ์„น์…˜์€ FireFox์˜ ์Šคํฌ๋ฆฐ์ƒท์„ ์˜ˆ์‹œ๋กœ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ๊ฑฐ์˜ ๋ชจ๋“  ๋ชจ๋˜ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋น„์Šทํ•œ ๊ธฐ๋Šฅ๋“ค์„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€๋งŒ, ๋ธŒ๋ผ์šฐ์ €๋งˆ๋‹ค ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์— ์•ฝ๊ฐ„์˜ ์ฐจ์ด๊ฐ€ ์žˆ์„ ์ˆ˜๋Š” ์žˆ์Šต๋‹ˆ๋‹ค. ๋œฏ์–ด๋ณด๊ฒŒ ๋  ํ”„๋กœํŒŒ์ผ ์ •๋ณด๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฐ™์ง€๋งŒ, ๋ณด๊ฒŒ ๋  ๋ทฐ๋‚˜ ๋„๊ตฌ ์ด๋ฆ„ ๋“ฑ์ด ์‚ด์ง ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๋Š” ์ ์„ ๋ฏธ๋ฆฌ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.

๊ตฌํ˜„ํ•œ Game of Life ์„ธ์ƒ์„ ๋” ํฌ๊ฒŒ ๋งŒ๋“ค์–ด๋ณด๋ฉด ์–ด๋–จ๊นŒ์š”? 64 x 64 ์‚ฌ์ด์ฆˆ์˜ ์„ธ์ƒ์„ 128 x 128 ์‚ฌ์ด์ฆˆ๋กœ ๋Š˜๋ ค๋ด…์‹œ๋‹ค. (wasm-game-of-life/src/lib.rs ํŒŒ์ผ์—์„œ Universe::new๋ฅผ ์ˆ˜์ •ํ•ด ์ฃผ์„ธ์š”.) ์ œ ์ปดํ“จํ„ฐ์—์„œ๋Š” ์‚ฌ์ด์ฆˆ๋ฅผ ๋Š˜๋ ธ์„ ๋•Œ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์ž‘๋™ํ•˜๋˜ 60fps ํ™”๋ฉด์ด ๋ฒ„๋ฒ…์ด๋ฉด์„œ 40fps ์ฒ˜๋Ÿผ ๋ณด์ด๋Š” ๋ถ€๋ถ„์ด ํ™•์ธ๋ฉ๋‹ˆ๋‹ค.

ํ”„๋กœํŒŒ์ผ์„ ๊ธฐ๋กํ•˜๊ณ  waterfall ๋ทฐ๋ฅผ ํ™•์ธํ•˜๋ฉด, ๊ฐ ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์ฒ˜๋ฆฌ๋˜๋Š”๋ฐ 20 ๋ฐ€๋ฆฌ์ดˆ๋ณด๋‹ค ๋” ๋งŽ์ด ๊ฑธ๋ฆฌ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 60fps๋กœ ํ‘œ์‹œ๋์„ ๋•Œ๋Š” ํ”„๋ ˆ์ž„ ์ „์ฒด๋ฅผ ๋ Œ๋”ํ•˜๋Š”๋ฐ 16 ๋ฐ€๋ฆฌ์ดˆ๊ฐ€ ๊ฑธ๋ฆฐ ๊ฒƒ์„ ๋– ์˜ฌ๋ ค๋ณด๋ฉด ํ™•์‹คํžˆ ์ฐจ์ด๊ฐ€ ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ฐธ๊ณ ๋กœ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์™ธ์—๋„ ํŽ˜์ด์ง€๋ฅผ ๊ทธ๋ฆฌ๋Š” ๋“ฑ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ˆ˜ํ–‰ํ•˜๋Š” ๋‹ค๋ฅธ ์ž‘์—…์˜ ์˜ํ–ฅ๋„ ์žˆ์œผ๋‹ˆ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.

ํŽ˜์ด์ง€ ๋ Œ๋”๋ง ์ฒ˜๋ฆฌ์˜ waterfall ๋ทฐ ์Šคํฌ๋ฆฐ์ƒท

ํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํ”„๋ ˆ์ž„ ๋™์•ˆ ์–ด๋–ค ์ผ์ด ์ผ์–ด๋‚˜๋Š”์ง€ ์ž˜ ํ™•์ธํ•ด ๋ณด๋ฉด, CanvasRenderingContext2D.fillStyle์˜ setter๊ฐ€ ๋งŽ์€ ์„ฑ๋Šฅ์„ ์š”๊ตฌํ•˜๋Š” ๋ถ€๋ถ„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โš ๏ธ FireFox ๋ธŒ๋ผ์šฐ์ €์—์„œ ์œ„ ๋‚ด์šฉ์—์„œ ์–ธ๊ธ‰๋œ CanvasRenderingContext2D.fillStyle ๋Œ€์‹ ์— "DOM"์ด ํ‘œ์‹œ๋œ๋‹ค๋ฉด ์„ฑ๋Šฅ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ (performance developer tools) ์—์„œ "Gecko ํ”Œ๋žซํผ ๋ฐ์ดํ„ฐ ํ‘œ์‹œํ•˜๊ธฐ (Show Gecko Platform Data)" ์˜ต์…˜์„ ํ™œ์„ฑํ™”ํ•ด์ค˜์•ผ ํ• ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค:

"Gecko ํ”Œ๋žซํผ ๋ฐ์ดํ„ฐ ํ‘œ์‹œํ•˜๊ธฐ (Show Gecko Platform Data)" ์˜ต์…˜ ํ™œ์„ฑํ™”ํ•˜๊ธฐ

ํŽ˜์ด์ง€ ๋ Œ๋”๋ง ์ฒ˜๋ฆฌ์˜ flamegraph ๋ทฐ ์Šคํฌ๋ฆฐ์ƒท

๋งŽ์€ ํ”„๋ ˆ์ž„์˜ ํ˜ธ์ถœ ํŠธ๋ฆฌ ์ง‘๊ณ„ (call tree's aggregation) ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ์ด๊ฒŒ ์ „ํ˜€ ์ด์ƒํ•œ ๋™์ž‘์ด ์•„๋‹˜์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

ํŽ˜์ด์ง€ ๋ Œ๋”๋ง ์ฒ˜๋ฆฌ์˜ flamegraph ๋ทฐ ์Šคํฌ๋ฆฐ์ƒท

์–ด์ด์ฟ ! ๊ฑฐ์˜ 40% ๋ถ„๋Ÿ‰์„ ์ด setter์— ์‚ฌ์šฉํ•ด๋ฒ„๋ ธ๋„ค์š”!

โšก tick ๋ฉ”์†Œ๋“œ๊ฐ€ ์„ฑ๋Šฅ ๋ณ‘๋ชฉ์„ ์ผ์œผํ‚ค๋Š”๋ฐ ํŠน๋ณ„ํ•œ ์ด์œ ๊ฐ€ ์žˆ์„ ๊ฒƒ ๊ฐ™์•˜์ง€๋งŒ, ์‚ฌ์‹ค ๊ทธ๋ ‡์ง€ ์•Š์€ ๋ถ€๋ถ„์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์ž‘์—…์„ ํ•˜๋‹ค ๋ณด๋ฉด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ถ€๋ถ„์—์„œ ์‹œ๊ฐ„์„ ๋งŽ์ด ์“ฐ๊ฒŒ ๋  ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ, ํ•ญ์ƒ ์ •๋ง ์ค‘์š”ํ•œ ํ”„๋กœํŒŒ์ผ๋ง ๋„๊ตฌ๋ฅผ ๋จผ์ € ์‚ดํŽด๋ณด๋„๋ก ํ•ฉ์‹œ๋‹ค.

wasm-game-of-life/www/index.js ํŒŒ์ผ ๋‚ด์˜ drawCells ํ•จ์ˆ˜์—์„œ fillStyle ํ”„๋กœํผํ‹ฐ๊ฐ€ ํ•œ๋ฒˆ ์ •ํ•ด์ง€๋ฉด ์„ธ์ƒ ๋‚ด์˜ ๋ชจ๋“  ์„ธํฌ์™€ ๋ชจ๋“  ์• ๋‹ˆ๋ฉ”์ด์…˜์— ์ด ํ”„๋กœํผํ‹ฐ๊ฐ€ ์‚ฌ์šฉ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

for (let row = 0; row < height; row++) {
  for (let col = 0; col < width; col++) {
    const idx = getIndex(row, col);

    ctx.fillStyle = cells[idx] === DEAD
      ? DEAD_COLOR
      : ALIVE_COLOR;

    ctx.fillRect(
      col * (CELL_SIZE + 1) + 1,
      row * (CELL_SIZE + 1) + 1,
      CELL_SIZE,
      CELL_SIZE
    );
  }
}

fillStyle๊ฐ€ ๋งŽ์€ ์„ฑ๋Šฅ์„ ์š”๊ตฌํ•˜๋Š” ๋ถ€๋ถ„์„ ํ™•์ธํ–ˆ๋Š”๋ฐ, ๊ทธ๋ ‡๋‹ค๋ฉด ์–ด๋–ค ์‹์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์„œ ๊ฐœ์„ ์‹œํ‚ฌ์ˆ˜ ์žˆ์„๊นŒ์š”? ์„ธํฌ์˜ ์ƒ์กด ์—ฌ๋ถ€์— ๋”ฐ๋ผ fillStyle๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๋ฐ”๊ฟ”๋ด…์‹œ๋‹ค. fillStyle = ALIVE_COLOR๋ฅผ ์ถ”๊ฐ€ํ•ด ์ค˜์„œ ์‚ด์•„์žˆ๋Š” ์„ธํฌ๋งŒ ๊ทธ๋ฆฌ๋„๋ก ํ•˜๊ณ  fillStyle = DEAD_COLOR๋„ ์ถ”๊ฐ€ํ•ด ์ค˜์„œ ์ฃฝ์€ ์„ธํฌ๋„ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ค€๋‹ค๋ฉด ์„ธํฌ๋“ค์„ ๋ชจ๋‘ ํ•œ ๋ฒˆ์— ๊ทธ๋ฆฌ๋Š” ๋Œ€์‹  fillStyle์„ ๋‘ ๋ฒˆ๋งŒ ์„ค์ •ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

// ์‚ด์•„์žˆ๋Š” ์„ธํฌ๋“ค์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
ctx.fillStyle = ALIVE_COLOR;
for (let row = 0; row < height; row++) {
  for (let col = 0; col < width; col++) {
    const idx = getIndex(row, col);
    if (cells[idx] !== Cell.Alive) {
      continue;
    }

    ctx.fillRect(
      col * (CELL_SIZE + 1) + 1,
      row * (CELL_SIZE + 1) + 1,
      CELL_SIZE,
      CELL_SIZE
    );
  }
}

// ์ฃฝ์–ด์žˆ๋Š” ์„ธํฌ๋“ค์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
ctx.fillStyle = DEAD_COLOR;
for (let row = 0; row < height; row++) {
  for (let col = 0; col < width; col++) {
    const idx = getIndex(row, col);
    if (cells[idx] !== Cell.Dead) {
      continue;
    }

    ctx.fillRect(
      col * (CELL_SIZE + 1) + 1,
      row * (CELL_SIZE + 1) + 1,
      CELL_SIZE,
      CELL_SIZE
    );
  }
}

์ฝ”๋“œ ํŒŒ์ผ์„ ์ €์žฅํ•˜๊ณ  http://localhost:8080/ ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจ ํ•ด์ฃผ๋ฉด, ์›น์‚ฌ์ดํŠธ๊ฐ€ 60fps๋กœ ๋‹ค์‹œ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๋ Œ๋” ๋ฉ๋‹ˆ๋‹ค.

ํ”„๋กœํŒŒ์ผ์„ ๋‹ค์‹œ ํ™•์ธํ•ด ๋ณด๋ฉด, ๊ฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํ”„๋ ˆ์ž„๋งˆ๋‹ค ์˜ค์ง 10 ๋ฐ€๋ฆฌ์ดˆ๋งŒ ๊ฑธ๋ฆฌ๋Š” ๋ถ€๋ถ„๋„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

drawCells ํ•จ์ˆ˜๋ฅผ ์—…๋ฐ์ดํŠธํ•œ ์ดํ›„ ํŽ˜์ด์ง€ ๋ Œ๋”๋ง ์ฒ˜๋ฆฌ์˜ waterfall ๋ทฐ ์Šคํฌ๋ฆฐ์ƒท

ํ•œ ํ”„๋ ˆ์ž„์„ ๋‹ค์‹œ ๋ถ„์„ํ•ด ๋ณด๋ฉด, fillStyle ์ด ๋” ์ด์ƒ ์„ฑ๋Šฅ์„ ๋งŽ์ด ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ , fillRect๊ฐ€ ๊ฐ ์„ธํฌ ์‚ฌ๊ฐํ˜•์„ ๊ทธ๋ฆฌ๋Š”๋ฐ ๋Œ€๋ถ€๋ถ„์˜ ์‹œ๊ฐ„์ด ์†Œ๋น„๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

drawCells ํ•จ์ˆ˜๋ฅผ ์—…๋ฐ์ดํŠธํ•œ ์ดํ›„ ํŽ˜์ด์ง€ ๋ Œ๋”๋ง ์ฒ˜๋ฆฌ์˜ flamegraph ๋ทฐ ์Šคํฌ๋ฆฐ์ƒท

Game of Life๊ฐ€ ๋” ๋น ๋ฅด๊ฒŒ ์ง„ํ–‰๋˜๋„๋ก ๋งŒ๋“ค์–ด๋ณด๊ธฐ

์–ด๋–ค ์‚ฌ๋žŒ๋“ค์€ ๋นจ๋ฆฌ๋นจ๋ฆฌ ์ง„ํ–‰ํ•˜๋Š” ๊ฒƒ์„ ์„ ํ˜ธํ•ด์„œ ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค 1ํ‹ฑ์ด ์•„๋‹ˆ๋ผ 9ํ‹ฑ์”ฉ ์ง„ํ–‰๋˜๋„๋ก ์ˆ˜์ •ํ•˜๊ณ  ์‹ถ์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋†€๋ž๊ฒŒ๋„ ์ด ์ž‘์—…์€ wasm-game-of-life/www/index.js ํŒŒ์ผ์˜ renderLoop ํ•จ์ˆ˜๋ฅผ ์ˆ˜์ •ํ•ด์„œ ์ƒ๊ฐ ์™ธ๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

for (let i = 0; i < 9; i++) {
  universe.tick();
}

์ œ ์ปดํ“จํ„ฐ์—์„œ ๊ตฌํ˜„ํ•œ ๊ฒŒ์ž„์ด 35fps ์†๋„๋กœ ๋‹ค์‹œ ๋Š๋ ค์ง„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ข‹์ง€ ์•Š์€ ํ˜„์ƒ์ด๋‹ˆ ๋‹ค์‹œ 60fps ๋กœ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!

์ž˜ ํ™•์ธํ•ด๋ณด๋ฉด Universe::tick ์—์„œ ์‹œ๊ฐ„์ด ๋งŽ์ด ์†Œ์š”๋˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. Timer๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ console.time๊ณผ console.timeEnd ํ˜ธ์ถœ์„ ๊ฐ์‹ธ๊ณ  ๋‹ค์‹œ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ œ ์˜ˆ์ƒ๋Œ€๋กœ๋ผ๋ฉด ๋งค ํ‹ฑ๋งˆ๋‹ค ์„ธํฌ์˜ ์ƒˆ ๋ฒกํ„ฐ๋ฅผ ํ• ๋‹นํ•˜๊ณ  ๊ธฐ์กด ๋ฒกํ„ฐ๋ฅผ ํ•ด์ œ(freeing)ํ•˜๋Š” ์ž‘์—…์€ ๋งŽ์€ ์„ฑ๋Šฅ์„ ์‚ฌ์šฉํ•  ๋ฟ ์•„๋‹ˆ๋ผ ์‹œ๊ฐ„๋„ ๋งŽ์ด ์†Œ๋น„ํ•˜๊ฒŒ ๋ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

#![allow(unused)]
fn main() {
pub fn tick(&mut self) {
    let _timer = Timer::new("Universe::tick");

    let mut next = {
        let _timer = Timer::new("๋‹ค์Œ ์„ธํฌ๋“ค์„ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.");
        self.cells.clone()
    };

    {
        let _timer = Timer::new("๋‹ค์Œ ์„ธ๋Œ€๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.");
        for row in 0..self.height {
            for col in 0..self.width {
                let idx = self.get_index(row, col);
                let cell = self.cells[idx];
                let live_neighbors = self.live_neighbor_count(row, col);

                let next_cell = match (cell, live_neighbors) {
                    // ๊ทœ์น™ 1: ์ธ๊ตฌ ๋ถ€์กฑ์œผ๋กœ 2๊ฐœ ๋ฏธ๋งŒ์˜ ์ด์›ƒ์„ ๊ฐ€์ง„ ์„ธํฌ๋Š” ์ฃฝ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
                    (Cell::Alive, x) if x < 2 => Cell::Dead,
                    // ๊ทœ์น™ 2: 2๊ฐœ ํ˜น์€ 3๊ฐœ์˜ ์ด์›ƒ์„ ๊ฐ€์ง„ ์„ธํฌ๋Š” ๋‹ค์Œ ์„ธ๋Œ€์—์„œ ๊ณ„์† ์‚ด์•„์žˆ์Šต๋‹ˆ๋‹ค.
                    (Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive,
                    // ๊ทœ์น™ 3: ๊ณผ์ž‰ ์ธ๊ตฌ๋กœ 3๊ฐœ ์ดˆ๊ณผ์˜ ์ด์›ƒ์„ ๊ฐ€์ง„ ๋ชจ๋“  ์„ธํฌ๋Š” ์ฃฝ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
                    (Cell::Alive, x) if x > 3 => Cell::Dead,
                    // ๊ทœ์น™ 4: ์„ธํฌ ์ฆ์‹์œผ๋กœ ์ •ํ™•ํžˆ 3๊ฐœ์˜ ์ด์›ƒ์„ ๊ฐ€์ง„ ์„ธํฌ๋Š” ์‚ด์•„๋‚˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
                    (Cell::Dead, 3) => Cell::Alive,
                    // ๊ทœ์น™์ด ์ ์šฉ๋˜์ง€ ์•Š๋Š” ์„ธํฌ๋“ค์˜ ์ƒํƒœ๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
                    (otherwise, _) => otherwise,
                };

                next[idx] = next_cell;
            }
        }
    }

    let _timer = Timer::new("๊ธฐ์กด ์„ธํฌ๋“ค์„ ํ•ด์ œํ•ฉ๋‹ˆ๋‹ค.");
    self.cells = next;
}
}

๋ธŒ๋ผ์šฐ์ € ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์—์„œ ํƒ€์ด๋ฐ(timing)์„ ์ž˜ ํ™•์ธํ•ด ๋ณด๋ฉด ์ด ์˜ˆ์ƒ์ด ์‚ฌ์‹ค์€ ๋ช…๋ฐฑํ•˜๊ฒŒ ํ‹€๋ฆฐ ๋ถ€๋ถ„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ๋Š” ๋Œ€๋ถ€๋ถ„์˜ ์‹œ๊ฐ„์ด ๋‹ค์Œ ์„ธ๋Œ€ ์„ธํฌ๋“ค์„ ๊ณ„์‚ฐํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์˜์™ธ๋กœ ๋งค ํ‹ฑ๋งˆ๋‹ค ๋ฒกํ„ฐ ๊ฐ’์„ ํ• ๋‹นํ•˜๊ณ  ํ•ด์ œํ•˜๋Š” ์ž‘์—…์ด ๊ทธ๋ ‡๊ฒŒ ์„ฑ๋Šฅ์„ ๋งŽ์ด ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ํ•œ๋ฒˆ ์ •๋ง ์ค‘์š”ํ•œ ํ”„๋กœํŒŒ์ผ๋ง์„ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!

Universe::tick ํƒ€์ด๋จธ ๊ฒฐ๊ณผ ๊ฐ’์˜ ์Šคํฌ๋ฆฐ์ƒท

์‚ฌ์šฉํ•˜๊ฒŒ ๋  ํ…Œ์ŠคํŠธ ๊ธฐ๋Šฅ ๊ฒŒ์ดํŠธ (test feature gate) ๊ฐ€ nightly ๋ฒ„์ „์— ๊ฐ™์ด ํฌํ•จ๋ผ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, nightly ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋ฒค์น˜๋งˆํ‚น์— ์ด ๊ธฐ๋Šฅ์„ ํ•œ๋ฒˆ ์‚ฌ์šฉํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. cargo benchcmp์ด๋ผ๋Š” ํˆด๋„ ํ•„์š”ํ•˜๋‹ˆ ์„ค์น˜ํ•ด ์ฃผ๋„๋ก ํ•ฉ์‹œ๋‹ค. ์ฐธ๊ณ ๋กœ cargo benchcmp๋Š” cargo bench๋กœ ์ƒ์„ฑํ•œ ๋งˆ์ดํฌ๋กœ ๋ฒค์น˜๋งˆํ‚น์„ ๋น„๊ตํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜๋Š” ์ž‘์€ ์‚ฌ์ด์ฆˆ์˜ ์œ ํ‹ธ๋ฆฌํ‹ฐ์ž…๋‹ˆ๋‹ค.

์›น์–ด์…ˆ๋ธ”๋ฆฌ์™€ ๋™์ผํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ์ธ #[bench]๋ฅผ ์ž‘์„ฑํ•ด ๋ด…์‹œ๋‹ค. ์ด๋ ‡๊ฒŒ ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ฒŒ ๋˜๋ฉด ๋” ๋งŽ์€ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด์ œ ์ƒˆ๋กญ๊ฒŒ ์ž‘์„ฑ๋œ wasm-game-of-life/benches/bench.rs์„ ํ™•์ธํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

#![allow(unused)]
#![feature(test)]

fn main() {
extern crate test;
extern crate wasm_game_of_life;

#[bench]
fn universe_ticks(b: &mut test::Bencher) {
    let mut universe = wasm_game_of_life::Universe::new();

    b.iter(|| {
        universe.tick();
    });
}
}

#[wasm_bindgen] ์†์„ฑ์„ ๋ชจ๋‘ ์ฃผ์„ ์ฒ˜๋ฆฌ ํ•ด์ฃผ๋„๋ก ํ•˜๊ณ , Cargo.toml ํŒŒ์ผ์˜ "cdylib"๋„ ์ฃผ์„ ์ฒ˜๋ฆฌ ํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์ฃผ์„ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜์ง€ ์•Š์œผ๋ฉด ๋นŒ๋“œ๊ฐ€ ์‹คํŒจํ•˜๊ณ  ๋งํฌ ์˜ค๋ฅ˜(link errors)๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ค€๋น„๊ฐ€ ๋‹ค ๋๋‹ค๋ฉด, cargo bench | tee before.txt ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ด์„œ ์ฝ”๋“œ๋ฅผ ์ปดํŒŒ์ผํ•˜๊ณ  ๋ฒค์น˜๋งˆํฌ๋ฅผ ์‹คํ–‰ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. | tee before.txt๋ฅผ ํฌํ•จํ•˜๋ฉด์„œ cargo bench ๋ช…๋ น์–ด์˜ ์ถœ๋ ฅ๊ฐ’์„ ๊ฐ€์ ธ์™€์„œ before.txt ํŒŒ์ผ์— ์ €์žฅํ•  ์˜ˆ์ •์ด๋‹ˆ ์ด ๋ถ€๋ถ„๋„ ๋‹ค์‹œ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.

$ cargo bench | tee before.txt
    Finished release [optimized + debuginfo] target(s) in 0.0 secs
     Running target/release/deps/wasm_game_of_life-91574dfbe2b5a124

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target/release/deps/bench-8474091a05cfa2d9

running 1 test
test universe_ticks ... bench:     664,421 ns/iter (+/- 51,926)

test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out

์—ฌ๊ธฐ์„œ ๋ฐ”์ด๋„ˆ๋ฆฌ ํŒŒ์ผ์˜ ๊ฒฝ๋กœ๋„ ๊ฐ™์ด ํ‘œ์‹œ๋˜๋Š” ๋ถ€๋ถ„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ์—๋Š” ์šด์˜์ฒด์ œ์˜ ํ”„๋กœํŒŒ์ผ๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฒค์น˜๋งˆํ‚น์„ ๋‹ค์‹œ ํ•œ๋ฒˆ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ €๋Š” ๋ฆฌ๋ˆ…์Šค(Linux)๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋‹ˆ perf๋ฅผ ์˜ˆ์ œ๋กœ ์‚ฌ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค:

$ perf record -g target/release/deps/bench-8474091a05cfa2d9 --bench
running 1 test
test universe_ticks ... bench:     635,061 ns/iter (+/- 38,764)

test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out

[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.178 MB perf.data (2349 samples) ]

perf report ๋ช…๋ น์–ด๋กœ ํ”„๋กœํŒŒ์ผ์„ ๋กœ๋“œํ•˜๋ฉด, ์˜ˆ์ƒํ•œ ๋Œ€๋กœ ๋Œ€๋ถ€๋ถ„์˜ ์‹œ๊ฐ„์ด Universe::tick๋ฅผ ์‹คํ–‰ํ•˜๋Š”๋ฐ ์†Œ๋น„๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

perf report ์Šคํฌ๋ฆฐ์ƒท

perf๋ฅผ ์‹คํ–‰ํ•œ ๋‹ค์Œ a ํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ์–ด๋–ค ์–ด์…ˆ๋ธ”๋ฆฌ ๋ช…๋ น์–ด(instruction)์ด ํ•จ์ˆ˜๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

perf ํ™”๋ฉด์—์„œ ์–ด์…ˆ๋ธ”๋ฆฌ ๋ช…๋ น์–ด๋ฅผ ํ‘œ์‹œํ•˜๋Š” ํ™”๋ฉด์˜ ์Šคํฌ๋ฆฐ์ƒท

์œ„ ๋‚ด์šฉ์„ ํ™•์ธํ•˜๋ฉด 26.67%์˜ ์‹œ๊ฐ„์ด ์ด์›ƒ ์„ธํฌ๋“ค์„ ๋งŒ๋“ค์–ด๋‚ด๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๊ณ , 23.41%๋ฅผ ์ด์›ƒ๋“ค์˜ ์—ด ์ธ๋ฑ์Šค, ๊ทธ๋ฆฌ๊ณ  ๋‚˜๋จธ์ง€ 15.42%๋ฅผ ํ–‰ ์ธ๋ฑ์Šค๋ฅผ ๋”ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ œ์ผ ๋งŽ์€ ์„ฑ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋Š” ์„ธ ๋ช…๋ น์–ด๋“ค ์ค‘, ๋‘ ๋ฒˆ์งธ์™€ ์„ธ ๋ฒˆ์งธ๋กœ ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ๋ช…๋ น์–ด๊ฐ€ div ๋ช…๋ น์–ด์ธ ๋ถ€๋ถ„๋„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด div ๊ตฌํ˜„๋“ค์ด Universe::live_neighbor_count ํ•จ์ˆ˜์—์„œ ๋‚˜๋จธ์ง€ ์—ฐ์‚ฐ์ž๋กœ ์ธ๋ฑ์‹ฑ ํ•˜๋„๋ก ๊ตฌํ˜„ํ–ˆ๋˜ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. (modulo indexing logic)

wasm-game-of-life/src/lib.rs ํŒŒ์ผ์—์„œ live_neighbor_count๋ฅผ ์ •์˜ํ–ˆ๋˜ ๋‚ด์šฉ์„ ๋‹ค์‹œ ๋– ์˜ฌ๋ ค๋ด…์‹œ๋‹ค:

#![allow(unused)]
fn main() {
fn live_neighbor_count(&self, row: u32, column: u32) -> u8 {
    let mut count = 0;
    for delta_row in [self.height - 1, 0, 1].iter().cloned() {
        for delta_col in [self.width - 1, 0, 1].iter().cloned() {
            if delta_row == 0 && delta_col == 0 {
                continue;
            }

            let neighbor_row = (row + delta_row) % self.height;
            let neighbor_col = (column + delta_col) % self.width;
            let idx = self.get_index(neighbor_row, neighbor_col);
            count += self.cells[idx] as u8;
        }
    }
    count
}
}

์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ํ™•์ธํ•ด ๋ณด๋ฉด if ๋ธ”๋Ÿญ๋“ค๋กœ ์ฒซ์งธ์™€ ๋งˆ์ง€๋ง‰ ํ–‰๊ณผ ์—ด์˜ ์—ฃ์ง€ ์ผ€์ด์Šค(edge case) ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ ์ฝ”๋“œ๋ฅผ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๊ธธ๊ฒŒ ์ž‘์„ฑํ•˜๋Š” ๋Œ€์‹  ๋‚˜๋จธ์ง€ ์—ฐ์‚ฐ์ž (modulo operator) ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ๋ถ€๋ถ„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. row๊ณผ column ๋‘˜ ๋‹ค ์„ธ์ƒ์˜ ๊ฐ€์žฅ์ž๋ฆฌ์— ์žˆ์ง€ ์•Š๊ณ  ๋‚˜๋จธ์ง€ ์—ฐ์‚ฐ์ž๋ฅผ ์“ธ ํ•„์š”๊ฐ€ ์—†๋Š” ์ผ๋ฐ˜์ ์ธ ๊ฒฝ์šฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ๋„ div ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋งŽ์€ ์„ฑ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ถ€๋ถ„๋„ ํ™•์ธ๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ณต๋ฌธ์„ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹ ์— if ๋ฌธ์œผ๋กœ ์ด๋Ÿฌํ•œ ์—ฃ์ง€ ์ผ€์ด์Šค๋“ค์„ ์ฒ˜๋ฆฌํ•˜๊ณ , ์—ฌ๋Ÿฌ ๋ถ„๊ธฐ๋“ค์„ CPU์˜ ๋ถ„๊ธฐ ์˜ˆ์ธก๊ธฐ(branch predictor)๊ฐ€ ์˜ˆ์ธกํ•˜๊ธฐ ์‰ฝ๋„๋ก ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

live_neighbor_count๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‹ค์‹œ ์ž‘์„ฑํ•ด ๋ด…์‹œ๋‹ค:

#![allow(unused)]
fn main() {
fn live_neighbor_count(&self, row: u32, column: u32) -> u8 {
    let mut count = 0;

    let north = if row == 0 {
        self.height - 1
    } else {
        row - 1
    };

    let south = if row == self.height - 1 {
        0
    } else {
        row + 1
    };

    let west = if column == 0 {
        self.width - 1
    } else {
        column - 1
    };

    let east = if column == self.width - 1 {
        0
    } else {
        column + 1
    };

    let nw = self.get_index(north, west);
    count += self.cells[nw] as u8;

    let n = self.get_index(north, column);
    count += self.cells[n] as u8;

    let ne = self.get_index(north, east);
    count += self.cells[ne] as u8;

    let w = self.get_index(row, west);
    count += self.cells[w] as u8;

    let e = self.get_index(row, east);
    count += self.cells[e] as u8;

    let sw = self.get_index(south, west);
    count += self.cells[sw] as u8;

    let s = self.get_index(south, column);
    count += self.cells[s] as u8;

    let se = self.get_index(south, east);
    count += self.cells[se] as u8;

    count
}
}

๋ฒค์น˜๋งˆํ‚น์„ ๋‹ค์‹œ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค! ์ด๋ฒˆ์—๋Š” ์ถœ๋ ฅ๋˜๋Š” ๋ฉ”์„ธ์ง€๋“ค์„ after.txt๋กœ ์ถœ๋ ฅํ•ด ๋ด…์‹œ๋‹ค.

$ cargo bench | tee after.txt
   Compiling wasm_game_of_life v0.1.0 (file:///home/fitzgen/wasm_game_of_life)
    Finished release [optimized + debuginfo] target(s) in 0.82 secs
     Running target/release/deps/wasm_game_of_life-91574dfbe2b5a124

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target/release/deps/bench-8474091a05cfa2d9

running 1 test
test universe_ticks ... bench:      87,258 ns/iter (+/- 14,632)

test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out

ํ›จ์”ฌ ๋‚˜์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค! benchcmp ํˆด๊ณผ ๋ฐฉ๊ธˆ ์ƒ์„ฑํ•œ ๋‘ ํ…์ŠคํŠธ ํŒŒ์ผ์„ ํ™•์ธํ•ด ๋ณด๋ฉด ์–ผ๋งˆ๋‚˜ ๋‚˜์ด์กŒ๋Š”์ง€ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

$ cargo benchcmp before.txt after.txt
 name            before.txt ns/iter  after.txt ns/iter  diff ns/iter   diff %  speedup
 universe_ticks  664,421             87,258                 -577,163  -86.87%   x 7.61

์šฐ์™€! 7.61 ๋ฐฐ๋‚˜ ๋นจ๋ผ์กŒ๋„ค์š”!

์›น์–ด์…ˆ๋ธ”๋ฆฌ๋Š” ์˜๋„์ ์œผ๋กœ ์ผ๋ฐ˜์ ์ธ ํ•˜๋“œ์›จ์–ด ์•„ํ‚คํ…์ฒ˜์™€ ๋ฐ€์ ‘ํ•˜๊ฒŒ ๋งคํ•‘(mapping)๋˜๋„๋ก ์„ค๊ณ„๋ผ ์žˆ์ง€๋งŒ, ์ด๋Ÿฌํ•œ ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ์˜ ์†๋„ ํ–ฅ์ƒ์ด ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์„ฑ๋Šฅ ํ–ฅ์ƒ์œผ๋กœ๋„ ์ด์–ด์งˆ ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์‹คํžˆ ํ™•์ธํ•ด ๋ณด๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

wasm-pack build ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ด์„œ .wasm ํŒŒ์ผ์„ ๋‹ค์‹œ ๋นŒ๋“œํ•˜๊ณ  http://localhost:8080/ ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจ ํ•ด์ฃผ์„ธ์š”. ์ œ ์ปดํ“จํ„ฐ์—์„œ๋Š” 60 fps์˜ ์†๋„๋กœ ๋‹ค์‹œ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์ด ํ™•์ธ๋ฉ๋‹ˆ๋‹ค. ๋ธŒ๋ผ์šฐ์ €์˜ ํ”„๋กœํŒŒ์ผ๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋‹ค์‹œ ํ™•์ธํ•ด ๋ณด๋‹ˆ ๊ฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํ”„๋ ˆ์ž„์ด 10 ๋ฐ€๋ฆฌ์ดˆ์”ฉ ๊ฑธ๋ฆฌ๋Š” ๊ฒƒ์œผ๋กœ ํ™•์ธ๋ฉ๋‹ˆ๋‹ค.

์„ฑ๊ณต์ ์œผ๋กœ ์ž˜ ๋งˆ๋ฌด๋ฆฌํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค!

๋‚˜๋จธ์ง€ ์—ฐ์‚ฐ์ž๋ฅผ if๋ฌธ ๋ถ„๊ธฐ๋“ค๋กœ ๋ฐ”๊พผ ์ดํ›„ ํŽ˜์ด์ง€ ๋ Œ๋”๋ง ์ฒ˜๋ฆฌ์˜ waterfall ๋ทฐ ์Šคํฌ๋ฆฐ์ƒท

์—ฐ์Šตํ•ด ๋ณด๊ธฐ

  • ํ˜„์žฌ๋กœ๋Š” ํ• ๋‹น๊ณผ ํ•ด์ œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ง€์šฐ๋Š” ๊ฒŒ Universe::tick ์†๋„๋ฅผ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๊ฐ€์žฅ ์‰ฌ์šด ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. Universe๊ฐ€ ๋‘ ๋ฒกํ„ฐ๋งŒ ๊ด€๋ฆฌํ•˜๋„๋ก ํ•˜๊ณ , ๋‘ ๋ฒกํ„ฐ๋ฅผ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๋Š” ๋‚ด๋‚ด ํ•ด์ œ๋˜๊ฑฐ๋‚˜ tick ํ•จ์ˆ˜์—์„œ ์ƒˆ ๋ฒ„ํผ๋ฅผ ํ• ๋‹นํ•˜์ง€ ์•Š๋„๋ก ์„ธํฌ๋“ค์„ ์ด์ค‘ ๋ฒ„ํผ๋ง (double buffering) ๊ธฐ๋ฒ•์œผ๋กœ ๊ตฌํ˜„ํ•ด ๋ณด์„ธ์š”.

  • "Game of Life ๊ตฌํ˜„ํ•˜๊ธฐ" ์„น์…˜์—์„œ ๋ธํƒ€ ๊ธฐ๋ฐ˜์œผ๋กœ ์„ค๊ณ„ํ•œ ๋‚ด์šฉ์„ ํ† ๋Œ€๋กœ, ๋Ÿฌ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ์ƒํƒœ๊ฐ€ ๋ฐ”๋€ ์„ธํฌ๋“ค์˜ ๋ชฉ๋ก์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋‹ค์‹œ ์ฝ”๋“œ๋ฅผ ์„ค๊ณ„ํ•ด ๋ณด์„ธ์š”. <canvas> ๊ฐ€ ๋” ๋นจ๋ฆฌ ๋ Œ๋”๋˜๋Š” ๊ฒŒ ๋ณด์ด์‹œ๋‚˜์š”? ๋งค ํ‹ฑ๋งˆ๋‹ค ์ƒˆ ๋ธํƒ€ ๋ชฉ๋ก์„ ํ• ๋‹นํ•˜์ง€ ์•Š์œผ๋ฉด์„œ ๊ตฌํ˜„ํ•˜์‹ค์ˆ˜ ์žˆ์„๊ฒƒ ๊ฐ™์œผ์‹ ๊ฐ€์š”?

  • ํ”„๋กœํŒŒ์ผ๋ง ํˆด๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋“ฏ, 2D <canvas> ๋ Œ๋”๋ง์ด ํŠน๋ณ„ํžˆ ๋น ๋ฅด์ง€๋Š” ์•Š์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. 2D ์บ”๋ฒ„์Šค ๋ Œ๋”๋Ÿฌ ๋Œ€์‹  WebGL ๋ Œ๋”๋Ÿฌ๋ฅผ ๋Œ€์‹  ์‚ฌ์šฉํ•ด์„œ ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ๊ตฌํ˜„ํ•ด ๋ณด์„ธ์š”. WebGL๋กœ ๊ตฌํ˜„ํ•œ ๋ฒ„์ „์€ ์–ผ๋งˆ๋‚˜ ๋” ๋น ๋ฅธ๊ฐ€์š”? ๋‹ต๋‹ตํ•˜๊ฒŒ ๋Š๋ ธ๋˜ WebGL๊ณผ ๋น„๊ตํ•˜๋ฉด ์–ผ๋งˆ๋‚˜ ํฐ ์‚ฌ์ด์ฆˆ์˜ ์„ธ์ƒ์„ ๊ฐ€๋ณ๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋๋‚˜์š”?

.wasm ํŒŒ์ผ ์‚ฌ์ด์ฆˆ ์ค„์ด๊ธฐ

๊ตฌํ˜„ํ–ˆ๋˜ Game of Life ํ”„๋กœ๊ทธ๋žจ๊ณผ ๊ฐ™์ด .wasm ํ˜•์‹์œผ๋กœ ์ปดํŒŒ์ผ๋œ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์œ ์ €๋“ค์—๊ฒŒ ๋„คํŠธ์›Œํฌ๋กœ ์ „์†กํ•  ๋•Œ ์ฝ”๋“œ ์‚ฌ์ด์ฆˆ๋ฅผ ์‹ ๊ฒฝ ์“ฐ๋Š” ํŽธ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ์—๋Š” .wasm ํŒŒ์ผ์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ ์ž‘์„์ˆ˜๋ก ํŽ˜์ด์ง€๊ฐ€ ๋นจ๋ฆฌ ๋กœ๋“œ๋˜๊ณ  ์œ ์ € ๊ฒฝํ—˜์ด ๋” ๋‚˜์•„์ง‘๋‹ˆ๋‹ค.

๋นŒ๋“œ ์„ค์ •์œผ๋กœ .wasm ๋ฐ”์ด๋„ˆ๋ฆฌ ์‚ฌ์ด์ฆˆ๋ฅผ ์–ผ๋งˆ๋‚˜ ์ค„์ผ ์ˆ˜ ์žˆ๋‚˜์š”?

wasm ๋ฐ”์ด๋„ˆ๋ฆฌ ์‚ฌ์ด์ฆˆ๋ฅผ ์ค„์ผ ๋•Œ ์–ด๋–ค ๋นŒ๋“œ ์„ค์ •์„ ์ˆ˜์ •ํ•ด ๋ณผ ์ˆ˜ ์žˆ๋Š”์ง€ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด์„ธ์š”.

(debug ์‹ฌ๋ณผ์ด ๋น ์ ธ์žˆ๋Š”) ๊ธฐ๋ณธ ๋นŒ๋“œ ์„ค์ •์œผ๋กœ Game of Life๋ฅผ ๋นŒ๋“œํ•˜๋ฉด 29,410 bytes ์‚ฌ์ด์ฆˆ์˜ ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ์ถœ๋ ฅ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค:

$ wc -c pkg/wasm_game_of_life_bg.wasm
29410 pkg/wasm_game_of_life_bg.wasm

LTO๋ฅผ ํ™œ์„ฑํ™”ํ•œ ๋‹ค์Œ opt-level = "z"๋ฅผ ์„ค์ •ํ•˜๊ณ  wasm-opt -Oz ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ด์„œ ๋นŒ๋“œํ•˜๋ฉด ์ถœ๋ ฅ๋˜๋Š” .wasm ๋ฐ”์ด๋„ˆ๋ฆฌ์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ 17,317 bytes ์ •๋„๋กœ ์ค„์–ด๋“œ๋Š” ๋ถ€๋ถ„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

$ wc -c pkg/wasm_game_of_life_bg.wasm
17317 pkg/wasm_game_of_life_bg.wasm

๊ทธ๋‹ค์Œ์—๋Š” ์ถœ๋ ฅ๋œ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ (๊ฑฐ์˜ ๋ชจ๋“  HTTP ์„œ๋ฒ„๊ฐ€ ํ•˜๋Š” ๋Œ€๋กœ) gzip๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์••์ถ•ํ•ด ๋ณด๋Š”๋ฐ, ์ •๋ง ๊ท€์—ฝ๊ฒŒ๋„ ๋ฐ”์ด๋„ˆ๋ฆฌ ํŒŒ์ผ์˜ ์‚ฌ์ด์ฆˆ๋ฅผ 9,045 bytes๊นŒ์ง€ ์ค„์ผ ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค!

$ gzip -9 < pkg/wasm_game_of_life_bg.wasm | wc -c
9045

์—ฐ์Šตํ•ด ๋ณด๊ธฐ

  • wasm-snip ํˆด์„ ์‚ฌ์šฉํ•ด์„œ ๊ตฌํ˜„ํ–ˆ๋˜ Game of Life์˜ .wasm ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ ํŒจ๋‹‰ ๋””๋ฒ„๊น… ํ•จ์ˆ˜๋ฅผ ์ œ์™ธ์‹œ์ผœ๋ณด์„ธ์š”. ํŒŒ์ผ์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ ์–ผ๋งˆ๋‚˜ ์ค„์–ด๋“œ๋‚˜์š”?

  • wee_alloc ๋ฅผ ์ „์—ญ ํ• ๋‹น์ž (global allocator)๋ฅผ ์‚ฌ์šฉํ•ด ๋ณด๊ณ  ํŒŒ์ผ์˜ ์‚ฌ์ด์ฆˆ๋ฅผ ์ด์ „๊ณผ ๋น„๊ตํ•ด ๋ณด์„ธ์š”. ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•  ๋•Œ ํด๋ก  ํ–ˆ๋˜ rustwasm/wasm-pack-template ํ…œํ”Œ๋ฆฟ์€ "wee_alloc" ์ด๋ผ๋Š” cargo ๊ธฐ๋Šฅ์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š”๋ฐ, ์ด ๊ธฐ๋Šฅ์€ wasm-game-of-life/Cargo.toml ํŒŒ์ผ์— ์žˆ๋Š” [features] ์„น์…˜์˜ default ํ•„๋“œ์— ์ถ”๊ฐ€ํ•ด์„œ ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    [features]
    default = ["wee_alloc"]
    

    wee_alloc์„ ํ™œ์„ฑํ™”ํ•˜๋ฉด .wasm ๋ฐ”์ด๋„ˆ๋ฆฌ ์‚ฌ์ด์ฆˆ๊ฐ€ ์–ผ๋งˆ๋‚˜ ์ค„์–ด๋“œ๋‚˜์š”?

  • ํŠœํ† ๋ฆฌ์–ผ์„ ์ง„ํ–‰ํ•˜๋Š” ๋‚ด๋‚ด ํ•œ Universe๋งŒ ํŽ˜์ด์ง€์— ํฌํ•จ์‹œ์ผฐ๋Š”๋ฐ, ์ƒ์„ฑ์ž๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹  ๋‹จ ํ•œ ๊ฐœ์˜ static mut ์ „์—ญ ์ธ์Šคํ„ด์Šค (global instance) ๋งŒ ์ˆ˜์ •ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ต์ŠคํฌํŠธ ํ•ด๋ณผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์ „ ์ฑ•ํ„ฐ์—์„œ ๋‹ค๋ฃฌ ์ด์ค‘ ๋ฒ„ํผ๋ง ๊ธฐ๋ฒ• (double buffering technique) ์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ์ด๋Ÿฌํ•œ ๋ฒ„ํผ๋„ static mut ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ „์—ญ์ ์œผ๋กœ ๋งŒ๋“ค์–ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ, ๊ตฌํ˜„ํ•œ ๊ฒŒ์ž„์—์„œ ๋ชจ๋“  ๋™์  ํ• ๋‹น (dynamic allocation) ์„ ์—†์•จ ์ˆ˜ ์žˆ๊ณ , #![no_std] ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ํ• ๋‹น๊ธฐ(allocator)๊ฐ€ ์—†๋Š” ํฌ๋ ˆ์ดํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋™์  ํ• ๋‹น์— ํ•„์š”ํ•œ ์ข…์†์„ฑ์„ ๋‹ค ์—†์• ๋ฉด .wasm ํŒŒ์ผ์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ ์–ผ๋งˆ๋‚˜ ์ค„์–ด๋“œ๋‚˜์š”?

npm์— ๋ฐฐํฌํ•˜๊ธฐ

์‚ฌ์ด์ฆˆ๊ฐ€ ์ž‘๊ณ  ๋น ๋ฅด๊ฒŒ ์ž‘๋™ํ•˜๋Š” wasm-game-of-life ํŒจํ‚ค์ง€๊ฐ€ ์ค€๋น„๋์Šต๋‹ˆ๋‹ค. ์ด์ œ ๋‹ค๋ฅธ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐœ๋ฐœ์ž๋“ค์ด Game of Life ์ฝ”๋“œ๋ฅผ ๋ฐ”๋กœ ์ข…์†์„ฑ์œผ๋กœ ๋ฐ›์•„ ์‚ฌ์šฉํ•ด ๋ณผ ์ˆ˜ ์žˆ๋„๋ก npm์— ๋ฐฐํฌํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์ค€๋น„ ์‚ฌํ•ญ

์šฐ์„ , ๊ธฐ์กด์— ์ƒ์„ฑ๋œ npm ๊ณ„์ •์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ด์ฃผ์„ธ์š”.

๊ทธ ๋‹ค์Œ, ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ๋กœ์ปฌ ๋จธ์‹ ์—์„œ ๊ณ„์ •์— ๋กœ๊ทธ์ธ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ด ์ฃผ์„ธ์š”:

wasm-pack login

๋ฐฐํฌ

wasm-game-of-life ๊ฒฝ๋กœ์—์„œ wasm-pack ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜์—ฌ wasm-game-of-life/pkg ๊ฒฝ๋กœ์— ์ž‘์„ฑํ•œ ์ฝ”๋“œ๊ฐ€ ์ž˜ ๋นŒ๋“œ๋ผ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด ์ฃผ์„ธ์š”:

wasm-pack build

wasm-game-of-life/pkg ํด๋”์˜ ๋‚ด์šฉ๋ฌผ๋“ค์„ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ํด๋”์— ์žˆ๋Š” ํŒŒ์ผ๋“ค์„ ์ด์ œ npm์— ๋ฐฐํฌํ•ด ๋ณผ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค!

์ค€๋น„๊ฐ€ ๋๋‹ค๋ฉด, wasm-pack publish ๋ฅผ ์‹คํ–‰ํ•ด์„œ ํŒจํ‚ค์ง€๋ฅผ npm์— ์—…๋กœ๋“œํ•ด๋ณด์„ธ์š”:

wasm-pack publish

์ •๋ง ๋†€๋ž๊ฒŒ๋„ ์ด๊ฒŒ ๋‹ต๋‹ˆ๋‹ค! ์ด๋ ‡๊ฒŒ npm์— ํŒจํ‚ค์ง€๋ฅผ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ...

... ์ด ํŠœํ† ๋ฆฌ์–ผ์„ ๋๋‚ธ ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค๋„ npm์— ๋ฐฐํฌ๋ฅผ ํ–ˆ์„ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— wasm-game-of-life ๋ผ๋Š” ํŒจํ‚ค์ง€ ์ด๋ฆ„์ด ๋†’์€ ํ™•๋ฅ ๋กœ ์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ์‹คํ–‰ํ•œ ๋ช…๋ น์–ด๊ฐ€ ์ด๋Ÿฌํ•œ ์ด์œ ๋กœ ์„ฑ๊ณต์ ์œผ๋กœ ์‹คํ–‰๋˜์ง€ ๋ชปํ•œ ๋ถ€๋ถ„์ด ํ™•์ธ๋˜๋‚˜์š”?

wasm-game-of-life/Cargo.toml ํŒŒ์ผ์„ ์—ด๊ณ  ์ž‘์„ฑํ•œ ํŒจํ‚ค์ง€๊ฐ€ ๊ณ ์œ ํ•œ ์ด๋ฆ„์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋„๋ก name ํ•„๋“œ์— ์›ํ•˜๋Š” ์œ ์ € ์ด๋ฆ„์„ ์ถ”๊ฐ€ํ•ด ์ฃผ์„ธ์š”.

[package]
name = "wasm-game-of-life-my-username"

์ด์ œ ๋‹ค์‹œ ๋นŒ๋“œํ•˜๊ณ  ๋ฐฐํฌํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

wasm-pack build
wasm-pack publish

์ด๋ฒˆ์—๋Š” ์ž˜ ๋ฐฐํฌ๋์„ ๊ฒ๋‹ˆ๋‹ค!

์ฐธ์กฐ

์ด ์„น์…˜์€ ๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๊ฐœ๋ฐœ์— ๋„์›€์ด ๋˜๋Š” ์ฐธ์กฐ ์ž๋ฃŒ๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ˆœ์„œ๊ฐ€ ์ •ํ•ด์ ธ ์žˆ์ง€ ์•Š์•„ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋๊นŒ์ง€ ์ฝ์„ ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๊ฐ ํ•˜์œ„ ์„น์…˜์€ ๊ฐœ๋ณ„์ ์œผ๋กœ ๊ฐ์ž ๋‹ค๋ฅธ ๋‚ด์šฉ์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

์•Œ๋ฉด ์ข‹์€ ํฌ๋ ˆ์ดํŠธ๋“ค

๋Ÿฌ์ŠคํŠธ์™€ WebAssembly๋กœ ๊ฐœ๋ฐœํ•  ๋•Œ ์•Œ๋ฉด ์ข‹์€ ํฌ๋ ˆ์ดํŠธ๋“ค์„ ๋ชจ์•„๋†“์€ ๋ชฉ๋ก์ž…๋‹ˆ๋‹ค.

creates.io ์›น์‚ฌ์ดํŠธ์—์„œ WebAssembly ์นดํ…Œ๊ณ ๋ฆฌ๋กœ ๋“ฑ๋ก๋œ ํฌ๋ ˆ์ดํŠธ๋“ค์„ ํ•„ํ„ฐ๋งํ•ด์„œ ๋ณผ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ DOM ์กฐ์ž‘ํ•˜๊ธฐ

wasm-bindgen | crates.io | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

wasm-bindgen์€ ๋Ÿฌ์ŠคํŠธ์™€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์‚ฌ์ด์˜ ๊ณ ๋ ˆ๋ฒจ ์ƒํ˜ธ์ž‘์šฉ์„ ๋„์™€์ฃผ๋Š” ํฌ๋ ˆ์ดํŠธ์ž…๋‹ˆ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ ๋Ÿฌ์ŠคํŠธ๋ฅผ ๋„˜๋‚˜๋“ค๋ฉด์„œ ์ž„ํฌํŠธ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค.

wasm-bindgen-futures | crates.io | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

wasm-bindgen-futures๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ Promise์™€ ๋Ÿฌ์ŠคํŠธ์˜ Future์„ ์—ฐ๊ฒฐํ•ด ์ฃผ๋Š” ๋‹ค๋ฆฌ ์—ญํ• ์„ ํ•˜๋Š” ํฌ๋ ˆ์ดํŠธ์ž…๋‹ˆ๋‹ค. ๋Ÿฌ์ŠคํŠธ์™€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์‚ฌ์ด์—์„œ ์–‘๋ฐฉํ–ฅ์œผ๋กœ ๋ณ€ํ™˜์ด ๊ฐ€๋Šฅํ•˜๊ณ  ๋Ÿฌ์ŠคํŠธ์—์„œ ๋น„๋™๊ธฐ(asynchronous) ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. DOM ์ด๋ฒคํŠธ ๋ฐ I/O ์ž‘์—…๊ณผ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค๋‹ˆ๋‹ค.

js-sys | crates.io | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

wasm-bindgen์œผ๋กœ ์ž‘์„ฑ๋œ js-sys ํฌ๋ ˆ์ดํŠธ๋Š” Object, Function, eval ๋“ฑ์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ „์—ญ ํƒ€์ž…๊ณผ ๋ฉ”์†Œ๋“œ๋ฅผ ๋ชจ๋‘ ์ž„ํฌํŠธํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ API๋“ค์€ ์›น ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ Node.js๋ฅผ ํฌํ•จํ•œ ๋‹ค๋ฅธ ๋ชจ๋“  ECMAScript ํ™˜๊ฒฝ์— ์ด์‹ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

web-sys | crates.io | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

DOM ์กฐ์ž‘, setTimeout, Web GL, Web Audio ๋“ฑ๊ณผ ๊ฐ™์€ ๋‹ค๋ฅธ ๋ชจ๋“  ์›น API๋“ค์„ wasm-bindgen์œผ๋กœ ์ž‘์„ฑํ•œ ํฌ๋ ˆ์ดํŠธ์ž…๋‹ˆ๋‹ค.

์˜ค๋ฅ˜ ๋ณด๊ณ  ๋ฐ ๋กœ๊น…

console_error_panic_hook | crates.io | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

console.error๋กœ ๋ฉ”์„ธ์ง€๋ฅผ ํŒจ๋‹‰ ๋ฉ”์„ธ์ง€๋ฅผ ๋„˜๊ฒจ์ฃผ๋Š” ํŒจ๋‹‰ ํ›… (panic hook) ๊ณผ ํ•จ๊ป˜, wasm32-unknown-unknown ํƒ€๊ฒŸ์œผ๋กœ ํŒจ๋‹‰๋“ค์„ ๋””๋ฒ„๊น…ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ํฌ๋ ˆ์ดํŠธ์ž…๋‹ˆ๋‹ค.

console_log | crates.io | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

log ํฌ๋ ˆ์ดํŠธ์˜ ๋ฐฑ์—”๋“œ๋ฅผ ์ œ๊ณตํ•ด ์ฃผ๋Š” ํฌ๋ ˆ์ดํŠธ์ž…๋‹ˆ๋‹ค. ๋กœ๊ทธ ๋œ ๋ฉ”์„ธ์ง€๋“ค์„ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ (devtools) ์ฝ˜์†”๋กœ ๋„˜๊ฒจ์ค๋‹ˆ๋‹ค.

๋™์  ํ• ๋‹น

wee_alloc | crates.io | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

์ด ํฌ๋ ˆ์ดํŠธ์˜ ์ด๋ฆ„์€ Wasm-Enabled, Elfin Allocator ์—์„œ ์œ ๋ž˜๋๋Š”๋ฐ, (1000 bytes ์ดํ•˜ ์‚ฌ์ด์ฆˆ์˜ ์••์ถ•๋˜์ง€ ์•Š์€ .wasm ๋ฐ”์ด๋„ˆ๋ฆฌ๋กœ ๊ตฌ์„ฑ๋œ) ์ฝ”๋“œ ์‚ฌ์ด์ฆˆ๊ฐ€ ํ• ๋‹น ์„ฑ๋Šฅ๋ณด๋‹ค ๋” ์ค‘์š”ํ•  ๋•Œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ• ๋‹น์ž๋ฅผ ๊ตฌํ˜„ํ•œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

.wasm ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ํŒŒ์‹ฑ(parsing)ํ•˜๊ณ  ์ƒ์„ฑํ•˜๊ธฐ

parity-wasm | crates.io | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

์ง๋ ฌํ™”(serializing)๊ณผ ์—ญ์ง๋ ฌํ™”(deserializing), ๊ทธ๋ฆฌ๊ณ  .wasm ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๋นŒ๋“œํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•˜๋Š” ์ €๋ ˆ๋ฒจ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํฌ๋งท์ž…๋‹ˆ๋‹ค. "names" ๋ฐ "reloc.WHATEVER"๊ณผ ๊ฐ™์ด ์ž˜ ์•Œ๋ ค์ง„ ์„น์…˜๋“ค์ด ์ž˜ ์ง€์›๋ผ ์žˆ์Šต๋‹ˆ๋‹ค.

wasmparser | crates.io | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ฐ”์ด๋„ˆ๋ฆฌ ํŒŒ์ผ์„ ์ฝ๋Š” ๋ฐ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ„๋‹จํ•œ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ (event-driven) ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. ๊ฐ๊ฐ ํŒŒ์‹ฑํ•œ ๋‚ด์šฉ์˜ ๋ฐ”์ดํŠธ ์˜คํ”„์…‹ (byte offset) ์„ ์ œ๊ณตํ•˜๋Š”๋ฐ, ์˜ˆ๋ฅผ ๋“ค์–ด reloc์„ ์ฝ๋Š” ์ž‘์—… ๋“ฑ์— ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์ปดํŒŒ์ผํ•˜๊ณ  ์ธํ„ฐํ”„๋ฆฌํŒ…(Interpreting)ํ•˜๊ธฐ

wasmi | crates.io | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

Parity ๋ผ๋Š” ํšŒ์‚ฌ์—์„œ ๋งŒ๋“  ์ž„๋ฒ ๋”ฉ ํ•  ์ˆ˜ ์žˆ๋Š” ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์ธํ„ฐํ”„๋ฆฌํ„ฐ์ž…๋‹ˆ๋‹ค.

cranelift-wasm | crates.io | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

์›น์–ด์…ˆ๋ธ”๋ฆฌ ์ฝ”๋“œ๋ฅผ ๋„ค์ดํ‹ฐ๋ธŒ ํ˜ธ์ŠคํŠธ์˜ ๊ธฐ๊ณ„์–ด(machine code)๋กœ ์ปดํŒŒ์ผํ•ด ์ฃผ๋Š” ํฌ๋ ˆ์ดํŠธ์ธ Cranelift (nรฉ Cretonne) ์ฝ”๋“œ ์ƒ์„ฑ๊ธฐ ํ”„๋กœ์ ํŠธ์˜ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค.

์•Œ๋ฉด ์ข‹์€ ํˆด๋“ค

๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋กœ ๊ฐœ๋ฐœํ•  ๋•Œ ์•Œ๋ฉด ์ข‹์€ ํˆด๋“ค์„ ๋ชจ์•„๋†“์€ ๋ชฉ๋ก์ž…๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ, ๋นŒ๋“œ ๋ฐ ์›Œํฌํ”Œ๋กœ์šฐ ๊ด€๋ฆฌ (Orchestration)

wasm-pack | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

wasm-pack์€ Web๊ณผ Node.js ํ™˜๊ฒฝ์—์„œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ ์ƒํ˜ธ ์šด์šฉํ•˜๋ฉด์„œ ๋Ÿฌ์ŠคํŠธ๋กœ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์ž‘์—…์„ ํ•  ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋งŒ๋Šฅ ํˆด์ž…๋‹ˆ๋‹ค. wasm-pack์„ ์‚ฌ์šฉํ•ด์„œ ๋” ์‰ฝ๊ฒŒ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ๋นŒ๋“œํ•˜๊ณ  npm ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ๋ฐฐํฌํ•˜๊ณ , ๊ธฐ์กด์— ์ด๋ฏธ ์‚ฌ์šฉํ•˜๋Š” ์›Œํฌํ”Œ๋กœ์šฐ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ํŒจํ‚ค์ง€๋“ค๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค.

.wasm ๋ฐ”์ด๋„ˆ๋ฆฌ ์ตœ์ ํ™”์™€ ์กฐ์ž‘ํ•˜๊ธฐ

wasm-opt | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

wasm-opt๋Š” ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›๊ณ  ๋ณ€ํ˜•ํ•˜๊ณ  ์ตœ์ ํ™”ํ•œ ๋‹ค์Œ, ์›ํ•œ๋‹ค๋ฉด ์ฝ”๋“œ๋ฅผ ์ธก์ •/๋ถ„์„๊นŒ์ง€ ํ•œ ๋‹ค์Œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํŒŒ์ผ์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. rustc๊ฐ€ ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์ฒ˜๋Ÿผ, LLVM์œผ๋กœ ์ƒ์„ฑํ•œ .wasm ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋„ฃ๊ณ  wasm-opt๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋” ์ž‘๊ณ  ๋” ๋น ๋ฅด๊ฒŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” .wasm ๋ฐ”์ด๋„ˆ๋ฆฌ ํŒŒ์ผ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

wasm2js | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

wasm2js ํˆด์€ WebAssembly ํŒŒ์ผ์„ "๊ฑฐ์˜ asm.js"์ฒ˜๋Ÿผ ์ปดํŒŒ์ผ ํ•ด์ค๋‹ˆ๋‹ค. Internet Explorer 11์ฒ˜๋Ÿผ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๊ธฐ๋Šฅ์ด ๊ตฌํ˜„๋˜์ง€ ์•Š์€ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ง€์›ํ•  ๋•Œ ์ข‹์Šต๋‹ˆ๋‹ค. ์ด ํˆด์€ binaryen ํ”„๋กœ์ ํŠธ์˜ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค.

wasm-gc | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ๊ฐ€๋น„์ง€ ์ฝœ๋ ‰ํŒ…ํ•˜๊ณ  ํ•„์š”ํ•˜์ง€ ์•Š์€ ์ต์ŠคํฌํŠธ, ์ž„ํฌํŠธ, ํ•จ์ˆ˜ ๋“ฑ์„ ์ง€์šธ ๋•Œ ์‚ฌ์šฉ ์ˆ˜ ์žˆ๋Š” ์‹ฌํ”Œํ•œ ํˆด์ž…๋‹ˆ๋‹ค. ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํŒŒ์ผ๊ณผ ํ•จ๊ป˜ --gc-sections ๋ง์ปค ํ”Œ๋ž˜๊ทธ (linker flag) ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋” ํšจ๊ณผ์ ์ž…๋‹ˆ๋‹ค.

๋ณดํ†ต์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์œ ๋กœ ์ด ํˆด์„ ๊ฐœ๋ฐœ์ž๊ฐ€ "์ง์ ‘" ํ”„๋กœ์ ํŠธ์— ํฌํ•จ์‹œํ‚ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค:

  1. rustc ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ด์ œ ์ƒˆ ๋ฒ„์ „์˜ lid๋ฅผ ์ง€์›ํ•˜๋Š”๋ฐ, ์ด ๋ฒ„์ „์ด --gc-sections ๋ผ๋Š” ํ”Œ๋ž˜๊ทธ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ด ํ”Œ๋ž˜๊ทธ๋Š” LTO ๋นŒ๋“œ๋ฅผ ํ•˜๋Š”๋ฐ ์ž๋™์œผ๋กœ ํ™œ์„ฑํ™”๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  2. wasm-bindgen CLI (์ปค๋งจ๋“œ ๋ผ์ธ ์ธํ„ฐํŽ˜์ด์Šค / Command Line Interface) ํˆด์ด ์ž๋™์œผ๋กœ wasm-gc ๋ฅผ ์‹คํ–‰์‹œ์ผœ ์ค๋‹ˆ๋‹ค.

wasm-snip | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

wasm-snip๋Š” ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํ•จ์ˆ˜์˜ ๋‚ด์šฉ์„ unreachable ๋ช…๋ น์–ด(instruction)์œผ๋กœ ๋ฐ”๊ฟ”์ค๋‹ˆ๋‹ค.

์–ด๋–ค ํ•จ์ˆ˜๊ฐ€ ์‹ค์ œ๋กœ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๊ฑธ ์•„๋Š”๋ฐ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ด๊ฑธ ๋ชจ๋ฅผ ๋•Œ๊ฐ€ ์žˆ๋‚˜์š”? ์ผ๋‹จ ์ปดํŒŒ์ผํ•˜๊ณ  wasm-gc๋ฅผ ์‹คํ–‰ํ•ด ๋ณด์„ธ์š”! (๋Ÿฐํƒ€์ž„(runtime)์—์„œ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”) ๋‹ค๋ฅธ ๊ฐ„์ ‘์ ์œผ๋กœ ํ˜ธ์ถœ๋˜๋Š” ๋‹ค๋ฅธ ํ•จ์ˆ˜๋“ค๋„ ๋ชจ๋‘ ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค.

๋””๋ฒ„๊น… ์ฝ”๋“œ๊ฐ€ ์—†๋Š” ์‹ค์ œ๋กœ ๋ฐฐํฌํ•˜๋Š” ๋นŒ๋“œ (production build) ์—์„œ ๊ฐ•์ œ๋กœ ๋Ÿฌ์ŠคํŠธ ์˜ ํŒจ๋‹‰ ์ธํ”„๋ผ(infrastructure)๋ฅผ ์ œ์™ธํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

.wasm ๋ฐ”์ด๋„ˆ๋ฆฌ ์‚ดํŽด๋ณด๊ธฐ

twiggy | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

twiggy๋Š” .wasm ๋ฐ”์ด๋„ˆ๋ฆฌ์— ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ ํ”„๋กœํŒŒ์ผ๋Ÿฌ์ž…๋‹ˆ๋‹ค. ๋ฐ”์ด๋„ˆ๋ฆฌ์˜ ํ˜ธ์ถœ ๊ทธ๋ž˜ํ”„ (call graph) ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‚ด์šฉ์„ ์•Œ๋ ค์ค๋‹ˆ๋‹ค:

  • ์• ์ดˆ์— ๋นŒ๋“œํ•  ๋•Œ ์–ด๋–ค ํ•จ์ˆ˜๊ฐ€ ์™œ ๋ฐ”์ด๋„ˆ๋ฆฌ์— ํฌํ•จ๋๋‚˜์š”? ์˜ˆ: ์–ด๋–ค ์ต์ŠคํฌํŠธํ•œ ํ•จ์ˆ˜๋“ค์ด ๊ฐ„์ ‘์ ์œผ๋กœ ํ˜ธ์ถœ๋˜๊ณ  ์žˆ๋‚˜์š”?

  • ์–ด๋–ค ํ•จ์ˆ˜๋ฅผ ์ง€์šฐ๋ฉด ์–ผ๋งˆ๋‚˜ ๊ณต๊ฐ„์„ ์•„๋‚„ ์ˆ˜ ์žˆ๋‚˜์š”? ์˜ˆ: ์ด ํ•จ์ˆ˜์™€ ๋‹ค๋ฅธ ์‚ฌ์šฉ๋˜๋˜ ํ•จ์ˆ˜๋“ค๊นŒ์ง€ ์ง€์šฐ๋ฉด ์–ผ๋งˆ๋‚˜ ๊ณต๊ฐ„์ด ์ ˆ์•ฝ๋˜๋‚˜์š”?

twiggy๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๋” ๊ฐ€๋ณ๊ฒŒ ๋งŒ๋“ค์–ด๋ณด์„ธ์š”!

wasm-objdump | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

wasm ๋ฐ”์ด๋„ˆ๋ฆฌ์˜ ์ €๋ ˆ๋ฒจ ์ƒ์„ธ ์ •๋ณด์™€ ์„น์…˜๋“ค์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. WAT ํ…์ŠคํŠธ ํฌ๋งท์œผ๋กœ ๋””์–ด์…ˆ๋ธ”๋ง(disassembling)ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์›น์–ด์…ˆ๋ธ”๋ฆฌ์— ์‚ฌ์šฉํ•˜๋Š” objdump์œผ๋กœ ์ƒ๊ฐํ•ด๋„ ์ข‹์Šต๋‹ˆ๋‹ค. ์ด ํˆด์€ WABT ํ”„๋กœ์ ํŠธ์˜ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค.

wasm-nm | ๋ ˆํฌ์ง€ํ† ๋ฆฌ

.wasm ๋ฐ”์ด๋„ˆ๋ฆฌ ๋‚ด์˜ ์ž„ํฌํŠธ๋˜๊ณ , ์ต์ŠคํฌํŠธ๋˜๊ณ , ๊ทธ๋ฆฌ๊ณ  private์ธ ํ•จ์ˆ˜ ์‹ฌ๋ณผ๋“ค (function symbols) ์„ ๋‚˜์—ดํ•ฉ๋‹ˆ๋‹ค. ์›น์–ด์…ˆ๋ธ”๋ฆฌ์— ์‚ฌ์šฉํ•˜๋Š” nm์œผ๋กœ ์ƒ๊ฐํ•ด๋„ ์ข‹์Šต๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ ํ…œํ”Œ๋ฆฟ

"The Rust and WebAssembly Working Group"์€ ๊ฐœ๋ฐœ์ž๋“ค์ด ๋น ๋ฅด๊ฒŒ ์ƒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•˜๊ณ  ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ํ”„๋กœ์ ํŠธ ํ…œํ”Œ๋ฆฟ ๋ชฉ๋ก์„ ๋งŒ๋“ค๊ณ  ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

wasm-pack-template

wasm-pack์œผ๋กœ ์…‹์—… ๋œ ์ด ํ…œํ”Œ๋ฆฟ์€ ๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํ”„๋กœ์ ํŠธ๋ฅผ ์†์‰ฝ๊ฒŒ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ค€๋น„๋ผ ์žˆ์Šต๋‹ˆ๋‹ค.

cargo generate ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ํ”„๋กœ์ ํŠธ ํ…œํ”Œ๋ฆฟ์„ ํด๋ก ํ•ด๋ณด์„ธ์š”:

cargo install cargo-generate
cargo generate --git https://github.com/rustwasm/wasm-pack-template.git

create-wasm-app

์ด ํ…œํ”Œ๋ฆฟ์€ wasm-pack์„ ํ†ตํ•ด ๋ฐฐํฌ๋œ npm ํŒจํ‚ค์ง€๋ฅผ ๊ฐ„ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ๋•Œ ์œ ์šฉํ•œ ๊ธฐ๋Šฅ๋“ค์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

npm init ๋ช…๋ น์–ด๋กœ ์‚ฌ์šฉํ•ด ๋ณด์„ธ์š”:

mkdir my-project
cd my-project/
npm init wasm-app

์ด ํ…œํ”Œ๋ฆฟ์€ ์ฃผ๋กœ wasm-pack-template์ด๋ผ๋Š” ์ข…์†์„ฑ๊ดด ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜๋Š”๋ฐ, create-wasm-app์œผ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด npm link ๊ธฐ๋Šฅ์œผ๋กœ ์ƒ์„ฑํ•œ ํ”„๋กœ์ ํŠธ์™€ ์—ฐ๊ฒฐํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

rust-webpack-template

์ด ํ…œํ”Œ๋ฆฟ์€ Rust ์ฝ”๋“œ๋ฅผ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋กœ ์ปดํŒŒ์ผํ•˜๊ณ  ์ถœ๋ ฅ๋œ ํŒŒ์ผ๋“ค์„ webpack์˜ rust-loader๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฐ”๋กœ ํŒŒ์ดํ”„๋ผ์ธ์œผ๋กœ ํ›„ํ‚น ํ•˜์—ฌ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ฑฐ์˜ ๋ชจ๋“  ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ๋“ค์„ ๋Œ€์‹  ์„ค์ •ํ•ด ์ค๋‹ˆ๋‹ค.

npm init ๋ช…๋ น์–ด๋กœ ์‚ฌ์šฉํ•ด ๋ณด์„ธ์š”:

mkdir my-project
cd my-project/
npm init rust-webpack

๋Ÿฌ์ŠคํŠธ๋กœ ์ƒ์„ฑํ•œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋””๋ฒ„๊น…ํ•˜๊ธฐ

์ด ์„น์…˜์€ ๋Ÿฌ์ŠคํŠธ๋กœ ์ƒ์„ฑํ•œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ๋””๋ฒ„๊น…ํ•˜๋Š”๋ฐ ์œ ์šฉํ•œ ์ •๋ณด๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

debug ์‹ฌ๋ณผ์„ ํฌํ•จํ•ด์„œ ๋นŒ๋“œํ•˜๊ธฐ

โšก ๋””๋ฒ„๊น…ํ•  ๋•Œ ํ•ญ์ƒ debug ์‹ฌ๋ณผ์„ ํฌํ•จํ•˜๊ณ  ๋นŒ๋“œํ•˜๋Š”์ง€ ํ™•์ธํ•ด ์ฃผ์„ธ์š”!

debug ์‹ฌ๋ณผ์ด ํ™œ์„ฑํ™” ๋˜์–ด์žˆ์ง€ ์•Š๋‹ค๋ฉด "name" ์ปค์Šคํ…€ ์„น์…˜์ด ์ปดํŒŒ์ผ๋œ .wasm ํŒŒ์ผ์— ๋ฐ˜์˜๋˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ์—๋Š” ์Šคํƒ ์ถ”์  (stack trace) ์„ ํ•  ๋•Œ ํ•จ์ˆ˜ ์ด๋ฆ„์ด ๋Ÿฌ์ŠคํŠธ ํ•จ์ˆ˜ ์ด๋ฆ„ ๋Œ€์‹  wasm-function[42] ์ฒ˜๋Ÿผ ์ฝ๊ธฐ ์–ด๋ ต๊ฒŒ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ์ •์ƒ์ ์œผ๋กœ ์ด ์‹ฌ๋ณผ์ด ํ™œ์„ฑํ™” ๋œ ๊ฒฝ์šฐ์—๋Š” wasm_game_of_life::Universe::live_neighbor_count ์™€ ๊ฐ™์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

(wasm-pack build --debug ํ˜น์€ cargo build ๋ช…๋ น์–ด๋กœ) "debug" ๋นŒ๋“œ๋ฅผ ํ•  ๊ฒฝ์šฐ ์ด ์‹ฌ๋ณผ์ด ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค.

"release" ๋นŒ๋“œ๋ฅผ ํ•  ๋•Œ๋Š” debug ์‹ฌ๋ณผ์ด ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ํ™œ์„ฑํ™”๋˜์ง€ ์•Š์œผ๋‹ˆ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”. ๊ทธ๋ž˜๋„ ํ™œ์„ฑํ™”๋ฅผ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด Cargo.toml ํŒŒ์ผ์„ ์—ด๊ณ  [profile.release] ์„น์…˜์— debug = true๋ฅผ ํฌํ•จ์‹œ์ผœ์ฃผ์„ธ์š”.

[profile.release]
debug = true

console API๋กœ ๋กœ๊น… ํ•˜๊ธฐ

๋กœ๊น…(logging) ์ž‘์—…์€ ์ฝ”๋“œ๋ฅผ ์„ค๊ณ„ํ•˜๋ฉด์„œ ๋งŒ๋“  ๊ฐ€์„ค๋“ค์„ ํ™•์ธํ•˜๊ณ  ํ”„๋กœ๊ทธ๋žจ์˜ ๋ฒ„๊ทธ๋ฅผ ์žก์•„๋‚ด๋Š” ๋ฐ ๋งค์šฐ ํšจ๊ณผ์ ์ž…๋‹ˆ๋‹ค. ๋ณดํ†ต์€ ์›น ํ™˜๊ฒฝ์—์„œ console.log ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €์˜ ๊ฐœ๋ฐœ์ž ์ฝ˜์†”์— ๋ฉ”์„ธ์ง€๋ฅผ ๋กœ๊ทธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ web-sys ํฌ๋ ˆ์ดํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ console ๋กœ๊น… ํ•จ์ˆ˜๋ฅผ ํ•ด๋ณผ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

#![allow(unused)]
fn main() {
extern crate web_sys;

web_sys::console::log_1(&"Hello, world!".into());
}

๋˜ ๋‹ค๋ฅธ ์˜ต์…˜์œผ๋กœ๋Š” console.error ํ•จ์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. console.error๋Š” console.log์™€ ๊ฐ™์€ ํƒ€์ž… ์‹œ๊ทธ๋‹ˆ์ฒ˜(signature)๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์ง€๋งŒ ๊ฐœ๋ฐœ์ž ํˆด์—์„œ ๋กœ๊ทธ ๋ฉ”์„ธ์ง€์™€ ํ•จ๊ป˜ ์Šคํƒ ์ถ”์ ์„ ์ฐพ๊ณ  ํ‘œ์‹œํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ์กฐ

ํŒจ๋‹‰ ๋กœ๊ทธ ํ•˜๊ธฐ

console_error_panic_hook ํฌ๋ ˆ์ดํŠธ๋Š” console.error ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ํŒจ๋‹‰๋“ค์„ ๋กœ๊ทธ ํ•ฉ๋‹ˆ๋‹ค. ์ด ํฌ๋ ˆ์ดํŠธ๋ฅผ ํ†ตํ•ด ์™ธ๊ณ„์–ด๊ฐ™์ด ๋ณด์ด๊ณ  ๋””๋ฒ„๊น…ํ•˜๊ธฐ ์–ด๋ ค์šด RuntimeError: unreachable executed ์—๋Ÿฌ ๋ฉ”์„ธ์ง€ ๋Œ€์‹  ๋Ÿฌ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ ๋กœ๊ทธ ํ–ˆ๋˜ ๊ฒƒ๊ณผ ๊ฐ™์ด ๊น”๋”ํ•˜๊ฒŒ ํฌ๋งท๋œ ํŒจ๋‹‰ ๋ฉ”์„ธ์ง€๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ •๋ง ๊ฐ„๋‹จํ•˜๊ฒŒ๋„ ์ฝ”๋“œ๊ฐ€ ์‹œ์ž‘๋˜๋Š” ํ•จ์ˆ˜์—์„œ console_error_panic_hook::set_once()๋ฅผ ํ˜ธ์ถœํ•ด์„œ ํ›…(hook)์„ ์„ค์ •ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

#![allow(unused)]
fn main() {
#[wasm_bindgen]
pub fn init_panic_hook() {
    console_error_panic_hook::set_once();
}
}

๋””๋ฒ„๊ฑฐ ์‚ฌ์šฉํ•˜๊ธฐ

์ •๋ง ์•„์‰ฝ๊ฒŒ๋„ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋””๋ฒ„๊น… ๊ฒฝํ—˜์€ ์•„์ง ๊ฐœ์„ ๋ผ์•ผํ•  ๋ถ€๋ถ„์ด ๋งŽ์Šต๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ์œ ๋‹‰์Šค ์‹œ์Šคํ…œ์—์„œ๋Š” DWARF๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์†Œ์Šค ๋ ˆ๋ฒจ์—์„œ ์‹คํ–‰ํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ์„ ์‚ดํŽด๋ณด๊ฒŒ ๋˜๋Š”๋ฐ, ์ด DWARF๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋””๋ฒ„๊ฑฐ๊ฐ€ ํ•„์š”๋กœ ํ•˜๋Š” ์ •๋ณด๋ฅผ ์ธ์ฝ”๋”ฉ(encoding)ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด์— ์œˆ๋„์šฐ์—์„œ๋Š” ๋Œ€์‹  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํฌ๋งท์ด ์žˆ์Œ์—๋„ ํ˜„์žฌ๋กœ์„œ๋Š” ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์ง์ ‘์ ์œผ๋กœ ์ง€์›ํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ๋”ฐ๋กœ ์ž‘์„ฑํ–ˆ๋˜ ๋Ÿฌ์ŠคํŠธ ์†Œ์Šค ํ…์ŠคํŠธ ๋Œ€์‹  ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ถœ๋ ฅํ•œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ช…๋ น์–ด๋ฅผ ๊ทธ๋Œ€๋กœ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

W3C ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๊ทธ๋ฃน์˜ ๋””๋ฒ„๊น… ํ•˜์œ„ ์กฐํ•ญ๋„ ์žˆ์œผ๋ฏ€๋กœ ๋ฏธ๋ž˜์—๋Š” ์ด๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ๊ฐœ์„ ๋  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋ฉ๋‹ˆ๋‹ค!

๊ทธ๋ž˜๋„ ์›น์–ด์…ˆ๋ธ”๋ฆฌ์™€ ํ•จ๊ป˜ ์ž‘์„ฑ๋œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๊ณ  ์›น์–ด์…ˆ๋ธ”๋ฆฌ์˜ ์ƒํƒœ๋ฅผ ์ง์ ‘ ์‚ดํŽด๋ณด๋Š”๋ฐ ๋””๋ฒ„๊ฑฐ๊ฐ€ ๋งค์šฐ ์œ ์šฉํ•˜๋ฏ€๋กœ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.

์ฐธ์กฐ

์ฒ˜์Œ๋ถ€ํ„ฐ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋””๋ฒ„๊น…์„ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ๋„๋ก ์‹ ๊ฒฝ ์จ์„œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ์ฃผ์„ธ์š”

๊ณ ์ณ์•ผ ํ•  ๋ฒ„๊ทธ๊ฐ€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋‚˜ Web API ํ™˜๊ฒฝ์„ ํ•„์š”๋กœ ํ•œ๋‹ค๋ฉด wasm-bindgen-test์œผ๋กœ ํ…Œ์ŠคํŒ… ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ณด์„ธ์š”.

๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด #[test] ์†์„ฑ์„ ํฌํ•จํ•˜์—ฌ ์ผ๋ฐ˜์ ์ธ ๋Ÿฌ์ŠคํŠธ ์ฝ”๋“œ์ฒ˜๋Ÿผ ์‹คํ–‰ ํ™˜๊ฒฝ์„ ์žฌํ˜„ํ•ด ๋ณด์„ธ์š”. ์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ ์šด์˜์ฒด์ œ์˜ ์„ฑ์ˆ™ํ•œ ๋„ค์ดํ‹ฐ๋ธŒ ํˆด๋ง์„ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํ•˜์—ฌ ๋””๋ฒ„๊น…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. quickcheck๊ณผ ๊ฐ™์€ ํ…Œ์ŠคํŒ… ํฌ๋ ˆ์ดํŠธ์™€ ํ…Œ์ŠคํŒ… ์ฝ”๋“œ ์ถ•์†Œ๊ธฐ๋„ ๋ณด๋ฉด์„œ ๊ธฐ๊ณ„์ ์œผ๋กœ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ์‚ฌ์ด์ฆˆ๋ฅผ ์ค„์—ฌ๋ณผ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ตœ์ข…์ ์œผ๋กœ๋Š”, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์ด ์š”๊ตฌ๋˜์ง€ ์•Š๋„๋ก ๋” ์ž‘์€ ๋Ÿฌ์ŠคํŠธ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋กœ ๋ถ„๋ฆฌ์‹œํ‚ค๋Š” ์‹์œผ๋กœ ๋ฒ„๊ทธ๋ฅผ ๋” ์‰ฝ๊ฒŒ ์ฐพ๊ณ  ๊ณ ์น  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ปดํŒŒ์ผ๋Ÿฌ์™€ ๋ง์ปค(linker) ์˜ค๋ฅ˜ ์—†์ด ๋„ค์ดํ‹ฐ๋ธŒ #[test] ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด Cargo.toml ํŒŒ์ผ์˜ [lib.crate-type] ๋ฐฐ์—ด์— rlib๊ฐ€ ํฌํ•จ๋ผ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.

[lib]
crate-type ["cdylib", "rlib"]

ํƒ€์ž„ ํ”„๋กœํŒŒ์ผ๋ง

์ด ์„น์…˜์€ ๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„ฑ๋Šฅ์„ ๋œ ์“ฐ๋ฉด์„œ๋„ ์ง€์—ฐ ์‹œ๊ฐ„๋„ ์ค„์ผ ์ˆ˜ ์žˆ๋„๋ก ํŽ˜์ด์ง€๋ฅผ ํ”„๋กœํŒŒ์ผ๋งํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋‹ค๋ฃน๋‹ˆ๋‹ค.

โšก ํ”„๋กœํŒŒ์ผ๋ง์„ ํ•  ๋•Œ์—๋Š” ํ•ญ์ƒ ์ตœ์ ํ™”๋œ ๋นŒ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์ฃผ์„ธ์š”! wasm-pack build ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ์ด๋Ÿฌํ•œ ์ตœ์ ํ™” ์˜ต์…˜์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ํ™œ์„ฑํ™”๋ผ์žˆ์œผ๋‹ˆ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.

์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํˆด๋“ค

window.performance.now() ํƒ€์ด๋จธ

performance.now() ํ•จ์ˆ˜๋Š” ์›น ํŽ˜์ด์ง€๊ฐ€ ๋กœ๋“œ๋œ ์‹œ์ ๋ถ€ํ„ฐ ์ธก์ •๋œ ๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„์˜ ๋‹จ์กฐ ์‹œ๊ฐ„ (monotonic timestamp)๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

performance.now๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ์—๋Š” ๊ฑฐ์˜ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ์‹œ์Šคํ…œ ์„ฑ๋Šฅ์— ํฐ ์˜ํ–ฅ์„ ์ฃผ๊ฑฐ๋‚˜ ์ธก์ • ๊ฐ’์— ํŽธํ–ฅ์„ ์ฃผ์ง€ ์•Š์œผ๋ฉด์„œ ๊ฐ„๋‹จํ•˜๊ณ  ์ƒ์„ธํ•œ ์ธก์ •์„ ์›ํ™œํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋กœ๊ทธ๋žจ์˜ ์—ฌ๋Ÿฌ ์ž‘์—…๋“ค์ด ์‹คํ–‰๋˜๋Š” ์‹œ๊ฐ„์„ ์ธก์ •ํ•ด ๋ณผ ์ˆ˜ ์žˆ๊ณ , web-sys ํฌ๋ ˆ์ดํŠธ๋ฅผ ํ†ตํ•ด window.performance.now()์— ์ ‘๊ทผํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค:

#![allow(unused)]
fn main() {
extern crate web_sys;

fn now() -> f64 {
    web_sys::window()
        .expect("should have a Window")
        .performance()
        .expect("should have a Performance")
        .now()
}
}

๊ฐœ๋ฐœ์ž ๋„๊ตฌ ๋‚ด์˜ ํ”„๋กœํŒŒ์ผ๋Ÿฌ

๋ชจ๋“  ์›น ๋ธŒ๋ผ์šฐ์ €์˜ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋Š” ํ”„๋กœํŒŒ์ผ๋Ÿฌ๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํ”„๋กœํŒŒ์ผ๋Ÿฌ๋“ค์€ "call tree"๋‚˜ "flame graph"์™€ ๊ฐ™์€ ์ผ๋ฐ˜์ ์ธ ์‹œ๊ฐ ์ž๋ฃŒ์™€ ํ•จ๊ป˜ ์–ด๋–ค ํ•จ์ˆ˜๋“ค์ด ๊ฐ€์žฅ ๋งŽ์€ ์‹œ๊ฐ„์„ ์†Œ๋น„ํ•˜๋Š”์ง€ ํ‘œ์‹œํ•ด ์ค๋‹ˆ๋‹ค.

"name" ์ปค์Šคํ…€ ์„น์…˜์ด ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ฐ”์ด๋„ˆ๋ฆฌ์— ํฌํ•จ๋˜๋„๋ก debug ์‹ฌ๋ณผ์„ ํฌํ•จํ•ด์„œ ๋นŒ๋“œํ•˜๋ฉด ํ”„๋กœํŒŒ์ผ๋Ÿฌ๊ฐ€ wasm-function[123]์™€ ๊ฐ™์ด ๋ณต์žกํ•˜๊ณ  ๋ถˆํˆฌ๋ช…ํ•œ ์ด๋ฆ„ ๋Œ€์‹  Rust ํ•จ์ˆ˜ ์ด๋ฆ„์„ ๋Œ€์‹  ํ‘œ์‹œํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ํ”„๋กœํŒŒ์ผ๋Ÿฌ๋“ค์€ ์ธ๋ผ์ธ ํ•จ์ˆ˜๋ฅผ ํ‘œ์‹œํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์„ ๊ธฐ์–ตํ•ด ์ฃผ์„ธ์š”. ๋Ÿฌ์ŠคํŠธ์™€ LLVM๋Š” ์ธ๋ผ์ธ ์ž‘์—…์„ ๋งค์šฐ ๋ฌด๊ฒ๊ฒŒ ์ˆ˜ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฌํ•œ ์ž‘์—…์„ ํ•ด์ฃผ๋”๋ผ๋„ ๊ฒฐ๊ด๊ฐ’์ด ์กฐ๊ธˆ์€ ์ฝ๊ธฐ ์–ด๋ ค์šธ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋Ÿฌ์ŠคํŠธ ์‹ฌ๋ณผ์„ ํ‘œ์‹œํ•˜๋Š” ํ”„๋กœํŒŒ์ผ๋Ÿฌ ์Šคํฌ๋ฆฐ์ƒท

์ถ”๊ฐ€ ์ž๋ฃŒ

console.time์™€ console.timeEnd ํ•จ์ˆ˜

console.time ๊ณผ console.timeEnd ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ธŒ๋ผ์šฐ์ € ๊ฐœ๋ฐœ์ž ๋„๊ตฌ ์ฝ˜์†”์— ๋ช…๋ช…๋œ ์ž‘์—…์˜ ํƒ€์ด๋ฐ์„ ๊ธฐ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž‘์—…์ด ์‹œ์ž‘๋  ๋•Œ console.time("์–ด๋–ค ๊ฑฐ ํ•˜๋Š” ์ž‘์—…")๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ์ž‘์—…์ด ์™„๋ฃŒ๋  ๋•Œ๋Š” console.timeEnd("์–ด๋–ค ๊ฑฐ ํ•˜๋Š” ์ž‘์—…")๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ฐธ๊ณ ๋กœ ์ž‘์—…์˜ ์ด๋ฆ„์„ ์ง€์–ด์ฃผ๋Š” ๋ฌธ์ž์—ด ์ธ์ž๋Š” ํ•„์ˆ˜๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.

web-sys ํฌ๋ ˆ์ดํŠธ๋ฅผ ํ†ตํ•ด ์ด๋Ÿฌํ•œ ํ•จ์ˆ˜๋“ค์„ ์ง์ ‘์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

๋ธŒ๋ผ์šฐ์ €์˜ ์ฝ˜์†”์— console.time ๋กœ๊ทธ๋ฅผ ํ‘œ์‹œํ•˜๋Š” ์Šคํฌ๋ฆฐ์ƒท์„ ํ™•์ธํ•ด๋ณด์„ธ์š”:

console.time ๋กœ๊ทธ ์Šคํฌ๋ฆฐ์ƒท

์ถ”๊ฐ€๋กœ, ๋‹ค์Œ ์ด๋ฏธ์ง€์— ๋ณด์ด๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ด ๋ธŒ๋ผ์šฐ์ € ํ”„๋กœํŒŒ์ผ๋Ÿฌ์˜ "timeline"๊ณผ "waterfall" ๋ทฐ์— console.time๊ณผ console.timeEnd์˜ ๋กœ๊ทธ๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

console.time ๋กœ๊ทธ ์Šคํฌ๋ฆฐ์ƒท

#[bench] ์†์„ฑ์œผ๋กœ ๋„ค์ดํ‹ฐ๋ธŒ ํ™˜๊ฒฝ์„ ์‚ฌ์šฉํ•  ๋•Œ

์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ #[test] ์†์„ฑ์„ ๋Œ€์‹  ์ด์šฉํ•˜์—ฌ ์šด์˜์ฒด์ œ์˜ ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ๋กœ ๋””๋ฒ„๊น…์„ ํ–ˆ๋˜ ๊ฒƒ๊ณผ ๊ฐ™์ด, #[bench] ์†์„ฑ๊ณผ ํ•จ๊ป˜ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•ด์„œ ์šด์˜ ์ฒด์ œ์˜ ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ ํ”„๋กœํŒŒ์ผ๋ง ํˆด์„ ์‚ฌ์šฉํ•ด ๋ณผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž‘์—… ์ค‘์ธ ํฌ๋ ˆ์ดํŠธ์˜ ํ•˜์œ„ ๊ฒฝ๋กœ์ธ benches์— ๋ฒค์น˜๋งˆํ‚น ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋จผ์ € crate-type์ด "rlib"์ด ํฌํ•จํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•ด ์ฃผ์„ธ์š”. ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด ๋ฒค์น˜๋งˆํฌ ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ๋ฉ”์ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (main lib) ์— ๋งํฌ๋˜์ง€ ๋ชปํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ๊ฑฐ์˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ฝ”๋“œ์— ์‹œ๊ฐ„์„ ์†Œ๋น„ํ•˜๊ณ  ์žˆ์„ ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ, ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ๋กœ ๋ฒค์น˜๋งˆํ‚น์„ ํ•˜๊ธฐ ์ „์— ๋ธŒ๋ผ์šฐ์ € ํ”„๋กœํŒŒ์ผ๋ง ํˆด์„ ๋จผ์ € ํ™•์ธํ•ด ๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์Šต๋‹ˆ๋‹ค!

์ถ”๊ฐ€ ์ž๋ฃŒ

.wasm ํŒŒ์ผ ์‚ฌ์ด์ฆˆ ์ค„์ด๊ธฐ

์ด ์„น์…˜์—์„œ๋Š” ์–ด๋–ป๊ฒŒ ๋Ÿฌ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๊ณ  .wasm ๋นŒ๋“œ๋ฅผ ์ตœ์ ํ™”ํ•ด์•ผ ๋” ์ž‘์€ ์‚ฌ์ด์ฆˆ์˜ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์™œ ์ถœ๋ ฅ๋˜๋Š” ํŒŒ์ผ์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ ์ค‘์š”ํ•œ๊ฐ€์š”?

.wasm ํŒŒ์ผ์„ ๋„คํŠธ์›Œํฌ๋กœ ์ „์†กํ•  ๋•Œ ํŒŒ์ผ์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ ์ž‘์„์ˆ˜๋ก ํด๋ผ์ด์–ธํŠธ์—์„œ ๋” ๋น ๋ฅด๊ฒŒ ๋‹ค์šด๋กœ๋“œ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. .์›น์–ด์…ˆ๋ธ”๋ฆฌ ํŒŒ์ผ์ด ๋นจ๋ฆฌ ๋‹ค์šด๋กœ๋“œ ๋ ์ˆ˜๋ก ํŽ˜์ด์ง€๊ฐ€ ๋” ๋นจ๋ฆฌ ๋กœ๋“œ๋˜๊ณ  ์œ ์ € ๊ฒฝํ—˜์ด ๋” ๋‚˜์•„์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ฝ”๋“œ ์‚ฌ์ด์ฆˆ๊ฐ€ ์–ด์ฉŒ๋ฉด ์ œ์ผ ์ค‘์š”ํ•˜๊ฒŒ ํ™•์ธํ•ด์•ผ ํ•  ๋ถ€๋ถ„์ด ์•„๋‹ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจํ˜ธํ•˜๊ณ  ์ธก์ •ํ•˜๊ธฐ ์–ด๋ ต๊ฒ ์ง€๋งŒ "ํŽ˜์ด์ง€๊ฐ€ ๋กœ๋“œ๋˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋  ๋•Œ๊นŒ์ง€ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„"์ด ์‹ค์ œ๋กœ๋Š” ๋” ์ค‘์š”ํ•˜๊ฒŒ ๊ณ ๋ คํ•ด ๋ด์•ผ ํ•  ๋ถ€๋ถ„์ผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. (์ฝ”๋“œ๊ฐ€ ๋กœ๋“œ๋ผ์•ผ ์‚ฌ์ดํŠธ๊ฐ€ ์ž‘๋™ํ•˜๊ธฐ ์‹œ์ž‘ํ•œ๋‹ค๋Š” ๋‚ด์šฉ์„ ์ƒ๊ฐํ•ด ๋ณธ๋‹ค๋ฉด) ์ฝ”๋“œ ์‚ฌ์ด์ฆˆ๊ฐ€ ์ด ์‹œ๊ฐ„์— ํฐ ์˜ํ–ฅ์„ ๋ฏธ์น˜๊ธด ํ•˜์ง€๋งŒ ์ด๊ฒŒ ์œ ์ผํ•˜๊ฒŒ ํ™•์ธํ•ด์•ผ ํ•  ๋ถ€๋ถ„์€ ์•„๋‹Œ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์›น์–ด์…ˆ๋ธ”๋ฆฌ๋Š” ๋ณดํ†ต gzip ํŒŒ์ผ ํฌ๋งท ํ˜•์‹์œผ๋กœ ์••์ถ•๋˜์–ด ์ „์†ก๋˜๋Š”๋ฐ, ๊ทธ๋Ÿฌ๋ฏ€๋กœ ์œ ์„ ์„ ํ†ตํ•ด ํŒŒ์ผ์„ ๋” ๋น ๋ฅด๊ฒŒ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋„๋ก gzip ํฌ๋งท์œผ๋กœ ์••์ถ•๋œ ํŒŒ์ผ์˜ ์‚ฌ์ด์ฆˆ๋ฅผ ๋น„๊ตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ฐธ๊ณ ๋กœ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ฐ”์ด๋„ˆ๋ฆฌ ํฌ๋งท์€ gzip ํฌ๋งท์— ์ ํ•ฉํ•˜๋ฏ€๋กœ 50% ์ด์ƒ์œผ๋กœ ์‚ฌ์ด์ฆˆ๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฒŒ๋‹ค๊ฐ€, ์›น์–ด์…ˆ๋ธ”๋ฆฌ์˜ ๋ฐ”์ด๋„ˆ๋ฆฌ ํฌ๋งท์€ ๋งค์šฐ ๋น ๋ฅด๊ฒŒ ์ฝ๊ณ  ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์ตœ์ ํ™”๊ฐ€ ์ž˜ ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค. ์š”์ฆ˜ ์‚ฌ์šฉ๋˜๋Š” ๋ธŒ๋ผ์šฐ์ €๋“ค์€ ๋ณดํ†ต "baseline compilers" ๋ผ๋Š” ๊ธฐ๋Šฅ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”๋ฐ, ์ด ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ๋„คํŠธ์›Œํฌ๋กœ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํŒŒ์ผ์„ ๋ณด๋‚ด๋Š” ๋™์‹œ์— ๋™์ผํ•˜๊ฒŒ ๋น ๋ฅธ ์†๋„๋กœ ์ „์†ก๋ฐ›์€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์ฝ”๋“œ๋ฅผ ๋„ค์ดํ‹ฐ๋ธŒ ๊ธฐ๊ณ„์–ด๋กœ ์ปดํŒŒ์ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— instantiateStreaming์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์›นํŽ˜์ด์ง€๊ฐ€ ํ•œ ๋ฒˆ ๋กœ๋“œ ๋œ ์ดํ›„๋ถ€ํ„ฐ๋Š” ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ชจ๋“ˆ์„ ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด์— ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ๋Š” ์ฃผ๋กœ ํŒŒ์‹ฑํ•˜๋Š” ์ฒ˜๋ฆฌ ์™ธ์—๋„ ์ฝ”๋“œ๋ฅผ ๋น ๋ฅด๊ฒŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก JIT ์ปดํŒŒ์ผ ๊ณผ์ • ๋“ฑ์„ ๊ฑฐ์ณ์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ๋กœ ์‹œ๊ฐ„์ด ๋” ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ, ์›น์–ด์…ˆ๋ธ”๋ฆฌ๊ฐ€ ์‹คํ–‰ ์†๋„ ์ธก๋ฉด์—์„œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ ๋น„๊ตํ–ˆ์„ ๋•Œ ํ›จ์”ฌ ์ตœ์ ํ™”๊ฐ€ ์ž˜ ๋ผ์žˆ๋‹ค๋Š” ๋ถ€๋ถ„์„ ๊ธฐ์–ตํ•ด์ฃผ์„ธ์š”. ๋” ํ™•์‹คํ•˜๊ฒŒ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋Ÿฐํƒ€์ž„ ์†๋„๋ฅผ ๊ฐ๊ฐ ์ธก์ •ํ•ด ๋ณด๊ณ  ์ฝ”๋“œ ์‚ฌ์ด์ฆˆ๊ฐ€ ์–ผ๋งˆ๋‚˜ ์ค‘์š”ํ•œ์ง€ ํ™•์ธํ•ด๋ณผ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ .wasm ํŒŒ์ผ์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ ์˜ˆ์ƒ๋ณด๋‹ค ํฌ๋”๋ผ๋„ ๋ฐ”๋กœ ๋‚™๋‹ดํ•˜์ง€๋Š” ๋ง์•„์ฃผ์„ธ์š”! ์ฝ”๋“œ ์‚ฌ์ด์ฆˆ๋Š” ํฐ ๊ทธ๋ฆผ์˜ ์ผ๋ถ€์ผ ๋ฟ์ž…๋‹ˆ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ๋น„๊ตํ•  ๋•Œ ์ฝ”๋“œ ์‚ฌ์ด์ฆˆ๋งŒ ๋น„๊ตํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ๋งŽ์€ ๋ถ€๋ถ„์„ ๋†“์น˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ฝ”๋“œ ์‚ฌ์ด์ฆˆ๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๋„๋ก ๋นŒ๋“œ ์ตœ์ ํ™”ํ•˜๊ธฐ

rustc์—๋Š” ๋” ์ž‘์€ .wasm ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์œ ์šฉํ•œ ์˜ต์…˜์„ ๋ช‡๊ฐ€์ง€ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ์ƒํ™ฉ์—์„œ๋Š”, ์ปดํŒŒ์ผ ์‹œ๊ฐ„์ด ๋” ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๋ถ€๋ถ„์„ ํฌ์ƒํ•ด์„œ .wasm ์‚ฌ์ด์ฆˆ๋ฅผ ๋” ์ž‘๊ฒŒ ์ค„์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ๋˜ ๋‹ค๋ฅธ ๊ฒฝ์šฐ์—๋Š”, ๋” ๋น ๋ฅธ ๋Ÿฐํƒ€์ž„์„ ์œ„ํ•ด .wasm ํŒŒ์ผ ์‚ฌ์ด์ฆˆ๋ฅผ ํฌ๊ธฐํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ์˜ต์…˜์˜ ์žฅ๋‹จ์ ์„ ์ž˜ ์ดํ•ดํ•˜๊ณ , ํ”„๋กœํŒŒ์ผ๋ง๊ณผ ์ธก์ • ์ž‘์—…์„ ํ•ด๋ณด๋ฉด์„œ ์ฝ”๋“œ ์‚ฌ์ด์ฆˆ์™€ ๋Ÿฐํƒ€์ž„ ์†๋„ ์ค‘ ์–ด๋–ค ๊ฒƒ์ด ์šฐ์„ ์‹œ๋˜์–ด์•ผ ํ•˜๋Š”์ง€ ์ž˜ ์•Œ๊ณ  ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

Cargo.toml ํŒŒ์ผ์˜ [profile.release] ์„น์…˜์— lto = true๋ฅผ ์ถ”๊ฐ€ํ•ด ์ฃผ์„ธ์š”:

[profile.release]
lto = true

์ด๋ ‡๊ฒŒ LLVML์ด ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ฝ”๋“œ๋ฅผ ๋” ๊ณต๊ฒฉ์ ์œผ๋กœ ์ œ๊ฑฐํ•˜๊ณ  ์ธ๋ผ์ธ ์ž‘์—…์„ ๋” ์ ๊ทน์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. LTO๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด .wasm ํŒŒ์ผ์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ ์ž‘์•„์งˆ ๋ฟ ์•„๋‹ˆ๋ผ, ๋Ÿฐํƒ€์ž„์„ ๋” ๋น ๋ฅด๊ฒŒ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค! ํ•˜์ง€๋งŒ ์ปดํŒŒ์ผ ์ž‘์—…์ด ๋” ์˜ค๋ž˜ ๊ฑธ๋ฆฐ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์œผ๋‹ˆ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.

๋Ÿฐํƒ€์ž„ ์†๋„ ๋Œ€์‹  ์ฝ”๋“œ ์‚ฌ์ด์ฆˆ๋ฅผ ์ตœ์ ํ™”ํ•˜๋„๋ก LLVM ์„ค์ •ํ•˜๊ธฐ

LLVM์˜ ์ตœ์ ํ™” ์ž‘์—…์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์‚ฌ์ด์ฆˆ ๋Œ€์‹  ์†๋„ ๊ฐœ์„ ์— ์ค‘์ ์„ ๋‘๊ณ  ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค. [profile.release]ํŒŒ์ผ์˜ [profile.release] ์„น์…˜์„ ์ˆ˜์ •ํ•˜์—ฌ ์ด ์„ค์ •์„ ๋ณ€๊ฒฝํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค:

[profile.release]
opt-level = 's'

์ถ”๊ฐ€๋กœ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์†๋„ ์ €ํ•˜๋ฅผ ๊ฐ์ˆ˜ํ•ด์„œ๋ผ๋„ ๋” ๊ณต๊ฒฉ์ ์œผ๋กœ ์ตœ์ ํ™”๋ฅผ ํ•ด๋ณผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค:

[profile.release]
opt-level = 'z'

์ •๋ง ๋†€๋ž๊ฒŒ๋„, ์ถœ๋ ฅ๋˜๋Š” ํŒŒ์ผ์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ opt-level = "z"๋Œ€์‹  opt-level = "s"๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ ๋” ์ž‘์•„์ง€๋Š” ๊ฒฝ์šฐ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ญ์ƒ ์ž˜ ํ™•์ธํ•ด ๋ณด๋Š” ๊ฑธ ์žŠ์ง€ ๋ง์•„ ์ฃผ์„ธ์š”!

wasm-opt ํˆด ์‚ฌ์šฉํ•˜๊ธฐ

Binaryen ํˆดํ‚ท์€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ์— ํŠนํ™”๋œ ์ปดํŒŒ์ผ๋Ÿฌ ํˆด๋ง์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ LLVM์˜ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ฐฑ์—”๋“œ ์ž‘์—… ์™ธ์—๋„ ํ›จ์”ฌ ๋” ๋‹ค์–‘ํ•œ ์ž‘์—…์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ , wasm-opt ํˆด์„ ์‚ฌ์šฉํ•ด์„œ LLVM์ด ๋นŒ๋“œํ•œ .wasm ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์ตœ์ ํ™” ํ•˜๋ฉด ๋ณดํ†ต์€ ์ฝ”๋“œ ์‚ฌ์ด์ฆˆ๋ฅผ 15-20% ์ •๋„ ๋” ์ค„์ผ ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋Ÿฐํƒ€์ž„ ์†๋„๋„ ๊ฐ™์ด ๊ฐœ์„ ๋  ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”!

# ์‚ฌ์ด์ฆˆ ์ตœ์ ํ™”.
wasm-opt -Os -o output.wasm input.wasm

# ๊ณต๊ฒฉ์ ์ธ ์‚ฌ์ด์ฆˆ ์ตœ์ ํ™”.
wasm-opt -Oz -o output.wasm input.wasm

# ์†๋„ ์ตœ์ ํ™”.
wasm-opt -O -o output.wasm input.wasm

# ๊ณต๊ฒฉ์ ์ธ ์†๋„ ์ตœ์ ํ™”.
wasm-opt -O3 -o output.wasm input.wasm

๋””๋ฒ„๊ทธ ์ •๋ณด์— ๊ด€ํ•ด ์•Œ์•„๋‘๋ฉด ์ข‹์€ ์ 

์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ฐ”์ด๋„ˆ๋ฆฌ ์‚ฌ์ด์ฆˆ๋ฅผ ์ค„์ด๋Š” ๋ฐ ๋ฐ”์ด๋„ˆ๋ฆฌ ํŒŒ์ผ์— ํฌํ•จ๋œ ๋””๋ฒ„๊ทธ ์ •๋ณด์™€ names ์„น์…˜์ด ์ •๋ง ํฐ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ wasm-pack์ด ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋””๋ฒ„๊ทธ ์ •๋ณด๋ฅผ ์‚ญ์ œํ•˜๊ณ , ์ถ”๊ฐ€๋กœ wasm-opt๋„ ๋ช…๋ น์–ด์— -g๊ฐ€ ํฌํ•จ๋˜์ง€ ์•Š๋Š” ์ด์ƒ names ์„น์…˜์„ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์šฐ๋Š” ๋ถ€๋ถ„์„ ์ž˜ ๊ธฐ์–ตํ•ด ์ฃผ์„ธ์š”.

์ด ์ฑ…์„ ์ž˜ ๋”ฐ๋ผ์™”๋‹ค๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” ๋””๋ฒ„๊ทธ ์ •๋ณด๋‚˜ names ์„น์…˜ ์—†์ด ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํŒŒ์ผ์„ ๋นŒ๋“œํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ด๋Ÿฌํ•œ ๋””๋ฒ„๊น… ์ •๋ณด๊ฐ€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ฐ”์ด๋„ˆ๋ฆฌ์— ํฌํ•จ๋ผ์•ผ ํ•˜๋Š” ์ƒํ™ฉ์—์„œ๋Š” ์ด ๋‚ด์šฉ์„ ์ž˜ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”!

์‚ฌ์ด์ฆˆ ํ”„๋กœํŒŒ์ผ๋งํ•˜๊ธฐ

๋นŒ๋“œ ์ตœ์ ํ™” ์„ค์ •์„ ๋ฐ”๊ฟจ๋Š”๋ฐ๋„ .wasm ์ฝ”๋“œ ์‚ฌ์ด์ฆˆ๊ฐ€ ์ถฉ๋ถ„ํžˆ ์ค„์–ด๋“ค์ง€ ์•Š๋Š”๋‹ค๋ฉด, ์–ด๋–ค ๋ถ€๋ถ„์ด ๋‚˜๋จธ์ง€ ๊ณต๊ฐ„์„ ์‚ฌ์šฉํ•˜๋Š”์ง€ ํ”„๋กœํŒŒ์ผ๋ง ์ž‘์—…์„ ํ•ด๋ณด๋ฉด์„œ ์•Œ์•„๋ณด๋„๋ก ํ•ฉ์‹œ๋‹ค.

โšก ํƒ€์ž„ ํ”„๋กœํŒŒ์ผ๋ง ๊ฐ€์ด๋“œ๋ฅผ ๋”ฐ๋ผ์™”๋˜ ๊ฒƒ์ฒ˜๋Ÿผ, ์‚ฌ์ด์ฆˆ ํ”„๋กœํŒŒ์ผ๋ง ๊ฐ€์ด๋“œ๋„ ํ•œ๋ฒˆ ์ฝ์–ด๋ณด๊ณ  ์‹œ๋„ํ•ด ๋ด…์‹œ๋‹ค. ์ฝ์–ด๋ณด๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ ์‹œ๊ฐ„์„ ์ •๋ง ๋งŽ์ด ์•„๋‚„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

twiggy ์ฝ”๋“œ ์‚ฌ์ด์ฆˆ ํ”„๋กœํŒŒ์ผ๋Ÿฌ

twiggy๋Š” ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›๋Š” ์ฝ”๋“œ ์‚ฌ์ด์ฆˆ ํ”„๋กœํŒŒ์ผ๋Ÿฌ์ž…๋‹ˆ๋‹ค. ๋ฐ”์ด๋„ˆ๋ฆฌ์˜ ํ˜ธ์ถœ ๊ทธ๋ž˜ํ”„ (call graph) ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ๋‹ค์Œ๊ณฝ ๊ฐ™์€ ๋‚ด์šฉ์„ ์•Œ๋ ค์ค๋‹ˆ๋‹ค:

  • ์–ด๋–ค ํ•จ์ˆ˜๋“ค์ด ์• ์ดˆ์— ์™œ ๋ฐ”์ด๋„ˆ๋ฆฌ์— ํฌํ•จ๋˜๋Š” ๊ฑด๊ฐ€์š”?

  • ์ด ํ•จ์ˆ˜์— ์‚ฌ์šฉ๋˜๋Š” ๊ณต๊ฐ„์˜ ํฌ๊ธฐ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋˜๋‚˜์š”? ์˜ˆ: ์ด ํ•จ์ˆ˜์™€ ๋‚ด๋ถ€์—์„œ ํ˜ธ์ถœํ•ด์„œ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋Š” ํ•จ์ˆ˜๋“ค๊นŒ์ง€ ์ œ๊ฑฐํ•œ๋‹ค๋ฉด ์–ผ๋งˆ๋‚˜ ๊ณต๊ฐ„์„ ์•„๋‚„ ์ˆ˜ ์žˆ๋‚˜์š”?

$ twiggy top -n 20 pkg/wasm_game_of_life_bg.wasm
 Shallow Bytes โ”‚ Shallow % โ”‚ Item
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
          9158 โ”Š    19.65% โ”Š "function names" subsection
          3251 โ”Š     6.98% โ”Š dlmalloc::dlmalloc::Dlmalloc::malloc::h632d10c184fef6e8
          2510 โ”Š     5.39% โ”Š <str as core::fmt::Debug>::fmt::he0d87479d1c208ea
          1737 โ”Š     3.73% โ”Š data[0]
          1574 โ”Š     3.38% โ”Š data[3]
          1524 โ”Š     3.27% โ”Š core::fmt::Formatter::pad::h6825605b326ea2c5
          1413 โ”Š     3.03% โ”Š std::panicking::rust_panic_with_hook::h1d3660f2e339513d
          1200 โ”Š     2.57% โ”Š core::fmt::Formatter::pad_integral::h06996c5859a57ced
          1131 โ”Š     2.43% โ”Š core::str::slice_error_fail::h6da90c14857ae01b
          1051 โ”Š     2.26% โ”Š core::fmt::write::h03ff8c7a2f3a9605
           931 โ”Š     2.00% โ”Š data[4]
           864 โ”Š     1.85% โ”Š dlmalloc::dlmalloc::Dlmalloc::free::h27b781e3b06bdb05
           841 โ”Š     1.80% โ”Š <char as core::fmt::Debug>::fmt::h07742d9f4a8c56f2
           813 โ”Š     1.74% โ”Š __rust_realloc
           708 โ”Š     1.52% โ”Š core::slice::memchr::memchr::h6243a1b2885fdb85
           678 โ”Š     1.45% โ”Š <core::fmt::builders::PadAdapter<'a> as core::fmt::Write>::write_str::h96b72fb7457d3062
           631 โ”Š     1.35% โ”Š universe_tick
           631 โ”Š     1.35% โ”Š dlmalloc::dlmalloc::Dlmalloc::dispose_chunk::hae6c5c8634e575b8
           514 โ”Š     1.10% โ”Š std::panicking::default_hook::{{closure}}::hfae0c204085471d5
           503 โ”Š     1.08% โ”Š <&'a T as core::fmt::Debug>::fmt::hba207e4f7abaece6

LLVM-IR ์ง์ ‘ ์‚ดํŽด๋ณด๊ธฐ

LLMV-IR์€ LLVM์ด ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์ถœ๋ ฅํ•˜๊ธฐ ์ „์— ๊ฑฐ์น˜๊ฒŒ ๋˜๋Š” ์ตœ์ข… ์ค‘๊ฐ„ ํ‘œํ˜„ (final intermediate representation) ์ž…๋‹ˆ๋‹ค. ์ด ์ตœ์ข… ์ค‘๊ฐ„ ํ‘œํ˜„์€ ์ตœ์ข…์ ์œผ๋กœ ์ถœ๋ ฅํ•˜๋Š” ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ฐ”์ด๋„ˆ๋ฆฌ์™€ ๋งค์šฐ ์œ ์‚ฌํ•˜๊ฒŒ ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค. LLVM-IR์˜ ํฌ๊ธฐ๊ฐ€ ํด์ˆ˜๋ก ์ถœ๋ ฅ๋˜๋Š” .wasm ํŒŒ์ผ์˜ ํฌ๊ธฐ๋„ ์ปค์ง€๊ฒŒ ๋˜๊ณ , ํ•จ์ˆ˜๋“ค์ด LLVM-IR ํฌ๊ธฐ์˜ 25%๊นŒ์ง€ ์ฐจ์ง€ํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด .wasm ํŒŒ์ผ์—์„œ๋„ ํ•จ์ˆ˜๋“ค์ด ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ 25%๋ฅผ ์ฐจ์ง€ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ˆ˜์น˜๋“ค์ด ๋ณดํ†ต์€ ์ผ์น˜ํ•˜์ง€๋งŒ, LLVM-IR์€ .wasm ํŒŒ์ผ์ด (DWARF์™€ ๊ฐ™์€ ๋””๋ฒ„๊น… ์ •๋ณด ์ฒ˜๋Ÿผ) ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๋Š” ๋‹ค๋ฅธ ์ค‘์š”ํ•œ ์ •๋ณด๋“ค๋„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์  ๋˜ํ•œ ์ž˜ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”. ์ด๋Ÿฌํ•œ ํ•˜์œ„ ๋ฃจํ‹ด๋“ค์€ ํ•ด๋‹น ํ•จ์ˆ˜์˜ ์œ„์น˜์— ์ธ๋ผ์ธ๋ฉ๋‹ˆ๋‹ค.

cargo ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ด์„œ LLVM-IR ํŒŒ์ผ์„ ์ง์ ‘ ์ƒ์„ฑํ•ด ๋ณด์„ธ์š”:

cargo rustc --release -- --emit llvm-ir

๊ทธ๋‹ค์Œ find ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ด์„œ .ll ํŒŒ์ผ์„ ๊ฒ€์ƒ‰ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด LLVM-IR ํŒŒ์ผ์€ cargo์˜ target ๊ฒฝ๋กœ์— ์œ„์น˜ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค:

find target/release -type f -name '*.ll'

์ฐธ์กฐ

ํˆด๊ณผ ํ…Œํฌ๋‹‰์„ ํ™œ์šฉํ•˜์—ฌ ๋” ๊นŠ๊ฒŒ ํŒŒ๊ณ ๋“ค์–ด์„œ ์ตœ์ ํ™”ํ•˜๊ธฐ

.wasm ๋ฐ”์ด๋„ˆ๋ฆฌ ์‚ฌ์ด์ฆˆ๋ฅผ ์ค„์ด๋Š” ์„ค์ •์€ ๋ณดํ†ต ์ž๋™ํ™”๊ฐ€ ๋ผ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ถ”๊ฐ€๋กœ ๋ถˆํ•„์š”ํ•œ ์ฝ”๋“œ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ์ตœ์ ํ™”๋ฅผ ํ•ด์ค˜์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ๋” ๊นŠ๊ฒŒ ๋“ค์–ด๊ฐ€์„œ ์ˆ˜์ •ํ•ด ์ค˜์•ผ ํ•  ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์„น์…˜์—์„œ๋Š” ์ฝ”๋“œ ์‚ฌ์ด์ฆˆ๋ฅผ ์ค„์ผ ๋•Œ ์‚ฌ์šฉํ•ด ๋ณผ ์ˆ˜ ์žˆ๋Š” ํˆฌ๋ฐ•ํ•œ ๋ฐฉ๋ฒ•๋“ค์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ฌธ์ž์—ด ํฌ๋งท ํ”ผํ•˜๊ธฐ

format!์ด๋‚˜ to_string ๊ณผ ๊ฐ™์€ ํ•จ์ˆ˜/๋งคํฌ๋กœ๋“ค์„ ์‚ฌ์šฉํ•˜๋ฉด ์ถœ๋ ฅ๋˜๋Š” ๋ฐ”์ด๋„ˆ๋ฆฌ์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์ปค์งˆ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ€๋Šฅํ•˜๋ฉด ๋ฌธ์ž์—ด ํฌ๋งท์€ ๋””๋ฒ„๊ทธ ๋ชจ๋“œ์—์„œ๋งŒ ์‚ฌ์šฉํ•˜๊ณ , ๋ฐฐํฌ ๋ฒ„์ „์„ ๋นŒ๋“œํ•  ๋•Œ์—๋Š” ์ •์  ๋ฌธ์ž์—ด (static string) ์„ ์‚ฌ์šฉํ•ด ๋ณด์„ธ์š”.

์ฝ”๋“œ ํŒจ๋‹‰ ํ”ผํ•˜๊ธฐ

๋ง์ฒ˜๋Ÿผ ์‰ฝ์ง€๋Š” ์•Š๊ฒ ์ง€๋งŒ, twiggy ์™€ ๊ฐ™์€ ํˆด์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ LLVM-IR ํŒŒ์ผ์„ ์‚ดํŽด๋ณด๋ฉด์„œ ์–ด๋–ค ํ•จ์ˆ˜๋“ค์ด ํŒจ๋‹‰ํ•˜๋Š”์ง€ ์‚ดํŽด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŒจ๋‹‰์ด ํ•ญ์ƒ panic!() ๋งคํฌ๋กœ์˜ ํ˜•์‹์œผ๋กœ ๋‚˜ํƒ€๋‚˜์ง€๋Š” ์•Š๊ณ  ๋ณดํ†ต์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋‹ค์–‘ํ•œ ์ด์œ ๋กœ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  • ์Šฌ๋ผ์ด์Šค ์‚ฌ์ด์ฆˆ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚œ ์ธ๋ฑ์Šค์˜ ์š”์†Œ์— ์ ‘๊ทผํ•˜๊ณ ์ž ์‹œ๋„ํ•  ๋•Œ (out of bounds) : my_slice[i]

  • ๋‚˜๋จธ์ง€ ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋‚˜๋ˆ„๋Š” ์ˆ˜๊ฐ€ 0์ธ ๊ฒฝ์šฐ: ๋‚˜๋ˆ ์ง€๋Š” ์ˆ˜ / ๋‚˜๋ˆ„๋Š” ์ˆ˜

  • Option์ด๋‚˜ Result์˜ ๊ฐ’์„ unwrap()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ’์— ์ ‘๊ทผํ•  ๋•Œ: opt.unwrap() ๋˜๋Š” res.unwrap()

์ฒ˜์Œ ๋‘ ๋ฐฉ๋ฒ• ๋Œ€์‹  ์‚ฌ์šฉํ•ด ๋ณผ ์ˆ˜ ์žˆ๋Š” ๋” ์•ˆ์ „ํ•œ ๋ฐฉ๋ฒ•๋“ค๋„ ์žˆ์Šต๋‹ˆ๋‹ค. my_slice[i] ์ฒ˜๋Ÿผ ์ธ๋ฑ์Šค๋ฅผ ํ†ตํ•ด ์ง์ ‘ ์ ‘๊ทผํ•˜์ง€ ์•Š๊ณ  my_slice.get(i) ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Option ํƒ€์ž…์˜ ๊ฐ’์— ์ ‘๊ทผํ•ด ๋ณผ ์ˆ˜๋„ ์žˆ๊ณ , check_div ํ•จ์ˆ˜๋ฅผ ๋ถˆ๋Ÿฌ์„œ ๊ฐ’์„ ๋‚˜๋ˆŒ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด ๋ณผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ 3๋ฒˆ์งธ ๊ฒฝ์šฐ๋งŒ ์ง‘์ค‘์ ์œผ๋กœ ์‹ ๊ฒฝ ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ๋์Šต๋‹ˆ๋‹ค.

์ถ”๊ฐ€๋กœ, ์ฝ”๋“œ๋ฅผ ํŒจ๋‹‰ ์‹œํ‚ค์ง€ ์•Š์œผ๋ฉด์„œ Option์ด๋‚˜ Result ํƒ€์ž…์˜ ๊ฐ’์„ "์•ˆ์ „ํ•œ ๋ฐฉ๋ฒ•๊ณผ ๋ถˆ์•ˆ์ „ํ•œ ๋ฐฉ๋ฒ•", ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ ์ฒ˜๋ฆฌํ•ด๋ณผ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์•ˆ์ „ํ•œ ๋ฐฉ๋ฒ•๋ถ€ํ„ฐ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด๋„๋ก ํ•ฉ์‹œ๋‹ค. None์ด๋‚˜ Error ๊ฐ’์„ ๋ฐ˜ํ™˜๋ฐ›์•˜์„ ๋•Œ ์ฝ”๋“œ๋ฅผ ํŒจ๋‹‰์‹œํ‚ค๋Š” ๋Œ€์‹  abort ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

#![allow(unused)]
fn main() {
#[inline]
pub fn unwrap_abort<T>(o: Option<T>) -> T {
    use std::process;
    match o {
        Some(t) => t,
        None => process::abort(),
    }
}
}

์ตœ์ข…์ ์œผ๋กœ๋Š” ํŒจ๋‹‰ ์ฝ”๋“œ๊ฐ€ wasm32-unknown-unknown ํƒ€๊ฒŸ์˜ abort ๋ช…๋ น์–ด๋กœ ์˜ฎ๊ฒจ์ง€๊ธฐ ๋•Œ๋ฌธ์—, ์ด๋Ÿฐ ์‹์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด์„œ ๋ถˆํ•„์š”ํ•œ ์ฝ”๋“œ๋ฅผ ์ง€์šธ ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ์‹œ๋„ํ•ด๋ณผ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ๋Š”, unreachable ํฌ๋ ˆ์ดํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํฌ๋ ˆ์ดํŠธ๋Š” Option๊ณผ Result ํƒ€์ž…์˜ ๊ฐ’๋“ค๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ถˆ์•ˆ์ „ํ•œ unchecked_unwrap ํ™•์žฅ ํฌ๋ ˆ์ดํŠธ๋“ค์„ ์ œ๊ณตํ•˜๋Š”๋ฐ, ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ Option์„ Some์œผ๋กœ, Result๋ฅผ Ok๋กœ ์ถ”์ธกํ•ด์„œ ์˜ฎ๊ธธ ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋Ÿฐ ์ถ”์ธก์ด ๋งž์•„๋–จ์–ด์ง€์ง€ ์•Š์œผ๋ฉด ์ •์˜ํ•˜์ง€ ์•Š์€ ๋™์ž‘ (undefined behavior) ์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ ์ฝ”๋“œ๊ฐ€ ์ž˜ ์ž‘๋™ํ•œ๋‹ค๊ณ  ํ™•์‹ ํ•˜์ง€๋งŒ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ž˜ ๋ชจ๋ฅด๊ณ  ์žˆ๋Š” ์ƒํ™ฉ์„ ์ž˜ ์ดํ•ดํ•˜๊ณ  ์žˆ์„ ๋•Œ๋งŒ ์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•ด ์ฃผ์„ธ์š”. ์ผ๋ฐ˜์ ์œผ๋กœ๋Š” ์ด ๋ฐฉ๋ฒ•์„ ๋ฐฐํฌ ๋ฒ„์ „์—์„œ๋งŒ ์ ์šฉํ•˜๊ณ  ๊ทธ ์™ธ์—๋Š” ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋„๋ก ๋””๋ฒ„๊ทธ ๋นŒ๋“œ๋ฅผ ๋”ฐ๋กœ ์„ค์ •ํ•˜๊ธธ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

ํ• ๋‹น์„ ํ”ผํ•˜๊ฑฐ๋‚˜ wee_alloc์„ ๋Œ€์‹  ์‚ฌ์šฉํ•ด ๋ณด์„ธ์š”.

๋Ÿฌ์ŠคํŠธ๋Š” ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ dlmalloc๋ผ๋Š” ํ• ๋‹น์ž๋ฅผ ์ด์‹ํ•ด์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ํ• ๋‹น์ž๋Š” 10 KB ์ •๋„์˜ ์‚ฌ์ด์ฆˆ๋ฅผ ์ฐจ์ง€ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ๋™์  ํ• ๋‹น์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๊ดœ์ฐฎ๋‹ค๋ฉด ์ด ์‚ฌ์ด์ฆˆ๋ฅผ ์ ˆ์•ฝํ•ด ๋ณผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์™„์ „ํžˆ ๋™์  ํ• ๋‹น ์—†์ด ์ž‘์—…ํ•˜๊ธฐ๊ฐ€ ์‚ฌ์‹ค์€ ์‰ฝ์ง€๋Š” ์•Š์€ ํŽธ์ธ๋ฐ, ์ฝ”๋“œ์˜ ํ•ซ ์ŠคํŒŸ (hot spot) ์—์„œ ํ• ๋‹น์„ ์—†์• ๋Š” ์ž‘์—…์€ ๋Œ€๋ถ€๋ถ„์€ ํ›จ์”ฌ ์‰ฌ์šด ํŽธ์ž…๋‹ˆ๋‹ค. (๋ณดํ†ต์€ ์ด ์ž‘์—…์„ ํ†ตํ•ด ํ•ซ ์ŠคํŒŸ์ธ ์ฝ”๋“œ๋“ค์„ ํ›จ์”ฌ ๋น ๋ฅด๊ฒŒ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.) ์ด๋Ÿฌํ•œ ์ƒํ™ฉ์—์„œ ์ „์—ญ ํ• ๋‹น์ž ๋Œ€์‹  wee_alloc๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด (์ „๋ถ€๋Š” ์•„๋‹ˆ์ง€๋งŒ) ์ด 10 KB ๋งŒํผ ์ฐจ์ง€๋˜๋Š” ๊ณต๊ฐ„์˜ ๋Œ€๋ถ€๋ถ„์„ ์ ˆ์•ฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. wee_alloc๋Š” ํ• ๋‹น์ž๋ฅผ ํ•„์š”๋กœ ํ•˜์ง€๋งŒ ๊ตณ์ด ์•„์ฃผ ๋น ๋ฅผ ํ•„์š”๊ฐ€ ์—†๊ณ , ์‹คํ–‰ ์†๋„๊ฐ€ ๋Š๋ ค์ง€๋Š” ๋Œ€์‹  ์ฝ”๋“œ ์‚ฌ์ด์ฆˆ๋ฅผ ์ค„์—ฌ๋„ ๊ดœ์ฐฎ์„ ๋•Œ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค๊ณ„๋์Šต๋‹ˆ๋‹ค.

์ œ๋„ค๋ฆญ ํƒ€์ž… ๋งค๊ฐœ๋ณ€์ˆ˜ ๋Œ€์‹  ํŠธ๋ ˆ์ดํŠธ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด ๋ณด์„ธ์š”

๋‹ค์Œ ์˜ˆ์‹œ์™€ ๊ฐ™์ด ํƒ€์ž… ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ œ๋„ค๋ฆญ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ด…์‹œ๋‹ค:

#![allow(unused)]
fn main() {
fn whatever<T: MyTrait>(t: T) { ... }
}

rustc์™€ LLVM๋Š” ์ œ๋„ค๋ฆญ ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ ์‚ฌ์šฉ๋œ ํƒ€์ž…์— ํ•ด๋‹นํ•˜๋Š” ๋ฐ”์ด๋„ˆ๋ฆฌ ์ฝ”๋“œ๋ฅผ ๊ฐ๊ฐ ๋”ฐ๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ์ ‘๊ทผ์€ ์–ด๋–ค T ํƒ€์ž…์„ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ฒ˜๋ฆฌํ•˜๋Š”์ง€์— ๋”ฐ๋ผ ์ปดํŒŒ์ผ๋Ÿฌ ์ตœ์ ํ™”์— ์œ ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋™์‹œ์— ์ฝ”๋“œ ์‚ฌ์ด์ฆˆ๊ฐ€ ๋น ๋ฅด๊ฒŒ ๋Š˜์–ด๋‚  ์ˆ˜๋„ ์žˆ๋‹ค๋Š” ๋‹จ์ ๋„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์ด ํƒ€์ž… ๋งค๊ฐœ๋ณ€์ˆ˜ ๋Œ€์‹  ํŠธ๋ ˆ์ดํŠธ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

#![allow(unused)]
fn main() {
fn whatever(t: Box<MyTrait>) { ... }
// or
fn whatever(t: &MyTrait) { ... }
// etc...
}

์ด ์ฝ”๋“œ๋ฅผ ์ปดํŒŒ์ผํ• ๋•Œ ๊ฐ€์ƒ ํ™˜๊ฒฝ์—์„œ ํ•จ์ˆ˜๋“ค์„ ๋™์  ๋””์ŠคํŒจ์น˜ (dynamic dispatch) ๋ผ๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์œผ๋กœ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ํ•œ ๊ฐ€์ง€ ๋ฒ„์ „์˜ ํ•จ์ˆ˜๋งŒ .wasm ํŒŒ์ผ๋กœ ์ฒ˜๋ฆฌ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋ฉด์„œ ์ปดํŒŒ์ผ๋Ÿฌ ์ตœ์ ํ™” ์ธก๋ฉด์—์„œ ์†์‹ค์ด ์žˆ์„ ์ˆ˜ ์žˆ๊ณ , ๊ฐ„์ ‘์ ์ด๊ณ  ๋™์ ์œผ๋กœ ๋””์ŠคํŒจ์น˜๋œ ํ•จ์ˆ˜๋ฅผ ๋ถ€๋ฅด๋Š”๋ฐ ์ถ”๊ฐ€์ ์ธ ๋น„์šฉ์ด ๋“ค์ˆ˜๋„ ์žˆ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

wasm-snip ํˆด์„ ์‚ฌ์šฉํ•ด ๋ณด์„ธ์š”

wasm-snip์€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํ•จ์ˆ˜์˜ ์ฝ”๋“œ๋ฅผ unreachable Assembly ๋ช…๋ น์–ด๋กœ ๊ต์ฒดํ•ด ์ค๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ž˜ ์ƒ๊ฐํ•ด๋ณด๋ฉด ๋ชป ํ•˜๋‚˜๋ฅผ ๋ฐ•๋Š”๋‹ค๊ณ  ์•„์ฃผ ๊ฑฐ๋Œ€ํ•œ ๋ง์น˜๋ฅผ ๊ฐ€์ ธ์™€์„œ ์—ด์‹ฌํžˆ ๋‘๋“œ๋ฆฌ๋Š” ๊ฒƒ ๊ฐ™์€ ๋Š๋‚Œ์ด ์กฐ๊ธˆ์”ฉ ๋“ญ๋‹ˆ๋‹ค.

๋Ÿฐํƒ€์ž„์—์„œ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํ•จ์ˆ˜๋“ค๊ณผ ์ด ํ•จ์ˆ˜๋“ค์ด ๊ฐ„์ ‘์ ์œผ๋กœ ํ˜ธ์ถœํ•˜๋Š” ๋‹ค๋ฅธ ํ•จ์ˆ˜๋“ค์„ ์ œ๊ฑฐํ•˜๊ณ  ์‹ถ์€๋ฐ, ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์–ด๋–ค ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”์ง€ ๋ชจ๋ฅธ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ์š”? ์šฐ์„ ์€ ์ฝ”๋“œ๋ฅผ ๋นŒ๋“œํ•ด๋ณด๊ณ  wasm-opt๋ฅผ --dce ํ”Œ๋ž˜๊ทธ๋ฅผ ํฌํ•จํ•ด์„œ ๋‹ค์‹œ ์‹คํ–‰ํ•ด ๋ณด์„ธ์š”! ๊ฐ„์ ‘์ ์œผ๋กœ ํ˜ธ์ถœ๋˜๋Š” (๋Ÿฐํƒ€์ž„์—์„œ ๋ถ€๋ฅด์ง€ ์•Š๋Š”) ํ•จ์ˆ˜๋“ค๊นŒ์ง€ ์ง€์›Œ๋ฒ„๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŒจ๋‹‰ ์ฝ”๋“œ๊ฐ€ ์ด๋Ÿฐ ๋ฌธ์ œ๋“ค๋กœ ์ข…์ข… ์ด์–ด์งˆ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ํŒจ๋‹‰ ์ธํ”„๋ผ (panicking infrastructure) ๋ฅผ ์ง€์šธ ๋•Œ ์ด ํˆด์ด ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ƒํ˜ธ ์šด์šฉํ•˜๊ธฐ

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ•จ์ˆ˜ ์ž„ํฌํŠธํ•˜๊ณ  ์ต์ŠคํฌํŠธํ•˜๊ธฐ

๋Ÿฌ์ŠคํŠธ ์‚ฌ์ด๋“œ

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์—์„œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋Ÿฌ์ŠคํŠธ ์‚ฌ์ด๋“œ์—์„œ ํ•จ์ˆ˜๋“ค์„ ์ž„ํฌํŠธํ•˜๊ณ  ์ต์ŠคํฌํŒ…ํ•˜๋Š” ์ž‘์—…์€ ์˜์™ธ๋กœ ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  C ์–ธ์–ด์™€ ์œ ์‚ฌํ•˜๊ฒŒ ์ž‘๋™ํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.

์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ชจ๋“ˆ์€ ์ž„ํฌํŠธ ์‹œํ€€์Šค(sequence)๋ฅผ ์„ ์–ธํ•˜๋Š”๋ฐ, ๊ฐ๊ฐ ์‹œํ€€์Šค๋Š” ๋ชจ๋“ˆ ์ด๋ฆ„๊ณผ ์ž„ํฌํŠธ ์ด๋ฆ„์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ "env" ํŒŒ์ผ์— ์„ค์ •๋œ ๊ฒƒ๊ณผ ๊ฐ™์ด, #[link(wasm_import_module)]๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ extern { ... } ๋ธ”๋Ÿญ ๋ชจ๋“ˆ ์ด๋ฆ„์„ ์ง์ ‘ ์ง€์ •ํ•ด ์ค„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์ต์ŠคํฌํŠธ๋Š” ํ•œ๊ฐ€์ง€ ์ด๋ฆ„๋งŒ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š”๋ฐ, ๋‹ค๋ฅธ extern ํ•จ์ˆ˜๋“ค๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์ธ์Šคํ„ด์Šค์˜ ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ๋Š” ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ๋Š” "memory" ๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ์ต์ŠคํฌํŠธ ๋ฉ๋‹ˆ๋‹ค.

#![allow(unused)]
fn main() {
// `foo`๋ผ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ•จ์ˆ˜๋ฅผ `mod` ๋ชจ๋“ˆ์—์„œ ์ž„ํฌํŠธํ•ด์˜ต๋‹ˆ๋‹ค.
#[link(wasm_import_module = "mod")]
extern { fn foo(); }

// `bar`๋ผ๋Š” ๋Ÿฌ์ŠคํŠธ ํ•จ์ˆ˜๋ฅผ ์ต์ŠคํฌํŠธํ•ฉ๋‹ˆ๋‹ค.
#[no_mangle]
pub extern fn bar() { /* ... */ }
}

์›น์–ด์…ˆ๋ธ”๋ฆฌ๋Š” ์ œํ•œ๋œ ์ˆ˜์˜ ๊ฐ’ ํƒ€์ž…๋“ค์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์ด๋Ÿฌํ•œ ํ•จ์ˆ˜๋“ค์€ ์›์‹œ ์ˆซ์ž ํƒ€์ž…์—์„œ๋งŒ ์ž‘๋™ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋ถ€๋ถ„๋„ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์‚ฌ์ด๋“œ

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ ๋‚ด์—์„œ๋Š” ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ES6 ๋ชจ๋“ˆ๋กœ ๋ณ€ํ™˜๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ชจ๋“ˆ์€ ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ์™€ ํ•จ๊ป˜ ๋จผ์ € ์ธ์Šคํ„ด์Šคํ™” ๋ผ์•ผํ•˜๊ณ , ์ž„ํฌํŠธํ•œ ๋‚ด์šฉ๊ณผ ์ผ์น˜ํ•˜๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ•จ์ˆ˜ ๊ทธ๋ฃน์„ ํ•„์š”๋กœ ํ•ฉ๋‹ˆ๋‹ค. ์ธ์Šคํ„ด์Šคํ™”์— ๋Œ€ํ•ด ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณด๋ ค๋ฉด MDN๋ฅผ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.

์ถœ๋ ฅํ•˜๊ฒŒ ๋˜๋Š” ES6 ๋ชจ๋“ˆ์€ ๋Ÿฌ์ŠคํŠธ์—์„œ ์ต์ŠคํฌํŠธํ•œ ํ•จ์ˆ˜๋“ค์„ ๋ชจ๋‘ ํฌํ•จํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ์ด๋Ÿฐ ํ•จ์ˆ˜๋“ค์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ•จ์ˆ˜์—์„œ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์…‹์—…์„ ํ•˜๊ณ  ์‹คํ–‰์‹œํ‚ค๋Š” ์ „๋ฐ˜์ ์œผ๋กœ ๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ๋ฅผ ํ™•์ธํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์—ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.

์ˆซ์ž ์™ธ์— ๋‹ค๋ฅธ ๊ฐ’๋„ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ชจ๋“ˆ์˜ ๋ฉ”๋ชจ๋ฆฌ์™€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์ด๊ฐ€ ๋ช…ํ™•ํ•˜๊ฒŒ ๊ตฌ๋ถ„๋ฉ๋‹ˆ๋‹ค:

  • (์ด ๋ฌธ์„œ ์ตœ์ƒ๋‹จ์— ์„ค๋ช…๋œ ๋Œ€๋กœ) ๊ฐ๊ฐ์˜ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ชจ๋“ˆ์€ ์ธ์Šคํ„ด์Šคํ™” ๊ณผ์ •์—์„œ ์ƒ์„ฑํ•˜๊ฒŒ ๋˜๋Š” ์„ ํ˜• ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ฐ€์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋Š” ์ž์œ ๋กญ๊ฒŒ ์ด ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ฝ๊ณ  ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋ฐ˜๋ฉด์—, ์›น์–ด์…ˆ๋ธ”๋ฆฌ์—์„œ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ ์ด์œ ๋กœ ๋ณดํ†ต์€ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ ์ •๊ตํ™”๋œ ์ƒํ˜ธ์ž‘์šฉ์„ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค:

  • ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ๋ณต์‚ฌํ•˜๊ฑฐ๋‚˜ ๋ถ™์—ฌ๋†“์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด์„œ, ์ด ๋ฐฉ๋ฒ•์œผ๋กœ ๋Ÿฌ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ์†Œ์œ ํ•˜๋Š” String ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋ช…์‹œ์ ์œผ๋กœ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด์˜ ํž™์„ ์„ค์ •ํ•˜๊ณ  ์ดํ›„์— "addresses"๋ฅผ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์ฝ”๋“œ์—์„œ (์ •์ˆ˜ ๊ฐ’์„ ์‚ฌ์šฉํ•˜์—ฌ) ๊ฐ„์ ‘์ ์œผ๋กœ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๊ณ , ์ž„ํฌํŠธํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋‹คํ–‰์Šค๋Ÿฝ๊ฒŒ๋„ ์ด ์ƒํ˜ธ ์šด์šฉ ์ž‘์—…์€ wasm-bindgen์ด๋ผ๋Š” bindgen ์Šคํƒ€์ผ์˜ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์•„์ฃผ ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ด€์šฉ์ ์ธ ์Šคํƒ€์ผ์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ•จ์ˆ˜์— ์ž๋™์œผ๋กœ ๋งคํ•‘๋˜๋„๋ก ๊ด€์šฉ์ ์ธ ๋Ÿฌ์ŠคํŠธ ํ•จ์ˆ˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์ •์˜ ์„น์…˜ (Custom Sections)

์‚ฌ์šฉ์ž ์ •์˜ ์„น์…˜์„ ํ†ตํ•ด ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ชจ๋“ˆ์— ๋ช…๋ช…๋œ ์ž„์˜์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ž„๋ฒ ๋”ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์„น์…˜ ๋ฐ์ดํ„ฐ๋Š” ์ปดํŒŒ์ผ ์‹œ์ ์— ์„ค์ •๋˜๋ฉฐ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ๋ชจ๋“ˆ์—์„œ ์ง์ ‘ ์ฝ์„ ์ˆ˜ ์žˆ์ง€๋งŒ ๋Ÿฐํƒ€์ž„์—์„œ๋Š” ์ด ๋ชจ๋“ˆ์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋Ÿฌ์ŠคํŠธ ์ฝ”๋“œ์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉ์ž ์ •์˜ ์„น์…˜์„ ์ •์  ๋ฐฐ์—ด ([T; size])๋กœ ๋‚˜ํƒ€๋‚ด๊ณ  #[link_section] ์†์„ฑ์œผ๋กœ ๋…ธ์ถœ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

#![allow(unused)]
fn main() {
#[link_section = "hello"]
pub static SECTION: [u8; 24] = *b"This is a custom section";
}

์ด ์ฝ”๋“œ๋Š” wasm ํŒŒ์ผ์— hello ๋ผ๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ์„น์…˜์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๋ณ€์ˆ˜ ์ด๋ฆ„ SECTION ์€ ์ž„์˜๋กœ ๋ช…๋ช…๋์œผ๋ฉฐ ์ด ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ•ด๋„ ์ฝ”๋“œ์˜ ๋™์ž‘์ด ๋ฐ”๋€Œ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ๋ณ€์ˆ˜์— ํ…์ŠคํŠธ ๋ฐ”์ดํŠธ (bytes of text) ๋ฅผ ์‚ฌ์šฉํ–ˆ์ง€๋งŒ ๋‹ค๋ฅธ ์ž„์˜์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฐ ์‚ฌ์šฉ์ž ์ •์˜ ์„น์…˜์€ JS ์‚ฌ์ด๋“œ์—์„œ WebAssembly.Module.customSections ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ, wasm ๋ชจ๋“ˆ๊ณผ ์„น์…˜ ์ด๋ฆ„์„ ์ธ์ž๋กœ ์ฃผ๊ณ  ArrayBuffer์˜ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ์„น์…˜๋“ค์ด ๊ฐ™์€ ์ด๋ฆ„์„ ๊ณต์œ ํ•˜๊ฒŒ ํ•  ์ˆ˜๋„ ์žˆ๋Š”๋ฐ, ์ด ๊ฒฝ์šฐ์—๋Š” ํ•œ ๋ฐฐ์—ด์— ์—ฌ๋Ÿฌ ์„น์…˜๋“ค์„ ๋‹ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

WebAssembly.compileStreaming(fetch("sections.wasm"))
.then(mod => {
  const sections = WebAssembly.Module.customSections(mod, "hello");

  const decoder = new TextDecoder();
  const text = decoder.decode(sections[0]);

  console.log(text); // -> "์‚ฌ์šฉ์ž ์ •์˜ ์„น์…˜์„ ์ฝ˜์†”์— ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค"
});

์–ด๋–ค ํฌ๋ ˆ์ดํŠธ๋“ค์„ ์›น์–ด์…ˆ๋ธ”๋ฆฌ์—์„œ ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‚˜์š”?

๊ฐ€์žฅ ๊ฐ„๋‹จํ•˜๊ฒŒ ์–ด๋–ค ๊ธฐ๋Šฅ๋“ค์ด ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํ™˜๊ฒฝ์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š”์ง€ ๋ชฉ๋ก์„ ํ•œ๋ฒˆ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ๋‚ด์šฉ์— ํ•ด๋‹นํ•˜์ง€ ์•Š๋Š” ํฌ๋ ˆ์ดํŠธ๋“ค์€ ํœด๋Œ€์„ฑ์ด ์ข‹๊ณ  ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํ™˜๊ฒฝ์—์„œ ์ž˜ ์ž‘๋™ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋„ ์ข‹๊ณ , ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ๊ณผ #![no_std]๋ฅผ ์ง€์›ํ•˜๋Š” ํฌ๋ ˆ์ดํŠธ๋“ค๋„ ๋ณดํ†ต์€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋„ ์ง€์›ํ•œ๋‹ค๊ณ  ๋ณผ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ํฌ๋ ˆ์ดํŠธ๋“ค์€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํ™˜๊ฒฝ์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค

C ์–ธ์–ด์™€ ์‹œ์Šคํ…œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ข…์†์„ฑ์ด ์žˆ๋Š” ๊ฒฝ์šฐ

์›น์–ด์…ˆ๋ธ”๋ฆฌ์—๋Š” ์‹œ์Šคํ…œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์—†๊ธฐ ๋–„๋ฌธ์— ์‹œ์Šคํ…œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋ฐ”์ธ๋”ฉ๋ผ ์žˆ๋Š” ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๋ฉด ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์›น์–ด์…ˆ๋ธ”๋ฆฌ์—๋Š” ์–ธ์–ด ๊ฐ„ ํ†ต์‹ ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์•ˆ์ •๋œ ๋ฒ„์ „์˜ ABI์™€ ์–ธ์–ด ๊ฐ„ ๋งํ‚น(linking)์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ C ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํฌ๋ ˆ์ดํŠธ๋„ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํŠนํžˆ clang ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ wasm32 ํƒ€๊ฒŸ์„ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฒฐ๊ตญ์€ ์ž‘๋™ํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋˜์ง€๋งŒ, ํ˜„์žฌ๋กœ์„œ๋Š” ์™„๋ฒฝํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํŒŒ์ผ I/O

์›น์–ด์…ˆ๋ธ”๋ฆฌ๋Š” ํŒŒ์ผ ์‹œ์Šคํ…œ์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. โ€” ํŒŒ์ผ ์‹œ์Šคํ…œ์„ ํ•„์š”๋กœ ํ•˜๊ณ  ์›น์–ด์…ˆ๋ธ”๋ฆฌ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฝ”๋“œ๊ฐ€ ๋งˆ๋ จ๋˜์ง€ ์•Š์€ ํฌ๋ ˆ์ดํŠธ๋Š” ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์Šค๋ ˆ๋“œ ์ƒ์„ฑ

์Šค๋ ˆ๋”ฉ์„ ์›น์–ด์…ˆ๋ธ”๋ฆฌ์— ์ง€์›ํ•˜๋Š” ๊ณ„ํš์ด ์žˆ๊ธด ํ•˜์ง€๋งŒ ์•„์ง ์ค€๋น„๊ฐ€ ์™„๋ฒฝํžˆ ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. wasm32-unknown-unknown ํƒ€๊ฒŸ์—์„œ ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๋ฉด wasm ํŠธ๋žฉ (wasm trap) ์ด ๋ฐœ์ƒํ•˜๋ฉด์„œ ์ฝ”๋“œ๊ฐ€ ํŒจ๋‹‰ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์–ด๋–ค ๋‹ค๋ชฉ์  ํฌ๋ ˆ์ดํŠธ๋“ค์ด ์›น์–ด์…ˆ๋ธ”๋ฆฌ์—์„œ ๋ฐ”๋กœ ์ž‘๋™ํ•˜๋Š” ํŽธ์ธ๊ฐ€์š”?

์•Œ๊ณ ๋ฆฌ์ฆ˜๊ณผ ์ž๋ฃŒ ๊ตฌ์กฐ

A* ์•Œ๊ณ ๋ฆฌ์ฆ˜๋‚˜ splay trees์ฒ˜๋Ÿผ, ํŠน์ •ํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‚˜ data structure์˜ ๊ตฌํ˜„์„ ์ œ๊ณตํ•˜๋Š” ํฌ๋ ˆ์ดํŠธ๋“ค์€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ์™€ ์ž˜ ์ž‘๋™ํ•˜๋Š” ํŽธ์ž…๋‹ˆ๋‹ค.

#![no_std]

๋Ÿฌ์ŠคํŠธ ์Šคํƒ ๋‹ค๋“œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํฌ๋ ˆ์ดํŠธ๋“ค๋„ ์›น์–ด์…ˆ๋ธ”๋ฆฌ์™€ ์ž˜ ์ž‘๋™ํ•˜๋Š” ํŽธ์ž…๋‹ˆ๋‹ค.

ํŒŒ์„œ (Parser)

์ž…๋ ฅ์„ ๋ฐ›๊ณ  I/O ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๋Š” ์ด์ƒ, ํŒŒ์„œ๋“ค์€ ๋ณดํ†ต ์›น์–ด์…ˆ๋ธ”๋ฆฌ์™€ ์ž˜ ์ž‘๋™ํ•˜๋Š” ํŽธ์ž…๋‹ˆ๋‹ค.

ํ…์ŠคํŠธ ์ฒ˜๋ฆฌ

์ธ๊ฐ„ ์–ธ์–ด๋ฅผ ํ…์ŠคํŠธ ํ˜•ํƒœ๋กœ ๋‚˜ํƒ€๋ƒˆ์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋ณต์žก์„ฑ์„ ๋‹ค๋ฃจ๋Š” ํฌ๋ ˆ์ดํŠธ๋“ค์€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ์™€ ์ž˜ ์ž‘๋™ํ•˜๋Š” ํŽธ์ž…๋‹ˆ๋‹ค.

๋Ÿฌ์ŠคํŠธ ํŒจํ„ด

Rust ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ํŠน์ •ํ•œ ์ƒํ™ฉ์— ์“ฐ๋„๋ก ๊ณต์œ ๋˜๋Š” ํ•ด๊ฒฐ์ฑ…๋“ค์€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ์™€ ์ž˜ ์ž‘๋™ํ•˜๋Š” ํŽธ์ž…๋‹ˆ๋‹ค.

๋‹ค๋ชฉ์  ํฌ๋ ˆ์ดํŠธ๊ฐ€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์ง€์›ํ•˜๋„๋ก ์ฝ”๋“œ ์ˆ˜์ •ํ•˜๊ธฐ

์ด ์„น์…˜์€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์ง€์›ํ•˜๋Š” ๋ฐ์— ๊ด€์‹ฌ์ด ์žˆ๋Š” ๋‹ค๋ชฉ์  ํฌ๋ ˆ์ดํŠธ ์ €์ž๋“ค์„ ์œ„ํ•ด ์ž‘์„ฑ๋์Šต๋‹ˆ๋‹ค.

ํฌ๋ ˆ์ดํŠธ๊ฐ€ ์ด๋ฏธ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์ง€์›ํ•  ์ˆ˜๋„ ์žˆ์–ด์š”!

์šฐ์„  ์–ด๋–ค ์ž‘์—…์„ ํ•˜๋Š” ๋‹ค๋ชฉ์  ํฌ๋ ˆ์ดํŠธ๊ฐ€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ์— ์ ํ•ฉํ•˜์ง€ ์•Š๋‚˜์š”? ์˜ ๋‚ด์šฉ์„ ๋จผ์ € ์ฝ์–ด๋ณด์„ธ์š”. ์ž‘์„ฑํ•˜๋Š” ํฌ๋ ˆ์ดํŠธ๊ฐ€ ํ•ด๋‹น ์‚ฌํ•ญ์ด ์—†๋‹ค๋ฉด, ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์ง€์›ํ•  ํ™•๋ฅ ์ด ๋†’์Šต๋‹ˆ๋‹ค.

์ถ”๊ฐ€๋กœ, ์–ธ์ œ๋“  cargo build ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•ด์„œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํƒ€๊ฒŸ์„ ์ง€์›ํ•˜๋Š”์ง€ ํ™•์ธํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

cargo build --target wasm32-unknown-unknown

๋ช…๋ น์–ด ์‹คํ–‰์ด ์‹คํŒจํ•œ๋‹ค๋ฉด ํ˜„์žฌ๋กœ์„œ๋Š” ํฌ๋ ˆ์ดํŠธ๊ฐ€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด ๋ช…๋ น์–ด๊ฐ€ ์„ฑ๊ณตํ•˜๋”๋ผ๋„ ํฌ๋ ˆ์ดํŠธ๊ฐ€ ๊ผญ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์ง€์›ํ•œ๋‹ค๋Š” ์˜๋ฏธ๋Š” ์•„๋‹™๋‹ˆ๋‹ค. ๋” ํ™•์‹คํ•˜๊ฒŒ ํ™•์ธํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํ…Œ์ŠคํŒ… ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์ง€์†์„ฑ ํ†ตํ•ฉ (continuous integration) ํ™˜๊ฒฝ์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•ด๋ณด๊ธฐ ๋ฅผ ์ฝ์–ด๋ณด์„ธ์š”.

์›น์–ด์…ˆ๋ธ”๋ฆฌ ์ง€์› ์ถ”๊ฐ€ํ•˜๊ธฐ

๋ฐ”๋กœ I/O๋ฅผ ์ˆ˜ํ–‰ํ•˜์ง€ ๋ง์•„์ฃผ์„ธ์š”

์›น ํ™˜๊ฒฝ์—์„œ I/O๋Š” ํ•ญ์ƒ ๋น„๋™๊ธฐ์ ์ผ ๋ฟ ์•„๋‹ˆ๋ผ ํŒŒ์ผ ์‹œ์Šคํ…œ๋„ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ I/O๋ฅผ ๋ถ„๋ฆฌํ•˜๊ณ  ์œ ์ €๋“ค์ด I/O๋ฅผ ์ง์ ‘ ์ˆ˜ํ–‰ํ•˜๊ณ  ์Šฌ๋ผ์ด์Šค๋ฅผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๋Œ€์‹  ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋„๋ก ์ˆ˜์ •ํ•ด ๋ด…์‹œ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด์„œ, ์ด ์ฝ”๋“œ๋ฅผ ๋ฆฌํŒฉํ† ๋งํ•ด ์ฃผ์„ธ์š”:

#![allow(unused)]
fn main() {
use std::fs;
use std::path::Path;

pub fn parse_thing(path: &Path) -> Result<MyThing, MyError> {
    let contents = fs::read(path)?;
    // ...
}
}

๋‹ค์Œ์€ ๋ฆฌํŒฉํ† ๋ง ๋œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค:

#![allow(unused)]
fn main() {
pub fn parse_thing(contents: &[u8]) -> Result<MyThing, MyError> {
    // ...
}
}

wasm-bindgen์„ ์ข…์†์„ฑ์œผ๋กœ ์ถ”๊ฐ€ํ•˜๊ธฐ

(์˜ˆ๋ฅผ ๋“ค์–ด์„œ, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ์ž๊ฐ€ ์ƒํ˜ธ์ž‘์šฉ์„ ์ง์ ‘ ์ปจํŠธ๋กคํ•˜๋„๋ก ํ—ˆ์šฉํ•˜๋ฉด ์•ˆ ๋˜๋Š” ๊ฒฝ์šฐ์ฒ˜๋Ÿผ) ์™ธ๋ถ€ ํ™˜๊ฒฝ๊ณผ ๋”ฐ๋กœ ์ƒํ˜ธ์ž‘์šฉ์„ ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ, (ํ•„์š”ํ•˜๋‹ค๋ฉด js-sys์™€ web-sys์™€ ํ•จ๊ป˜) wasm-bindgen์„ ์ปดํŒŒ์ผ, ํƒ€๊ฒŸํŒ… ์ข…์†์„ฑ์œผ๋กœ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:

[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = "0.2"
js-sys = "0.3"
web-sys = "0.3"

๋™๊ธฐ์  I/O๋ฅผ ํ”ผํ•ด์ฃผ์„ธ์š”

์›น ํ™˜๊ฒฝ์—์„œ๋Š” ๋น„๋™๊ธฐ์  I/O๋งŒ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ I/O ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ๋•Œ๋Š” ๋™๊ธฐ์ ์œผ๋กœ๋Š” ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. futures ํฌ๋ ˆ์ดํŠธ ์™€ wasm-bindgen-futures ํฌ๋ ˆ์ดํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋น„๋™๊ธฐ I/O๋ฅผ ๊ด€๋ฆฌํ•ด ๋ณด์„ธ์š”. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ Future ํƒ€์ž… F๋ฅผ ์ œ๋„ค๋ฆญ ํƒ€์ž…์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ์›น ํ™˜๊ฒฝ์˜ fetch๋‚˜ ์šด์˜์ฒด์ œ์—์„œ ์ œ๊ณตํ•˜๋Š” ๋…ผ๋ธ”๋กœํ‚น (non-blocking) I/O๋กœ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

#![allow(unused)]
fn main() {
pub fn do_stuff<F>(future: F) -> impl Future<Item = MyOtherThing>
where
    F: Future<Item = MyThing>,
{
    // ...
}
}

ํŠธ๋ ˆ์ดํŠธ๋ฅผ ์ •์˜ํ•˜๊ณ  ์›น์–ด์…ˆ๋ธ”๋ฆฌ์™€ ์›น, ๋„ค์ดํ‹ฐ๋ธŒ ํƒ€๊ฒŸ์—์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ•ด ๋ณผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค:

#![allow(unused)]
fn main() {
trait ReadMyThing {
    type F: Future<Item = MyThing>;
    fn read(&self) -> Self::F;
}

#[cfg(target_arch = "wasm32")]
struct WebReadMyThing {
    // ...
}

#[cfg(target_arch = "wasm32")]
impl ReadMyThing for WebReadMyThing {
    // ...
}

#[cfg(not(target_arch = "wasm32"))]
struct NativeReadMyThing {
    // ...
}

#[cfg(not(target_arch = "wasm32"))]
impl ReadMyThing for NativeReadMyThing {
    // ...
}
}

์Šค๋ ˆ๋“œ ์ƒ์„ฑ์„ ํ”ผํ•ด์ฃผ์„ธ์š”

์›น์–ด์…ˆ๋ธ”๋ฆฌ๋Š” ์•„์ง ์Šค๋ ˆ๋“œ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. (ํ•˜์ง€๋งŒ ์‹คํ—˜์ ์œผ๋กœ ์ง€์› ์ž‘์—…์ด ์ง„ํ–‰๋˜๊ณ  ์žˆ๊ธด ํ•ฉ๋‹ˆ๋‹ค.) ๊ทธ๋Ÿฌ๋ฏ€๋กœ, ์›น์–ด์…ˆ๋ธ”๋ฆฌ์—์„œ ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ํŒจ๋‹‰ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

#[cfg(..)]๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํƒ€๊ฒŸ์ด ์›น์–ด์…ˆ๋ธ”๋ฆฌ์ธ์ง€ ์•„๋‹Œ์ง€ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ์Šค๋ ˆ๋“œ์™€ ๋น„์Šค๋ ˆ๋“œ ์ฝ”๋“œ๋ฅผ ๋”ฐ๋กœ ์ž‘์„ฑํ•ด ๋ณผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค:

#![allow(unused)]
#![cfg(target_arch = "wasm32")]
fn main() {
fn do_work() {
    // ์ด ์Šค๋ ˆ๋“œ์—์„œ๋งŒ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค...
}

#![cfg(not(target_arch = "wasm32"))]
fn do_work() {
    use std::thread;

    // ํ—ฌํผ ์Šค๋ ˆ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ž‘์—…์„ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค...
    thread::spawn(|| {
        // ...
    });
}
}

ํŒŒ์ผ I/O๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ์œ ์ €๋“ค์ด I/O ์ฝ”๋“œ๋ฅผ ๋”ฐ๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉํ•˜๋Š” ์ ‘๊ทผ ๋ฐฉ์‹๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ, ์Šค๋ ˆ๋“œ ์ƒ์„ฑ ์ฝ”๋“œ๋ฅผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ œ์™ธ์‹œํ‚จ ๋‹ค์Œ ์œ ์ €๋“ค์ด ์Šค๋ ˆ๋“œ ์ฝ”๋“œ๋‚˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋”ฐ๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ๋ณ€๊ฒฝํ•ด ๋ณผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๊ตฌํ˜„ํ–ˆ์„ ๋•Œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ์ž๋“ค์ด ์ง์ ‘ ์Šค๋ ˆ๋“œ ํ’€ ์ฝ”๋“œ๋ฅผ ๋งˆ๋ จํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋Š” ๊ธ์ •์ ์ธ ๋ถ€์ˆ˜ ํšจ๊ณผ๋ฅผ ๋ณด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์›น์–ด์…ˆ๋ธ”๋ฆฌ์˜ ์ง€์†์ ์ธ ์ง€์› ์œ ์ง€ํ•˜๊ธฐ

์ง€์†์„ฑ ํ†ตํ•ฉ (CI) ํ™˜๊ฒฝ์„ ์‚ฌ์šฉํ•ด์„œ wasm32-unknown-unknown ํƒ€๊ฒŸ์œผ๋กœ ๋นŒ๋“œํ•˜๊ธฐ

์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ํƒ€๊ฒŸ์œผ๋กœ ํ• ๋–„, ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ CI ์Šคํฌ๋ฆฝํŠธ์— ํฌํ•จ์‹œ์ผœ์„œ ์ปดํŒŒ์ผ์ด ์‹คํŒจํ•˜์ง€ ์•Š๋Š” ์ด์œ ๋ฅผ ๋” ์ž์„ธํ•˜๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

rustup target add wasm32-unknown-unknown
cargo check --target wasm32-unknown-unknown

์˜ˆ๋ฅผ ๋“ค์–ด์„œ, Travis CI ์„ค์ • ํŒŒ์ผ์ธ .travis.yml ์— ๋‹ค์Œ ๋‚ด์šฉ์„ ์ถ”๊ฐ€ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:


matrix:
  include:
    - language: rust
      rust: stable
      name: "check wasm32 support"
      install: rustup target add wasm32-unknown-unknown
      script: cargo check --target wasm32-unknown-unknown

Node.js์™€ ํ—ค๋“œ๋ฆฌ์Šค ๋ธŒ๋ผ์šฐ์ € (Headless Browsers) ์—์„œ ํ…Œ์ŠคํŒ…ํ•˜๊ธฐ

wasm-bindgen-test์™€ wasm-pack-test ํ•˜์œ„ ๋ช…๋ น์–ด (subcommand) ๋ฅผ ์‚ฌ์šฉํ•ด์„œ wasm ํ…Œ์ŠคํŠธ๋“ค Node.js๋‚˜ ํ—ค๋“œ๋ฆฌ์Šค ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹คํ–‰ํ•ด๋ณด์„ธ์š”. ์ด๋Ÿฌํ•œ ํ…Œ์ŠคํŠธ๋“ค์„ CI์— ํ†ตํ•ฉ์‹œ์ผœ ๋ณผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํ…Œ์ŠคํŒ…์— ๋Œ€ํ•ด ๋” ์•Œ์•„๋ณด์„ธ์š”.

๋Ÿฌ์ŠคํŠธ๋กœ ์ž‘์„ฑํ•œ WebAssembly ์ฝ”๋“œ๋ฅผ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์— ๋ฐฐํฌํ•˜๊ธฐ

โšก ์‚ฌ์‹ค ๋Ÿฌ์ŠคํŠธ, ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋กœ ์›น ์•ฑ์„ ์ž‘์„ฑํ–ˆ๋”๋ผ๋„ ๋ฐฐํฌ ๊ณผ์ •์ด ๋‹ค๋ฅธ ์›น ์•ฑ์„ ๋ฐฐํฌํ•˜๋Š” ๊ณผ์ •๊ณผ ๊ฑฐ์˜ ๋™์ผํ•ฉ๋‹ˆ๋‹ค!

๋Ÿฌ์ŠคํŠธ๋กœ ์ƒ์„ฑํ•œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์›น ์•ฑ์„ ๋ฐฐํฌํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋นŒ๋“œ๋œ ์›น ์•ฑ์„ ํ”„๋กœ๋•์…˜ ์„œ๋ฒ„์˜ ํŒŒ์ผ ์‹œ์Šคํ…œ์œผ๋กœ ๋ณต์‚ฌํ•˜๊ณ , ๋ณต์‚ฌํ•œ ํŒŒ์ผ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก HTTP ์„œ๋ฒ„๋ฅผ ์„ค์ •ํ•ด ์ฃผ์„ธ์š”.

HTTP ์„œ๋ฒ„๊ฐ€ application/wasm MIME ํƒ€์ž…์„ ์ง€์›ํ•˜๋Š”์ง€ ํ™•์ธํ•ด์ฃผ์„ธ์š”

ํŽ˜์ด์ง€๊ฐ€ ๋น ๋ฅด๊ฒŒ ๋กœ๋“œ๋  ์ˆ˜ ์žˆ๋„๋ก, ๋„คํŠธ์›Œํฌ ์ „์†ก์„ ํ†ตํ•ด WebAssembly.instantiateStreaming ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ์ปดํŒŒ์ผ๊ณผ ์ธ์Šคํ„ด์Šคํ™” ๊ณผ์ •์„ ํŒŒ์ดํ”„๋ผ์ธ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (๋ฒˆ๋“ค๋Ÿฌ๊ฐ€ ์ด ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€๋„ ํ™•์ธํ•ด์ฃผ์„ธ์š”.) ํ•˜์ง€๋งŒ instantiateStreaming๋ฅผ ์‹คํ–‰ํ•  ๋•Œ HTTP ์‘๋‹ต์ด application/wasm MIME ํƒ€์ž… ์œ ํ˜•์„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๋‹ค๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๋‹ˆ ์ด ๋ถ€๋ถ„๋„ ์ž˜ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.

์ถ”๊ฐ€ ์ž๋ฃŒ

  • ํ”„๋กœ๋•์…˜ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ webpack์„ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋ฒ” ์‚ฌ๋ก€. ๋งŽ์€ ๋Ÿฌ์ŠคํŠธ์™€ ์›น์–ด์…ˆ๋ธ”๋ฆฌ ํ”„๋กœ์ ํŠธ๋“ค์€ ๋Ÿฌ์ŠคํŠธ๋กœ ์ƒ์„ฑํ•œ ์›น์–ด์…ˆ๋ธ”๋ฆฌ์™€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ, CSS, HTML ์ฝ”๋“œ๋ฅผ ๋ฒˆ๋“ค๋ง ํ•˜๊ธฐ ์œ„ํ•ด webpack์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ€์ด๋“œ๋Š” ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์— ๋ฐฐํฌํ•  ๋•Œ ์–ด๋–ป๊ฒŒ Webpack์„ ๊ฐ€์žฅ ์ž˜ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ๋ ค์ฃผ๋Š” ์—ฌ๋Ÿฌ ์œ ์šฉํ•œ ์ •๋ณด๋“ค์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.
  • Apache ๋ฌธ์„œ. Apache๋Š” ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” HTTP ์„œ๋ฒ„์ž…๋‹ˆ๋‹ค.
  • NGINX ๋ฌธ์„œ. NGNIX๋Š” ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” HTTP ์„œ๋ฒ„์ž…๋‹ˆ๋‹ค.

๋ฒˆ์—ญ๋ณธ

์ด ์ฑ…์„ ๋‹ค๋ฅธ ์–ธ์–ด๋กœ ๋ฒˆ์—ญํ•˜๋Š” ๋ฐ์— ๊ด€์‹ฌ์ด ์žˆ๋‹ค๋ฉด ๋ฉ”์ธ ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ํฌํฌํ•˜๊ณ  ํ’€ ๋ฆฌํ€˜์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•ด๋ณด์„ธ์š”.