blog.holy.kiwi

๐ŸŠ React์˜ JSX ํŒŒ๊ณ ๋“ค๊ธฐ

January 20, 2019 โ€ข โ˜•๏ธ 7 min read

๐Ÿ˜ป JSX๊ฐ€ ๋Œ€์ฒด ๋ญ์ง€

const element = <div>Hello world</div>;

React๋ฅผ ์ฒ˜์Œ ์ ‘ํ•  ๋•Œ, ๋ณดํ†ต์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€๋Š” ๋‹ค๋ฅธ ๋ฌธ๋ฒ•์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์œ„ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ๋ณ€์ˆ˜์— <div>Hello world</div>๋ฅผ ํ• ๋‹นํ•˜๊ณ  ์žˆ๋Š”๋ฐ ์ด๋Š” ๋ฌธ์ž์—ด๋„ ์•„๋‹ˆ๊ณ , HTML๋„ ์•„๋‹ˆ๋‹ค. ๋ฐ”๋กœ JSX๋‹ค.

JSX๋Š” JS + XML์˜ ์ค„์ž„๋ง๋กœ์จ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์•ˆ์—์„œ <div>hello world</div> ์™€ ๊ฐ™์ด HTML ๊ฐ™์ด ์ƒ๊ธด XML ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ํ™•์žฅ๋œ ํŒŒ์ผํ˜•์‹์ด๋‹ค. ๋‚˜๋Š” React์˜ JSX๋ฅผ ์ฒ˜์Œ ๋ณด๊ณ  ์ •๋ง ์ฐธ์‹ ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค. UI์— ํ•ด๋‹นํ•˜๋Š” HTML๊ณผ ๋กœ์ง์— ํ•ด๋‹นํ•˜๋Š” Javascript๋ฅผ ๋‚˜๋ˆ„์ง€ ์•Š๊ณ  JSX๋ผ๋Š” ํŒŒ์ผํ˜•์‹์„ ์ฐฝ์กฐํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„๋กœ UI์™€ ๋กœ์ง์„ ํ•œ ํŒŒ์ผ์— ํ•ฉ์ณค๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋ฌผ๋ก  React์—์„œ JSX๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, Javascript ์ฝ”๋“œ ์•ˆ์—์„œ UI๊ฐ€ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์„ ์‹œ๊ฐ์ ์œผ๋กœ ๋ฐ”๋กœ ๋ณผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ์‚ฌ์šฉํ•œ๋‹ค.

๋‹ค์Œ์€ React์—์„œ JSX์˜ ์˜ˆ์ œ์ด๋‹ค.

class AwesomeComponent extends React.Component {
    render() {
        return (
            <MyButton color="blue" shadowSize={2}>
                Click Me
            </MyButton>
        );
    }
}

์œ„ JSX ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ปดํŒŒ์ผ๋œ๋‹ค.

class AwesomeComponent extends React.Component {
    render() {
        return React.createElement(
            MyButton,
            { color: "blue", shadowSize: 2 },
            "Click Me"
        );
    }
}

์˜จ๋ผ์ธ ๋ฐ”๋ฒจ ์ปดํŒŒ์ผ๋Ÿฌ์—์„œ ์œ„ ๋‚ด์šฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๐ŸŽ JSX๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ฃผ์˜ํ•  ์ ๋“ค

JSX๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด React๊ฐ€ Scope ์•ˆ์— ์žˆ์–ด์•ผ ํ•œ๋‹ค.

XML ํ˜•์‹์˜ ์ฝ”๋“œ๋Š” React.createElement ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ฝ”๋“œ๋กœ ์ปดํŒŒ์ผ๋˜๊ธฐ ๋•Œ๋ฌธ์— React๋ฅผ ์ง์ ‘ ์“ฐ์ง€ ์•Š๋”๋ผ๋„ React๋ฅผ ์ž„ํฌํŠธํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

import React from 'react';
import AwesomeButton from './AwesomeButton';

function BlueButton() {
  // return React.createElement(AwesomeButton, {color: 'blue'}, null);
  return <AwesomeButton color="blue" />;
}

์ตœ์ƒ์œ„ ๋ถ€๋ชจ DOM์€ ํ•˜๋‚˜์—ฌ์•ผ ํ•œ๋‹ค.

React.createElement ํ•จ์ˆ˜์˜ ์ธ์ž๋Š” React.createElement(component, props, ...children)์™€ ๊ฐ™๋‹ค. ์ฒซ๋ฒˆ์งธ ์ธ์ž์— component๊ฐ€ ์˜ค๊ณ , ๋‘๋ฒˆ์งธ์—๋Š” Object ํ˜•์‹์˜ props๊ฐ€ ์˜ค๊ณ  ์„ธ๋ฒˆ์งธ ์ธ์ž๋Š” ๊ฐ€๋ณ€์ธ์ˆ˜๋กœ children๋“ค์ด ์˜จ๋‹ค. ์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•˜๊ฒŒ ๋ด์•ผํ•  ์ ์€ ์ตœ์ƒ์œ„ ๋ถ€๋ชจ DOM์ด๋‹ค. ์ตœ์ƒ์œ„์—์„œ ์ œ์ผ ์ฒ˜์Œ ํ˜ธ์ถœ๋˜๋Š” React.createElement๋Š” ๋‹จ ํ•˜๋‚˜๋งŒ์ด ํ˜ธ์ถœ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ตœ์ƒ์œ„ ๋ถ€๋ชจ DOM์€ ์˜ค๋กœ์ง€ ํ•˜๋‚˜์—ฌ์•ผ ํ•œ๋‹ค.

