์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ๋ฆฌ์กํธ ๊ฐ๋ฐ์ ์์ํ๊ฒ ์ต๋๋ค. ์ด๋ฒ ์ฅ์์๋ ์ ์ ์ธ ์ปดํฌ๋ํธ๋ณด๋ค ๋์ ์ธ ์ปดํฌ๋ํธ์ ์ธํฐ๋ ์ ์ ๊ตฌํํด๋ณด๋ฉด์ ์ปดํฌ๋ํธ๋ฅผ ์ ์ธ๊ณผ ์ฌ์ฌ์ฉ์ด ๊ฐ๋ฅํ ์ปดํฌ๋ํธ๋ฅผ ๋ฐฐ์๋๋ค. ๋ง์ง๋ง์ผ๋ก ์ปดํฌ๋ํธ์ ์จ๊ฒฐ์ ๋ถ์ด๋ฃ์ด ์๋ช ์ฒด๋ก ๋ง๋ค์ด ๋ด ๋๋ค.
์ปดํฌ๋ํธ ๋ด๋ถ ์ํ(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๋ฅผ ์ฌ์ฉํ๊ณ ํ์ํฉ๋๋ค.
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 ๊ฐ์ฒด ์ด๊ธฐ์๋ฅผ ์์ฑํฉ๋๋ค.
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()
๋ฉ์๋๊ฐ ์ฌ์คํ๋์ด ์ต์ข
๋ทฐ๊ฐ ์
๋ฐ์ดํธ๋ฉ๋๋ค.
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
๋ฅผ ์ถ๋ ฅํฉ๋๋ค.
์ด๋ฒ ์ ์์๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ(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
ํธ๋ค๋ฌ์์ ์ฌ๋ฌ๊ฐ์ง ํจ์ ์ฌ์ฉ๋ฒ์ ์ฌ์ฉํด๋ด ๋๋ค.
์ด๋ฒ ์ ์์๋ ๊ฒ์ ๊ธฐ๋ฅ์ ๊ตฌํํด๋ณด๋ฉด์ 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>
);
}
}
๋ธ๋ผ์ฐ์ ๋ฅผ ์ด์ด ๊ฒ์ ๊ธฐ๋ฅ์ด ์ ์๋๋๋์ง ํ์ธํด๋ณด์ธ์.
- [๋ฆฌ์กํธ ๊ณต์ ๋ฌธ์] ๋ฆฌ์กํธ ์ด๋ฒคํธ
- [์ํคํผ๋์] ๊ณ ์ฐจ ํจ์(higher order functions)
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 ๋ฌธ๋ฒ์ ์น์ํด์ง๊ธธ ๋ฐ๋๋๋ค.
์์์ ์ฐ๋ฆฌ๋ ์ด๋ฏธ ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ์ ๋ํด ๋ฐฐ์ ์ต๋๋ค. ์์์ ๊ตฌํํ ๊ฒ์์ด ์
๋ ฅ ํ๋ ์ญ์ ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ ๊ท์น์ด ์ ์ฉ๋ฉ๋๋ค. ์
๋ ฅ ํ๋๋ ๋ฆฌ์คํธ๋ฅผ ํํฐ๋งํ๊ธฐ ์ํด 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 ํ๋ ์์ํฌ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ญ์ ๋ฆฌ์กํธ์ ์ํฅ์ ๋ฐ์ ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ์ ๋์ ํ์ต๋๋ค.
์ด๋์ 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 ์ปดํฌ๋ํธ๋ก ๋ถ๋ฆฌํ ๊ฒ์ฒ๋ผ ์ถ๊ฐ๋ก ๋ ๋ถ๋ฆฌํ ์ ์๋ ์ปดํฌ๋ํธ ์๋ ๋จผํธ๋ฅผ ์ฐพ์๋ด ๋๋ค. ย ย * ๋ค์ ์ฅ์์ ์ค์ต ์ฝ๋์ ์ถฉ๋์ด ์๊ธธ ์ ์์ผ๋ ์ง๊ธ ๋ฐ๋ก ์ฝ๋ฉ์ ํ์ง ๋ง์ธ์.
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
ํ๋กํผํฐ๋ฅผ ์ฌ์ฉํด ์ฌ๋ฌ ์ปดํฌ๋ํธ๋ฅผ ์๋ก ๋ผ์ ๋ฃ๊ณ ๋ง๋ฌผ๋ ค ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ปดํฌ๋ํธ์ ๊ตฌ์ฑ ๊ฐ๋ฅํ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํด ์ปดํฌ๋ํธ ๊ฐ ๊ณ์ธต์ ๋ง๋ค ์ ์์ต๋๋ค. ์ด ๊ฒ์ด ๋ฐ๋ก ๋ฆฌ์กํธ ๋ทฐ ๋ ์ด์ด์ ๊ธฐ์ด์ ๋๋ค. ๋ง์ง๋ง ์ฅ์์ ์ฌ์ฌ์ฉ์ฑ(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
๊ฐ ํ์๋์ง ์๊ณ ๋น ๋ฌธ์์ด๋ก ํ์๋ฉ๋๋ค.
์ง๊ธ๊น์ง 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 ์ปดํฌ๋ํธ๋ฅผ ๋น ์ํ ํจ์ ์ปดํฌ๋ํธ๋ก ๋ฆฌํฉํฐ๋งํฉ๋๋ค.
์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ์ปดํฌ๋ํธ๋ฅผ ์์๊ฒ ๊พธ๋ฉฐ ๋ด ์๋ค. 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์ ์ธ๋ผ์ธ ์คํ์ผ๋ง์ ์ฌ์ฉํ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
- [๋ฆฌ์กํธ ์ปดํฌ๋ํธ ์คํ์ผ๋ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ] styled-components
- [๋ฆฌ์กํธ ์ปดํฌ๋ํธ ์คํ์ผ๋ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ] CSS Modules
{pagebreak}
2์ฅ์์๋ ๊ธฐ๋ณธ์ ์ธ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค์์ต๋๋ค. ์ง๊ธ๊น์ง ํ์ตํ ๋ด์ฉ์ ์ ๋ฆฌํด๋ด ์๋ค.
- ๋ฆฌ์กํธ
this.state
์setState()
๋ฅผ ์ฌ์ฉํด ์ปดํฌ๋ํธ ๋ด๋ถ ์ํ ๊ด๋ฆฌ- ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ก ํจ์ ๋๋ ํด๋์ค ๋ฉ์๋๋ฅผ ์ ๋ฌํ๋ ๋ฐฉ๋ฒ
- ํผ๊ณผ ์ด๋ฒคํธ ์ฒ๋ฆฌ
- ๋ฆฌ์กํธ ๋จ๋ฑกํฅ ๋ฐ์ดํฐ ํ๋ฆ ์์น
- ์ ์ด๋์ง ์์ ์ปดํฌ๋ํธ๋ฅผ ์ ์ด๋๋ ์ปดํฌ๋ํธ๋ก ๋ง๋ค๊ธฐ
- ์์ ์ปดํฌ๋ํธ์ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ปดํฌ๋ํธ ๊ตฌํ
- ES6 ํด๋์ค ์ปดํฌ๋ํธ ๋ฐ ๋น์ํ ํจ์ํ ์ปดํฌ๋ํธ์ ์ฌ์ฉ๊ณผ ๊ตฌํ
- ์ปดํฌ๋ํธ ์คํ์ผ๋ง ๋ฐฉ๋ฒ
- ES6
- ํด๋์ค ๋ฉ์๋์ ํจ์ ๋ฐ์ธ๋ฉ ๋ฐฉ๋ฒ
- ๊ฐ์ฒด์ ๋ฐฐ์ด์ ๊ตฌ์กฐํด์ฒด
- ๊ธฐ๋ณธ ๋งค๊ฐ๋ณ์
- ๊ธฐ๋ณธ
- ๊ณ ์ฐจ ํจ์
์ ์ ํด์์๊ฐ์ ๊ฐ์ง์๋ค. ํ์ตํ ๋ด์ฉ์ ๋์๊ธฐ๊ณ ์ ์ฉํด๋ณด๋ฉฐ ์ด๊ฒ์ ๊ฒ ๋ง๋ค์ด๋ณด๋ฉฐ ํ ์คํธํด๋ณด๊ธธ ๋ฐ๋๋๋ค.
์ค์ต ์ฝ๋๋ ๊ณต์ ๊นํ๋ธ ๋ฆฌํผ์งํ ๋ฆฌ์์ ํ์ธํ ์ ์์ต๋๋ค.