,

1.01365 = 37.8

JavaScriptReact

Vanila JS์—์„œ React๋กœ ๋ณ€ํ•˜๋Š” ๊ณผ์ •

August 24, 2022

๋…ธ๋งˆ๋“œ์ฝ”๋”์˜ ReactJS๋กœ ์˜ํ™” ์›น ์„œ๋น„์Šค ๋งŒ๋“ค๊ธฐ ๊ฐ•์ขŒ์˜ ์ฒ˜์Œ ~ 3.4์ ˆ ๋‚ด์šฉ์„ ๋ชจํ‹ฐ๋ธŒ๋กœ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.

JavaScript๋กœ ํ† ์ดํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋‚˜๋งŒ ํ•ด๋ด๋„ document.querySelector๋กœ ์š”์†Œ๋ฅผ ๊ณ ๋ฅด๊ณ  innerText ํ”„๋กœํผํ‹ฐ๋กœ HTML์„ ์ˆ˜์ •ํ•˜๋Š”, ์•„์ฃผ ๋ถˆํŽธํ•œ ์ผ์ด ๋น„์ผ๋น„์žฌํ•˜๊ฒŒ ์ผ์–ด๋‚œ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์ฒด๊ฐํ•œ๋‹ค.

์ด๋ฒˆ ๊ธ€์—์„œ getElementById, createElement, JSX, React Hooks๋ฅผ ์ฐจ๊ทผ์ฐจ๊ทผ ๋„์ž…ํ•ด๋ณด๋ฉด์„œ Vanila JS์—์„œ React๋กœ ์ฝ”๋“œ๊ฐ€ ๋ณ€ํ•˜๋Š” ๊ณผ์ •์„ ์‚ดํŽด๋ณด์ž.

v1.0 Vanila JS

<!DOCTYPE html>
<html>
  <body>
    <h3>Total clicks: 0</h3>
    <button id="btn">Click me</button>

    <script>
      <!-- ํ•˜๋‹จ ์ฐธ๊ณ  -->
    </script>
  </body>
</html>
let counter = 0;
const h3 = document.querySelector('h3');
const button = document.getElementById('btn');
function handleClick() {
  counter += 1;
  h3.innerText = `Total clicks: ${counter}`;
}
button.addEventListener('click', handleClick);

๊ฒฐ๊ณผ๋ฌผ ์˜ˆ์‹œ

์ด๋ฏธ ์ง์ž‘ํ•˜์…จ๋‹ค์‹œํ”ผ, ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅผ ๋•Œ๋งˆ๋‹ค 1์”ฉ ์˜ฌ๋ผ๊ฐ€๋Š” ์ฝ”๋“œ๋‹ค.

v2.0 createElement

<!DOCTYPE html>
<html>
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
    <script>
      <!-- ํ•˜๋‹จ์— ์ฐธ๊ณ  -->
    </script>
  </body>
</html>

react์™€ react-dom ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ถ”๊ฐ€ํ–ˆ๋‹ค.

let counter = 0;
const root = document.getElementById('root');
const h3 = React.createElement('h3', null, 'Total clicks: 0');

function handleClick() {
  counter += 1;
  const h3 = React.createElement('h3', null, `Total clicks: ${counter}`);
  const container = React.createElement('div', null, h3, btn);
  ReactDOM.render(container, root);
}

const btn = React.createElement(
  'button',
  {
    id: 'btn',
    onClick: handleClick,
  },
  'Click me'
);

const container = React.createElement('div', null, h3, btn);
ReactDOM.render(container, root);

์ด์ „ ๋ฒ„์ „๊ณผ์˜ ์ฐจ์ด์ ์€?

HTML์—๋Š” div#root ๋ฐ–์— ์—†๋‹ค. CSR์˜ ๊ธฐ๋ฏธ๊ฐ€ ์Šฌ์Šฌ ๋ณด์ธ๋‹ค.

  1. React.createElement()๋กœ ์š”์†Œ๋ฅผ ๋งŒ๋“ ๋‹ค.
  2. ReactDOM.render()๋กœ ๋ Œ๋”๋งํ•œ๋‹ค.
  • ์ฐธ๊ณ  : createElement(type, props, ...children) ํ”„๋กœํ† ํƒ€์ž… ๋งํฌ