// ๋‚˜์œ ์˜ˆ: ์ตœ์ƒ์œ„ ๋ถ€๋ชจ DOM์ด ๋‘ ๊ฐœ์ด๋‹ค. ์ปดํŒŒ์ผํ•  ์ˆ˜ ์—†๋‹ค.
function BadComponent() {
    return (
        <div>hello</div>
        <div>world</div>
    );
}

// ์ข‹์€ ์˜ˆ: ์ตœ์ƒ์œ„ ๋ถ€๋ชจ DOM์ด ํ•œ ๊ฐœ์ด๋‹ค.
function GoodComponent() {
    return (
        <div>
            <div>hello</div>
            <div>world</div>
        </div>
    );
}

// ์œ„ GoodComponent.jsx๋ฅผ ์ปดํŒŒ์ผํ•œ GoodComponent.js
function GoodComponent() {
    return React.createElement(
        "div",
        null,
        React.createElement(
            "div",
            null,
            "hello"
        ),
        React.createElement(
            "div",
            null,
            "world"
        )
    );
}

์ง์ ‘ ์ •์˜ํ•œ ์ปดํฌ๋„ŒํŠธ๋Š” ๋Œ€๋ฌธ์ž๋กœ ์‹œ์ž‘ํ•ด์•ผ ํ•œ๋‹ค.

React๋Š” ์†Œ๋ฌธ์ž๋กœ ์‹œ์ž‘ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ HTML tag๋กœ ์ธ์ง€ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋Œ€๋ฌธ์ž๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋„ค์ด๋ฐ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ์ด๋Š” ๋ณดํ†ต ํด๋ž˜์Šค๋Š” ๋Œ€๋ฌธ์ž๋กœ ์‹œ์ž‘ํ•˜๋Š”๊ฒŒ ๊ด€์Šต์ด๋ฏ€๋กœ ์‹ ๊ฒฝ์“ฐ์ง€๋Š” ์•Š์•„๋„ ๋ ๋“ฏ ์‹ถ๋‹ค.

props๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์Šคํ”„๋ ˆ๋“œ ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

props๋ฅผ Object type์œผ๋กœ ์ •์˜ํ•˜์—ฌ <Greeting {...props} />์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์˜ props๋ฅผ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ทธ๋Œ€๋กœ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—๊ฒŒ ๋ฌผ๋ ค์ค„ ๋•Œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

function App1() {
  return <Greeting firstName="Ben" lastName="Hector" />;
}

function App2() {
  const props = {firstName: 'Ben', lastName: 'Hector'};
  return <Greeting {...props} />;
}

Children์— Javascript์˜ ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฐฐ์—ด์— ์žˆ๋Š” ์•„์ดํ…œ์„ ์ฐจ๋ก€๋Œ€๋กœ <li></li>๋กœ ๋ณด์—ฌ์ฃผ๊ณ  ์‹ถ์„ ๋•Œ Javascript์˜ map์„ ์‚ฌ์šฉํ•˜๋ฉด ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

function TodoList() {
  const todoItems = ['You don\'t know JS ์ฑ… ์ฝ๊ธฐ', '๊นƒํ—™ ์ปค๋ฐ‹ํ•˜๊ธฐ', '๋ธ”๋กœ๊ทธ ํฌ์ŠคํŒ…ํ•˜๊ธฐ'];
  return (
    <ul>
      {todoItems.map((item, index) => <li key={index}>{item}</li>)}
    </ul>
  );
}

boolean, undefined, null์€ ๋ Œ๋”๋˜์ง€ ์•Š๋Š”๋‹ค.

true, false undefined, null์€ ๋ Œ๋”๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ด ํŠน์ง•์„ ์‚ฌ์šฉํ•˜์—ฌ ์•„๋ž˜์™€ ๊ฐ™์ด ์“ธ ์ˆ˜ ์žˆ๋‹ค.

// ์ข‹์€ ์˜ˆ
<div>
  {showHeader && <Header />}
  <Content />
</div>

/* NOPE. ํ•˜์ง€๋งŒ ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด props.messages.length์ด 0์ผ ๋•Œ
falsyํ•œ ๊ฐ’์œผ๋กœ ๋’ค์˜ ๊ฐ’๋“ค์ด ๋‚˜์˜ค์ง€ ์•Š๊ฒ ์ง€๋งŒ 0์ด ๋ Œ๋”๋˜๊ธฐ ๋•Œ๋ฌธ์— ์˜ณ์€ ๋ฐฉ๋ฒ•์€ ์•„๋‹ˆ๋‹ค. */
<div>
  {props.messages.length &&
    <MessageList messages={props.messages} />
  }
