Skip to content
This repository has been archived by the owner on Apr 13, 2020. It is now read-only.

Latest commit

ย 

History

History
1830 lines (1459 loc) ยท 59.4 KB

File metadata and controls

1830 lines (1459 loc) ยท 59.4 KB

2. ๋ฆฌ์•กํŠธ ๊ธฐ์ดˆ ํ–ฅ์ƒํ•˜๊ธฐ

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

2.1 ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€ ์ƒํƒœ ๊ด€๋ฆฌ

์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€ ์ƒํƒœ(Internal component state)๋Š” ์ปดํฌ๋„ŒํŠธ์— ์ €์žฅ๋œ ์ƒํƒœ(state)๋ฅผ ๋งํ•˜๋ฉฐ, ๋‹ค๋ฅธ ๋ง๋กœ๋Š” ๋กœ์ปฌ ์ƒํƒœ(local state)๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ ์ƒํƒœ๋Š” ํ”„๋กœํผํ‹ฐ๋กœ ๊ทธ ๊ฐ’์„ ์ˆ˜์ •, ์‚ญ์ œ, ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ES6 ํด๋ž˜์Šค ์ƒ์„ฑ์ž(class constructor)๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค๊ณ  ๋‚ด๋ถ€ ์ปดํฌ๋„ŒํŠธ ์ƒํƒœ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋ฉฐ, ์ƒ์„ฑ์ž๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ดˆ๊ธฐํ™”๋  ๋•Œ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

์ด์ œ ํด๋ž˜์Šค์— ๋Œ€ํ•ด ์•Œ์•„๋ด…์‹œ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

# leanpub-start-insert
  constructor(props) {
    super(props);
  }
# leanpub-end-insert

  ...

}

ES6 ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์— ์ƒ์„ฑ์ž๊ฐ€ ์žˆ์œผ๋ฉด, ๋ฐ˜๋“œ์‹œ super();๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. App ์ปดํฌ๋„ŒํŠธ๋Š” Component์˜ ํ•˜์œ„ ํด๋ž˜์Šค์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ App ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„ ์–ธํ•  ๋•Œ extends Component๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ™•์žฅ์‹œํ‚ต๋‹ˆ๋‹ค. ์ด ๋ถ€๋ถ„์€ ES6 ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ ๋ถ€๋ถ„์—์„œ ์ข€ ๋” ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์ƒ์„ฑ์ž์—์„œ super(props);์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ƒ์„ฑ์ž์—์„œ this.props๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์ƒ์„ฑ์ž์—์„œ this.props๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด, ์ƒ์„ฑ์ž์—์„œ this.props๋ฅผ ์ ‘๊ทผํ•  ๋•Œ, undefined๋กœ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค. ์ด ๋ถ€๋ถ„์€ props๋ฅผ ์„ค๋ช…ํ•  ๋•Œ ๋ณด๋‹ค ์ž์„ธํžˆ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์•„๋ž˜์™€ ๊ฐ™์ด list ๋ฐฐ์—ด์„ ๋งŒ๋“ค๊ณ , ์ด๋ฅผ ์ปดํฌ๋„ŒํŠธ ์ดˆ๊ธฐ state๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

const list = [
  {
    title: 'React',
    url: 'https://reactjs.org/',
    author: 'Jordan Walke',
    num_comments: 3,
    points: 4,
    objectID: 0,
  },
  ...
];

class App extends Component {

  constructor(props) {
    super(props);

# leanpub-start-insert
    this.state = {
      list: list,
    };
# leanpub-end-insert
  }

  ...

}

state๋Š” this ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด ํด๋ž˜์Šค์— ๋ฐ”์ธ๋”ฉ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ „์ฒด ์ปดํฌ๋„ŒํŠธ ๋‚ด ๋กœ์ปฌ ์ƒํƒœ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์–ด, render() ๋ฉ”์„œ๋“œ์—์„œ state๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€์— ์žˆ๋Š” list๋ฅผ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€ ์ƒํƒœ๋กœ ์ •์˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด list state๋ฅผ render() ๋ฉ”์„œ๋“œ์— ์‚ฌ์šฉํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  ...

  render() {
    return (
      <div className="App">
# leanpub-start-insert
        {this.state.list.map(item =>
# leanpub-end-insert
          <div key={item.objectID}>
            <span>
              <a href={item.url}>{item.title}</a>
            </span>
            <span>{item.author}</span>
            <span>{item.num_comments}</span>
            <span>{item.points}</span>
          </div>
        )}
      </div>
    );
  }
}

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

๊ทธ๋Ÿฌ๋‚˜ ์ ˆ๋Œ€๋กœ ์ƒํƒœ๋ฅผ ์ง์ ‘ ๋ณ€๊ฒฝํ•ด์„œ๋Š” ์•ˆ๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ setState() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ์žฅ์—์„œ setState() ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์‹ค์Šตํ•˜๊ธฐ

  • ์ปดํฌ๋„ŒํŠธ์— state๋ฅผ ๋งŒ๋“ค๊ณ  ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค
    • ์ƒ์„ฑ์ž์— ์ดˆ๊ธฐ ์ƒํƒœ ๊ฐ’์„ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.
    • render()๋กœ state๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

์ฝ์–ด๋ณด๊ธฐ

2.2 ES6 ๊ฐ์ฒด ์ดˆ๊ธฐ์ž

ES6์—์„œ ๊ฐ์ฒด ์ดˆ๊ธฐ์ž(Object Initializer)๊ฐ€ ๋„์ž…๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๊ฐ์ฒด ์ •์˜๋ฅผ ํ•ด์™”์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

{title="Code Playground",lang="javascript"}

const name = 'Robin';

const user = {
  name: name,
};

ES6์—์„œ๋Š” ๊ฐ์ฒด ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„๊ณผ ๋ณ€์ˆ˜ ์ด๋ฆ„์ด ๊ฐ™๋‹ค๋ฉด, ํ•˜๋‚˜๋กœ ์ถ•์•ฝํ•ด ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

{title="Code Playground",lang="javascript"}

const name = 'Robin';

const user = {
  name,
};

๋ฆฌ์•กํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์—ญ์‹œ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. state ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„๊ณผ ๋ณ€์ˆ˜ ์ด๋ฆ„์ด ๋™์ผํ•˜๋ฉด list ํ•˜๋‚˜๋งŒ ์ž‘์„ฑํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

{title="Code Playground",lang="javascript"}

// ES5
this.state = {
  list: list,
};

// ES6
this.state = {
  list,
};

ES6์—์„œ๋Š” ๊ฐ์ฒด ๋ฉ”์„œ๋“œ๋„ ์ถ•์•ฝํ•ฉ๋‹ˆ๋‹ค.

{title="Code Playground",lang="javascript"}

// ES5
var userService = {
  getUserName: function (user) {
    return user.firstname + ' ' + user.lastname;
  },
};

// ES6
const userService = {
  getUserName(user) {
    return user.firstname + ' ' + user.lastname;
  },
};

๋˜ํ•œ ES6์—์„œ๋Š” ๊ณ„์‚ฐ๋œ ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„(computed property name)์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

{title="Code Playground",lang="javascript"}

// ES5
var user = {
  name: 'Robin',
};

// ES6
const key = 'name';
const user = {
  [key]: 'Robin',
};

๊ทธ๋ ‡๋‹ค๋ฉด ๊ณ„์‚ฐ๋œ ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„(computed property name)์€ ์–ธ์ œ ํ•„์š”ํ• ๊นŒ์š”? ๋‹ค์Œ ์žฅ์—์„œ ํ‚ค(key)๋ฅผ ์‚ฌ์šฉํ•ด ๊ฐ์ฒด์—์„œ ๋™์ ์ธ ๋ฐฉ์‹์œผ๋กœ ๊ฐ’์„ ํ• ๋‹นํ•  ๊ฒƒ์ธ๋ฐ ์ด ๋•Œ ์‚ฌ์šฉ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ๋ฃฉ์—… ํ…Œ์ด๋ธ”(lookup tables: ์ฃผ์–ด์ง„ ์—ฐ์‚ฐ์— ๋Œ€ํ•ด ๋ฏธ๋ฆฌ ๊ณ„์‚ฐ๋œ ๊ฒฐ๊ณผ๋“ค์˜ ๋ฐฐ์—ด)์„ ์ƒ์„ฑํ•˜๊ฒŒ ๋˜๋ฉด ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์ด ๋†’์•„์ง‘๋‹ˆ๋‹ค.

์ฝ์–ด๋ณด๊ธฐ

์‹ค์Šตํ•˜๊ธฐ

  • ES6 ๊ฐ์ฒด ์ดˆ๊ธฐ์ž๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

2.3 ๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„

App ์ปดํฌ๋„ŒํŠธ์— ๋‚ด๋ถ€ ์ƒํƒœ๊ฐ€ ์žˆ์ง€๋งŒ ๊ทธ ๊ฐ’์ด ๋ณ€๋™๋˜์ง€ ์•Š์•„ ์•„์ง์€ ์ •์ ์ธ ์ปดํฌ๋„ŒํŠธ๋ผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. state๋ฅผ ์กฐ์ž‘ํ•˜์—ฌ ๋™์ ์ธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด ๋ด…์‹œ๋‹ค.

๊ฐ ๋ฐฐ์—ด์˜ ์•„์ดํ…œ๋งˆ๋‹ค dismiss ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ•˜๊ณ , ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ๊ฐ ๋Œ€์ƒ์ด ์‚ญ์ œ๋˜๊ฒŒ ๊ตฌํ˜„ํ•ด๋ด…์‹œ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  ...

  render() {
    return (
      <div className="App">
        {this.state.list.map(item =>
          <div key={item.objectID}>
            <span>
              <a href={item.url}>{item.title}</a>
            </span>
            <span>{item.author}</span>
            <span>{item.num_comments}</span>
            <span>{item.points}</span>
# leanpub-start-insert
            <span>
              <button
                onClick={() => this.onDismiss(item.objectID)}
                type="button"
              >
                dismiss
              </button>
            </span>
# leanpub-end-insert
          </div>
        )}
      </div>
    );
  }
}

