์›น ์ปดํฌ๋„ŒํŠธ(5) - lit-html๋กœ React ์ฒ˜๋Ÿผ ์ฝ”๋”ฉํ•˜๊ธฐ


image

์ด๋ฒˆ ๊ธ€์€ ์›น ์ปดํฌ๋„ŒํŠธ ์†Œ๊ฐœ ์—ฐ์žฌ 5๋ฒˆ์งธ๋กœ ์›น ์ปดํฌ๋„ŒํŠธ๋ฅผ react์ฒ˜๋Ÿผ ์ฝ”๋”ฉํ•˜๊ธฐ๋ฅผ ํ•ด๋ณด๊ฒ ๋‹ค. ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์ œ ์ „์ฒด ์ฝ”๋“œ๋Š” Todo Web Components์—์„œ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ์ง€๋‚œ ์—ฐ์žฌ์—์„œ ์•Œ์•„๋ณด์•˜๋˜ ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ, ์‰๋„์šฐ ๋” ๊ทธ๋ฆฌ๊ณ  lit-HTML์„ ์‚ฌ์šฉํ•˜์—ฌ ์›น ์ปดํฌ๋„ŒํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์–ด๋–ป๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž.

์›น ์ปดํฌ๋„ŒํŠธ TODO APP

๊ธ€์„ ์ฝ๊ธฐ ์ „์— ์•„๋ž˜์˜ ๋งํฌ์—์„œ ์˜ˆ์ œ ํŽ˜์ด์ง€๋ฅผ ์—ด์–ด๋‘๊ณ  ์‹œ์ž‘ํ•˜์ž.

Todo Web Components ์•ฑ ์ €์žฅ์†Œ

Todo Web Components ์•ฑ ๋ฐ๋ชจ

์ด๋ฒˆ ๊ธ€์—์„œ ์‚ฌ์šฉํ•˜๋Š” Todo Web Components ์˜ˆ์ œ๋Š” TodoMVC๋ฅผ ๋”ฐ๋ผ ๋งŒ๋“ค์—ˆ๋‹ค. TodoMVC๋Š” ์ˆ˜ ๋งŽ์€ ํ”„๋ก ํŠธ์—”๋“œ ํ”„๋ ˆ์ž„์›Œํฌ ์ค‘์— ์„ ํƒ์„ ํ•ด์•ผ ํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋ฅผ ๋•๊ธฐ ์œ„ํ•ด ๋งŒ๋“ค์–ด์กŒ๋‹ค. ์ด๊ณณ์—์„œ ๊ฐ™์€ TODO ์•ฑ์„ ๊ฐ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ด์šฉํ•˜์—ฌ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š”์ง€, ์˜ˆ์ œ๋“ค์„ ํ™•์ธํ•˜์—ฌ ๋น„๊ตํ•ด ๋ณผ ์ˆ˜ ์žˆ๋‹ค. TodoMVC ์˜ˆ์ œ๋Š” ์–ด๋– ํ•œ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๊ฐ•์ ์„ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด ์ง€๋‚˜์น˜๊ฒŒ ๊ฐ„๋žตํ™”๋˜๊ณ  ํŽธ์ค‘๋œ ์˜ˆ์ œ๊ฐ€ ์•„๋‹ˆ๋ผ, ์–ด๋Š ์ •๋„ ์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ด ๊ตฌ์„ฑ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ๊ฐ๊ด€์ ์œผ๋กœ ๋น„๊ตํ•ด ๋ณผ ์ˆ˜ ์žˆ๋Š” ์žฅ์ ์ด ์žˆ๋‹ค. ์œ„์˜ ์ด์œ ๋กœ ์˜ˆ์ œ ์ฝ”๋“œ๊ฐ€ ์ด๋ฒˆ ์„ค๋ช…์—์„œ ํ•„์š”ํ•œ ๊ฒƒ๋ณด๋‹ค ๋‹ค์†Œ ๊ธธ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ์ „์ฒด ์ฝ”๋“œ๊ฐ€ ์•„๋‹Œ ์ผ๋ถ€ ์ฝ”๋“œ๋“ค๋งŒ ๋–ผ์–ด ์„ค๋ช…ํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค. ๋˜ํ•œ, ์ด ์˜ˆ์ œ๋Š” ์ค€๋น„๊ฐ€ ๋˜๋ฉด TodoMVC์— ์ œ์ถœํ•  ์˜ˆ์ •์ด๋ฏ€๋กœ, ์—ฌ๋Ÿฌ๋ถ„์ด ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ๊ฒธํ•ด์ฃผ์–ด๋„ ์ข‹๊ฒ ๋‹ค.

ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