v3.0 JSX

<!DOCTYPE html>
<html>
  <body>
    <div id="root"></div>

    <script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      <!-- ํ•˜๋‹จ ์ฐธ๊ณ  -->
    </script>
  </body>
</html>

babel ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ถ”๊ฐ€ํ–ˆ๋‹ค.

let counter = 0;
const root = document.getElementById('root');

function handleClick() {
  counter += 1;
  const H3 = () => <h3>Total Clicks: {counter}</h3>;
  const Container = () => (
    <div>
      <H3 /> <Button />
    </div>
  );
  ReactDOM.render(<Container />, root);
}

const H3 = () => <h3>Total Clicks: 0</h3>;

const Button = () => (
  <button id="btn" onClick={handleClick}>
    Click me
  </button>
);

const Container = () => (
  <div>
    <H3 /> <Button />
  </div>
);
ReactDOM.render(<Container />, root);

์ด์ „ ๋ฒ„์ „๊ณผ์˜ ์ฐจ์ด์ ์€?

JS ๋Œ€์‹  JSX๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค.
JSX๋Š” SCSS์ฒ˜๋Ÿผ ํŠธ๋žœ์Šค์ปดํŒŒ์ผ(Source-to-Source Compile)์ด ํ•„์š”ํ•œ๋ฐ, ๊ทธ ํŠธ๋žœ์Šค์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ babel์ด๋‹ค.

โ—๏ธ ์ปดํฌ๋„ŒํŠธ๋ช…์€ ๋ฐ˜๋“œ์‹œ capitalize์—ฌ์•ผ ํ•œ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด babel์ด ์ผ๋ฐ˜ HTML ์š”์†Œ๋กœ ์ธ์‹ํ•œ๋‹ค.

'use strict';

H3 = () => /*#__PURE__*/ React.createElement('h3', null);
h3 = () => /*#__PURE__*/ React.createElement('h3', null);

const Container = () =>
  /*#__PURE__*/ React.createElement(
    'div',
    null,
    /*#__PURE__*/ React.createElement(H3, null),
    ' ',
    /*#__PURE__*/ React.createElement('h3', null)
  );

์žฌ๋ฏธ์žˆ๊ฒŒ๋„ v2.0์˜ ์ฝ”๋“œ๊ฐ€ ๋ฐ”๋กœ babel์—์„œ ํŠธ๋žœ์Šค์ปดํŒŒ์ผํ•œ ๊ฒฐ๊ณผ์™€ ๊ฐ™๋‹ค.

v3.1 ๋” JSX์Šค๋Ÿฝ๊ฒŒ!