onDismiss() ๋ฉ”์„œ๋“œ๋Š” ์•„์ง ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์šฐ์„  ๋ฒ„ํŠผ์˜ onClick ํ•ธ๋“ค๋Ÿฌ๋ถ€ํ„ฐ ์‚ดํŽด๋ด…์‹œ๋‹ค. onDismiss() ๋ฉ”์„œ๋“œ๋Š” ํ™”์‚ดํ‘œ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ๊ฐ item์˜ objectID ํ”„๋กœํผํ‹ฐ๋กœ ์‚ญ์ œ๋  ์•„์ดํ…œ์„ ์‹๋ณ„ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์€ onClick ํ•ธ๋“ค๋Ÿฌ ๋ฐ–์—์„œ ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•œ ํ›„ ์ด ํ•จ์ˆ˜๋ฅผ ํ•ธ๋“ค๋Ÿฌ์— ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ ์žฅ์—์„œ ํ•ธ๋“ค๋Ÿฌ์— ๋Œ€ํ•ด ๋ฐฐ์šธ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

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

onDismiss() ํ•จ์ˆ˜ ๋‚ด์šฉ์„ ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์•„์ดํ…œ์„ ์‹๋ณ„ํ•˜๊ธฐ ์œ„ํ•ด ID๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ํด๋ž˜์Šค์— ๋ฐ”์ธ๋”ฉ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ onDismiss()๊ฐ€ ์•„๋‹ˆ๋ผ this.onDismiss()๋กœ ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค. this ๊ฐ์ฒด๋Š” ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค์ž…๋‹ˆ๋‹ค. onDismiss()๋ฅผ ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋กœ ์ •์˜ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ƒ์„ฑ์ž์—์„œ ๋ฐ”์ธ๋”ฉ(binding)์„ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ”์ธ๋”ฉ์€ ๋‹ค์Œ ์žฅ์—์„œ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  constructor(props) {
    super(props);

    this.state = {
      list,
    };

# leanpub-start-insert
    this.onDismiss = this.onDismiss.bind(this);
# leanpub-end-insert
  }

  render() {
    ...
  }
}

์ด์ œ ํด๋ž˜์Šค ํ•จ์ˆ˜์™€ ํ•จ์ˆ˜์˜ ํ•  ์ผ์„ ์ •์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  constructor(props) {
    super(props);

    this.state = {
      list,
    };

    this.onDismiss = this.onDismiss.bind(this);
  }

# leanpub-start-insert
  onDismiss(id) {
    ...
  }
# leanpub-end-insert

  render() {
    ...
  }
}

ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์— ์ผ์–ด๋‚˜๋Š” ์ผ์„ ์ฐจ๊ทผ์ฐจ๊ทผ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. list์—์„œ ID๊ฐ’๊ณผ ๋งค์นญ ๋˜๋Š” ์•„์ดํ…œ์„ ์ œ๊ฑฐํ•˜๊ณ  ์—…๋ฐ์ดํŠธ๋œ list๋ฅผ state์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋‹ค์Œ render() ๋ฉ”์„œ๋“œ๊ฐ€ ์žฌ ์‹คํ–‰๋˜๊ณ  ์—…๋ฐ์ดํŠธ๋œ list๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ์ œ๊ฑฐ๋œ ์•„์ดํ…œ์€ ๋” ์ด์ƒ ๋ณด์ด์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

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

{title="src/App.js",lang=javascript}

onDismiss(id) {
# leanpub-start-insert
  const updatedList = this.state.list.filter(function isNotId(item) {
    return item.objectID !== id;
  });
# leanpub-end-insert
}

filter() ๋ฉ”์„œ๋“œ๋ฅผ ๋ณด๋‹ค ๊ฐ„๊ฒฐํ•œ ๊ตฌ๋ฌธ์œผ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

onDismiss(id) {
# leanpub-start-insert
  function isNotId(item) {
    return item.objectID !== id;
  }

  const updatedList = this.state.list.filter(isNotId);
# leanpub-end-insert
}

ES6 ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋กœ ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

onDismiss(id) {
# leanpub-start-insert
  const isNotId = item => item.objectID !== id;
  const updatedList = this.state.list.filter(isNotId);
# leanpub-end-insert
}

๋ฒ„ํŠผ onClick ํ•ธ๋“ค๋Ÿฌ ์•ˆ์— ์ธ๋ผ์ธ์œผ๋กœ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์œผ๋‚˜, ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์ง‘๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

onDismiss(id) {
# leanpub-start-insert
  const updatedList = this.state.list.filter(item => item.objectID !== id);
# leanpub-end-insert
}

๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ๊ฐ ์•„์ดํ…œ์ด ์‚ญ์ œ๋˜์ง€๋งŒ, list ์ƒํƒœ๋กœ ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์— setState() ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋กœ this.state.list๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

onDismiss(id) {
  const isNotId = item => item.objectID !== id;
  const updatedList = this.state.list.filter(isNotId);
# leanpub-start-insert
  this.setState({ list: updatedList });
# leanpub-end-insert
}

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์žฌ์‹คํ–‰ํ•ด "dismiss" ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ด ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ์ง€๊ธˆ๊นŒ์ง€ ์‹ค์Šตํ•œ ๋‚ด์šฉ์ด ๋ฐ”๋กœ ๋ฆฌ์•กํŠธ์˜ ๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„(unidirectional data flow) ์ž…๋‹ˆ๋‹ค. onClick()์„ ์‚ฌ์šฉํ•ด ๋ทฐ์—์„œ ์•ก์…˜์„ ํŠธ๋ฆฌ๊ฑฐํ•˜๋ฉด, ํ•จ์ˆ˜ ๋˜๋Š” ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๊ฐ€ ๋‚ด๋ถ€ ์ปดํฌ๋„ŒํŠธ ์ƒํƒœ๋ฅผ ์ˆ˜์ •ํ•˜๊ณ  render() ๋ฉ”์„œ๋“œ๊ฐ€ ์žฌ์‹คํ–‰๋˜์–ด ์ตœ์ข… ๋ทฐ๊ฐ€ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค.

๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„์œผ๋กœ ๋‚ด๋ถ€ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•ฉ๋‹ˆ๋‹ค.

์ฝ์–ด๋ณด๊ธฐ

2.4 ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ ๋ฐ”์ธ๋”ฉ

ES6 ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ฐ”์ธ๋”ฉ(binding) ๊ฐœ๋…์„ ์ž˜ ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด์ „ ์žฅ์—์„œ ์ƒ์„ฑ์ž ์•ˆ์— onDismiss() ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋ฅผ ๋ฐ”์ธ๋”ฉํ–ˆ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      list,
    };

    this.onDismiss = this.onDismiss.bind(this);
  }

  ...
}

์™œ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋ฐ”์ธ๋”ฉ์„ ํ–ˆ์„๊นŒ์š”? ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋Š” ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค์— ์ž๋™์œผ๋กœ this๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ผ์ผ์ด ๋ฐ”์ธ๋”ฉ์„ ํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜ ES6 ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ ExplainBindingsComponent๋ฅผ ์˜ˆ์‹œ๋กœ ๋“ค๊ฒ ์Šต๋‹ˆ๋‹ค.

{title="Code Playground",lang=javascript}

class ExplainBindingsComponent extends Component {
  onClickMe() {
    console.log(this);
  }

  render() {
    return (
      <button
        onClick={this.onClickMe}
        type="button"
      >
        Click Me
      </button>
    );
  }
}

์œ„ ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง์€ ์ž˜ ๋˜์ง€๋งŒ, ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ๊ฐœ๋ฐœ์ž ์ฝ˜์†” ๋กœ๊ทธ์— undefined๊ฐ€ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค. ๋ฆฌ์•กํŠธ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋งŽ์ด ์‹ค์ˆ˜ํ•˜๋Š” ๋ฒ„๊ทธ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ์—์„œ this.state์— ์ ‘๊ทผํ•˜๊ณ ์ž ํ•  ๋•Œ this๊ฐ€ undefined์ด๋ฉด ์ ‘๊ทผํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ์—์„œ this๋ฅผ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋ ค๋ฉด ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋ฅผ this์— ๋ฐ”์ธ๋”ฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์•„๋ž˜ ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋Š” ํด๋ž˜์Šค ์ƒ์„ฑ์ž์—์„œ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋ฐ”์ธ๋”ฉํ–ˆ์Šต๋‹ˆ๋‹ค.

{title="Code Playground",lang=javascript}

class ExplainBindingsComponent extends Component {
# leanpub-start-insert
  constructor() {
    super();

    this.onClickMe = this.onClickMe.bind(this);
  }
# leanpub-end-insert

  onClickMe() {
    console.log(this);
  }

  render() {
    return (
      <button
        onClick={this.onClickMe}
        type="button"
      >
        Click Me
      </button>
    );
  }
}

๋ฒ„ํŠผ์„ ๋งŒ๋“ค ๋•Œ, ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค๋ฅผ ๊ตฌ์ฒด์ ์œผ๋กœ ์ง€์ •ํ•˜๊ธฐ ์œ„ํ•ด this ๊ฐ์ฒด๋ฅผ ์ •์˜ํ•œ ํ›„ this.state์— ์ ‘๊ทผํ•˜๊ฑฐ๋‚˜, this.props๋ฅผ ์‚ฌ์šฉํ•ด ์ ‘๊ทผํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ ๋ฐ”์ธ๋”ฉ์€ ์–ด๋Š ๊ณณ์—์„œ๋“ ์ง€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ์ธ render()์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

{title="Code Playground",lang=javascript}

class ExplainBindingsComponent extends Component {
  onClickMe() {
    console.log(this);
  }

  render() {
    return (
      <button
# leanpub-start-insert
        onClick={this.onClickMe.bind(this)}
# leanpub-end-insert
        type="button"
      >
        Click Me
      </button>
    );
  }
}