์ด ํ”„๋กœ์ ํŠธ๋Š” ์ง€๋‚œ ์—ฐ์žฌ์—์„œ ์•Œ์•„๋ณด์•˜๋˜ ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ, ์‰๋„์šฐ ๋” ๊ทธ๋ฆฌ๊ณ  lit-HTML์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค. ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ES6 Class ๋ฌธ๋ฒ•์ด ํ•„์ˆ˜์ด๋‹ค. ๋” ๋งŽ์€ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด์„œ babel์„ ํ†ตํ•ด ES5 ๋ฌธ๋ฒ•์œผ๋กœ src์— ์žˆ๋Š” ์†Œ์Šค ํŒŒ์ผ๋“ค์„ ํŠธ๋žœ์ŠคํŒŒ์ผ๋ง ํ•˜๋ฉฐ, ๊ทธ ํˆด๋ง์€ webpack์„ ํ†ตํ•ด dist์— ์ €์žฅํ•˜๊ณ  ์žˆ๋‹ค. ํ”„๋กœ์ ํŠธ ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ตฌ์กฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  • src: ์†Œ์Šค ํŒŒ์ผ๋“ค

    • components: ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ๋“ค
    • todoApp.js: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฉ”์ธ ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ
    • todoInput.js: ์ƒ๋‹จ TODO ์•„์ดํ…œ ์ž…๋ ฅ์ฐฝ
    • todoItem.js: ์ž…๋ ฅ๋œ ํ•˜๋‚˜์˜ TODO
    • todoList.js: todoItem๋“ค์„ ๋ฆฌ์ŠคํŠธ ํ˜•ํƒœ๋กœ ๋ณด์—ฌ์คŒ
    • todoToolbar: ํ•˜๋‹จ ํˆด๋ฐ”. ๋‚จ์€ TODO ๊ฐœ์ˆ˜, ์ƒํƒœ์— ๋”ฐ๋ฅธ TODO ์•„์ดํ…œ ๋…ธ์ถœ ํ† ๊ธ€ ๋ฒ„ํŠผ๋“ค
    • libs: ์ด์™ธ์˜ ์†Œ์Šค๋“ค
    • actions.js: redux-zero ์•ก์…˜๋“ค. TODO ์•ฑ์—์„œ ์‚ฌ์šฉ๋˜์–ด ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•  ์ˆ˜ ์žˆ๋Š” ์•ก์…˜๋“ค์ด ์ •์˜
    • litRender.js: lit-HTML ์ปดํฌ๋„ŒํŠธ ํ—ฌํผ. ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ์—์„œ invalidate๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ํ™”๋ฉด ์—…๋ฐ์ดํŠธ๋ฅผ ์Šค์ผ€์ค„
    • store.js: redux-zero ์Šคํ† ์–ด, ๊ธฐ๋ณธ๊ฐ’ ์„ค์ •
    • index.js: index
  • index.html: index HTML
  • webpack.config.js: ์›นํŒฉ ์„ค์ •
  • package.json: ํŒจํ‚ค์ง€ ์„ค์ •๊ณผ ์Šคํฌ๋ฆฝํŠธ

image

ํ”„๋กœ์ ํŠธ ์‚ฌ์šฉ

์ด๋ฒˆ ๊ธ€์„ ๋”ฐ๋ผ๊ฐ€๋Š”๋ฐ ๋ฐ˜๋“œ์‹œ ๋กœ์ปฌ์—์„œ ์ด ํ”„๋กœ์ ํŠธ๋ฅผ ์‹คํ–‰ํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋ฏ€๋กœ, ์›์น˜ ์•Š๋Š” ๋…์ž๋Š” ์ด ์„น์…˜์„ ๋„˜์–ด๊ฐ€๋„ ๋ฌด๋ฐฉํ•˜๋‹ค. ์œ„์—์„œ ์•Œ๋ ค์ค€ Todo Web Components ์•ฑ ์ €์žฅ์†Œ์™€ ๋ฐ๋ชจ ํŽ˜์ด์ง€๋งŒ ์ฐธ์กฐํ•ด๋„ ์ถฉ๋ถ„ํ•˜๋‹ค.

๋กœ์ปฌ์—์„œ ํ™•์ธํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด ์šฐ์„  git ์ปค๋งจ๋“œ๋กœ Todo Web Components ์•ฑ ์ €์žฅ์†Œ์—์„œ ์ „์ฒด ํ”„๋กœ์ ํŠธ๋ฅผ ๊ฐ€์ ธ์˜ค์ž.

git clone git@github.com:kyuwoo-choi/todo-web-components.git

