Hook์ ๊ท์น
Hook์ React 16.8์ ์๋ก ์ถ๊ฐ๋ ๊ธฐ๋ฅ์ ๋๋ค. Hook์ class๋ฅผ ์์ฑํ์ง ์๊ณ ๋ state์ ๋ค๋ฅธ React์ ๊ธฐ๋ฅ๋ค์ ์ฌ์ฉํ ์ ์๋๋ก ํด์ค๋๋ค.
Hook์ JavaScript ํจ์์ ๋๋ค. ํ์ง๋ง Hook์ ์ฌ์ฉํ ๋๋ ๋ ๊ฐ์ง ๊ท์น์ ์ค์ํด์ผ ํฉ๋๋ค. ์ฐ๋ฆฌ๋ ์ด๋ฌํ ๊ท์น๋ค์ ์๋์ผ๋ก ๊ฐ์ ํ๊ธฐ ์ํ linter ํ๋ฌ๊ทธ์ธ์ ์ ๊ณตํ๊ณ ์์ต๋๋ค.
์ต์์(at the Top Level)์์๋ง Hook์ ํธ์ถํด์ผ ํฉ๋๋ค
๋ฐ๋ณต๋ฌธ, ์กฐ๊ฑด๋ฌธ ํน์ ์ค์ฒฉ๋ ํจ์ ๋ด์์ Hook์ ํธ์ถํ์ง ๋ง์ธ์. ๋์ early return์ด ์คํ๋๊ธฐ ์ ์ ํญ์ React ํจ์์ ์ต์์(at the top level)์์ Hook์ ํธ์ถํด์ผ ํฉ๋๋ค. ์ด ๊ท์น์ ๋ฐ๋ฅด๋ฉด ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง ๋ ๋๋ง๋ค ํญ์ ๋์ผํ ์์๋ก Hook์ด ํธ์ถ๋๋ ๊ฒ์ด ๋ณด์ฅ๋ฉ๋๋ค. ์ด๋ฌํ ์ ์ React๊ฐ useState
์ useEffect
๊ฐ ์ฌ๋ฌ ๋ฒ ํธ์ถ๋๋ ์ค์๋ Hook์ ์ํ๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ์ ์งํ ์ ์๋๋ก ํด์ค๋๋ค. ์ด ์ ์ ๋ํด์ ๊ถ๊ธํ๋ค๋ฉด ์๋์์ ์์ธํ ์ค๋ช
ํด ๋๋ฆฌ๊ฒ ์ต๋๋ค.
์ค์ง React ํจ์ ๋ด์์ Hook์ ํธ์ถํด์ผ ํฉ๋๋ค
Hook์ ์ผ๋ฐ์ ์ธ JavaScript ํจ์์์ ํธ์ถํ์ง ๋ง์ธ์. ๋์ ์๋์ ๊ฐ์ด ํธ์ถํ ์ ์์ต๋๋ค.
- โ React ํจ์ ์ปดํฌ๋ํธ์์ Hook์ ํธ์ถํ์ธ์.
- โ Custom Hook์์ Hook์ ํธ์ถํ์ธ์. (๋ค์ ํ์ด์ง์์ ์ด ๋ถ๋ถ์ ์ดํด๋ณผ ์์ ์ ๋๋ค)
์ด ๊ท์น์ ์งํค๋ฉด ์ปดํฌ๋ํธ์ ๋ชจ๋ ์ํ ๊ด๋ จ ๋ก์ง์ ์์ค์ฝ๋์์ ๋ช ํํ๊ฒ ๋ณด์ด๋๋ก ํ ์ ์์ต๋๋ค.
ESLint ํ๋ฌ๊ทธ์ธ
์ฐ๋ฆฌ๋ ์ด ๋ ๊ฐ์ง ๊ท์น์ ๊ฐ์ ํ๋ eslint-plugin-react-hooks
๋ผ๋ ESLint ํ๋ฌ๊ทธ์ธ์ ์ถ์ํ์ต๋๋ค. ์ด ํ๋ฌ๊ทธ์ธ์ ํ๋ก์ ํธ์ ์ถ๊ฐํ ์ ์์ต๋๋ค.
์ด ํ๋ฌ๊ทธ์ธ์ Create React App์ ๊ธฐ๋ณธ์ ์ผ๋ก ํฌํจ๋์ด ์์ต๋๋ค.
npm install eslint-plugin-react-hooks --save-dev
// ESLint ์ค์ ํ์ผ
{
"plugins": [
// ...
"react-hooks"
],
"rules": {
// ...
"react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
"react-hooks/exhaustive-deps": "warn" // Checks effect dependencies
}
}
์ด๋ป๊ฒ ๋๋ง์ Hook์ ์์ฑํ ์ ์๋์ง ์ค๋ช ํ๋ ๋ค์ ์ฅ์ผ๋ก ์ง๊ธ ๋์ด๊ฐ๋ ์ข์ต๋๋ค. ์ด๋ฒ ์ฅ์์๋ ๊ณ์ํด์ ์ด๋ฌํ ๊ท์น๋ค์ ๋ ผ๋ฆฌ์ ๊ทผ๊ฑฐ์ ๋ํด ์ค๋ช ํ ์์ ์ ๋๋ค.
์ค๋ช
์ด์ ์ ๋ฐฐ์ ๋ฏ์ด ํ ์ปดํฌ๋ํธ์์ State๋ Effect Hook์ ์ฌ๋ฌ ๊ฐ ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
function Form() {
// 1. name์ด๋ผ๋ state ๋ณ์๋ฅผ ์ฌ์ฉํ์ธ์.
const [name, setName] = useState('Mary');
// 2. Effect๋ฅผ ์ฌ์ฉํด ํผ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ์ธ์.
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
// 3. surname์ด๋ผ๋ state ๋ณ์๋ฅผ ์ฌ์ฉํ์ธ์.
const [surname, setSurname] = useState('Poppins');
// 4. Effect๋ฅผ ์ฌ์ฉํด์ ์ ๋ชฉ์ ์
๋ฐ์ดํธํฉ๋๋ค.
useEffect(function updateTitle() {
document.title = name + ' ' + surname;
});
// ...
}
๊ทธ๋ ๋ค๋ฉด React๋ ์ด๋ป๊ฒ ํน์ state๊ฐ ์ด๋ค useState
ํธ์ถ์ ํด๋นํ๋์ง ์ ์ ์์๊น์? ์ ๋ต์ React๊ฐ Hook์ด ํธ์ถ๋๋ ์์์ ์์กดํ๋ค๋ ๊ฒ์
๋๋ค. ๋ชจ๋ ๋ ๋๋ง์์ Hook์ ํธ์ถ ์์๋ ๊ฐ๊ธฐ ๋๋ฌธ์ ์์๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ๋์ํ ์ ์์ต๋๋ค.
// ------------
// ์ฒซ ๋ฒ์งธ ๋ ๋๋ง
// ------------
useState('Mary') // 1. 'Mary'๋ผ๋ name state ๋ณ์๋ฅผ ์ ์ธํฉ๋๋ค.
useEffect(persistForm) // 2. ํผ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ธฐ ์ํ effect๋ฅผ ์ถ๊ฐํฉ๋๋ค.
useState('Poppins') // 3. 'Poppins'๋ผ๋ surname state ๋ณ์๋ฅผ ์ ์ธํฉ๋๋ค.
useEffect(updateTitle) // 4. ์ ๋ชฉ์ ์
๋ฐ์ดํธํ๊ธฐ ์ํ effect๋ฅผ ์ถ๊ฐํฉ๋๋ค.
// -------------
// ๋ ๋ฒ์งธ ๋ ๋๋ง
// -------------
useState('Mary') // 1. name state ๋ณ์๋ฅผ ์ฝ์ต๋๋ค.(์ธ์๋ ๋ฌด์๋ฉ๋๋ค)
useEffect(persistForm) // 2. ํผ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ธฐ ์ํ effect๊ฐ ๋์ฒด๋ฉ๋๋ค.
useState('Poppins') // 3. surname state ๋ณ์๋ฅผ ์ฝ์ต๋๋ค.(์ธ์๋ ๋ฌด์๋ฉ๋๋ค)
useEffect(updateTitle) // 4. ์ ๋ชฉ์ ์
๋ฐ์ดํธํ๊ธฐ ์ํ effect๊ฐ ๋์ฒด๋ฉ๋๋ค.
// ...
Hook์ ํธ์ถ ์์๊ฐ ๋ ๋๋ง ๊ฐ์ ๋์ผํ๋ค๋ฉด React๋ ์ง์ญ์ ์ธ state๋ฅผ ๊ฐ Hook์ ์ฐ๋์ํฌ ์ ์์ต๋๋ค. ํ์ง๋ง Hook์ ์กฐ๊ฑด๋ฌธ ์์์(์๋ฅผ ๋ค์ด persistForm
effect) ํธ์ถํ๋ค๋ฉด ์ด๋ค ์ผ์ด ์ผ์ด๋ ๊น์?
// ๐ด ์กฐ๊ฑด๋ฌธ์ Hook์ ์ฌ์ฉํจ์ผ๋ก์จ ์ฒซ ๋ฒ์งธ ๊ท์น์ ๊นผ์ต๋๋ค
if (name !== '') {
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
}
name !== ''
์กฐ๊ฑด์ ์ฒซ ๋ฒ์งธ ๋ ๋๋ง์์ true
๊ธฐ ๋๋ฌธ์ Hook์ ๋์ํฉ๋๋ค. ํ์ง๋ง ์ฌ์ฉ์๊ฐ ๊ทธ๋ค์ ๋ ๋๋ง์์ ํผ์ ์ด๊ธฐํํ๋ฉด์ ์กฐ๊ฑด์ false
๋ก ๋ง๋ค ๊ฒ๋๋ค. ๋ ๋๋ง ๊ฐ์ Hook์ ๊ฑด๋๋ฐ๊ธฐ ๋๋ฌธ์ Hook ํธ์ถ ์์๋ ๋ฌ๋ผ์ง๊ฒ ๋ฉ๋๋ค.
useState('Mary') // 1. name state ๋ณ์๋ฅผ ์ฝ์ต๋๋ค. (์ธ์๋ ๋ฌด์๋ฉ๋๋ค)
// useEffect(persistForm) // ๐ด Hook์ ๊ฑด๋๋ฐ์์ต๋๋ค!
useState('Poppins') // ๐ด 2 (3์ด์๋). surname state ๋ณ์๋ฅผ ์ฝ๋ ๋ฐ ์คํจํ์ต๋๋ค.
useEffect(updateTitle) // ๐ด 3 (4์๋). ์ ๋ชฉ์ ์
๋ฐ์ดํธํ๊ธฐ ์ํ effect๊ฐ ๋์ฒด๋๋ ๋ฐ ์คํจํ์ต๋๋ค.
React๋ ๋ ๋ฒ์งธ useState
Hook ํธ์ถ์ ๋ํด ๋ฌด์์ ๋ฐํํ ์ง ๋ชฐ๋์ต๋๋ค. React๋ ์ด์ ๋ ๋๋ง ๋์ฒ๋ผ ์ปดํฌ๋ํธ ๋ด์์ ๋ ๋ฒ์งธ Hook ํธ์ถ์ด persistForm
effect์ ์ผ์นํ ๊ฒ์ด๋ผ ์์ํ์ง๋ง ๊ทธ๋ ์ง ์์์ต๋๋ค. ๊ทธ ์์ ๋ถํฐ ๊ฑด๋๋ด Hook ๋ค์์ ํธ์ถ๋๋ Hook์ด ์์๊ฐ ํ๋์ฉ ๋ฐ๋ฆฌ๋ฉด์ ๋ฒ๊ทธ๋ฅผ ๋ฐ์์ํค๊ฒ ๋ฉ๋๋ค.
์ด๊ฒ์ด ์ปดํฌ๋ํธ ์ต์์(the top of level)์์ Hook์ด ํธ์ถ๋์ด์ผ๋ง ํ๋ ์ด์ ์ ๋๋ค. ์กฐ๊ฑด๋ถ๋ก effect๋ฅผ ์คํํ๊ธฐ๋ฅผ ์ํ๋ค๋ฉด, ์กฐ๊ฑด๋ฌธ์ Hook ๋ด๋ถ์ ๋ฃ์ ์ ์์ต๋๋ค.
useEffect(function persistForm() {
// ๐ ๋ ์ด์ ์ฒซ ๋ฒ์งธ ๊ท์น์ ์ด๊ธฐ์ง ์์ต๋๋ค
if (name !== '') {
localStorage.setItem('formData', name);
}
});
์ ๊ณต๋ lint ๊ท์น์ ํ์ฉํ๋ค๋ฉด ์ด ๋ฌธ์ ์ ๋ํด ๊ฑฑ์ ํ ํ์๋ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด์ ์ Hook์ด ์ด๋ฐ ์์ผ๋ก ๋์ํ๋์ง ๊ทธ๋ฆฌ๊ณ ์ด ๊ท์น์ด ์ด๋ค ๋ฌธ์ ๋ฅผ ๋ฐฉ์งํ๋์ง ์๊ณ ์์ต๋๋ค.
๋ค์ ๋จ๊ณ
๋ง์นจ๋ด Custom Hook์ ์์ฑํ๋ ๋ฒ์ ๋ฐฐ์ธ ์ค๋น๊ฐ ๋์์ต๋๋ค! Custom Hook์ React์์ ์ ๊ณตํ๋ Hook์ ์ถ์ํ๋ ๋ก์ง์ผ๋ก ์ฌ์ฉํ ์ ์๋๋ก ๊ฒฐํฉํด์ฃผ๊ณ ๋ค๋ฅธ ์ปดํฌ๋ํธ ์ฌ์ด์์ ๊ณตํต์ ์ํ ๊ด๋ จ ๋ก์ง์ ์ฌ์ฌ์ฉ ํ ์ ์๋๋ก ํด์ค๋๋ค.