๊ทธ๋Ÿฌ๋‚˜ render() ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋  ๋•Œ๋งˆ๋‹ค ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋ฅผ ๋ฐ”์ธ๋“œ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์—…๋ฐ์ดํŠธ๋  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰๋˜์–ด ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— render() ๋ฉ”์„œ๋“œ์—์„œ ๋ฐ”๋กœ ๋ฐ”์ธ๋”ฉํ•˜์ง€ ์•Š๋Š” ํŽธ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ƒ์„ฑ์ž์—์„œ ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋ฅผ ๋ฐ”์ธ๋”ฉํ•ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ธ์Šคํ„ด์Šคํ™” ๋  ๋•Œ ์ฒ˜์Œ์— ํ•œ ๋ฒˆ๋งŒ ๋ฐ”์ธ๋”ฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

์ด์™ธ์— ์ƒ์„ฑ์ž์—์„œ ๋ฐ”๋กœ ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ์˜ ํ•  ์ผ์„ ์ •์˜ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

{title="Code Playground",lang=javascript}

class ExplainBindingsComponent extends Component {
  constructor() {
    super();

# leanpub-start-insert
    this.onClickMe = () => {
      console.log(this);
    }
# leanpub-end-insert
  }

  render() {
    return (
      <button
        onClick={this.onClickMe}
        type="button"
      >
        Click Me
      </button>
    );
  }
}

์ด ๋ฐฉ๋ฒ• ๋˜ํ•œ ์•ž์œผ๋กœ ์ƒ์„ฑ์ž์—๊ฒŒ ํ˜ผ๋ž€์„ ์ค„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ƒ์„ฑ์ž๋Š” ํด๋ž˜์Šค์˜ ๋ชจ๋“  ํ”„๋กœํผํ‹ฐ๋ฅผ ์ธ์Šคํ„ด์Šค ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ์˜ ๋กœ์ง์€ ์ƒ์„ฑ์ž ์™ธ๋ถ€์—์„œ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

{title="Code Playground",lang=javascript}

class ExplainBindingsComponent extends Component {
  constructor() {
    super();

    this.doSomething = this.doSomething.bind(this);
    this.doSomethingElse = this.doSomethingElse.bind(this);
  }

  doSomething() {
    // do something
  }

  doSomethingElse() {
    // do something else
  }

  ...
}

์ด์™ธ์— ES6 ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋ฅผ ์ƒ์„ฑ์ž ๋‚ด๋ถ€์— ๋ฐ”์ธ๋”ฉํ•˜์ง€ ์•Š๊ณ ๋„ ์ž๋™ ๋ฐ”์ธ๋”ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

{title="Code Playground",lang=javascript}

class ExplainBindingsComponent extends Component {
  onClickMe = () => {
    console.log(this);
  }

  render() {
    return (
      <button
        onClick={this.onClickMe}
        type="button"
      >
        Click Me
      </button>
    );
  }
}

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

์‹ค์Šตํ•˜๊ธฐ

  • ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฐ”์ธ๋”ฉ์„ ์‹œ๋„ํ•˜๋ฉฐ ์ฝ˜์†”์— this๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

2.5 ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ

์ด๋ฒˆ ์ ˆ์—์„œ๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ(Event Handler)์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ ์ ˆ์—์„œ๋Š” ๊ฐ ์•„์ดํ…œ์˜ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ํ•ด๋‹น ์•„์ดํ…œ์„ ์‚ญ์ œํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

...

<button
  onClick={() => this.onDismiss(item.objectID)}
  type="button"
>
  Dismiss
</button>

...

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

{title="src/App.js",lang=javascript}

...

<button
  onClick={this.onDismiss(item.objectID)}
  type="button"
>
  Dismiss
</button>

...

onClick={doSomething()}์ผ ๊ฒฝ์šฐ, doSomething() ํ•จ์ˆ˜๋Š” ๊ทธ ์ฆ‰์‹œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ํ•ธ๋“ค๋Ÿฌ ํ‘œํ˜„์‹์ด ํ‰๊ฐ€๋˜๋Š”๋ฐ, ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ํ•จ์ˆ˜๋กœ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ด๋„ ์•„๋ฌด ์ผ๋„ ์ผ์–ด๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ onClick={doSomething}์œผ๋กœ ์ˆ˜์ •ํ•˜๋ฉด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ onDismiss() ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ์— ์ ์šฉํ•ด๋ด…์‹œ๋‹ค.

onClick={this.onDismiss}๋กœ ๊ณ ์ณค๊ฒ ์ง€๋งŒ ์ด ์—ญ์‹œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ญ์ œํ•  ์•„์ดํ…œ์„ ์‹๋ณ„ํ•˜๋Š” item.objectID๊ฐ€ ๋น ์ ธ์žˆ์Šต๋‹ˆ๋‹ค. ํ•จ์ˆ˜ ์ธ์ž๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

...

<button
  onClick={() => this.onDismiss(item.objectID)}
  type="button"
>
  Dismiss
</button>

...

map() ๋ฉ”์„œ๋“œ๋กœ ๊ฐ ์•„์ดํ…œ๋งˆ๋‹ค ์•ก์…˜์„ ์‹คํ–‰ํ•˜๋„๋ก ์•„๋ž˜ ์ฝ”๋“œ์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  ...

  render() {
    return (
      <div className="App">
        {this.state.list.map(item => {
# leanpub-start-insert
          const onHandleDismiss = () =>
            this.onDismiss(item.objectID);
# leanpub-end-insert

          return (
            <div key={item.objectID}>
              <span>
                <a href={item.url}>{item.title}</a>
              </span>
              <span>{item.author}</span>
              <span>{item.num_comments}</span>
              <span>{item.points}</span>
              <span>
                <button
# leanpub-start-insert
                  onClick={onHandleDismiss}
# leanpub-end-insert
                  type="button"
                >
                  Dismiss
                </button>
              </span>
            </div>
          );
        }
        )}
      </div>
    );
  }
}

๊ฐ ์—˜๋ ˆ๋จผํŠธ๋งˆ๋‹ค onClick ํ•ธ๋“ค๋Ÿฌ์— ํ•จ์ˆ˜๊ฐ€ ์ „๋‹ฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ๋ฐฉ๋ฒ•๋„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์œ„์— ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  ...

  render() {
    return (
      <div className="App">
        {this.state.list.map(item =>
            ...
            <span>
              <button
# leanpub-start-insert
                onClick={console.log(item.objectID)}
# leanpub-end-insert
                type="button"
              >
                Dismiss
              </button>
            </span>
          </div>
        )}
      </div>
    );
  }
}

์œ„ ์ฝ”๋“œ์—์„œ๋Š” ๋ธŒ๋ผ์šฐ์ €์—์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์—ด์—ˆ์„ ๋•Œ ๋ฐ”๋กœ ์‹คํ–‰๋˜์ง€๋งŒ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋‹ค์‹œ ์•„๋ž˜ ์ฝ”๋“œ๋กœ ์ˆ˜์ •ํ•ด๋ด…์‹œ๋‹ค. ๋ฒ„ํŠผ์„ ํด๋ฆญ ํ•  ๋•Œ๋งŒ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

...

<button
# leanpub-start-insert
  onClick={function () {
    console.log(item.objectID)
  }}
# leanpub-end-insert
  type="button"
>
  Dismiss
</button>

...

ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ onDismiss()์—์„œ ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ–ˆ๋“ฏ์ด ์ด๋ฒˆ์—๋„ ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋กœ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•ด๋ด…์‹œ๋‹ค.

{title="src/App.js",lang=javascript}

...

<button
# leanpub-start-insert
  onClick={() => console.log(item.objectID)}
# leanpub-end-insert
  type="button"
>
  Dismiss
</button>

...

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

{title="src/App.js",lang=javascript}

class App extends Component {
  ...

  render() {
    return (
      <div className="App">
        {this.state.list.map(item =>
          <div key={item.objectID}>
            ...
            <span>
# leanpub-start-insert
              <button
                onClick={() => this.onDismiss(item.objectID)}
                type="button"
              >
                Dismiss
              </button>
# leanpub-end-insert
            </span>
          </div>
        )}
      </div>
    );
  }
}

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

์‹ค์Šตํ•˜๊ธฐ

  • onClick ํ•ธ๋“ค๋Ÿฌ์—์„œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ํ•จ์ˆ˜ ์‚ฌ์šฉ๋ฒ•์„ ์‚ฌ์šฉํ•ด๋ด…๋‹ˆ๋‹ค.

2.6 ํผ๊ณผ ์ด๋ฒคํŠธ

์ด๋ฒˆ ์ ˆ์—์„œ๋Š” ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด๋ณด๋ฉด์„œ HTML ํผ(Form)๊ณผ ์ด๋ฒคํŠธ ์ธํ„ฐ๋ ‰์…˜์„ ๋ฐฐ์›Œ๋ณผ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ž…๋ ฅ๋œ ๊ฒ€์ƒ‰์–ด์— ๋”ฐ๋ผ ๋ฆฌ์ŠคํŠธ๋ฅผ ํ•„ํ„ฐ๋งํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด ๋ด…์‹œ๋‹ค.

์ฒซ ๋ฒˆ์งธ ๋‹จ๊ณ„๋กœ, JSX์— ์ž…๋ ฅ ํ•„๋“œ๊ฐ€ ์žˆ๋Š” ํผ์„ ์•„๋ž˜์™€ ๊ฐ™์ด ์ •์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  ...

  render() {
    return (
      <div className="App">
# leanpub-start-insert
        <form>
          <input type="text" />
        </form>
# leanpub-end-insert
        {this.state.list.map(item =>
          ...
        )}
      </div>
    );
  }
}

๊ฒ€์ƒ‰์–ด๋ฅผ ์ž…๋ ฅํ•  ๋•Œ๋งˆ๋‹ค ๋ฆฌ์ŠคํŠธ๋ฅผ ํ•„ํ„ฐ๋งํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ž…๋ ฅ ํ•„๋“œ ๊ฐ’์ด ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€ ์ƒํƒœ ๊ฐ’์— ์ €์žฅ๋˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ์š”? ๋ฆฌ์•กํŠธ์˜ ํ†ตํ•ฉ์  ์ด๋ฒคํŠธ(synthetic event) ๋ฅผ ์‚ฌ์šฉํ•ด ์ด๋ฒคํŠธ ํŽ˜์ด๋กœ๋“œ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์œผ๋กœ ์ž…๋ ฅ ํ•„๋“œ์˜ onChange ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ •์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  ...

  render() {
    return (
      <div className="App">
        <form>
# leanpub-start-insert
          <input
            type="text"
            onChange={this.onSearchChange}
          />
# leanpub-end-insert
        </form>
        ...
      </div>
    );
  }
}