๊ทธ๋‹ค์Œ yarn ์ปค๋งจ๋“œ๋กœ ํ•„์š”ํ•œ ๋””ํŽœ๋˜์‹œ ํŒจํ‚ค์ง€๋“ค์„ ์„ค์น˜ํ•œ๋‹ค. yarn์ด ์„ค์น˜๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค๋ฉด ๋ฌผ๋ก  npm์„ ์‚ฌ์šฉํ•˜์—ฌ๋„ ๋ฌด๋ฐฉํ•˜๋‹ค.

yarn install
ํ˜น์€
npm install

์ด์ œ ํ•„์š”ํ•œ ์ค€๋น„๊ฐ€ ๋๋‚ฌ์œผ๋ฏ€๋กœ package.json์— ์ •์˜๋œ serve ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €๋กœ ํ™•์ธํ•ด๋ณด์ž. http://localhost:8080/

yarn run serve
ํ˜น์€
npm run serve

index.html: ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ ์‚ฌ์šฉ

<html>
  <head>
    ...
    <script
      src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/1.0.20/custom-elements-es5-adapter.js"
      defer
    ></script>
    <script
      src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/1.0.20/webcomponents-sd-ce.js"
      defer
    ></script>
    <script src="./dist/TodoApp.js" defer></script>
    ...
  </head>
</html>

<head>์—๋Š” ํŠธ๋žœ์ŠคํŒŒ์ผ ๋œ TodoApp.jsํŒŒ์ผ ๊ทธ๋ฆฌ๊ณ  ๋‘ ๊ฐœ์˜ ์›น ์ปดํฌ๋„ŒํŠธ ํด๋ฆฌํ•„ custom-elements-es5-adapter.js, webcomponents-sd-ce.js ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ํฌํ•จ๋˜์—ˆ๋‹ค. ํ˜„์žฌ ํฌ๋กฌ๊ณผ ์‚ฌํŒŒ๋ฆฌ ๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” ํด๋ฆฌํ•„ ์—†์ด ํ™•์ธํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋‘ ํด๋ฆฌํ•„์„ ์‚ฌ์šฉํ•˜๋ฉด ํŒŒ์ด์–ดํญ์Šค, ์—ฃ์ง€, IE11๋„ ์ง€์›ํ•  ์ˆ˜ ์žˆ๋‹ค.

์›น ์ปดํฌ๋„ŒํŠธ ํด๋ฆฌํ•„ ์ค‘ ์ด ํ”„๋กœ์ ํŠธ์—์„œ๋Š” Shadow DOM, Custom Elements๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, webcomponents-sd-ce.js๋ฅผ ์„ ํƒํ–ˆ๋‹ค. (ํด๋ฆฌํ•„ ํŒŒ์ผ ์ด๋ฆ„์— ํฌํ•จ๋œ sd, ce๋Š” Shadow DOM, Custom Elements์˜ ์•ฝ์ž์ด๋‹ค) ๋˜ํ•œ ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ ํ•„์š”๋กœ ํ•˜๋Š” ES6๋ฌธ๋ฒ•์„ ES5๋ฌธ๋ฒ•์œผ๋กœ ํŠธ๋žœ์ŠคํŒŒ์ผํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ custom-elements-es5-adapter.js๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

<body>
  <todo-app></todo-app>
  ...
</body>

</html>

<body>๋Š” ๊ฐ„๋žตํžˆ <todo-app>ํƒœ๊ทธ๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค. ์ด ํƒœ๊ทธ๋Š” TodoApp.js์— ํฌํ•จ๋œ ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ ์ •์˜ํ•˜๊ณ  ์žˆ๋‹ค. ์ด์ฒ˜๋Ÿผ ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ž…์žฅ์—์„œ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ๊ณผ ํƒœ๊ทธ๋ฅผ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋ฟ์œผ๋กœ ๋งค์šฐ ํŽธ๋ฆฌํ•˜๋‹ค.

todoApp.js: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปดํฌ๋„ŒํŠธ

todoApp.js๋Š” src/components์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์œ„ index.html์—์„œ ์‚ฌ์šฉํ•œ <todo-app>ํƒœ๊ทธ๋ฅผ ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ๋กœ ์ •์˜ํ•œ๋‹ค. ๋”๋ถˆ์–ด ํ•˜๋‚˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ์„œ ํ•„์š”ํ•œ API๋„ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋‹ค.

imports

import { html } from "lit-html";

import LitRender from "../libs/litRender";
import store from "../libs/store";
import {
  add,
  toggle,
  remove,
  toggleAll,
  clearCompleted,
  replace
} from "../libs/actions";

import "./todoInput";
import "./todoToolbar";
import "./todoList";