</div>

// OK. ์ด์™€ ๊ฐ™์ด ๋ฐ”๊ฟ”์ฃผ์–ด์•ผ ํ•œ๋‹ค.
<div>
  {props.messages.length > 0 &&
    <MessageList messages={props.messages} />
  }
</div>

React ์ฝ”๋“œ ์‚ดํŽด๋ณด๊ธฐ

React 16.4.0 ๋ฒ„์ „์˜ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ๋‹ค. ReactElement.js ํŒŒ์ผ์—์„œ createElement๋ฅผ ์ •์˜ํ•˜๋Š” ์ฝ”๋“œ์ด๋‹ค.

export function createElement(type, config, children) {

  // ... ์ค‘๋žต

  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}

React.createElement ํ•จ์ˆ˜๋Š” ReactElement๋ผ๋Š” ์ž๋ฃŒํ˜•์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    // This tag allows us to uniquely identify this as a React Element
    $$typeof: REACT_ELEMENT_TYPE,

    // Built-in properties that belong on the element
    type: type,
    key: key,
    ref: ref,
    props: props,

    // Record the component responsible for creating this element.
    _owner: owner,
  };

  // ... ์ค‘๋žต

  return element;
};

์œ„ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ReactElement ์ž๋ฃŒํ˜•์€ $$typeof, type, key, ref, props, _owner๋ฅผ ๊ฐ–๋Š” Object์ธ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

class AwesomeComponent extends React.Component {
    render() {
        return React.createElement(
            MyButton,
            { color: "blue", shadowSize: 2 },
            "Click Me"
        );
    }
}

๋”ฐ๋ผ์„œ ์œ„์—์„œ JSX๊ฐ€ ์ปดํŒŒ์ผ๋œ ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๋‹ค์‹œ ReactElement๋กœ ๋ฐ˜ํ™˜๋  ๊ฒƒ์ด๋‹ค.

class AwesomeComponent extends React.Component {
    render() {
        return {
            type: MyButton,
            props: {
                color: 'blue',
                shadowSize: 2,
                children: 'Click Me'
            }
        }
    }
}

๐Ÿ‘ฑ๐Ÿป JSX๋Š” ๋ฏธ์ณค๋‹ค.

React์—์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฐœ๋…์ธ Component๋Š” UI๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๊ฐœ๋ณ„์ ์ธ ๋ทฐ ๋‹จ์œ„์ด๋‹ค. React๋กœ ๊ฐœ๋ฐœํ•œ๋‹ค๋Š” ๊ฒƒ์€ ๋ธ”๋ก์„ ์กฐํ•ฉํ•ด ์ง‘์„ ์ง“๋Š” ๊ฒƒ๊ณผ ๊ฐ™๋‹ค. React ์•ฑ์€ Component๋“ค์˜ ๊ฒฐํ•ฉ์œผ๋กœ ๋งŒ๋“ค์–ด์ง€๋Š” ๊ฒƒ์ด๋‹ค. ์—ฌ๊ธฐ์„œ ์ฃผ์˜ ๊นŠ๊ฒŒ ๋ด์•ผํ•  ๋ถ€๋ถ„์€ Component๊ฐ€ ์žฌ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ์ ์ด๋‹ค. ์žฌ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•œ React์˜ Component๋Š” ์ƒ์‚ฐ์„ฑ์„ ๊ทน๋Œ€ํ™”์‹œ์ผœ์ฃผ์—ˆ๋‹ค. Component๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด React๋Š” JSX๋ผ๋Š” ํŠน์ดํ•œ Syntax๋ฅผ ๋งŒ๋“ค์–ด๋ƒˆ๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋Š” ํƒ์›”ํ–ˆ๋‹ค. Javascript ์ฝ”๋“œ ์•ˆ์— ํƒœ๊ทธ๋ฅผ ๋„ฃ์–ด ์‚ฌ์šฉํ•œ๋‹ค๋Š” ์ƒ๊ฐ์€ ์ •๋ง๋กœ ๋ฐœ์ƒ์˜ ์ „ํ™˜์ด์—ˆ๋‹ค. ์ •๋ง ๋ฏธ์นœ ๊ฒƒ ๊ฐ™๋‹ค.

์ถ”๊ฐ€์ ์œผ๋กœ React์˜ ๋˜ ํ•˜๋‚˜์˜ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ํŠน์ง•์ธ Virtual DOM์„ ๋นผ๋†“์„ ์ˆ˜ ์—†๊ฒ ๋‹ค. ์•„๋งˆ ๋‹ค์Œ ํฌ์ŠคํŠธ ์ฃผ์ œ๊ฐ€ ๋  ๊ฒƒ ๊ฐ™๋‹ค. ๐Ÿฅ

์ฐธ๊ณ 

blog.holy.kiwi

Jon Jee

Personal blog by Jon Jee.
I hope to explain with words and code.