์ด ํ•จ์ˆ˜๋Š” ์ปดํฌ๋„ŒํŠธ ๊ทธ๋ฆฌ๊ณ  ๋‹ค์‹œ ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ์— ๋ฐ”์šด๋“œ๋˜๋ฏ€๋กœ, ๋ฐ”์ธ๋”ฉ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  constructor(props) {
    super(props);

    this.state = {
      list,
    };

# leanpub-start-insert
    this.onSearchChange = this.onSearchChange.bind(this);
# leanpub-end-insert
    this.onDismiss = this.onDismiss.bind(this);
  }

# leanpub-start-insert
  onSearchChange() {
    ...
  }
# leanpub-end-insert

  ...
}

์—˜๋ ˆ๋จผํŠธ์—์„œ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋กœ ํ†ตํ•ฉ์  ์ด๋ฒคํŠธ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  ...

# leanpub-start-insert
  onSearchChange(event) {
# leanpub-end-insert
    ...
  }

  ...
}

ํผ์— ์ž…๋ ฅ๋œ ๊ฒ€์ƒ‰์–ด๋ฅผ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€ ์ƒํƒœ์— ์ €์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉํ•˜์—ฌ ํ˜„์žฌ ํƒ€์ผ“ ๊ฐ์ฒด์˜ ์ž…๋ ฅ ํ•„๋“œ ๊ฐ’์œผ๋กœ state๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  ...

  onSearchChange(event) {
# leanpub-start-insert
    this.setState({ searchTerm: event.target.value });
# leanpub-end-insert
  }

  ...
}

์ƒ์„ฑ์ž์—์„œ ๊ฒ€์ƒ‰์–ด๋ฅผ ์ง€์นญํ•˜๋Š” searchTerm ํ”„๋กœํผํ‹ฐ ์ดˆ๊นƒ๊ฐ’์„ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ฒ˜์Œ ์ž…๋ ฅ ํ•„๋“œ๋Š” ๋น„์–ด ์žˆ์–ด์•ผ ํ•˜๋ฏ€๋กœ ๊ฐ’์€ ๋นˆ ๋ฌธ์ž์—ด์ด ๋ฉ๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  constructor(props) {
    super(props);

    this.state = {
      list,
# leanpub-start-insert
      searchTerm: '',
# leanpub-end-insert
    };

    this.onSearchChange = this.onSearchChange.bind(this);
    this.onDismiss = this.onDismiss.bind(this);
  }

  ...
}

์ด์ œ ์ž…๋ ฅ ํ•„๋“œ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค state๊ฐ€ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค.

state ์—…๋ฐ์ดํŠธ ๊ณผ์ •์„ ๊ฐ„๋žตํžˆ ์ •๋ฆฌํ•ด๋ด…์‹œ๋‹ค. this.setState()๋Š” ํŠน์ • ํ”„๋กœํผํ‹ฐ๊ฐ€ ์—…๋ฐ์ดํŠธ๋˜์–ด๋„ ๋‹ค๋ฅธ state ํ”„๋กœํผํ‹ฐ๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. searchTerm ํ”„๋กœํผํ‹ฐ๊ฐ€ ์—…๋ฐ์ดํŠธ๋  ๋•Œ list๋Š” ๋ณ€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

App ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค์‹œ ๋ด…์‹œ๋‹ค. list๋ฅผ ํ•„ํ„ฐ๋งํ•˜๊ธฐ ์œ„ํ•ด searchTerm์„ ๊ฐ€์ง€๊ณ  ๋ฆฌ์ŠคํŠธ๋ฅผ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค. ์ด์ „์— render() ๋ฉ”์„œ๋“œ์—์„œ map() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ๋ฐฐ์—ด์˜ ๋ชจ๋“  ์—˜๋ ˆ๋จผํŠธ๋ฅผ ์ ‘๊ทผํ•˜๊ณ  ๊ฐ ํ”„๋กœํผํ‹ฐ๋ฅผ ํ‘œ์‹œํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ์—๋„ ์œ ์‚ฌํ•˜๊ฒŒ ๊ณผ์ •์— filter() ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. searchTerm์€ ๊ฐ ์—˜๋ ˆ๋จผํŠธ์˜ title ํ”„๋กœํผํ‹ฐ ๊ฐ’๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•ด ๋ฆฌ์ŠคํŠธ๋ฅผ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค. map() ๋ฉ”์„œ๋“œ ์•ž์— filter() ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  ...

  render() {
    return (
      <div className="App">
        <form>
          <input
            type="text"
            onChange={this.onSearchChange}
          />
        </form>
# leanpub-start-insert
        {this.state.list.filter(...).map(item =>
# leanpub-end-insert
          ...
        )}
      </div>
    );
  }
}

์ข€ ๋” ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ filter() ๋ฉ”์„œ๋“œ๋ฅผ ๋‹ค๋ค„๋ด…์‹œ๋‹ค. ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€์—์„œ filter() ๋ฉ”์„œ๋“œ์— ์ „๋‹ฌ๋˜๋Š” ์ธ์ž๋ฅผ ์ •์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ ์ƒํƒœ์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์—†์–ด searchTerm ํ”„๋กœํผํ‹ฐ๋ฅผ ์ ‘๊ทผํ•  ์ˆ˜๋„ ์—†์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ƒˆ๋กœ์šด ํ•จ์ˆ˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. filter()๋ฉ”์„œ๋“œ์— searchTerm์„ ์ „๋‹ฌํ•˜๊ณ  ์กฐ๊ฑด์„ ํ™•์ธํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋ง๋กœ ์ด ํ•จ์ˆ˜๋Š” ๊ณ ์ฐจ ํ•จ์ˆ˜(higher order function)๋ผ๊ณ  ๋ถˆ๋ฆฝ๋‹ˆ๋‹ค.

์ด ์ฑ…์€ ๊ณ ์ฐจ ํ•จ์ˆ˜๋ฅผ ๋‹ค๋ฃจ์ง€ ์•Š์ง€๋งŒ, ๋ฆฌ์•กํŠธ๋Š” ๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ(higher order components)๋ฅผ ๋‹ค๋ฃจ๊ธฐ ๋•Œ๋ฌธ์— ๊ณ ์ฐจ ํ•จ์ˆ˜์— ๋Œ€ํ•ด ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์‹œ filter() ๋ฉ”์„œ๋“œ๋ฅผ ๋ด…์‹œ๋‹ค. ์ œ์ผ ๋จผ์ € App ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€์— ๊ณ ์ฐจ ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

# leanpub-start-insert
function isSearched(searchTerm) {
  return function(item) {
    // ์กฐ๊ฑด์— ๋”ฐ๋ผ true ๋˜๋Š” false์„ ๋ฐ˜ํ™˜
  }
}
# leanpub-end-insert

class App extends Component {

  ...

}

์ด ํ•จ์ˆ˜๋Š” searchTerm์„ ์ทจํ•˜์—ฌ ๋‹ค๋ฅธ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  filter() ๋ฉ”์„œ๋“œ๊ฐ€ ์ด ํ•จ์ˆ˜๋ฅผ ์ž…๋ ฅ์œผ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜ํ™˜๋œ ํ•จ์ˆ˜๋Š” filter() ๋ฉ”์„œ๋“œ์— ์ „๋‹ฌ๋˜์–ด ๊ฐ ๊ฐ์ฒด์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜ํ™˜๋œ ํ•จ์ˆ˜๋Š” ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋ฐฐ์—ด์„ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด ์กฐ๊ฑด์„ ์ถ”๊ฐ€ํ•ด๋ด…์‹œ๋‹ค.

{title="src/App.js",lang=javascript}

function isSearched(searchTerm) {
  return function(item) {
# leanpub-start-insert
    return item.title.toLowerCase().includes(searchTerm.toLowerCase());
# leanpub-end-insert
  }
}

class App extends Component {

  ...

}

ES6 include() ๋ฉ”์„œ๋“œ๋กœ searchTerm๊ณผ title์˜ ๊ฐ’์ด ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ ๋ฌธ์ž์—ด์„ ๋ชจ๋‘ ์†Œ๋ฌธ์ž๋กœ ๋ฐ”๊ฟ”์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‘ ๋ฌธ์ž์—ด์ด 'redux'์™€ 'Redux'์ผ ๊ฒฝ์šฐ ์„œ๋กœ ๋ถˆ์ผ์น˜ํ•จ์œผ๋กœ ํŒ๋‹จํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. filter() ๋ฉ”์„œ๋“œ๋Š” ์ƒˆ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— this.state.list๋Š” ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ES5์˜ ๊ฒฝ์šฐ indexOf() ๋ฉ”์„œ๋“œ๋กœ ๋ฐฐ์—ด์˜ ์ธ๋ฑ์Šค ๊ฐ’์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.

{title="Code Playground",lang="javascript"}

// ES5
string.indexOf(pattern) !== -1

// ES6
string.includes(pattern)

ES6 ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋กœ ์ฝ”๋“œ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๋งŒ๋“ญ์‹œ๋‹ค.

{title="Code Playground",lang="javascript"}

// ES5
function isSearched(searchTerm) {
  return function(item) {
    return item.title.toLowerCase().includes(searchTerm.toLowerCase());
  }
}

// ES6
const isSearched = searchTerm => item =>
  item.title.toLowerCase().includes(searchTerm.toLowerCase());