์ฝ”๋“œ ์ƒ๋‹จ์—์„œ ํ•„์š”ํ•œ ๋””ํŽœ๋˜์‹œ๋“ค์„ ๊ฐ€์ ธ์˜จ๋‹ค. html๋ Œ๋”๋ง์— ํ•„์š”ํ•œ lit-HTML๊ณผ ์ด๊ฒƒ์„ ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ์—์„œ ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ •์˜ํ•œ LitRender๋ฏน์Šค์ธ ํ—ฌํผ. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒํƒœ์™€ ์•ก์…˜์„ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ Redux-Zero(Redux ๋ฏธ๋‹ˆ๋ฏธ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค) store์™€ add, toggle๊ฐ™์€ ์•ก์…˜๋“ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” todoInput, todoToolbar, todoList๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.

import './todoInput'์˜ ๋ฌธ๋ฒ•์ด ์˜๋ฌธ์Šค๋Ÿฌ์šด ๋…์ž๋„ ์žˆ์„ ๊ฒƒ์ด๋ผ ๋ณธ๋‹ค. ์ด๊ฒƒ์€ ๊ฐ€์ ธ์˜จ ๋ชจ๋“ˆ์„ ์ €์žฅํ•˜์ง€ ์•Š๊ณ  ๋ชจ๋“ˆ์„ ๋กœ๋“œ๋งŒ ์œ„ํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค. import TodoInput from './todoInput'๋„ ์˜ฌ๋ฐ”๋ฅธ ์‚ฌ์šฉ๋ฒ•์ด์ง€๋งŒ, ์ฝ”๋“œ์—์„œ TodoInput์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ์›นํŒฉ์ด Tree Shaking์œผ๋กœ ๋””ํŽœ๋˜์‹œ๋ฅผ ์ œ๊ฑฐํ•ด ๋ฒ„๋ฆฐ๋‹ค. ์ด๋ฅผ ํ”ผํ•˜๊ธฐ ์œ„ํ•œ ๋ฌธ๋ฒ•์ด๋ฉฐ, ๋”ฑํžˆ ์ปดํฌ๋„ŒํŠธ ํด๋ž˜์Šค๋“ค์„ ์ง์ ‘ ์‚ฌ์šฉํ•˜์ง€๋„ ์•Š์œผ๋ฏ€๋กœ ํ˜„์žฌ์˜ ํ˜•ํƒœ๊ฐ€ ๋˜์—ˆ๋‹ค๊ณ  ์ดํ•ดํ•˜๋ฉด ๋˜๊ฒ ๋‹ค.

์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ ํด๋ž˜์Šค

...
class TodoApp extends LitRender(HTMLElement) {
  constructor(name) {
    super();

    this.attachShadow({ mode: 'open' });

    this.invalidate();
  }
...

ES6 class๋ฌธ๋ฒ•์œผ๋กœ TodoApp ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ์ •์˜ํ•œ๋‹ค. ์ด ํด๋ž˜์Šค๋Š” HTMLElement๊ณผ LitRender๋ฏน์Šค์ธ์„ ํ™•์žฅํ•œ๋‹ค. constructor์—์„œ๋Š” ์‰๋„์šฐ ๋”์„ open๋ชจ๋“œ๋กœ ์ด ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ์— ์ƒ์„ฑํ•œ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ invalidate()๋ฅผ ํ•˜๊ณ  ์žˆ๋Š”๋ฐ, ์ด๋Š” LitRender์— ์ •์˜๋œ ํ•จ์ˆ˜๋กœ ์ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋ง ํ•˜๋„๋ก ํ•ด์ค€๋‹ค. LitRender์™€ invalidate์— ๋Œ€ํ•ด์„œ๋Š” ์ดํ›„ ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณด๊ณ , ์—ฌ๊ธฐ์„œ๋Š” ์ง๊ด€์ ์œผ๋กœ invalidate์˜ ํšจ์šฉ๋งŒ ๋– ์˜ฌ๋ฆฌ๋ฉด ์ถฉ๋ถ„ํ•˜๋‹ค.

์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ API

...
  add(title) {
    add(title);
  }
...
  get length() {
    const todoList = store.getState().todoList;

    return todoList.length;
  }
...

API๋ฅผ ์ •์˜ํ•œ๋‹ค. ๋ฐ๋ชจ ํŽ˜์ด์ง€ ํ˜น์€ ๋กœ์ปฌ ์„œ๋ฒ„ http://localhost:8080์— ์ ‘์†ํ•ด์„œ API๋ฅผ ์‚ฌ์šฉํ•ด๋ณด์ž. document.querySelector('todo-app').add('hello'), document.querySelector('todo-app').length ์˜ ์ปค๋งจ๋“œ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ํŽธํ•˜์ง€ ์•Š์€๊ฐ€?! ์šฐ๋ฆฌ๋Š” ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ ํด๋ž˜์Šค์— ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•ด ์ฃผ๋Š” ๊ฒƒ์œผ๋กœ ์ด์ฒ˜๋Ÿผ ์ง๊ด€์ ์ธ API๋ฅผ ์ œ๊ณตํ•ด ์ค„ ์ˆ˜ ์žˆ๋‹ค.

image

HTML ๋ Œ๋”๋ง

...
  render() {
    return html`
      <style>
        host: {
          display: block;
        }
        section {
          background: #fff;
          margin: 130px 0 40px 0;
          position: relative;
          box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
        }
      </style>
      <section>
        <todo-input></todo-input>
        <todo-list></todo-list>
        <todo-toolbar></todo-toolbar>
      </section>
    `;
  }
}
...

