๐ธ goTrip ์ค์ต - React
๋์์ด๋์ ์์ด์ดํ๋ ์์ผ๋ก ์๊ฒฌ์ ๋๋ ๋ ๋ถํฐ ๊ฐ๋ฐํด์ผํ๋ ๊ธฐ๋ฅ์ ์์ฝํด๋๋๊ฒ ์ข์
goTrip ๊ธฐ๋ฅ ์์ฝ
- ์ฌ์ฉ์๊ฐ ์
๋ ฅ ํ๋์ ํ
์คํธ๋ฅผ ์
๋ ฅํ๋ฉด ์ํ์ ์ ์ฅ๋จ
- ์
๋ ฅ๋ ํ
์คํธ๊ฐ ์๊ฑฐ๋ 2๊ธ์ ์ดํ์ผ ๊ฒฝ์ฐ ๊ฒฝ๊ณ ๋ฉ์์ง ํ์
- ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํต๊ณผํ ํ
์คํธ๋ ์๋ก์ด ํญ๋ชฉ์ผ๋ก ๋ฆฌ์คํธ์ ์ถ๊ฐ๋จ
- ์ฌ์ฉ์๊ฐ ์ํฐํค๋ฅผ ๋๋ฅด๊ฑฐ๋ ADD ๋ฒํผ์ ํด๋ฆญํ๋ฉด ํญ๋ชฉ ์ถ๊ฐ ๊ธฐ๋ฅ ์คํ
useState์ ์ํ ๋ถ๋ณ์ฑ
1
2
3
4
5
6
| const changeText = () => {
setData(prev => [...prev, 'test'])
setAge(prev => prev + num) // โ
์ด์ ๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก ์
๋ฐ์ดํธ
setAge(age + num) // โ ๏ธ ์
๋ฐ์ดํธ๊ฐ ๋น๋๊ธฐ๋ผ ์์ธก ๋ถ๊ฐ
}
|
์๋ ๋ฐฉ์
prev
: ์ด์ ๋ฐ์ดํฐ ์ํ[...prev, 'test']
: ๊ธฐ์กด ๋ฐ์ดํฐ๋ฅผ ๋ณต์ ํ ์๋ก์ด ๊ฐ ์ถ๊ฐ- setData(โฆ) : ์ํ ์
๋ฐ์ดํธ โ ์ปดํฌ๋ํธ ์ฌ๋ ๋๋ง
- โ๏ธ ์ด๊ธฐ ์ํ(๊ธฐ์กด ๋ฐฐ์ด)๋ ์ง์ ๋ณ๊ฒฝํ๋ฉด ์ ๋จ โ ๋ถ๋ณ์ฑ ์ ์ง ํ์!
์ ํจ์ฑ ๊ฒ์ฌ ๋ฆฌํฉํ ๋ง (์ผํญ ์ฐ์ฐ์ ์ฌ์ฉ)
๊ธฐ์กด ์ฝ๋ ์กฐ๊ฑด๋ฌธ
1
2
3
4
5
6
7
8
9
| if (inputText.trim() === '') {
alert('์ฌํ์ง๋ฅผ ์
๋ ฅํ์ธ์')
document.querySelector('input').focus()
return
} else if (inputText.trim().length < 2) {
alert('2์ ์ด์๋ก ์
๋ ฅํ์ธ์')
document.querySelector('input').focus()
return
}
|
โก ์ด๊ฑธ ์ผํญ ์ฐ์ฐ์๋ก ๋ฆฌํฉํ ๋ง
1
2
3
4
5
| if (inputText.trim().length < 2) {
alert(inputText.trim() === '' ? '์ฌํ์ง๋ฅผ ์
๋ ฅํ์ธ์' : '2์ ์ด์์ผ๋ก ์
๋ ฅํ์ธ์');
document.querySelector('input').focus();
return;
}
|
input๊ณผ label์ ํจ๊ป ์ฌ์ฉํ๋ฉด ์ ๊ทผ์ฑ๊ณผ UX๊ฐ ํฅ์๋จ. label์ htmlFor๋ input์ id์ ์ฐ๊ฒฐ๋จ
1
2
3
4
5
6
7
8
9
10
11
| <div className="inputFild mw">
<label htmlFor="fild">์ฌํ์ง ์
๋ ฅ</label>
<input
placeholder="์ฌํ์ง๋ฅผ ์
๋ ฅํ์ธ์"
type="text"
id="fild"
onChange={inputItem}
/>
<button onClick={addItem}>์
๋ ฅ</button>
</div>
|
- ์ฌ์ฉ์๊ฐ label ํด๋ฆญํด๋ input์ ํฌ์ปค์ค๊ฐ ๊ฐ
- ์๊ฐ์ฅ์ ์ธ์ฉ ์คํฌ๋ฆฐ๋ฆฌ๋์ ์ ์ ํ ์ธ์๋จ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| import React, { useState } from 'react'
const InputFild = ({ setData }) => {
const [inputText, setInputText] = useState('');
// ์
๋ ฅ๊ฐ์ ์ํ์ ์ค์๊ฐ ๋ฐ์
const inputItem = e => {
setInputText(e.target.value)
}
const addItem = () => {
// ์
๋ ฅ๊ฐ ์ ํจ์ฑ ๊ฒ์ฌ
if (inputText.trim().length < 2) {
alert(inputText.trim() === '' ? '์ฌํ์ง๋ฅผ ์
๋ ฅํ์ธ์' : '2์ ์ด์๋ก ์
๋ ฅํ์ธ์')
document.querySelector('input').focus() // ํฌ์ปค์ค๋ฅผ ๋ค์ input์ ์ค
return
}
setData(prev => [inputText, ...prev]) // ๊ธฐ์กด ๋ฆฌ์คํธ ์์ ์ถ๊ฐํด์ ์๋ก์ด ๋ฐฐ์ด๋ก ๋ฐํ
setInputText('')
document.querySelector('input').focus()
}
const handleKeyup = e => {
if (e.key === 'Enter') addItem()
}
return (
<div className="inputFild mw">
<input
type="text"
placeholder="์ฌํ์ง๋ฅผ ์
๋ ฅํ์ธ์"
value={inputText}
onChange={inputItem}
onKeyUp={handleKeyup}
/>
<button onClick={addItem}>์
๋ ฅ</button>
</div>
)
}
export default InputFild
|
๐ onKeyDown
vs onKeyUp
์ด๋ฒคํธ | ๋ฐ์ ์์ | ํน์ง |
---|
onKeyDown | ํค๋ฅผ ๋๋ ์ ๋ | - ๊ฐ์ฅ ๋จผ์ ๋ฐ์ - ๊พน ๋๋ฅด๊ณ ์์ผ๋ฉด ๋ฐ๋ณต ๋ฐ์ |
onKeyUp | ํค๋ฅผ ๋์ ๋ | - ํ ๋ฒ๋ง ๋ฐ์ - ๋๋ฅด๊ณ ์๋ ๋์์ ์ ๋ฐ์ |
๐ ์ญ์ ๊ธฐ๋ฅ
๋ฆฌ์คํธ์์ ํน์ ํญ๋ชฉ ์ญ์ ์ filter()๋ฅผ ํ์ฉ
1
| setData(prev => prev.filter(item => item !== area))
|
๐ ๋ด๊ฐ ๊ณ์ ์๋ฌ๊ฐ ๋ฌ ์ฝ๋
1
2
3
| setData(prev => {
prev.filter(item => item !== area)
})
|
- ์ด ์ฝ๋๋ ์๋ก์ด ์ํ๊ฐ์ ๋ฆฌํดํ์ง ์์.
- setData์ ์ธ์๋ก ์ ๋ฌ๋ ํจ์๋ ์๋ก์ด ์ํ๊ฐ์ ๋ฆฌํดํด์ผ ํ๋๋ฐ, filter ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ์ง ์๊ณ ๋๋จ โ undefined ๋ฆฌํด๋จ.
- ๋ฐ๋ผ์ setData(undefined)๊ฐ ํธ์ถ๋ผ์ ๋น์ ์ ๋์
โ
์ฌ๋ฐ๋ฅธ ๋ฐฉ์
1
2
3
4
5
6
| setData(prev => {
return prev.filter(item => item !== area)
})
// ๋๋
setData(prev => prev.filter(item => item !== area))
|
์ ์ฒด ์ฝ๋ (src/components/List.jsx)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| import React from 'react'
const List = ({ area, setData }) => {
const removeItem = () => {
setData(prev => {
const newData = prev.filter(item => item !== area)
localStorage.setItem('trip', JSON.stringify(newData))
return newData
})
}
return (
<li>
<p>{area}</p>
<i className="fa-solid fa-trash-can" onClick={removeItem}></i>
</li>
)
}
export default List
|
โ
localstorage
setItem
: ๊ฐ ์ ์ฅ (JSON ๋ฌธ์์ด๋ก ๋ณํํด์ ์ ์ฅ)getItem
: ๊ฐ ์ฝ๊ธฐ (ํ์ฑํด์ ์ฌ์ฉ)
์ถ๊ฐํ ๋
1
2
3
4
5
| setData(prev => {
const newData = [...prev, inputText]
localStorage.setItem('trip', JSON.stringify(newData))
return newData
})
|
์ญ์ ํ ๋
1
2
3
4
5
| setData(prev => {
const newData = prev.filter(item => item !== area)
localStorage.setItem('trip', JSON.stringify(newData))
return newData
})
|
๋ถ๋ฌ์ฌ ๋
1
| const list = JSON.parse(localStorage.getItem('trip')) || []
|
๋ก๋ ํฝ์จ (Lorem Picsum)
๋๋ค ์ด๋ฏธ์ง ์์ฑ url Lorem Picsum
๋๋ค ์ด๋ฏธ์ง url ๊ฐ์ ธ์์ ์ฌ์ฉํ๋ฉด ๋จ !
CSS Modules
- CSS ๋ชจ๋์ ์ฌ์ฉํ๋ฉด ์ปดํฌ๋ํธ ๋จ์๋ก ์คํ์ผ์ ์บก์ํํ ์ ์์
- ํด๋์ค ์ด๋ฆ์ด ์๋์ผ๋ก ๊ณ ์ ํ๊ฒ ๋ง๋ค์ด์ง๊ธฐ ๋๋ฌธ์ ์ถฉ๋ ๊ฑฑ์ ์์ด ์ฌ์ฌ์ฉ ๊ฐ๋ฅ
- ํ์ผ๋ช
์
[์ปดํฌ๋ํธ์ด๋ฆ].module.css
ํ์์ผ๋ก ์ ์ฅํ๊ณ ์ฌ์ฉ
sample.module.css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| .imgCon {
border: 1px solid red;
width: 200px;
height: 200px;
margin: 1rem;
position: relative;
overflow: hidden;
border-radius: 2rem;
}
.imgCon > * {
position: absolute;
}
.imgCon img {
opacity: 0.5;
width: 100%;
height: 100%;
object-fit: cover;
}
|
jsxํ์ผ์ ์ ์ฉ - App.jsx
1
2
3
4
5
6
7
| import css from './sample.module.css'
<div className={css.imgCon}>
<p>{userInfo.artwork.title}</p>
<p>{userInfo.artwork.artist}</p>
<img src={userInfo.artwork.imgUrl} alt={userInfo.artwork.title} />
</div>
|
์ ์ญ CSS์ ํจ๊ป ์ฐ๊ธฐ
<div className={`${css.imgCon} mw`}>
- css.imgCon: CSS Module๋ก ๋ถ๋ฌ์จ ํด๋์ค
- mw: ์ ์ญ์ผ๋ก ์ ์ธํ ํด๋์ค (max-width ๋ฑ)
CSS Modules ์ฌ์ฉ ์ด์
- ์ปดํฌ๋ํธ๋ง๋ค ๊ณ ์ ํ ํด๋์ค ์ด๋ฆ์ด ์๋์ผ๋ก ์์ฑ๋จ
- ์:
imgCon
โ InputFild_imgCon__3h9Ks
์ฒ๋ผ ๋ณ๊ฒฝ
- ๊ฐ์ ์ด๋ฆ์ ๋ค๋ฅธ ์ปดํฌ๋ํธ์์ ์จ๋ ์ถฉ๋ ์์
- ๊ธ๋ก๋ฒ ์คํ์ผ์ด๋ Styled-components์ฒ๋ผ ๋ณ๋ ์ค์ ์ด ํ์ ์์
์ด๋ค ์คํ์ผ ๋ฐฉ๋ฒ์ ์ ํํ ๊น?
๋ฐฉ๋ฒ | ํน์ง ๋ฐ ์ถ์ฒ ์ํฉ |
---|
์ ์ญ CSS | ์ฑ ์ ์ฒด์ ์ ์ฉ๋๋ ๋ฆฌ์
, ํฐํธ, ์์ ๋ฑ ๊ณตํต ์คํ์ผ์ฉ |
CSS Modules | ์ปดํฌ๋ํธ๋ณ ์คํ์ผ๋ง์ด ํ์ํ์ง๋ง ๋ณ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด ์ฌ์ฉํ๊ณ ์ถ์ ๋ |
Styled-components | ๋์ ์คํ์ผ๋ง์ด ๋ง๊ฑฐ๋ ์ปดํฌ๋ํธ์ ์คํ์ผ์ ๋ฐ์ ํ๊ฒ ์ฐ๊ฒฐํ๊ณ ์ถ์ ๋ |
์ค์ต ํ๋ฉด
์ด๋ฏธ์ง hover์, ์ ๋ชฉ, ์ํฐ์คํธ๋ช
๋์ด
๐ธ useState
- useState๋ ์ปดํฌ๋ํธ์ ์ํ๋ฅผ ์ถ๊ฐํ๋ Hook
- ํ๋ฉด์ ๋ณด์ฌ์ฃผ๋ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ๋ณ๊ฒฝํ ์ ์๊ฒ ํด์ฃผ๋ ๊ธฐ๋ฅ
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
1
| const [state, setState] = useState(์ด๊ธฐ๊ฐ);
|
state
: ํ์ฌ ์ํ ๊ฐsetState
: ์ํ๋ฅผ ์
๋ฐ์ดํธํ๋ ํจ์์ด๊ธฐ๊ฐ
: ์ฒ์ ๋ ๋๋ง ์ ์ฌ์ฉํ ๊ฐ
ํต์ฌ ํน์ง
1. ์ํ ์
๋ฐ์ดํธ
์ํ๋ฅผ ์
๋ฐ์ดํธํ๋ฉด ์ปดํฌ๋ํธ๊ฐ ์๋์ผ๋ก ๋ค์ ๋ ๋๋ง๋๋ค.
1
2
| setState(์๋ก์ด๊ฐ); // ์ง์ ๊ฐ ์ ๋ฌ
setState(prevState => prevState + 1); // ์ด์ ์ํ ๊ธฐ๋ฐ ์
๋ฐ์ดํธ
|
1
2
3
4
5
6
7
8
9
10
11
| function ์ํ์นด๋() {
const [์๋, ์๋๋ณ๊ฒฝ] = useState(1);
return (
<div>
<p>์ ํ ์๋: {์๋}๊ฐ</p>
<button onClick={() => ์๋๋ณ๊ฒฝ(์๋ + 1)}>์ถ๊ฐ</button>
<button onClick={() => ์๋๋ณ๊ฒฝ(์๋ - 1)}>๊ฐ์</button>
</div>
);
}
|
2. ์ค์ํ ๊ท์น
- Hook์ ์ปดํฌ๋ํธ ์ต์์ ๋ ๋ฒจ์์๋ง ํธ์ถ
- ์กฐ๊ฑด๋ฌธ, ๋ฐ๋ณต๋ฌธ, ์ด๋ฒคํธ ํธ๋ค๋ฌ ์์์ ์ง์ ํธ์ถ X
- ๋์ , ์ด๋ฒคํธ ํธ๋ค๋ฌ์์๋ ์ฝ๋ฐฑ ์์์ setState๋ ํธ์ถ ๊ฐ๋ฅ
1
2
3
4
5
6
7
8
9
10
| // โ ์๋ชป๋ ์ฌ์ฉ
if (์กฐ๊ฑด) {
const [x, setX] = useState(0);
}
// โ
์ฌ๋ฐ๋ฅธ ์ฌ์ฉ
const [x, setX] = useState(0);
if (์กฐ๊ฑด) {
setX(5);
}
|
3. ๊ฐ์ฒด์ ๋ฐฐ์ด ์ํ
์ํ๋ก ๊ฐ์ฒด๋ ๋ฐฐ์ด์ ๋ค๋ฃฐ ๋๋ ๋ถ๋ณ์ฑ(immutability) ์ ์ง์ผ์ผ ํ๋ค.
์ฆ, ๊ธฐ์กด ๊ฐ์ ์ง์ ์์ ํ์ง ๋ง๊ณ , ๋ณต์ฌ ํ ๋ณ๊ฒฝํ๋ ๋ฐฉ์์ผ๋ก ์
๋ฐ์ดํธํด์ผ ํจ
1
2
3
4
5
6
7
8
9
10
11
| // โ ์๋ชป๋ ๋ฐฉ๋ฒ (์ง์ ์์ )
form.name = '๊น์ฒ ์';
// โ
์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ (๋ณต์ฌ ํ ๋ณ๊ฒฝ)
setForm({ ...form, name: '๊น์ฒ ์' });
// โ
๋ฐฐ์ด์ ํญ๋ชฉ ์ถ๊ฐ
setItems([...items, ์ํญ๋ชฉ]);
// โ
๋ฐฐ์ด์์ ํญ๋ชฉ ์ ๊ฑฐ
setItems(items.filter(item => item.id !== ์ญ์ ํ ID));
|
์์ : ํ์๊ฐ์
ํผ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| function ํ์๊ฐ์
ํผ() {
const [ํ์์ ๋ณด, ํ์์ ๋ณด๋ณ๊ฒฝ] = useState({
์์ด๋: '',
๋น๋ฐ๋ฒํธ: '',
์ด๋ฉ์ผ: '',
์ ํ๋ฒํธ: ''
});
const ์
๋ ฅ๋ณ๊ฒฝ = (e) => {
// ๊ธฐ์กด ๊ฐ์ฒด ๋ณต์ฌ ํ ํน์ ์์ฑ๋ง ๋ณ๊ฒฝ
ํ์์ ๋ณด๋ณ๊ฒฝ({
...ํ์์ ๋ณด,
[e.target.name]: e.target.value
});
};
return (
<form>
<input
name="์์ด๋"
value={ํ์์ ๋ณด.์์ด๋}
onChange={์
๋ ฅ๋ณ๊ฒฝ}
/>
<input
name="์ด๋ฉ์ผ"
value={ํ์์ ๋ณด.์ด๋ฉ์ผ}
onChange={์
๋ ฅ๋ณ๊ฒฝ}
/>
</form>
);
}
|
5. ์ํ ์
๋ฐ์ดํธ ํน์ฑ
- ๋น๋๊ธฐ์
1
2
| setCount(count + 1);
console.log(count); // ๋ฐ๋ก ๋ฐ์๋์ง ์์
|
setState ํธ์ถ ์งํ ์ํ๊ฐ ์ฆ์ ๋ณ๊ฒฝ๋๋๊ฒ ์๋๋ผ, ๋ค์ ๋ ๋๋ง ๋ ์ ์ฉ๋ผ.
์ผ๊ด ์ฒ๋ฆฌ(Batching) React๋ ์ฌ๋ฌ ์ํ ์
๋ฐ์ดํธ๋ฅผ ํ ๋ฒ์ ์ฒ๋ฆฌํจ. ๊ทธ๋์ ์ฌ๋ฌ ๋ฒ ์ฐ์ ํธ์ถํด๋ ์ต์ ์ํ๋ฅผ ๊ธฐ์ค์ผ๋ก ๋์ํ์ง ์์ ์ ์์.
- ์ด์ ์ํ ๊ธฐ๋ฐ ์
๋ฐ์ดํธ ์ด์ ์ํ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์
๋ฐ์ดํธํ ๋๋ ํจ์ ํํ๋ฅผ ์ฌ์ฉ
1
| setCount(prev => prev + 1);
|
์์ฃผ ํ๋ ์ค์
1. ์ํ ์
๋ฐ์ดํธ ํ ์ด์ ๊ฐ ์ถ๋ ฅ
1
2
3
4
| function ํด๋ฆญํธ๋ค๋ฌ() {
set๊ฐ์(๊ฐ์ + 1);
console.log(๊ฐ์); // ์ฌ์ ํ ์ด์ ๊ฐ ์ถ๋ ฅ
}
|
-> ์ํ ์
๋ฐ์ดํธ๋ ๋น๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌ๋์ด ์ฆ์ ๋ฐ์๋์ง ์์
2. ๊ฐ์ฒด/๋ฐฐ์ด ๋ณ๊ฒฝ์ด ํ๋ฉด์ ๋ฐ์๋์ง ์์
1
2
3
4
5
6
7
8
9
10
11
12
| // ์๋ชป๋ ๋ฐฉ๋ฒ
const ํญ๋ชฉ๋ค = [...ํ ์ผ๋ชฉ๋ก];
ํญ๋ชฉ๋ค[0].์๋ฃ = true;
setํ ์ผ๋ชฉ๋ก(ํญ๋ชฉ๋ค); // ์ฐธ์กฐ๊ฐ ๊ฐ์ ๋ณ๊ฒฝ ๊ฐ์ง ์๋จ -> ๊ฐ์ฒด ํ์
// ์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ
setํ ์ผ๋ชฉ๋ก(์ด์ ๋ชฉ๋ก =>
์ด์ ๋ชฉ๋ก.map((ํญ๋ชฉ, ์ธ๋ฑ์ค) =>
์ธ๋ฑ์ค === 0 ? {...ํญ๋ชฉ, ์๋ฃ: true} : ํญ๋ชฉ
)
);
|
3. โToo many re-rendersโ ์ค๋ฅ
1
2
3
4
5
6
| // ์๋ชป๋ ๋ฐฉ๋ฒ
return <button onClick={ํธ๋ค๋ฌ()}>ํด๋ฆญ</button> // โ
// ์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ
return <button onClick={ํธ๋ค๋ฌ}>ํด๋ฆญ</button> // โ
return <button onClick={() => ํธ๋ค๋ฌ()}>ํด๋ฆญ</button> // โ
|
์ ์ฉ tip
์ด๊ธฐ ์ํ๋ฅผ ๊ณ์ฐํ๋ ํจ์ ์ฌ์ฉ
๋ฌด๊ฑฐ์ด ๊ณ์ฐ์ด ํ์ํ ์ด๊ธฐ๊ฐ์ ํจ์๋ก ์ ๋ฌํ๋ฉด ์ฒซ ๋ ๋๋ง์๋ง ์คํ๋ฉ๋๋ค.
1
2
3
4
5
| // ๋งค ๋ ๋๋ง๋ง๋ค ์คํ (๋นํจ์จ์ )
const [items, setItems] = useState(createExpensiveList()); // โ
// ์ด๊ธฐํ ์์๋ง ํ ๋ฒ ์คํ (ํจ์จ์ )
const [items, setItems] = useState(() => createExpensiveList()); // โ
|
key๋ฅผ ์ฌ์ฉํ ์ํ ์ด๊ธฐํ
์ปดํฌ๋ํธ์ ๋ค๋ฅธ key๋ฅผ ์ ๋ฌํ๋ฉด React๋ ์ปดํฌ๋ํธ๋ฅผ ์๋ก ์์ฑํ๊ณ ์ํ๋ฅผ ์ด๊ธฐํํฉ๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
| function ์ฑ() {
const [์ฌ์ฉ์ID, ์ฌ์ฉ์ID๋ณ๊ฒฝ] = useState(1);
return (
<>
<button onClick={() => ์ฌ์ฉ์ID๋ณ๊ฒฝ(์ฌ์ฉ์ID + 1)}>
๋ค์ ์ฌ์ฉ์
</button>
<ํ๋กํ key={์ฌ์ฉ์ID} /> {/* key๊ฐ ๋ณ๊ฒฝ๋๋ฉด ์ํ ์ด๊ธฐํ */}
</>
);
}
|
๐ธ ๋ฐฐ์ด๊ณผ ํจ๊ป ์ฌ์ฉํ๋ ๊ณ ์ฐจํจ์
๊ณ ์ฐจํจ์(Higher Order Functions)๋ ๋ค๋ฅธ ํจ์๋ฅผ ์ธ์๋ก ๋ฐ๊ฑฐ๋ ํจ์๋ฅผ ๋ฐํํ๋ ํจ์
1. map() - ๋ฐฐ์ด์ ๋ชจ๋ ์์๋ฅผ ๋ณํํ์ฌ ์ ๋ฐฐ์ด ๋ฐํ
1
2
3
4
5
6
7
8
9
10
11
12
13
| // ์ซ์ ๋ฐฐ์ด์ ๊ฐ ์์๋ฅผ 2๋ฐฐ๋ก ๋ง๋ค๊ธฐ
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// ๊ฐ์ฒด ๋ฐฐ์ด์์ ํน์ ์์ฑ๋ง ์ถ์ถํ๊ธฐ
const users = [
{ id: 1, name: '๊น์ฒ ์', age: 25 },
{ id: 2, name: '์ด์ํฌ', age: 30 },
{ id: 3, name: '๋ฐ๋ฏผ์', age: 28 }
];
const names = users.map(user => user.name);
console.log(names); // ['๊น์ฒ ์', '์ด์ํฌ', '๋ฐ๋ฏผ์']
|
2. filter() - ์กฐ๊ฑด์ ๋ง๋ ์์๋ง ํฌํจํ๋ ์ ๋ฐฐ์ด ๋ฐํ
1
2
3
4
5
6
7
8
9
10
11
12
13
| // ์ง์๋ง ํํฐ๋งํ๊ธฐ
const numbers = [1, 2, 3, 4, 5, 6];
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4, 6]
// ํน์ ๋์ด ์ด์์ธ ์ฌ์ฉ์๋ง ํํฐ๋งํ๊ธฐ
const users = [
{ name: '๊น์ฒ ์', age: 25 },
{ name: '์ด์ํฌ', age: 30 },
{ name: '๋ฐ๋ฏผ์', age: 18 }
];
const adults = users.filter(user => user.age >= 20);
console.log(adults); // [{ name: '๊น์ฒ ์', age: 25 }, { name: '์ด์ํฌ', age: 30 }]
|
3. reduce() - ๋ฐฐ์ด์ ์์๋ฅผ ํ๋์ ๊ฐ์ผ๋ก ์ค์ด๊ธฐ
1
2
3
4
5
6
7
8
9
10
11
12
13
| // ๋ฐฐ์ด ์์์ ํฉ๊ณ ๊ตฌํ๊ธฐ
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((accumulator, current) => accumulator + current, 0);
console.log(sum); // 15
// ๊ฐ์ฒด ๋ฐฐ์ด์ ํน์ ์์ฑ ํฉ๊ณ ๊ตฌํ๊ธฐ
const cart = [
{ item: '๋
ธํธ๋ถ', price: 1200000 },
{ item: '๋ง์ฐ์ค', price: 35000 },
{ item: 'ํค๋ณด๋', price: 45000 }
];
const totalPrice = cart.reduce((total, product) => total + product.price, 0);
console.log(totalPrice); // 1280000
|
4. forEach() - ๋ฐฐ์ด์ ๊ฐ ์์์ ๋ํด ํจ์ ์คํ
1
2
3
4
5
6
7
8
9
10
11
12
13
| // ๋ฐฐ์ด์ ๊ฐ ์์ ์ถ๋ ฅํ๊ธฐ
const fruits = ['์ฌ๊ณผ', '๋ฐ๋๋', '์ค๋ ์ง'];
fruits.forEach(fruit => console.log(fruit));
// ์ฌ๊ณผ
// ๋ฐ๋๋
// ์ค๋ ์ง
// ๋ฐฐ์ด ์์์ ํฉ๊ณ ๊ณ์ฐํ๊ธฐ (๋ถ์ ํจ๊ณผ ์ฌ์ฉ)
let total = 0;
[1, 2, 3, 4].forEach(num => {
total += num;
});
console.log(total); // 10
|
5. find() - ์กฐ๊ฑด์ ๋ง์กฑํ๋ ์ฒซ ๋ฒ์งธ ์์ ๋ฐํ
1
2
3
4
5
6
7
8
| // ํน์ ID๋ฅผ ๊ฐ์ง ์ฌ์ฉ์ ์ฐพ๊ธฐ
const users = [
{ id: 1, name: '๊น์ฒ ์' },
{ id: 2, name: '์ด์ํฌ' },
{ id: 3, name: '๋ฐ๋ฏผ์' }
];
const user = users.find(user => user.id === 2);
console.log(user); // { id: 2, name: '์ด์ํฌ' }
|
6. some() - ํ๋๋ผ๋ ์กฐ๊ฑด์ ๋ง์กฑํ๋ฉด true ๋ฐํ
1
2
3
4
| // ๋ฐฐ์ด์ ์์๊ฐ ์๋์ง ํ์ธ
const numbers = [1, 2, 3, -1, 4];
const hasNegative = numbers.some(num => num < 0);
console.log(hasNegative); // true
|
7. every() - ๋ชจ๋ ์์๊ฐ ์กฐ๊ฑด์ ๋ง์กฑํ๋ฉด true ๋ฐํ
1
2
3
4
5
6
7
8
| // ๋ชจ๋ ์ฌ์ฉ์๊ฐ ์ฑ์ธ์ธ์ง ํ์ธ
const users = [
{ name: '๊น์ฒ ์', age: 25 },
{ name: '์ด์ํฌ', age: 30 },
{ name: '๋ฐ๋ฏผ์', age: 18 }
];
const allAdults = users.every(user => user.age >= 20);
console.log(allAdults); // false
|
8. flatMap() - map() ํ ๊ฒฐ๊ณผ๋ฅผ 1๋ ๋ฒจ ํํํ
1
2
3
4
| // ๋ฌธ์ฅ์ ๋จ์ด ๋ฐฐ์ด๋ก ๋ณํ
const sentences = ['Hello world', 'I love JavaScript'];
const words = sentences.flatMap(sentence => sentence.split(' '));
console.log(words); // ['Hello', 'world', 'I', 'love', 'JavaScript']
|
9. sort() - ๋ฐฐ์ด ์์ ์ ๋ ฌ
1
2
3
4
5
6
7
8
9
10
11
12
| const numbers = [3, 1, 4, 1, 5];
numbers.sort((a, b) => a - b);
console.log(numbers); // [1, 1, 3, 4, 5]
// ๊ฐ์ฒด ๋ฐฐ์ด ์ ๋ ฌ
const users = [
{ name: '๊น์ฒ ์', age: 25 },
{ name: '์ด์ํฌ', age: 30 },
{ name: '๋ฐ๋ฏผ์', age: 18 }
];
users.sort((a, b) => a.age - b.age);
console.log(users); // ๋์ด์์ผ๋ก ์ ๋ ฌ๋จ
|
10. findIndex() - ์กฐ๊ฑด์ ๋ง์กฑํ๋ ์ฒซ ๋ฒ์งธ ์์์ ์ธ๋ฑ์ค ๋ฐํ
1
2
3
| const fruits = ['์ฌ๊ณผ', '๋ฐ๋๋', '์ค๋ ์ง', 'ํฌ๋'];
const index = fruits.findIndex(fruit => fruit === '์ค๋ ์ง');
console.log(index); // 2
|
11. flat() - ์ค์ฒฉ ๋ฐฐ์ด์ ํํํ
1
2
3
| const nestedArray = [1, [2, 3], [4, [5, 6]]];
const flattened = nestedArray.flat(2); // ๊น์ด 2๊น์ง ํํํ
console.log(flattened); // [1, 2, 3, 4, 5, 6]
|
12. reduceRight() - ์ค๋ฅธ์ชฝ์์ ์ผ์ชฝ์ผ๋ก reduce ์คํ
1
2
3
| const array = ['a', 'b', 'c', 'd'];
const result = array.reduceRight((acc, curr) => acc + curr);
console.log(result); // 'dcba'
|
13. at() - ์์ ๋๋ ์์ ์ธ๋ฑ์ค๋ก ์์์ ์ ๊ทผ (ES2022)
1
2
3
| const array = [5, 12, 8, 130, 44];
console.log(array.at(2)); // 8
console.log(array.at(-1)); // 44 (๋ง์ง๋ง ์์)
|
14. Array.from() - ์ ์ฌ ๋ฐฐ์ด ๊ฐ์ฒด๋ ์ดํฐ๋ฌ๋ธ์ ๋ฐฐ์ด๋ก ๋ณํ
1
2
3
4
5
| // ๋ฌธ์์ด์ ๊ฐ ๋ฌธ์๋ฅผ ๋ฐฐ์ด๋ก ๋ณํ
console.log(Array.from('hello')); // ['h', 'e', 'l', 'l', 'o']
// ๋งคํ ํจ์ ์ฌ์ฉ (๋ ๋ฒ์งธ ์ธ์)
console.log(Array.from([1, 2, 3], x => x * 2)); // [2, 4, 6]
|
15. Array.of() - ์ฃผ์ด์ง ์ธ์๋ก ์ ๋ฐฐ์ด ์์ฑ
1
2
| console.log(Array.of(1, 2, 3)); // [1, 2, 3]
console.log(Array.of('a', 'b', 'c')); // ['a', 'b', 'c']
|
16. entries(), keys(), values() - ๋ฐฐ์ด์ ๋ฐ๋ณต์ ๋ฉ์๋
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| const array = ['a', 'b', 'c'];
// entries() - [์ธ๋ฑ์ค, ๊ฐ] ์์ ๋ฐ๋ณต์ ๋ฐํ
for (const [index, element] of array.entries()) {
console.log(index, element);
}
// keys() - ์ธ๋ฑ์ค ๋ฐํ
for (const index of array.keys()) {
console.log(index);
}
// values() - ๊ฐ ๋ฐํ
for (const element of array.values()) {
console.log(element);
}
|
๐ธ ๋ณต์ต
โ
array, object state ๋ณ๊ฒฝ ์ ์ฃผ์์ฌํญ
๐ก React์์ ๋ฐฐ์ด๊ณผ ๊ฐ์ฒด๋ ๋ถ๋ณ์ฑ์ ์งํค๋ฉฐ ์ํ๋ฅผ ์
๋ฐ์ดํธํด์ผ ํจ (์๋ณธ ์ง์ ์์ โ)
๋ฐฐ์ด(Array) ์ํ ๋ณ๊ฒฝ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| const addTodo = (newTodo) => {
setTodos([...todos, newTodo]);
// ์คํ๋ ๋ ์ฐ์ฐ์(...)๋ก ๊ธฐ์กด ๋ฐฐ์ด์ ๋ณต์ฌํ๊ณ ์ ํญ๋ชฉ ์ถ๊ฐ
};
// 2. ๋ฐฐ์ด์์ ํญ๋ชฉ ์ ๊ฑฐํ๊ธฐ
const removeTodo = (index) => {
setTodos(todos.filter((_, i) => i !== index));
// filter๋ก ํด๋น ์ธ๋ฑ์ค๋ฅผ ์ ์ธํ ์ ๋ฐฐ์ด ์์ฑ
};
// 3. ๋ฐฐ์ด์ ํน์ ํญ๋ชฉ ์์ ํ๊ธฐ
const updateTodo = (index, newText) => {
setTodos(todos.map((todo, i) => i === index ? newText : todo));
// map์ผ๋ก ํน์ ์ธ๋ฑ์ค์ ํญ๋ชฉ๋ง ์ ๊ฐ์ผ๋ก ๋ณ๊ฒฝ
};
|
๊ฐ์ฒด(Object) ์ํ ๋ณ๊ฒฝ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| const [user, setUser] = useState({
name: 'ํ๊ธธ๋',
age: 30,
email: 'hong@example.com'
});
// 1. ๊ฐ์ฒด์ ํน์ ์์ฑ ๋ณ๊ฒฝํ๊ธฐ
const updateEmail = (newEmail) => {
setUser({
...user, // ๊ธฐ์กด ๊ฐ์ฒด์ ๋ชจ๋ ์์ฑ์ ๋ณต์ฌ
email: newEmail // ๋ณ๊ฒฝํ ์์ฑ๋ง ๋ฎ์ด์ฐ๊ธฐ
});
};
// 2. ์ค์ฒฉ๋ ๊ฐ์ฒด ์์ฑ ๋ณ๊ฒฝํ๊ธฐ
const [product, setProduct] = useState({
name: '๋
ธํธ๋ถ',
specs: {
cpu: 'i7',
ram: '16GB',
storage: '512GB'
}
});
const upgradeRam = (newRam) => {
setProduct({
...product,
specs: {
...product.specs, // ์ค์ฒฉ ๊ฐ์ฒด๋ ๋ณต์ฌ
ram: newRam // ๋ณ๊ฒฝํ ์์ฑ๋ง ๋ฎ์ด์ฐ๊ธฐ
}
});
};
|
๋ฐฐ์ด ์ ๊ฐ์ฒด ๋ณ๊ฒฝํ๊ธฐ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| const [students, setStudents] = useState([
{ id: 1, name: '๊น์ฒ ์', grade: 'A' },
{ id: 2, name: '์ด์ํฌ', grade: 'B' },
{ id: 3, name: '๋ฐ๋ฏผ์', grade: 'C' }
]);
// ํน์ ํ์์ ์ฑ์ ๋ณ๊ฒฝํ๊ธฐ
const updateGrade = (id, newGrade) => {
setStudents(
students.map(student =>
student.id === id
? { ...student, grade: newGrade }
: student
)
);
};
|
- ๋ถ๋ณ์ฑ์ ์งํค๋ฉด React๊ฐ ๋ณ๊ฒฝ ์ฌํญ์ ๊ฐ์งํ๊ณ ํจ์จ์ ์ผ๋ก ๋ ๋๋งํจ
- ์ค์ฒฉ ๊ตฌ์กฐ๊ฐ ๋ณต์กํด์ง๋ฉด immer ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ์ฒ
โ
Props ์ ์ก
๊ธฐ๋ณธ ๊ฐ๋
- Props๋ ๋ถ๋ชจ โ ์์์ผ๋ก ๋ฐ์ดํฐ ์ ๋ฌ ๋ฐฉ์
- ๋ง์น ํจ์์ ์ธ์์ฒ๋ผ ์๋ํ๋ฉฐ ์ฝ๊ธฐ ์ ์ฉ
๊ธฐ๋ณธ ์ ๋ฌ ์์
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| // ๋ถ๋ชจ ์ปดํฌ๋ํธ
function ParentComponent() {
const userName = "ํ๊ธธ๋";
const userAge = 30;
return (
<ChildComponent name={userName} age={userAge} />
);
}
// ์์ ์ปดํฌ๋ํธ
function ChildComponent(props) {
return (
<div>
<h2>์ด๋ฆ: {props.name}</h2>
<p>๋์ด: {props.age}์ธ</p>
</div>
);
}
|
Props ์ ์ก ํจํด
1. ๊ตฌ์กฐ ๋ถํด ํ ๋น
1
2
3
4
5
6
7
8
9
| // ๋ ๊น๋ํ ๊ตฌ์กฐ ๋ถํด ํ ๋น ๋ฐฉ์
function ProfileCard({ name, age, isVerified = false }) {
return (
<div className="profile-card">
<h2>{name} {isVerified && 'โ'}</h2>
<p>๋์ด: {age}์ธ</p>
</div>
);
}
|
2. children prop ํ์ฉ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| function Card({ title, children }) {
return (
<div className="card">
<h2 className="card-title">{title}</h2>
<div className="card-content">
{children}
</div>
</div>
);
}
// ์ฌ์ฉ ์์
function App() {
return (
<Card title="ํ์ ์ ๋ณด">
<p>์ด ์นด๋์๋ ์ด๋ค ๋ด์ฉ์ด๋ ๋ฃ์ ์ ์์ต๋๋ค.</p>
<button>์์ ํ๊ธฐ</button>
</Card>
);
}
|
3. ๊ฐ์ฒด ํํ๋ก props ์ ๋ฌ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| function UserProfile() {
const userDetails = {
name: "๊น์ฒ ์",
age: 28,
email: "kim@example.com",
role: "๊ฐ๋ฐ์"
};
return <ProfileDetails {...userDetails} />;
}
function ProfileDetails({ name, age, email, role }) {
return (
<div className="profile">
<h2>{name}</h2>
<ul>
<li>๋์ด: {age}</li>
<li>์ด๋ฉ์ผ: {email}</li>
<li>์ง์
: {role}</li>
</ul>
</div>
);
}
|
๋ด์ฉ์ ์ข ๋ ๊ตฌ์ฒด์ ์ผ๋ก ์๊ณ ์ถ์ด์ ์์ธํ props์ ๋ํด์๋ ๋ฐ๋ก ํฌ์คํ
ํ๋ค !
[CS] Props?
1
2
3
4
5
6
7
8
| ๊ณต๋ถ ๋ฐฉ๋ฒ
- useEffect : ์๋ฌธ ๋ฒ์ docs ๋ณด๋ฉด์ ๊ณต๋ถํ๋ฉด ์ข์
- ์ฑ๋ฅ ํฅ์์ ํ์ํ ํ
: useCallback, useMemo
์ฐธ๊ณ docs
[๋ชจ๋ JS ํํ ๋ฆฌ์ผ - ์ฝ์ด ์๋ฐ์คํฌ๋ฆฝํธ](https://ko.javascript.info/)
๋๋
๋ชจ๋ ์๋ฐ์คํฌ๋ฆฝํธ ์ฑ
์ด๋ฒ๋ณด๊ธฐ ~
|
๐ธ ์๋ฌธ์ : setCount(count + 1)
๋ ๋ถ๋ณ์ฑ์ ์งํค๋๊ฐ?
Q.
useState
์์ setCount(count + 1)
์ ๊ฐ์ ๋ฐฉ์์ ์ง์ ๊ฐ์ ๋ณ๊ฒฝํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ด๋๋ฐ, ์ด์ฒ๋ผ ์ง์ ๊ฐ ๋ณ๊ฒฝ์ด ํ์ฉ๋๋ ๊ฒฝ์ฐ์ ๋ถ๋ณ์ฑ์ ๋ฐ๋์ ์ง์ผ์ผ ํ๋ ๊ฒฝ์ฐ์ ์ฐจ์ด์ ์?
โ
A.
์์ ์ ๋จผ์ ๋ง์๋๋ฆฌ๋ฉด ๋ถ๋ณ์ฑ์ ๊นจ๋ ๋ชจ๋ ์
๋ฐ์ดํธ๋ ์ง์ํฉ๋๋ค. ๊ฒ๋ณด๊ธฐ์ count
๋ฅผ ์ง์ ์์ ํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ฌ๋, ์ค์ ๋ก๋ ์๋ก์ด ๊ฐ์ ๋ง๋ค์ด์ React์๊ฒ ์ ๋ฌํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ๋ถ๋ณ์ฑ์ ์งํต๋๋ค.
ํต์ฌ ๊ฐ๋
์์ฝ
count + 1
์ ์๋ก์ด ์ซ์ ๊ฐ์ ๊ณ์ฐํฉ๋๋ค (์์ ํ์
โ ๊ฐ ์์ฒด ๋ณต์ฌ๋จ)setCount()
๋ ์ด ์ ๊ฐ์ React์๊ฒ ์ ๋ฌํ์ฌ ์ํ๋ฅผ ์
๋ฐ์ดํธํ๋๋ก ์์ฒญํฉ๋๋ค- React๋ ๋ด๋ถ์ ์ผ๋ก ์ด์ ๊ฐ๊ณผ ๋น๊ต ํ, ํ์ ์ ๋ฆฌ๋ ๋๋ง
์ซ์, ๋ฌธ์์ด, boolean๊ณผ ๊ฐ์ ์์ ํ์
(primitive types)์ ๊ฐ ์์ฒด๊ฐ ์ ์ฅ๋๋ฏ๋ก, ์ ๊ฐ์ ๊ณ์ฐํ ๋ ์๋์ผ๋ก ๋ถ๋ณ์ฑ์ด ์ ์ง๋ฉ๋๋ค.
๐ ์์ ํ์
vs ์ฐธ์กฐ ํ์
์ฐจ์ด
ํ์
| ๋ถ๋ณ์ฑ ์ ์ง ์ฌ๋ถ | ์์ |
---|
์์ ํ์
| ์๋ ์ ์ง | setCount(count + 1) |
์ฐธ์กฐ ํ์
| ์ง์ ์ ์ง ํ์ | setUser({ ...user, name: 'ํ๊ธธ๋' }) |
- ์์ ํ์
: ์ซ์, ๋ฌธ์์ด, boolean โ ๊ฐ์ ๋ฐ๋ก ์์ ํด๋ ์๋ ๋ถ๋ณ์ฑ ์ ์ง
- ์ฐธ์กฐ ํ์
: ๊ฐ์ฒด, ๋ฐฐ์ด โ ์ง์ ๋ณต์ฌ(
...
) ํ ์์ ํด์ผ ์์
END