ES5์™€ ES6 ํ•จ์ˆ˜ ๋‘˜ ์ค‘ ์–ด๋–ค ๊ฒƒ์ด ๋” ์ฝ๊ธฐ ์‰ฌ์šด๊ฐ€์š”? ์ €๋Š” ES6 ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋ฅผ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค. ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋Š” ์ฝ”๋“œ๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ํ‘œํ˜„ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋ฆฌ์•กํŠธ๋Š” ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ๊ณผ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜(๊ณ ์ฐจ ํ•จ์ˆ˜)๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ isSearched() ํ•จ์ˆ˜๋ฅผ filter() ๋ฉ”์„œ๋“œ์— ์ถ”๊ฐ€ํ•ด ๋ฆฌ์ŠคํŠธ๋ฅผ ํ•„ํ„ฐ๋งํ•˜๊ฒŒ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. this.state.searchTerm๋ฅผ ์ „๋‹ฌํ•˜๋ฉด, ๋ฐ˜ํ™˜๋œ ํ•จ์ˆ˜์—์„œ ์กฐ๊ฑด์— title๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•˜์—ฌ ๋ฆฌ์ŠคํŠธ๋ฅผ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  ...

  render() {
    return (
      <div className="App">
        <form>
          <input
            type="text"
            onChange={this.onSearchChange}
          />
        </form>
# leanpub-start-insert
        {this.state.list.filter(isSearched(this.state.searchTerm)).map(item =>
# leanpub-end-insert
          ...
        )}
      </div>
    );
  }
}

๋ธŒ๋ผ์šฐ์ €๋ฅผ ์—ด์–ด ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ์ด ์ž˜ ์ž‘๋™๋˜๋Š”์ง€ ํ™•์ธํ•ด๋ณด์„ธ์š”.

์ฝ์–ด๋ณด๊ธฐ

2.7 ES6 ๊ตฌ์กฐํ•ด์ฒด

ES6 ๊ตฌ์กฐํ•ด์ฒด(destructing) ๋ฌธ๋ฒ•์œผ๋กœ ๊ฐ์ฒด์™€ ๋ฐฐ์—ด ํ”„๋กœํผํ‹ฐ๋ฅผ ์‰ฝ๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ES5์™€ ES6 ๋ฌธ๋ฒ•์„ ์„œ๋กœ ๋น„๊ตํ•ด๋ด…์‹œ๋‹ค.

{title="Code Playground",lang="javascript"}

const user = {
  firstname: 'Robin',
  lastname: 'Wieruch',
};

// ES5
var firstname = user.firstname;
var lastname = user.lastname;

console.log(firstname + ' ' + lastname);
// ์ถœ๋ ฅ: Robin Wieruch

// ES6
const { firstname, lastname } = user;

console.log(firstname + ' ' + lastname);
// ์ถœ๋ ฅ: Robin Wieruch

ES5์—์„œ๋Š” ๊ฐ์ฒด ํ”„๋กœํผํ‹ฐ์— ์ ‘๊ทผํ•  ๋•Œ๋งˆ๋‹ค ํ–‰์ด ์ถ”๊ฐ€๋˜์ง€๋งŒ, ES6์—์„œ๋Š” ์ฝ”๋“œ ํ•œ ์ค„์ด๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๊ฐ€๋…์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ๊ฐ์ฒด๋ฅผ ํ•ด์ฒดํ•  ๋•Œ ๊ฐ ๊ฐ์ฒด ํ”„๋กœํผํ‹ฐ๋งˆ๋‹ค ์ค„์„ ๋ฐ”๊ฟ”์ฃผ๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

{title="Code Playground",lang="javascript"}

const {
  firstname,
  lastname
} = user;

๋ฐฐ์—ด์—์„œ๋„ ๊ตฌ์กฐํ•ด์ฒด๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ํ–‰์œผ๋กœ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

{title="Code Playground",lang="javascript"}

const users = ['Robin', 'Andrew', 'Dan'];
const [
  userOne,
  userTwo,
  userThree
] = users;

console.log(userOne, userTwo, userThree);
// ์ถœ๋ ฅ: Robin Andrew Dan

์•ž์„œ ๋งŒ๋“  App ์ปดํฌ๋„ŒํŠธ์—์„œ๋„ state ๊ฐ์ฒด๊ฐ€ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ๊ตฌ์กฐํ•ด์ฒด๋  ์ˆ˜ ์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ์„ ๊ฒ๋‹ˆ๋‹ค. ์ฝ”๋“œ๋„ ์งง๊ณ  ๊ฐ„๊ฒฐํ•ด์กŒ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

  render() {
# leanpub-start-insert
    const { searchTerm, list } = this.state;
# leanpub-end-insert
    return (
      <div className="App">
        ...
# leanpub-start-insert
        {list.filter(isSearched(searchTerm)).map(item =>
# leanpub-end-insert
          ...
        )}
      </div>
    );

๋ฌผ๋ก  ES5 ๋˜๋Š” ES6 ์–ด๋Š์ชฝ์œผ๋กœ๋„ ์ž‘์„ฑ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

{title="Code Playground",lang="javascript"}

// ES5
var searchTerm = this.state.searchTerm;
var list = this.state.list;

// ES6
const { searchTerm, list } = this.state;

์ด ์ฑ…์—์„œ๋Š” ES6๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ES6 ๋ฌธ๋ฒ•์— ์นœ์ˆ™ํ•ด์ง€๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์ฝ๊ธฐ์ž๋ฃŒ

2.8 ์ œ์–ด๋˜๋Š” ์ปดํฌ๋„ŒํŠธ

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

๊ทธ๋Ÿฌ๋‚˜ ์šฐ๋ฆฌ๊ฐ€ ๋†“์นœ ๊ฒƒ์ด ์žˆ์Šต๋‹ˆ๋‹ค. HTML input ํƒœ๊ทธ์˜ value ์†์„ฑ์€ ์ž…๋ ฅ ํ•„๋“œ์— ํ‘œ์‹œ๋œ ๊ฐ’์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค. ๋ฐ”๋กœ searchTerm์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ๋ฆฌ์•กํŠธ์—์„œ๋Š” value๋ฅผ ํ™œ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋ ๊นŒ์š”?

๊ทธ๋ ‡์ง€ ์•Š์Šต๋‹ˆ๋‹ค. <input>,<textarea>,<select> ์™€ ๊ฐ™์€ ํผ ์—˜๋ ˆ๋จผํŠธ๋Š” HTML๋กœ ์ž์‹ ์˜ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ์™ธ๋ถ€์—์„œ ๊ทธ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ๋‚ด๋ถ€ ๊ฐ’๋„ ์ˆ˜์ •๋˜๊ธฐ ๋•Œ๋ฌธ์—, ์ด๋Ÿฌํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฆฌ์•กํŠธ์—์„œ๋Š” ์ œ์–ด๋˜์ง€ ์•Š์€ ์ปดํฌ๋„ŒํŠธ(uncontrolled component) ๋ผ ๋ถ€๋ฆ…๋‹ˆ๋‹ค. ๋ฆฌ์•กํŠธ์—์„œ๋Š” ์ œ์–ด๋˜์ง€ ์•Š๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ œ์–ด๋˜๋Š” ์ปดํฌ๋„ŒํŠธ(controlled components) ๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ๋Š” ์ดˆ๊ธฐํ™” ์‹œ์  ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ์–ด๋–ค ์‹œ์ ์ด๋ผ๋„ ๋ฐ˜๋“œ์‹œ ๋ทฐ์˜ state๋ฅผ ๋‚˜ํƒ€๋‚ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ์ด์ œ ๋ฌด์—‡์„ ํ•˜๋ฉด ๋ ๊นŒ์š”? ์ž…๋ ฅ ํ•„๋“œ ๊ฐ’์— searchTerm ์ƒํƒœ ํ”„๋กœํผํ‹ฐ ๊ฐ’์„ ๊ธฐ๋ณธ ๊ฐ’์œผ๋กœ ์„ค์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  ...

  render() {
    const { searchTerm, list } = this.state;
    return (
      <div className="App">
        <form>
          <input
            type="text"
# leanpub-start-insert
            value={searchTerm}
# leanpub-end-insert
            onChange={this.onSearchChange}
          />
        </form>
        ...
      </div>
    );
  }
}

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

์ฝ์–ด๋ณด๊ธฐ

2.9 ์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌ

์–ด๋Š์ƒˆ App ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งค์šฐ ์ปค์กŒ์Šต๋‹ˆ๋‹ค. ๊ณ„์†ํ•ด์„œ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ๊ฐœ๋ฐœํ•˜๋‹ค๋ณด๋ฉด ์ ์  ๊ทœ๋ชจ๊ฐ€ ์ปค์งˆ ๊ฒ๋‹ˆ๋‹ค. ๋“œ๋””์–ด ์ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž‘์€ ์ปดํฌ๋„ŒํŠธ ๋ฉ์–ด๋ฆฌ๋กœ ๋ถ„๋ฆฌ ์ž‘์—…์„ ์‹œ์ž‘ํ•  ๋•Œ์ž…๋‹ˆ๋‹ค.

์•„๋ž˜์™€ ๊ฐ™์ด ๊ฒ€์ƒ‰์–ด๋ฅผ ์ž…๋ ฅํ•˜๋Š” <Search /> ์ปดํฌ๋„ŒํŠธ์™€ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋‚˜์—ดํ•˜๋Š” <Table /> ์ปดํฌ๋„ŒํŠธ๋กœ ๋ถ„๋ฆฌํ•ด๋ด…์‹œ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  ...

  render() {
    const { searchTerm, list } = this.state;
    return (
      <div className="App">
# leanpub-start-insert
        <Search />
        <Table />
# leanpub-end-insert
      </div>
    );
  }
}

๋‹ค์Œ์œผ๋กœ ๋ถ„๋ฆฌํ•œ ์ปดํฌ๋„ŒํŠธ์— ์‚ฌ์šฉํ•  ํ”„๋กœํผํ‹ฐ๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. App ์ปดํฌ๋„ŒํŠธ์˜ state์™€ ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋ฅผ ์ „๋‹ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  ...

  render() {
    const { searchTerm, list } = this.state;
    return (
      <div className="App">
# leanpub-start-insert
        <Search
          value={searchTerm}
          onChange={this.onSearchChange}
        />
        <Table
          list={list}
          pattern={searchTerm}
          onDismiss={this.onDismiss}
        />
# leanpub-end-insert
      </div>
    );
  }
}