render ํ•จ์ˆ˜๋Š” ์œ„์˜ invalidate์™€ ์Œ์„ ์ด๋ฃจ๋Š” ํ•จ์ˆ˜๋กœ LitRender๋ฅผ ํ†ตํ•ด ํ˜ธ์ถœ๋œ๋‹ค. ์ด ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด lit-HTML์˜ html Template Literal ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด ์ด ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ์˜ ํ•˜์œ„ ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ๋ Œ๋”๋งํ•œ๋‹ค. ์˜ค์˜ค! ์ œ๋ฒ• React ๊ฐ™์€ ๋ชจ์–‘์ƒˆ๊ฐ€ ๋˜์—ˆ์ง€ ์•Š์€๊ฐ€?

์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ ๋“ฑ๋ก

customElements.define("todo-app", TodoApp);

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

litRender.js

litRender.js๋Š” src/libs ๋ฐ‘์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ฐ ์ปดํฌ๋„ŒํŠธ๋“ค์˜ ๋ Œ๋”๋ง์„ ๋•๋Š”๋‹ค. ๊ฐ ์ปดํฌ๋„ŒํŠธ๋“ค์€ class SomeComponent extends LitRender(HTMLElement)์˜ ํ˜•์‹์œผ๋กœ litRender๋ฅผ ๋ฏน์Šค์ธ ํ™•์žฅํ•˜์—ฌ ์‚ฌ์šฉํ•œ๋‹ค. ํ•œ๋ฒˆ์— ์—ฌ๋Ÿฌ๋ฒˆ ๋‚ด์šฉ์ด ์—…๋ฐ์ดํŠธ๋˜๋Š” ๊ฒฝ์šฐ ๋งค๋ฒˆ ๋ Œ๋”๋ง ํ•˜์ง€ ์•Š๊ณ , ๋ชจ์•˜๋‹ค๊ฐ€ ํ•œ๋ฒˆ์— ๋ Œ๋”๋ง ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์„ฑ๋Šฅ ํ–ฅ์ƒ์— ๋„์›€ ์ฃผ๊ธฐ ์œ„ํ•œ ์ฝ”๋“œ์ด๋‹ค. ์ด๊ฒƒ์„ ํ™•์žฅํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ this.invalidate๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์ปดํฌ๋„ŒํŠธ์— ์ •์˜๋œ render ํ•จ์ˆ˜์˜ ํ˜ธ์ถœ์ด ์˜ˆ์•ฝ๋œ๋‹ค.

import { render } from "../../node_modules/lit-html/lib/lit-extended";

export default base =>
  class extends base {
    render() {}

    async invalidate(instant) {
      if (!this.needsRender) {
        if (!instant) {
          this.needsRender = true;
          await 0;
          this.needsRender = false;
        }
        render(this.render(), this.shadowRoot);
      }
    }
  };

์ปดํฌ๋„ŒํŠธ๋“ค: todoList.js, todoItem.js, todoInput.js, todoToolbar.js

Todo ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ตฌ์„ฑํ•˜๋Š” ๊ฐœ๋ณ„ ์ปดํฌ๋„ŒํŠธ๋“ค์„ ์ •์˜ํ•œ๋‹ค. todoApp.js ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด <todo-list>, <todo-toolbar>๋“ฑ์˜ ํ˜•ํƒœ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

connectedCallback / disconnectedCallback

import { toggle, remove, replace } from '../libs/actions';
...
class TodoItem extends LitRender(HTMLElement) {
...
  connectedCallback() {
    const root = this.shadowRoot;
...
    root.addEventListener('click', handlers.onClick);
...
  }