์ด์ œ๋ถ€ํ„ฐ HTML์€ ๋˜‘๊ฐ™๋‹ค. (react, react-dom, babel, div#root)

let counter = 0;
const root = document.getElementById('root');

function render() {
  ReactDOM.render(<Container />, root);
}

function countUp() {
  counter += 1;
  render();
}

const Container = () => (
  <div>
    <h3>Total clicks: {counter}</h3>
    <button onClick={countUp}>Click me</button>
  </div>
);

render();

์ด์ „ ๋ฒ„์ „๊ณผ์˜ ์ฐจ์ด์ ์€?

  1. H3, Button ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žฌ์‚ฌ์šฉ์„ฑ์ด ์—†๋‹ค๊ณ  ํŒ๋‹จํ•˜์—ฌ ์‚ญ์ œํ–ˆ๋‹ค.
  2. ํด๋ฆญ ์‹œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด Container ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งค๋ฒˆ ์ •์˜ํ•˜์ง€ ์•Š์•„ ๋ฆฌ์†Œ์Šค๋ฅผ ์•„๋‚€๋‹ค. (์ด์ „๊นŒ์ง€ ์ƒˆ๋กœ Container๋ฅผ ์ •์˜ํ•  ์ˆ˜๋ฐ–์— ์—†์—ˆ๋˜ ์ด์œ ๋Š” ์ดˆ๊ธฐ innerText ๊ฐ’์„ ๋‹จ์ˆœํžˆ Total Clicks: 0์ด๋ผ๋Š” ์Œฉ ๋ฌธ์ž์—ด๋กœ ํ• ๋‹นํ•ด๋ฒ„๋ ธ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.)
  3. ReactDOM.render(<Container />, root); ์ด ์ค‘๋ณต๋˜๊ธฐ ๋•Œ๋ฌธ์— render๋ผ๋Š” ํ•จ์ˆ˜๋กœ ๊ฐ์ŒŒ๋‹ค.

v4.0 useState

function App() {
  let [counter, setCounter] = React.useState(0);
  const onClick = () => {
    setCounter(counter + 1);
  };
  return (
    <>
      <h3>Total clicks: {counter}</h3>
      <button onClick={onClick}>Click me</button>
    </>
  );
}

const root = document.getElementById('root');
ReactDOM.render(<App />, root);

์ด์ „ ๋ฒ„์ „๊ณผ์˜ ์ฐจ์ด์ ์€?

  1. ๊ธฐ์กด ์‚ฌ์šฉํ•˜๋˜ counter์™€ counterUp ๋Œ€์‹  useState๋ฅผ ์ด์šฉํ•˜์—ฌ ๊น”๋”ํ•ด์กŒ๋‹ค.
  2. Container๋ผ๋Š” ์ด๋ฆ„์„ App์œผ๋กœ rename
  3. useState๋ฅผ ์ด์šฉํ•˜๋ฉด ๋งค๋ฒˆ ๋ Œ๋”๋งํ•ด์ฃผ์ง€ ์•Š์•„๋„ ์ƒํƒœ ๋ณ€ํ™” ์‹œ ์ž๋™์œผ๋กœ ๋ Œ๋”๋งํ•œ๋‹ค. ๋”ฐ๋ผ์„œ render ํ•จ์ˆ˜๋Š” ํ•„์š”์—†์–ด์กŒ๋‹ค. ๋งˆ์ง€๋ง‰์— ReactDOM.render ํ•œ๋ฒˆ๋งŒ ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.
  4. ์˜๋ฏธ์—†๋Š” div ๋Œ€์‹  Fragment ์‚ฌ์šฉ

v4.1 setCounter ํ•จ์ˆ˜ํ˜•์œผ๋กœ ๋ณ€ํ™˜

function App() {
  let [counter, setCounter] = React.useState(0);
  const onClick = () => {
    /* setCounter(counter+1); */
    setCounter((current) => current + 1);
  };
  return (
    <>
      <h3>Total clicks: {counter}</h3>
      <button onClick={onClick}>Click me</button>
    </>
  );
}

setCounter๋ฅผ ์‹คํ–‰ํ•˜์ž๋งˆ์ž counter๋กœ ๋ฐ”๋กœ ๋ฐ˜์˜๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ render ํ˜ธ์ถœ ์‹œ์ ๊นŒ์ง€ ์ง€์—ฐ๋˜๊ธฐ ๋•Œ๋ฌธ์— unsafeํ•˜๋‹ค.

const onClick = () => {
  // counter=100
  setCounter(counter + 1);
  console.log(counter); // 100
  setCounter(counter + 1);
  console.log(counter); // 100
}; // render() ์ดํ›„์—๋Š” counter=101

์ด ์ƒํ™ฉ์„ ๋‘๊ณ  useState๊ฐ€ ๋น„๋™๊ธฐ์ ์ด๋ผ ๋ถ€๋ฅธ๋‹ค.

const onClick = () => {
  // counter=100
  setCounter((counter) => counter + 1); // re-render
  console.log(counter); // 101
  setCounter((counter) => counter + 1); // re-render
  console.log(counter); // 102
}; // render() ์ดํ›„์—๋„ counter=102 ์œ ์ง€.

ํ•จ์ˆ˜์  ๊ฐฑ์‹ ์„ ์‚ฌ์šฉํ•˜์ž.

ยฉ 2023. yongjun park