App ์ปดํฌ๋„ŒํŠธ ์•„๋ž˜์— ๋ถ„๋ฆฌ์‹œํ‚จ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ปดํฌ๋„ŒํŠธ ์—ญ์‹œ ES6 ์ปดํฌ๋„ŒํŠธ๋กœ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

์ œ์ผ ๋จผ์ € Search ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {
  ...
}

# leanpub-start-insert
class Search extends Component {
  render() {
    const { value, onChange } = this.props;
    return (
      <form>
        <input
          type="text"
          value={value}
          onChange={onChange}
        />
      </form>
    );
  }
}
# leanpub-end-insert

๋‹ค์Œ์œผ๋กœ Table ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

...

# leanpub-start-insert
class Table extends Component {
  render() {
    const { list, pattern, onDismiss } = this.props;
    return (
      <div>
        {list.filter(isSearched(pattern)).map(item =>
          <div key={item.objectID}>
            <span>
              <a href={item.url}>{item.title}</a>
            </span>
            <span>{item.author}</span>
            <span>{item.num_comments}</span>
            <span>{item.points}</span>
            <span>
              <button
                onClick={() => onDismiss(item.objectID)}
                type="button"
              >
                Dismiss
              </button>
            </span>
          </div>
        )}
      </div>
    );
  }
}
# leanpub-end-insert

์ด์ œ ํ•˜๋‚˜์˜€๋˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ 3๊ฐœ๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด์ฏค์—์„œ props๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“œ๋Š” ๋ถ„๋„ ์žˆ์„ ๊ฒ๋‹ˆ๋‹ค. props์€ ํ”„๋กœํผํ‹ฐ(properties)์˜ ์•ฝ์ž๋กœ this๋ฅผ ์‚ฌ์šฉํ•ด ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค์— ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค. App ์ปดํฌ๋„ŒํŠธ์—์„œ ์ „๋‹ฌ๋œ ๋ชจ๋“  ๊ฐ’์„ ๊ฐ€์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋˜ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋กœ ๊ณ„์†ํ•ด์„œ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

App ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฝ‘์•„๋‚ธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ๋„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ๋Š” props์˜ ํ†ตํ•ด ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๊ธฐ ๋•Œ๋ฌธ์—, props ๊ฐ’์„ ๋ฐ”๊ฟ” ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‹ค์Šตํ•˜๊ธฐ

  • Search์™€ Table ์ปดํฌ๋„ŒํŠธ๋กœ ๋ถ„๋ฆฌํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ์ถ”๊ฐ€๋กœ ๋” ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ ์—˜๋ ˆ๋จผํŠธ๋ฅผ ์ฐพ์•„๋ด…๋‹ˆ๋‹ค. ย ย * ๋‹ค์Œ ์žฅ์—์„œ ์‹ค์Šต ์ฝ”๋“œ์™€ ์ถฉ๋Œ์ด ์ƒ๊ธธ ์ˆ˜ ์žˆ์œผ๋‹ˆ ์ง€๊ธˆ ๋ฐ”๋กœ ์ฝ”๋”ฉ์„ ํ•˜์ง€ ๋งˆ์„ธ์š”.

2.10 ๊ตฌ์„ฑ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ

prop ๊ฐ์ฒด ์•ˆ์„ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋กœํผํ‹ฐ๊ฐ€ ํ•˜๋‚˜ ๋” ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ”๋กœ children prop์ž…๋‹ˆ๋‹ค. children prop๋Š” ์•Œ ์ˆ˜ ์—†๋Š” ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๊ฐ€ ์ž์‹ ์—˜๋ ˆ๋จผํŠธ์— ์ „๋‹ฌ๋จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ์— ํ…์ŠคํŠธ(๋ฌธ์ž์—ด)๋ฅผ children props๋กœ ์ „๋‹ฌํ•  ๋•Œ ์–ด๋–ป๊ฒŒ ๋ณด์ด๋Š”์ง€ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  ...

  render() {
    const { searchTerm, list } = this.state;
    return (
      <div className="App">
# leanpub-start-insert
        <Search
          value={searchTerm}
          onChange={this.onSearchChange}
        >
          Search
        </Search>
# leanpub-end-insert
        <Table
          list={list}
          pattern={searchTerm}
          onDismiss={this.onDismiss}
        />
      </div>
    );
  }
}

์ด์ œ Search ์ปดํฌ๋„ŒํŠธ๋Š” props ๊ฐ์ฒด์—์„œ children ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ตฌ์กฐ ํ•ด์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. children props๊ฐ€ ํ‘œ์‹œ๋  ๊ณณ์„ ์ •ํ•ฉ๋‹ˆ๋‹ค.

class Search extends Component {
  render() {
# leanpub-start-insert
    const { value, onChange, children } = this.props;
# leanpub-end-insert
    return (
      <form>
# leanpub-start-insert
        {children} <input
# leanpub-end-insert
          type="text"
          value={value}
          onChange={onChange}
        />
      </form>
    );
  }
}

์ด์ œ ์ž…๋ ฅ ํ•„๋“œ ์˜†์— "Search"๋ผ๋Š” ํ…์ŠคํŠธ๊ฐ€ ๋ณด์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. Search ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค๋ฅธ ๊ณณ์— ์žฌ์‚ฌ์šฉํ•  ๋•Œ๋งˆ๋‹ค, ๋งค๋ฒˆ ๋‹ค๋ฅธ ํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ…์ŠคํŠธ(๋ฌธ์ž์—ด) ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์—˜๋ ˆ๋จผํŠธ์™€ ์—˜๋ ˆ๋จผํŠธ ํŠธ๋ฆฌ(๋‹ค์‹œ ์ปดํฌ๋„ŒํŠธ๋กœ ์บก์Šํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค)๋ฅผ ์ž์‹์œผ๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. children ํ”„๋กœํผํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•ด ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„œ๋กœ ๋ผ์›Œ ๋„ฃ๊ณ  ๋งž๋ฌผ๋ ค ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฝ์–ด๋ณด๊ธฐ

2.11 ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ

์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ์™€ ๊ตฌ์„ฑ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•ด ์ปดํฌ๋„ŒํŠธ ๊ฐ„ ๊ณ„์ธต์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒƒ์ด ๋ฐ”๋กœ ๋ฆฌ์•กํŠธ ๋ทฐ ๋ ˆ์ด์–ด์˜ ๊ธฐ์ดˆ์ž…๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰ ์žฅ์—์„œ ์žฌ์‚ฌ์šฉ์„ฑ(reusability)์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ ๋งŒ๋“  Table๊ณผ Search ์ปดํฌ๋„ŒํŠธ๋„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  App ์ปดํฌ๋„ŒํŠธ๋„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ๋Š” ๋‹ค๋ฅธ ๊ณณ์—์„œ ๋‹ค์‹œ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

Button ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ์ปดํฌ๋„ŒํŠธ ์—ญ์‹œ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class Button extends Component {
  render() {
    const {
      onClick,
      className,
      children,
    } = this.props;

    return (
      <button
        onClick={onClick}
        className={className}
        type="button"
      >
        {children}
      </button>
    );
  }
}

์ด๋ฏธ HTML์— button ์—˜๋ ˆ๋จผํŠธ๊ฐ€ ์žˆ์–ด, ๊ตณ์ด ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“ค ์ด์œ ๊ฐ€ ์—†๋‹ค๊ณ  ๋Š๋‚„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” button ์—˜๋ ˆ๋จผํŠธ ๋Œ€์‹ ์— Button ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ์ปดํฌ๋„ŒํŠธ๋Š” type ์†์„ฑ์ธ button๋งŒ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ๋กœ ๋‹ค์‹œ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ๋Š” ์žฅ๊ธฐ์ ์ธ ์•ˆ๋ชฉ์„ ๊ฐ€์ง€๊ณ  ์žฌ์‚ฌ์šฉ์„ฑ์„ ๊ณ ๋ คํ•ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด ๋ฒ„ํŠผ์˜ ์ข…๋ฅ˜๋Š” ๋‹ค์–‘ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœํผํ‹ฐ, ์Šคํƒ€์ผ, ๋™์ž‘์ด ๋ชจ๋‘ ์ œ๊ฐ๊ฐ์ž…๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์—†๋‹ค๋ฉด ์•„๋งˆ ๋ชจ๋“  ๋ฒ„ํŠผ์„ ์ผ์ผ์ด ๋ฆฌํŒฉํ„ฐ๋ง ํ•ด์•ผ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋Œ€์‹  Button ์ปดํฌ๋„ŒํŠธ๋Š” ๋‹จ์ผ ์†Œ์Šค์ž…๋‹ˆ๋‹ค. ํ•œ ์ปดํฌ๋„ŒํŠธ๋งŒ ์ˆ˜์ •ํ•˜๋ฉด ๋‹ค๋ฅธ ๋ฒ„ํŠผ์„ ํ•œ๊บผ๋ฒˆ์— ๋ฆฌํŒฉํ„ฐ๋ง ๋ฉ๋‹ˆ๋‹ค.

๊ธฐ์กด์˜ button ์—˜๋ ˆ๋จผํŠธ๋ฅผ ๋Œ€์ฒดํ•ด Button ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋„ ์ด๋Ÿฌํ•œ ์ด์œ  ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด๋ฏธ Button ์ปดํฌ๋„ŒํŠธ๊ฐ€ button ํƒ€์ž…์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— type ์†์„ฑ์€ ์ƒ๋žตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class Table extends Component {
  render() {
    const { list, pattern, onDismiss } = this.props;
    return (
      <div>
        {list.filter(isSearched(pattern)).map(item =>
          <div key={item.objectID}>
            <span>
              <a href={item.url}>{item.title}</a>
            </span>
            <span>{item.author}</span>
            <span>{item.num_comments}</span>
            <span>{item.points}</span>
            <span>
# leanpub-start-insert
              <Button onClick={() => onDismiss(item.objectID)}>
                Dismiss
              </Button>
# leanpub-end-insert
            </span>
          </div>
        )}
      </div>
    );
  }
}