  disconnectedCallback() {
    const root = this.shadowRoot;
...
    root.removeEventListener('click', this._handlers.onClick);
...
  }
...
  _onClick(event) {
    const id = this.todo.id;
    const classList = event.path[0].classList;

    if (classList.contains('toggle')) {
      toggle(id);
    } else if (classList.contains('destroy')) {
      remove(id);
    }
  }
...

TodoApp์—์„œ๋Š” ์‚ฌ์šฉ๋˜์ง€ ์•Š์•˜๋˜ connectedCallback, disconnectedCallback์ด ๋ณด์ธ๋‹ค. ์ด ํ•จ์ˆ˜๋“ค์€ ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ ์ฝœ๋ฐฑ์œผ๋กœ ์ด ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ DOM์— attach, detach๋  ๋•Œ ํ˜ธ์ถœ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ์ด ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋“ค์ด DOM ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ํ• ๋‹น/ํ•ด์ œํ•˜๋Š”๋ฐ ์ตœ์ ์˜ ์žฅ์†Œ์ด๋‹ค. ๋งŒ์•ฝ ์ ์ ˆํžˆ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ํ•ด์ œํ•ด์ฃผ์ง€ ์•Š์œผ๋ฉด ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๊ฒฝํ—˜ํ•  ์ˆ˜ ์žˆ์œผ๋‹ˆ ์žŠ์ง€ ๋ง์ž.

onClick ํ•ธ๋“ค๋Ÿฌ๋Š” ์กฐ๊ฑด์— ๋”ฐ๋ผ toggle, remove Redux ์•ก์…˜์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์žˆ๋‹ค.

render / html

  render() {
    const todo = this.todo;
    const classCompleted = todo.completed ? ' completed' : '';
    const inputToggle = todo.completed
      ? html`<input class="toggle" type="checkbox" checked>`
      : html`<input class="toggle" type="checkbox">`;

    const classEditing = this._editing ? ' editing' : '';

    return html`
      ${style}
      <div data-id$="${todo.id}" class$="${'item' +
      classCompleted +
      classEditing}">
        <div class="view">
          ${inputToggle}
          <label>${todo.title}</label>
          <button class="destroy"></button>
        </div>
        <input class="edit" type="text" />
      </div>
    `;
  }
}
...
const style = html`
  <style>
    host: {
      display: block;
    }
    .item {
      position: relative;
      font-size: 24px;
      border-bottom: 1px solid #ededed;
    }
...
  </style>
`;
...

renderํ•จ์ˆ˜๊ฐ€ ์กฐ๊ธˆ ๋ณต์žก์กŒ๋‹ค. lit-HTML html ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด ํ•จ์ˆ˜๋Š” ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด์„ ์ธ์ž๋กœ ๋ฐ›์•„ HTMLTemplateElement๋ฅผ ํฌํ•จํ•˜๋Š” ์˜ค๋ธŒ์ ํŠธ TemplateResult๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ${something}์—๋Š” ๋ณ€์ˆ˜๋‚˜ ์ƒ์ˆ˜ํ‘œํ˜„ ์ด์™ธ์—๋„ TemplateResult, Promise, Array, Iterables ๋“ฑ์„ ์ง€์›ํ•œ๋‹ค. ์—ฌ๋Ÿฌ ๋ฐฉ์‹์„ ์กฐํ•ฉํ•˜์—ฌ ์ž์œ ๋กญ๊ฒŒ ํ…œํ”Œ๋ฆฟ์„ ๊ตฌ์„ฑํ•˜๋ฉด ๋œ๋‹ค. ์œ„์˜ ์ฝ”๋“œ์—์„œ๋„ ํ…œํ”Œ๋ฆฟ์ด ๋ณต์žกํ•ด ๋ณด์ด์ง€ ์•Š๊ฒŒ <style>, <input>๋“ฑ์„ ๋ถ„๋ฆฌํ•œ ํ›„, html ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด์„ ์ค‘๋ณตํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค.

๊ฐ์ฒด์˜ ์ „๋‹ฌ

  // todoList.js
  render() {
...
    const todoItems = todoList
      .filter(todo => {
        return (
          route === '' ||
          (route === 'completed' && todo.completed) ||
          (route === 'active' && !todo.completed)
        );
      })
      .map(todo => html`<todo-item todo=${todo}></todo-item>`);

    return html`
      ${style}
      <div class="todo">
        ${btnToggleAll}
        <div class="todo-list">
          ${todoItems}
        </div>
      </div>
    `;
  }
...
  // todoItem.js
  set todo(todo) {
    this._todo = todo;
    this.invalidate();
  }

lit-HTML์˜ ํ™•์žฅ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด html`<todo-item todo=${todo}></todo-item>`์ฒ˜๋Ÿผ ๋‹ค๋ฅธ ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ์— object๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค. Attribute๋Š” html`<todo-item name$=${someText}></todo-item>`๋กœ ์ด๋ฆ„ ๋’ค์— $๋ฅผ ๋ถ™์ด๋ฉด ๋œ๋‹ค. ๋ฐ›์•„์˜ค๋Š” object๋ฅผ ์‚ฌ์šฉํ•˜๋Š” todoItems.js์—์„œ๋Š” ํ‰๋ฒ”ํ•˜๊ฒŒ this.todo๋กœ ์ ‘๊ทผํ•˜๋ฉด ๋œ๋‹ค. ์ด ์ฝ”๋“œ์—์„œ๋Š” getter๋ฅผ ๋งŒ๋“ค์–ด์„œ ๊ฐ’์ด ํ• ๋‹น๋˜๋ฉด ์ž๋™์œผ๋กœ invalidate๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ปค์Šคํ…€ ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ ์—…๋ฐ์ดํŠธ ๋˜๋„๋ก ํ–ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ํ•œ๊ฐ€์ง€ ์งš๊ณ  ๋„˜์–ด๊ฐˆ ์ ์€ lit-HTML์˜ ์ž‘๋™๋ฐฉ์‹์ด ์ถฉ๋ถ„ํžˆ ์„ฑ๋Šฅ์„ ๊ณ ๋ คํ•ด ๋งŒ๋“ค์–ด ์กŒ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์ด๊ฒƒ์€ ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด๋กœ ์ „๋‹ฌ๋œ ๊ฐ’์„ ๊ธฐ์–ตํ•˜๊ณ  ์žˆ๋‹ค๊ฐ€, ์ „๋‹ฌ๋œ ๊ฐ’์ด ๋‹ค๋ฅผ ๊ฒฝ์šฐ์—๋งŒ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์—…๋ฐ์ดํŠธํ•œ๋‹ค. ๋ฐ๋ชจ์—์„œ ์ด ์ฝ”๋“œ๊ฐ€ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•ด๋ณด๋ฉด ์ถ”๊ฐ€/์‚ญ์ œ/๋ณ€๊ฒฝ๋œ ์•„์ดํ…œ๋งŒ ์—…๋ฐ์ดํŠธ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

store.js, actions.js

Redux-Zero์˜ store, action๋“ค์„ ์ •์˜ํ•œ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„๊ป˜ ์ด๋ฏธ ์ต์ˆ™ํ•  Redux๊ฐ€ ๊ฐ„๋žตํ•ด์ง„ ๋ชจ์–‘์ด๋ฉฐ, ์›น ์ปดํฌ๋„ŒํŠธ ์„ค๋ช…๊ธ€์˜ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋ฏ€๋กœ ์ด ๋ถ€๋ถ„์˜ ์„ค๋ช…์€ ์ƒ๋žตํ•˜๊ฒ ๋‹ค.

import createStore from "redux-zero";

const initialState = { route: "", todoList: [] };
const store = createStore(initialState);

export default store;
import store from './store';

function actionCreator(action) {
  return function() {
    let state = store.getState();
    state = action(state, ...arguments);
    store.setState(state);
  };
}
...
export const remove = actionCreator((state, id) => {
  state.todoList = state.todoList.filter(todo => todo.id !== id);

  return state;
});
...

#UseThePlatform ํ”„๋ ˆ์ž„์›Œํฌ ์—†์ด๋„ React์ฒ˜๋Ÿผ ์ฝ”๋”ฉ

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

  1. ํ”„๋ ˆ์ž„์›Œํฌ ๋‹ค์šด๋กœ๋“œ ์‹œ๊ฐ„์ด ์—†์œผ๋ฏ€๋กœ ํŽ˜์ด์ง€๊ฐ€ ๊ฐ€๋ณ๊ณ  ๋น ๋ฅด๋‹ค.
  2. ์ง๊ด€์ ์ธ DOM Integration. document.querySelector('todo-app').add('hello') ๊ฐ™์€ ์ง๊ด€์ ์ธ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ด์ฃผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ์—†๋‹ค.
  3. ์™„๋ฒฝํžˆ ํ‘œ์ค€ ECMAScript์ฝ”๋“œ์ด๋ฏ€๋กœ, ํ”„๋ ˆ์ž„์›Œํฌ ์œ ํ–‰์„ ํƒ€์ง€ ์•Š๊ณ  ์˜ค๋ž˜ ์œ ์ง€๋ณด์ˆ˜ ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ ๋‹ค. ๋ถˆ๊ณผ 2, 3๋…„ ์ „ ๊ฐ€์žฅ ์ธ๊ธฐ ์žˆ๋˜ Angular์˜ ์œ„์ƒ์„ ์ƒ๊ฐํ•ด๋ณด์ž.
  4. ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๋ฌด๊ด€ํ•˜๋ฏ€๋กœ ์˜คํžˆ๋ ค ์–ด๋–ค ํ”„๋ ˆ์ž„์›Œํฌ์™€๋„ ๊ฐ™์ด ์“ฐ์ผ ์ˆ˜ ์žˆ๋‹ค.
  5. ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ์„ ํƒ์ด์ง€๋งŒ ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋กœ์„œ DOM๊ณผ ECMAScript๋Š” ๊ธฐ๋ณธ์ด๋‹ค. ์ง„์ž… ์žฅ๋ฒฝ์ด ๋‚ฎ๊ณ , ์–ด๋–ค ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ์ด๋‹ค.

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