Button ์ปดํฌ๋„ŒํŠธ๋Š” props์— className ํ”„๋กœํผํ‹ฐ๋ฅผ ์ „๋‹ฌ๋ฐ›์Šต๋‹ˆ๋‹ค. className๋Š” HTML ํด๋ž˜์Šค์—์„œ ํŒŒ์ƒ๋œ ๊ฒƒ์œผ๋กœ ํด๋ž˜์Šค ์ด๋ฆ„์„ ์ง€์นญํ•ฉ๋‹ˆ๋‹ค. ์•„์ง ์šฐ๋ฆฌ๋Š” className์„ ์ „๋‹ฌํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. className์ด ์„ ํƒ ์‚ฌํ•ญ์ž„์„ ๋ช…์‹œํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ์ฒด ๊ตฌ์กฐํ• ๋‹น ๋‚ด ๊ธฐ๋ณธ ๊ฐ’์„ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class Button extends Component {
  render() {
    const {
      onClick,
# leanpub-start-insert
      className = '',
# leanpub-end-insert
      children,
    } = this.props;

    ...
  }
}

์ด์ œ Button ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, className ํ”„๋กœํผํ‹ฐ ๊ฐ’์ด ์ง€์ •๋˜์ง€ ์•Š์•„๋„ undefined ๊ฐ€ ํ‘œ์‹œ๋˜์ง€ ์•Š๊ณ  ๋นˆ ๋ฌธ์ž์—ด๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

2.12 ์ปดํฌ๋„ŒํŠธ ์„ ์–ธ

์ง€๊ธˆ๊นŒ์ง€ 4๊ฐ€์ง€ ES6 ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ดค์Šต๋‹ˆ๋‹ค. ์ด์ œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“œ๋Š”๋ฐ ์ž์‹ ๊ฐ์ด ์ƒ๊ฒผ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค. ์ด๋ฒˆ ์ ˆ์—์„œ ๋น„ ์ƒํƒœ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ(Functional Stateless Components)๋ฅผ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ํ•œ๋ฒˆ ์•ž์—์„œ ๋ฐฐ์šด ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ ์œ ํ˜•์„ ์ •๋ฆฌํ•ด๋ด…์‹œ๋‹ค.

  • ๋น„ ์ƒํƒœ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ ๋น„ ์ƒํƒœ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ๋Š” props๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›๊ณ  JSX๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ๊นŒ์ง€๋Š” ES6 ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์™€ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋น„ ์ƒํƒœ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ๋Š” state๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— this.state ๋˜๋Š” this.setState()๋กœ state์— ์•ก์„ธ์Šค ํ•˜๊ฑฐ๋‚˜ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ƒ๋ช…์ฃผ๊ธฐ ๋ฉ”์„œ๋“œ๋„ ์—†์Šต๋‹ˆ๋‹ค. ์•„์ง ์ƒ๋ช…์ฃผ๊ธฐ ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•ด ๋ฐฐ์šฐ์ง€ ์•Š์•˜์ง€๋งŒ, ์šฐ๋ฆฌ๋Š” ์ƒ๋ช…์ฃผ๊ธฐ ๋ฉ”์„œ๋“œ์ธ constructor()๊ณผ render()์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ƒ๋ช…์ฃผ๊ธฐ ๋™์•ˆ constructor()๋Š” ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๋Š” ๋ฐ˜๋ฉด, render()์€ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์—…๋ฐ์ดํŠธ๋  ๋•Œ๋งˆ๋‹ค ํ•œ๋ฒˆ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ๋น„ ์ƒํƒœ ํ•จ์ˆ˜ํ˜•์€ ์ƒ๋ช…์ฃผ๊ธฐ ๋ฉ”์„œ๋“œ๊ฐ€ ์—†์Œ์„ ๊ผญ ๊ธฐ์–ตํ•˜๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.

  • ES6 ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ ์šฐ๋ฆฌ๋Š” ์ด๋ฏธ ES6 ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•ด๋ดค์Šต๋‹ˆ๋‹ค. ํด๋ž˜์Šค ์ •์˜ ์‹œ, extends Component๋ž€ ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ํ™•์žฅํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๋œปํ•ฉ๋‹ˆ๋‹ค. extend๋Š” ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ API์ธ ์ƒ๋ช…์ฃผ๊ธฐ ๋ฉ”์„œ๋“œ๋ฅผ ์ปดํฌ๋„ŒํŠธ๋กœ ์—ฐ๊ฒฐ์‹œํ‚ต๋‹ˆ๋‹ค. ๋•Œ๋ฌธ์— render() ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒ๋‹ˆ๋‹ค. ๋˜ํ•œ this.state์™€ this.setState()๋ฉ”์„œ๋“œ๋กœ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๊ณ  ์กฐ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

  • React.createClass: React.createClass์€ ๋ฆฌ์•กํŠธ ๊ตฌ๋ฒ„์ „์˜ ํด๋ž˜์Šค ์„ ์–ธ๋ฌธ์œผ๋กœ ES5 ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ํŽ˜์ด์Šค๋ถ์€ ES6์„ ์‚ฌ์šฉํ•จ์— ๋”ฐ๋ผ ๋” ์ด์ƒ React.createClass๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฆฌ์•กํŠธ 15.5 ๋ฒ„์ „์—์„œ ๋น„์ถ”์ฒœ ๊ฒฝ๊ณ  ๋ฌธ๊ตฌ๋กœ ๋“ฑ๋ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ์ฑ… ์—ญ์‹œ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

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

๊ฐœ๋ฐœ ์ค‘์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ๋Œ์•„๊ฐ‘์‹œ๋‹ค. App ์ปดํฌ๋„ŒํŠธ๋Š” state๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ES6 ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋จธ์ง€ ์„ธ ์ปดํฌ๋„ŒํŠธ๋Š” state๊ฐ€ ์—†์œผ๋ฉฐ this.state ๋˜๋Š” this.setState()๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ƒ๋ช…์ฃผ๊ธฐ ๋ฉ”์„œ๋“œ๋„ ์—†์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์ธ Search ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋น„ ์ƒํƒœ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฆฌํŒฉํ„ฐ๋ง ํ•ด๋ด…์‹œ๋‹ค. ์ดํ›„ Table๊ณผ Button ์ปดํฌ๋„ŒํŠธ๋„ ๋น„ ์ƒํƒœ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฆฌํŒฉํ„ฐ๋ง ํ•ด๋ณด๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

# leanpub-start-insert
function Search(props) {
  const { value, onChange, children } = props;
  return (
    <form>
      {children} <input
        type="text"
        value={value}
        onChange={onChange}
      />
    </form>
  );
}
# leanpub-end-insert

์•„์ฃผ ๊ธฐ๋ณธ์ ์ธ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. props๋Š” ํ•จ์ˆ˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜(function signature: ํ•จ์ˆ˜์˜ ์›ํ˜•์— ๋ช…์‹œ๋˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜ ๋ฆฌ์ŠคํŠธ)์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ณ  JSX๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์•ž์—์„œ ES6 ๊ตฌ์กฐ ํ•ด์ฒด๋ฅผ ๋ฐฐ์› ์Šต๋‹ˆ๋‹ค. ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์€ ํ•จ์ˆ˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋กœ props๋ฅผ ๊ตฌ์กฐ ํ•ด์ œํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

# leanpub-start-insert
function Search({ value, onChange, children }) {
# leanpub-end-insert
  return (
    <form>
      {children} <input
        type="text"
        value={value}
        onChange={onChange}
      />
    </form>
  );
}

ํ•จ์ˆ˜ํ˜• ๋น„ ์ƒํƒœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ES6 ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋กœ ๋ฐ”๊ฟ” ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ž‘์„ฑํ•ด๋ด…์‹œ๋‹ค. ๋ธ”๋ก์„ ์ œ๊ฑฐํ•˜๊ณ  return์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
{title="src/App.js",lang=javascript}

# leanpub-start-insert
const Search = ({ value, onChange, children }) =>
  <form>
    {children} <input
      type="text"
      value={value}
      onChange={onChange}
    />
  </form>
# leanpub-end-insert

์ง€๊ธˆ ๋งŒ๋“  ํ•จ์ˆ˜๋Š” props๋ฅผ ์ž…๋ ฅ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ  JSX๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋“ค ์‚ฌ์ด์— ํ•  ์ผ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. {title="Code Playground",lang=javascript}

const Search = ({ value, onChange, children }) => {

  // ํ•ด์•ผํ•  ์ผ

  return (
    <form>
      {children} <input
        type="text"
        value={value}
        onChange={onChange}
      />
    </form>
  );
}

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

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

์‹ค์Šตํ•˜๊ธฐ

  • Table๊ณผ Button ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋น„ ์ƒํƒœ ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฆฌํŒฉํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค.

์ฝ์–ด๋ณด๊ธฐ

2.13 ์ปดํฌ๋„ŒํŠธ ์Šคํƒ€์ผ๋ง

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์—์˜๊ฒŒ ๊พธ๋ฉฐ ๋ด…์‹œ๋‹ค. CSS ํŒŒ์ผ์€ src/App.css ํŒŒ์ผ๊ณผ src/index.css ์ž…๋‹ˆ๋‹ค. create-react-app ์œผ๋กœ ๋ถ€ํŠธ์ŠคํŠธ๋žฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ƒ๊ธด ํŒŒ์ผ์ž…๋‹ˆ๋‹ค. CSS ํŒŒ์ผ์€ src/App.js ๋ฐ src/index.js ํŒŒ์ผ๋กœ ๋ชจ๋‘ ๊ฐ€์ ธ์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜ ์ œ์‹œ๋œ CSS ์ฝ”๋“œ๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ๋ฐ๋กœ ์ž์œ ๋กญ๊ฒŒ ๊ณ ์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ „๋ฐ˜์ ์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์Šคํƒ€์ผ๋ง์„ ์ˆ˜์ •ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. src/index.css ํŒŒ์ผ์„ ์—ด์–ด ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ์ง€์šฐ๊ณ  ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

{title="src/index.css",lang="css"}

body {
  color: #222;
  background: #f4f4f4;
  font: 400 14px CoreSans, Arial,sans-serif;
}

a {
  color: #222;
}

a:hover {
  text-decoration: underline;
}

ul, li {
  list-style: none;
  padding: 0;
  margin: 0;
}

input {
  padding: 10px;
  border-radius: 5px;
  outline: none;
  margin-right: 10px;
  border: 1px solid #dddddd;
}

button {
  padding: 10px;
  border-radius: 5px;
  border: 1px solid #dddddd;
  background: transparent;
  color: #808080;
  cursor: pointer;
}

button:hover {
  color: #222;
}

*:focus {
  outline: none;
}

์ด์–ด์„œ src/App.css์„ ์—ด์–ด App ์ปดํฌ๋„ŒํŠธ ์Šคํƒ€์ผ์„ ์ˆ˜์ •ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ๋ชจ๋‘ ์ง€์šฐ๊ณ  ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

{title="src/App.css",lang="css"}

.page {
  margin: 20px;
}

.interactions {
  text-align: center;
}

.table {
  margin: 20px 0;
}

.table-header {
  display: flex;
  line-height: 24px;
  font-size: 16px;
  padding: 0 10px;
  justify-content: space-between;
}

.table-empty {
  margin: 200px;
  text-align: center;
  font-size: 16px;
}

.table-row {
  display: flex;
  line-height: 24px;
  white-space: nowrap;
  margin: 10px 0;
  padding: 10px;
  background: #ffffff;
  border: 1px solid #e3e3e3;
}

.table-header > span {
  overflow: hidden;
  text-overflow: ellipsis;
  padding: 0 5px;
}

.table-row > span {
  overflow: hidden;
  text-overflow: ellipsis;
  padding: 0 5px;
}

.button-inline {
  border-width: 0;
  background: transparent;
  color: inherit;
  text-align: inherit;
  -webkit-font-smoothing: inherit;
  padding: 0;
  font-size: inherit;
  cursor: pointer;
}

.button-active {
  border-radius: 0;
  border-bottom: 1px solid #38BB6C;
}

์ด์ œ ์ปดํฌ๋„ŒํŠธ์— ์Šคํƒ€์ผ์ด ์ ์šฉ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. HTML ์†์„ฑ์ธ class ๋Œ€์‹ ์— JSX์—์„œ๋Š” className์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋จผ์ € ES6 ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์ธ App ์ปดํฌ๋„ŒํŠธ์— ํด๋ž˜์Šค ์ด๋ฆ„์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

class App extends Component {

  ...

  render() {
    const { searchTerm, list } = this.state;
    return (
# leanpub-start-insert
      <div className="page">
        <div className="interactions">
# leanpub-end-insert
          <Search
            value={searchTerm}
            onChange={this.onSearchChange}
          >
            Search
          </Search>
# leanpub-start-insert
        </div>
# leanpub-end-insert
        <Table
          list={list}
          pattern={searchTerm}
          onDismiss={this.onDismiss}
        />
# leanpub-start-insert
      </div>
# leanpub-end-insert
    );
  }
}

์ด์–ด์„œ ๋น„์ƒํƒœ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์ธ Table ์ปดํฌ๋„ŒํŠธ์—๋„ ํด๋ž˜์Šค ์ด๋ฆ„์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

{title="src/App.js",lang=javascript}

const Table = ({ list, pattern, onDismiss }) =>
# leanpub-start-insert
  <div className="table">
# leanpub-end-insert
    {list.filter(isSearched(pattern)).map(item =>
# leanpub-start-insert
      <div key={item.objectID} className="table-row">
# leanpub-end-insert
        <span>
          <a href={item.url}>{item.title}</a>
        </span>
        <span>{item.author}</span>
        <span>{item.num_comments}</span>
        <span>{item.points}</span>
        <span>
          <Button
            onClick={() => onDismiss(item.objectID)}
# leanpub-start-insert
            className="button-inline"
# leanpub-end-insert
          >
            Dismiss
          </Button>
        </span>
# leanpub-start-insert
      </div>
# leanpub-end-insert
    )}
# leanpub-start-insert
  </div>
# leanpub-end-insert

CSS๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ์ปดํฌ๋„ŒํŠธ ์Šคํƒ€์ผ๋ง์„ ์ง€์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ์ข€ ์˜ˆ์˜๊ฒŒ ๋ณด์ด์ง€ ์•Š๋‚˜์š”? JSX๋ฌธ๋ฒ•์€ HTML๊ณผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ์„ž์–ด ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ธ๋ผ์ธ ์Šคํƒ€์ผ(Inline Style)๋กœ CSS๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์Šคํƒ€์ผ์„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋กœ ๋งŒ๋“  ๋‹ค์Œ ์Šคํƒ€์ผ ์†์„ฑ์— ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

์ธ๋ผ์ธ ์Šคํƒ€์ผ๋กœ ํ…Œ์ด๋ธ”์˜ ์—ด ๊ฐ€๋กœ ๋„“์ด๋ฅผ ์กฐ์ ˆํ•ด๋ด…์‹œ๋‹ค.

{title="src/App.js",lang=javascript}

const Table = ({ list, pattern, onDismiss }) =>
  <div className="table">
    {list.filter(isSearched(pattern)).map(item =>
      <div key={item.objectID} className="table-row">
# leanpub-start-insert
        <span style={{ width: '40%' }}>
          <a href={item.url}>{item.title}</a>
        </span>
        <span style={{ width: '30%' }}>
          {item.author}
        </span>
        <span style={{ width: '10%' }}>
          {item.num_comments}
        </span>
        <span style={{ width: '10%' }}>
          {item.points}
        </span>
        <span style={{ width: '10%' }}>
          <Button
            onClick={() => onDismiss(item.objectID)}
            className="button-inline"
          >
            Dismiss
          </Button>
        </span>
# leanpub-end-insert
      </div>
    )}
  </div>

๋‹ค์Œ์œผ๋กœ ์™ธ๋ถ€์— ์Šคํƒ€์ผ ๊ฐ์ฒด๋ฅผ ์ •์˜ํ•ด ์ข€ ๋” ์ฝ”๋“œ๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ๋งŒ๋“ค๊ฒ ์Šต๋‹ˆ๋‹ค.

{title="Code Playground",lang="javascript"}

const largeColumn = {
  width: '40%',
};

const midColumn = {
  width: '30%',
};

const smallColumn = {
  width: '10%',
};

์ด์ œ ๊ฐ ์—ด์— ์ ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ฐ€๋กœ ๋„“์ด๊ฐ€ 10%์ธ smallColumn ๊ฐ์ฒด๋Š” <span style={smallColumn}>์ด๋ผ๊ณ  ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

๋ฆฌ์•กํŠธ ์Šคํƒ€์ผ๋ง ๋ฐฉ๋ฒ•์€ ๋งค์šฐ ๋‹ค์–‘ํ•ฉ๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ์ˆœ์ˆ˜ํ•œ CSS์™€ ์ธ๋ผ์ธ ์Šคํƒ€์ผ์œผ๋กœ๋งŒ ์Šคํƒ€์ผ์„ ์ •์˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ์ด ์ •๋„๋กœ๋„ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.

์•ž์œผ๋กœ ์—ฌ๋Ÿฌ ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ ์Šคํƒ€์ผ๋ง ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์Šค์Šค๋กœ ์ฐพ์•„๋ณด๊ณ  ์ ์šฉํ•ด๋ณด๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ž…๋ฌธ ์ˆ˜์ค€์—์„œ๋Š” ์ˆœ์ˆ˜ํ•œ CSS์™€ ์ธ๋ผ์ธ ์Šคํƒ€์ผ๋งŒ์„ ์‚ฌ์šฉํ•  ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

{pagebreak}

2.14 ์ •๋ฆฌํ•˜๋ฉด

2์žฅ์—์„œ๋Š” ๊ธฐ๋ณธ์ ์ธ ๋ฆฌ์•กํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ๊นŒ์ง€ ํ•™์Šตํ•œ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•ด๋ด…์‹œ๋‹ค.

  • ๋ฆฌ์•กํŠธ
    • this.state์™€ setState()๋ฅผ ์‚ฌ์šฉํ•ด ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€ ์ƒํƒœ ๊ด€๋ฆฌ
    • ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋กœ ํ•จ์ˆ˜ ๋˜๋Š” ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ๋ฒ•
    • ํผ๊ณผ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ
    • ๋ฆฌ์•กํŠธ ๋‹จ๋ฑกํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„ ์›์น™
    • ์ œ์–ด๋˜์ง€ ์•Š์€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ œ์–ด๋˜๋Š” ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“ค๊ธฐ
    • ์ž์‹ ์ปดํฌ๋„ŒํŠธ์™€ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ ๊ตฌํ˜„
    • ES6 ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ ๋ฐ ๋น„์ƒํƒœ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์˜ ์‚ฌ์šฉ๊ณผ ๊ตฌํ˜„
    • ์ปดํฌ๋„ŒํŠธ ์Šคํƒ€์ผ๋ง ๋ฐฉ๋ฒ•
  • ES6
    • ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ์— ํ•จ์ˆ˜ ๋ฐ”์ธ๋”ฉ ๋ฐฉ๋ฒ•
    • ๊ฐ์ฒด์™€ ๋ฐฐ์—ด์˜ ๊ตฌ์กฐํ•ด์ฒด
    • ๊ธฐ๋ณธ ๋งค๊ฐœ๋ณ€์ˆ˜
  • ๊ธฐ๋ณธ
    • ๊ณ ์ฐจ ํ•จ์ˆ˜

์ž ์‹œ ํœด์‹์‹œ๊ฐ„์„ ๊ฐ€์ง‘์‹œ๋‹ค. ํ•™์Šตํ•œ ๋‚ด์šฉ์„ ๋˜์ƒˆ๊ธฐ๊ณ  ์ ์šฉํ•ด๋ณด๋ฉฐ ์ด๊ฒƒ์ €๊ฒƒ ๋งŒ๋“ค์–ด๋ณด๋ฉฐ ํ…Œ์ŠคํŠธํ•ด๋ณด๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์‹ค์Šต ์ฝ”๋“œ๋Š” ๊ณต์‹ ๊นƒํ—ˆ๋ธŒ ๋ฆฌํผ์ง€ํ† ๋ฆฌ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.