๋งˆ์น˜๋ฉฐ

Chrome Dev Summit 2017 - lit-HTML ์˜์ƒ์„ ๋ณธ ํ›„, ์—ฌ๊ธฐ์„œ ์†Œ๊ฐœ๋œ ๋Œ€๋กœ lit-HTML ๊ทธ๋ฆฌ๊ณ , ์›น ์ปดํฌ๋„ŒํŠธ ์ž์ฒด๊ฐ€ ์–ผ๋งˆ๋‚˜ ๋น ๋ฅธ ์„ฑ๋Šฅ์„ ๋ณด์—ฌ์ค„ ์ง€ ๊ถ๊ธˆํ–ˆ๋‹ค. ๊ธฐ์™•์ด๋ฉด ์ด์ „ ๊ธ€์—์„œ ์•ฝ์†ํ•œ ๋Œ€๋กœ ์˜ˆ์ œ๋„ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋‹ˆ todo preact benchmark์— ์ถ”๊ฐ€ํ•˜์—ฌ ์„ฑ๋Šฅ ๋น„๊ต๋ฅผ ํ•ด๋ณด๋ ค ํ–ˆ๋‹ค. ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๋Š” ๋„ˆ๋ฌด ๋น ๋ฅด๋‹ค. ๋‹ค๋งŒ ๋„ˆ๋ฌด ๋นจ๋ผ ์Šค์Šค๋กœ ์˜๋ฌธ์ด ์ƒ๊ฒจ ์กฐ๊ธˆ ๋” ์•Œ์•„๋ณธ ๊ฒฐ๊ณผ, Vue.js TodoMVC Benchmark๋ฅผ ์ฐพ์•˜๋‹ค. ํ•„์ž ์—ญ์‹œ ์ด ์˜๊ฒฌ์ฒ˜๋Ÿผ ํ”„๋ ˆ์ž„์›Œํฌ ๋ฒค์น˜๋งˆํฌ๊ฐ€ ์˜๋ฏธ ์—†๋‹ค๋Š” ๊ฒƒ์— ๋™์˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฒค์น˜๋งˆํฌ ๊ฒฐ๊ณผ๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“ค์ง€๋Š” ์•Š์•˜๊ณ , ์ด ํ”„๋กœ์ ํŠธ๋งŒ TodoMVC์— ์ œ์ถœํ•  ์˜ˆ์ •์ด๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ํ‘œ์ค€ ์Šคํฌ๋ฆฝํŠธ๋งŒ์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ์ด ๋ฐฉ์‹์ด ์ถฉ๋ถ„ํžˆ ๋น ๋ฅผ์ˆ˜ ๋ฐ–์— ์—†๋‹ค๋Š” ๊ฒƒ์„ ๋…์ž ์—ฌ๋Ÿฌ๋ถ„๋“ค๋„ ์ž˜ ์•„์‹œ๋ฆฌ๋ผ ์ƒ๊ฐํ•œ๋‹ค. ๊ฐœ์ธ์ ์œผ๋กœ #UseThePlatform์˜ ๊ฐ€์น˜, ์›น ์ปดํฌ๋„ŒํŠธ์˜ ๋น„์ „์— ๊ฑฐ๋Š” ๊ธฐ๋Œ€๊ฐ€ ํฌ๊ธฐ์— ์ด ๊ธ€์„ ์ฝ๋Š” ๋…์ž๊ฐ€ ํ•œ๋ช…์ด๋ผ๋„ ๋” ๋™์˜ํ•ด์„œ ์ฃผ๋ณ€์— ์›น ์ปดํฌ๋„ŒํŠธ ๊ฐœ๋ฐœ์„ ํ•˜๋Š” ํ™˜๊ฒฝ์„ ๋ณผ ์ˆ˜ ์žˆ์œผ๋ฉด ์ข‹๊ฒ ๋‹ค๋Š” ๋ฐ”๋žŒ์ด๋‹ค. ์ด ๊ธ€์„ ๋์œผ๋กœ ์›น ์ปดํฌ๋„ŒํŠธ ์—ฐ์žฌ๋ฅผ ๋งˆ์นœ๋‹ค.

References

์ตœ๊ทœ์šฐ2017.12.15
Back to list