useState์ useEffect, ๋ฆฌ์กํธ Hooks๋ฅผ ํ์ฉํ ์ํ ๊ด๋ฆฌ์ ์ปดํฌ๋ํธ ์ค๊ณ
๋ฆฌ์กํธ 16.8 ๋ฒ์ ์ดํ ๋ฑ์ฅํ Hooks๋ ํจ์ํ ์ปดํฌ๋ํธ์์๋ ์ํ ๊ด๋ฆฌ์ ์ฌ์ด๋ ์ดํํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์๊ฒ ํ์ฌ, ์ฝ๋์ ๊ฐ๊ฒฐ์ฑ๊ณผ ์ฌ์ฌ์ฉ์ฑ์ ํฌ๊ฒ ํฅ์์์ผฐ์ต๋๋ค. ์ด๋ฒ ํฌ์คํ ์์๋ useState, useEffect, useContext ๋ฑ ๊ธฐ๋ณธ ์ ๊ณต๋๋ ๋ค์ํ ํ ๋ค์ ํ์ฉํ์ฌ ์ปดํฌ๋ํธ์ ์ํ ๊ด๋ฆฌ ๋ฐ ๋ก์ง ๋ถ๋ฆฌ๋ฅผ ํจ์จ์ ์ผ๋ก ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์์ธํ ๋ค๋ฃจ๊ณ , ์ปค์คํ ํ ์ ์ ์ฌ๋ก๋ฅผ ํตํด ์ฝ๋์ ์ฌ์ฌ์ฉ์ฑ์ ๋์ด๋ ์ ๋ต์ ๋ํด ์ดํด๋ณด๊ฒ ์ต๋๋ค.
๋ฆฌ์กํธ Hooks์ ๊ธฐ๋ณธ ์๋ฆฌ์ ์ฅ์
๋ฆฌ์กํธ Hooks๋ ํจ์ํ ์ปดํฌ๋ํธ์์ ํด๋์คํ ์ปดํฌ๋ํธ์ ๋์ผํ ๊ธฐ๋ฅ(์ํ ๊ด๋ฆฌ, ๋ผ์ดํ์ฌ์ดํด ๋ฉ์๋ ๋์ฒด)์ ๊ตฌํํ ์ ์๋๋ก ์ค๊ณ๋์์ต๋๋ค. Hooks๋ฅผ ์ฌ์ฉํ๋ฉด ์ปดํฌ๋ํธ ๋ก์ง์ ๋ถ๋ฆฌํ์ฌ ์ฌ๋ฌ ์ปดํฌ๋ํธ์์ ์ฌ์ฌ์ฉํ ์ ์๊ณ , ์ฝ๋๊ฐ ๊ฐ๊ฒฐํด์ง๋ฉฐ ํ ์คํธ์ ์ ์ง๋ณด์๊ฐ ์ฉ์ดํด์ง๋๋ค.
ํนํ, ๋ค์๊ณผ ๊ฐ์ ์ ์์ Hooks๋ ๊ธฐ์กด ๋ฐฉ์๋ณด๋ค ๋ง์ ์ฅ์ ์ ์ ๊ณตํฉ๋๋ค.
- ์ํ ๊ด๋ฆฌ์ ๋จ์ํ: useState๋ฅผ ์ฌ์ฉํ๋ฉด ๋ณต์กํ ํด๋์คํ ์ปดํฌ๋ํธ์ this.state์ this.setState๋ฅผ ๋์ฒดํ์ฌ ๋ ์ง๊ด์ ์ธ ๋ฐฉ์์ผ๋ก ์ํ๋ฅผ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
- ์ฌ์ด๋ ์ดํํธ ์ ์ด: useEffect๋ฅผ ํตํด ์ปดํฌ๋ํธ์ ๋ง์ดํธ, ์ ๋ฐ์ดํธ, ์ธ๋ง์ดํธ ์์ ์ ์ํํด์ผ ํ๋ ์์ (๋ฐ์ดํฐ ํจ์นญ, ์ด๋ฒคํธ ๋ฑ๋ก ๋ฑ)์ ํ๋์ ํจ์ ๋ด์์ ๊ฐ๋จํ๊ฒ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
- ์ปจํ ์คํธ ํ์ฉ: useContext๋ฅผ ํตํด ์ ์ญ ์ํ๋ ์ค์ ์ ์ฝ๊ฒ ๊ณต์ ํ ์ ์์ด, ์ปดํฌ๋ํธ ๊ฐ์ ๋ฐ์ดํฐ ์ ๋ฌ์ด ๊ฐํธํด์ง๋๋ค.
- ๋ก์ง ๋ถ๋ฆฌ ๋ฐ ์ฌ์ฌ์ฉ: ์ปค์คํ ํ (Custom Hooks)์ ๋ง๋ค์ด ๊ณตํต ๋ก์ง์ ์บก์ํํจ์ผ๋ก์จ, ์ฝ๋ ์ค๋ณต์ ์ค์ด๊ณ ์ฌ๋ฌ ์ปดํฌ๋ํธ์์ ์์ฝ๊ฒ ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค.
useState์ ์ปดํฌ๋ํธ ์ํ ๊ด๋ฆฌ
useState ํ ์ ํจ์ํ ์ปดํฌ๋ํธ์์ ๋ด๋ถ ์ํ๋ฅผ ๊ด๋ฆฌํ ๋ ๊ฐ์ฅ ๊ธฐ๋ณธ์ด ๋๋ ํ ์ ๋๋ค. ์๋์ ์์ ๋ ๊ฐ๋จํ ์นด์ดํฐ ์ปดํฌ๋ํธ๋ฅผ ๊ตฌํํ ์ฌ๋ก์ ๋๋ค.
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
return (
<div style={{ textAlign: 'center', margin: '20px' }}>
<h2>ํ์ฌ ์นด์ดํธ: {count}</h2>
<button onClick={decrement} style={{ marginRight: '10px' }}>๊ฐ์</button>
<button onClick={increment}>์ฆ๊ฐ</button>
</div>
);
};
export default Counter;
์ ์์ ๋ useState๋ฅผ ํตํด count๋ผ๋ ์ํ ๋ณ์๋ฅผ ์์ฑํ๊ณ , ์ด๋ฅผ ๋ณ๊ฒฝํ๋ setCount ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ๋จํ ์นด์ดํฐ ๊ธฐ๋ฅ์ ๊ตฌํํ ๋ชจ์ต์ ๋๋ค. ํจ์ํ ์ปดํฌ๋ํธ ๋ด์์ ๊ฐ๋จํ๊ฒ ์ํ๋ฅผ ๋ค๋ฃฐ ์ ์๋ ์ ์ด Hooks์ ํฐ ์ฅ์ ์ ๋๋ค.
useEffect๋ฅผ ํ์ฉํ ์ฌ์ด๋ ์ดํํธ ๊ด๋ฆฌ
useEffect ํ ์ ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง ๋ ํ ์คํ๋๋ ํจ์๋ก, ๋ฐ์ดํฐ ํจ์นญ, ๊ตฌ๋ (subscription), DOM ๋ณ๊ฒฝ ๋ฑ ๋ค์ํ ์ฌ์ด๋ ์ดํํธ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. ์๋ ์์ ๋ ๋ฐ์ดํฐ ํจ์นญ์ ์ํ useEffect ์ฌ์ฉ ์ฌ๋ก์ ๋๋ค.
import React, { useState, useEffect } from 'react';
const DataFetcher = () => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
// API ํธ์ถ ๋ฐ ๋ฐ์ดํฐ ํจ์นญ
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(json => {
setData(json.slice(0, 5)); // ์์๋ก ์์ 5๊ฐ์ ๋ฐ์ดํฐ๋ง ์ฌ์ฉ
setLoading(false);
})
.catch(error => {
console.error('๋ฐ์ดํฐ ํจ์นญ ์๋ฌ:', error);
setLoading(false);
});
}, []); // ๋น ๋ฐฐ์ด์ ์์กด์ฑ ๋ฐฐ์ด๋ก ์ค์ ํ์ฌ ์ปดํฌ๋ํธ ๋ง์ดํธ ์ ํ ๋ฒ๋ง ์คํ
return (
<div style={{ padding: '20px' }}>
<h2>๋ฐ์ดํฐ ํจ์นญ ์์ </h2>
{loading ? (
<p>๋ก๋ฉ ์ค...</p>
) : (
<ul>
{data.map(item => (
<li key={item.id}>{item.title}</li>
))}
</ul>
)}
</div>
);
};
export default DataFetcher;
์ ์ฝ๋์์๋ ์ปดํฌ๋ํธ๊ฐ ์ฒ์ ๋ง์ดํธ๋ ๋ API ํธ์ถ์ ํตํด ๋ฐ์ดํฐ๋ฅผ ํจ์นญํ๊ณ , ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ ํ ๋ก๋ฉ ์ํ๋ฅผ ์ ๋ฐ์ดํธํ๋ ์์ ๋ฅผ ๋ณด์ฌ์ค๋๋ค. useEffect์ ์์กด์ฑ ๋ฐฐ์ด์ ๋น ๋ฐฐ์ด๋ก ์ค์ ํ๋ฉด ์ปดํฌ๋ํธ ๋ง์ดํธ ์์๋ง ์คํ๋๋๋ก ์ ์ดํ ์ ์์ต๋๋ค.
useContext๋ฅผ ํตํ ์ ์ญ ์ํ ๊ณต์
๋ฆฌ์กํธ์์ ์ ์ญ ์ํ๋ ๊ณตํต ๋ฐ์ดํฐ๋ฅผ ์ฌ๋ฌ ์ปดํฌ๋ํธ ๊ฐ์ ๊ณต์ ํ ๋ Context API๋ฅผ ์ฌ์ฉํฉ๋๋ค. useContext ํ ์ ์ฌ์ฉํ๋ฉด Context์ ๊ฐ์ ๊ฐ๋จํ๊ฒ ๊ตฌ๋ ํ ์ ์์ด, ์ปดํฌ๋ํธ ๊ฐ์ ๋ฐ์ดํฐ ์ ๋ฌ์ ํจ์ฌ ์์ํ๊ฒ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
์๋ ์์ ๋ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด๋ฅผ ์ ์ญ์ผ๋ก ๊ด๋ฆฌํ๋ Context๋ฅผ ์ค์ ํ๊ณ , ์ด๋ฅผ ํ์ ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ๋ ์ฌ๋ก์ ๋๋ค.
import React, { useContext, useState } from 'react';
// ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด๋ฅผ ๋ด์ Context ์์ฑ
const AuthContext = React.createContext();
const AuthProvider = ({ children }) => {
const [user, setUser] = useState({ name: 'ํ๊ธธ๋', isAuthenticated: true });
return (
<AuthContext.Provider value={{ user, setUser }}>
{children}
</AuthContext.Provider>
);
};
const UserProfile = () => {
const { user } = useContext(AuthContext);
return (
<div style={{ padding: '20px', border: '1px solid #ccc', marginTop: '20px' }}>
<h2>์ฌ์ฉ์ ํ๋กํ</h2>
<p>์ด๋ฆ: {user.name}</p>
<p>์ธ์ฆ ์ํ: {user.isAuthenticated ? '์ธ์ฆ๋จ' : '๋ฏธ์ธ์ฆ'}</p>
</div>
);
};
const App = () => {
return (
<AuthProvider>
<div style={{ maxWidth: '600px', margin: '0 auto' }}>
<h2>๋ฆฌ์กํธ Hooks๋ฅผ ํ์ฉํ ์ํ ๊ด๋ฆฌ</h2>
<UserProfile />
</div>
</AuthProvider>
);
};
export default App;
์ ์์ ๋ AuthContext๋ฅผ ํตํด ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ ์ญ์ผ๋ก ๊ด๋ฆฌํ๊ณ , ํ์ ์ปดํฌ๋ํธ์ธ UserProfile์์ useContext๋ฅผ ์ฌ์ฉํด ๊ฐํธํ๊ฒ ์ธ์ฆ ์ ๋ณด๋ฅผ ๋ฐ์ ์ถ๋ ฅํ๋ ์ฌ๋ก์ ๋๋ค.
์ปค์คํ ํ (Custom Hooks) ์ ์๊ณผ ํ์ฉ
์ปค์คํ ํ ์ ์ฌ๋ฌ ์ปดํฌ๋ํธ์์ ๊ณตํต์ ์ผ๋ก ์ฌ์ฉ๋๋ ๋ก์ง์ ํ๋์ ํจ์๋ก ์บก์ํํ์ฌ ์ฌ์ฌ์ฉ์ฑ์ ๋์ด๋ ๊ฐ๋ ฅํ ๋๊ตฌ์ ๋๋ค. ์๋ฅผ ๋ค์ด, ์๋์ฐ ํฌ๊ธฐ๋ฅผ ์ถ์ ํ๋ ๋ก์ง์ ์ปค์คํ ํ ์ผ๋ก ๋ง๋ค์ด ๋ณด๊ฒ ์ต๋๋ค.
import { useState, useEffect } from 'react';
const useWindowSize = () => {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
useEffect(() => {
const handleResize = () => {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
};
// ์๋์ฐ ๋ฆฌ์ฌ์ด์ฆ ์ด๋ฒคํธ ๋ฑ๋ก
window.addEventListener('resize', handleResize);
// ์ปดํฌ๋ํธ ์ธ๋ง์ดํธ ์ ์ด๋ฒคํธ ์ ๊ฑฐ
return () => window.removeEventListener('resize', handleResize);
}, []);
return windowSize;
};
export default useWindowSize;
์ด์ ์ด ์ปค์คํ ํ ์ ํ์ฉํ์ฌ ํ์ฌ ์๋์ฐ ํฌ๊ธฐ๋ฅผ ํ๋ฉด์ ํ์ํ๋ ์ปดํฌ๋ํธ๋ฅผ ๊ตฌํํ ์ ์์ต๋๋ค.
import React from 'react';
import useWindowSize from './useWindowSize';
const WindowSizeDisplay = () => {
const { width, height } = useWindowSize();
return (
<div style={{ padding: '20px', border: '1px solid #007acc', marginTop: '20px' }}>
<h2>ํ์ฌ ์๋์ฐ ํฌ๊ธฐ</h2>
<p>๋๋น: {width}px</p>
<p>๋์ด: {height}px</p>
</div>
);
};
export default WindowSizeDisplay;
์ด์ฒ๋ผ ์ปค์คํ ํ ์ ์ปดํฌ๋ํธ์ ๋ณต์กํ ๋ก์ง์ ์ฃผ์ ํ์ง ์๊ณ ๋ ๊ฐ๊ฒฐํ๊ฒ ์ํ๋ฅผ ๊ด๋ฆฌํ ์ ์๋๋ก ํด์ค๋๋ค. ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ๋ก์ง์ ๋ณ๋์ ํ ์ผ๋ก ๋ถ๋ฆฌํ๋ฉด, ์ฝ๋์ ๊ฐ๋ ์ฑ์ด ํฅ์๋๊ณ ์ ์ง๋ณด์๊ฐ ์ฌ์์ง๋๋ค.
๋ณตํฉ ์ปดํฌ๋ํธ์์์ ์ํ ๊ด๋ฆฌ์ ๋ก์ง ๋ถ๋ฆฌ ์ ๋ต
๋๊ท๋ชจ ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ์ฌ๋ฌ ์ปดํฌ๋ํธ๊ฐ ๋ณตํฉ์ ์ผ๋ก ์ฐ๋๋๊ธฐ ๋๋ฌธ์, ์ํ ๊ด๋ฆฌ์ ๋ก์ง ๋ถ๋ฆฌ๊ฐ ๋์ฑ ์ค์ํด์ง๋๋ค. ๋ค์์ ์ด๋ฅผ ์ํ ๋ช ๊ฐ์ง ์ ๋ต์ ๋๋ค.
- ๋ก์ง ๋ถ๋ฆฌ์ ์ฌ์ฌ์ฉ: ๊ณตํต ๊ธฐ๋ฅ์ ์ปค์คํ ํ ์ด๋ ๋ณ๋์ ์ ํธ๋ฆฌํฐ ํจ์๋ก ๋ถ๋ฆฌํ์ฌ, ์ค๋ณต ์ฝ๋๋ฅผ ์ ๊ฑฐํ๊ณ ์ฌ์ฌ์ฉ์ฑ์ ๊ทน๋ํํฉ๋๋ค.
- ์ปจํ ์คํธ์ ์ ์ญ ์ํ ๊ด๋ฆฌ: useContext์ ํจ๊ป Redux, MobX, Recoil ๋ฑ ์ ์ญ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋์ ํ๋ฉด, ์ปดํฌ๋ํธ ๊ฐ์ ์ํ ๊ณต์ ๋ฐ ์ ๋ฐ์ดํธ๊ฐ ์์ํด์ง๋๋ค.
- ๋ชจ๋ํ๋ ์ปดํฌ๋ํธ ์ค๊ณ: ์ปดํฌ๋ํธ๋ฅผ ๊ฐ๋ฅํ ํ ์์ ๋จ์๋ก ์ชผ๊ฐ์ด, ๊ฐ ์ปดํฌ๋ํธ๊ฐ ๋จ์ผ ์ฑ ์ ์์น(Single Responsibility Principle)์ ๋ฐ๋ฅด๋๋ก ์ค๊ณํฉ๋๋ค. ์ด๋ฅผ ํตํด ํ ์ปดํฌ๋ํธ ๋ด์ ์ํ์ ๋ก์ง์ ๋ช ํํ๊ฒ ๋ถ๋ฆฌํ ์ ์์ต๋๋ค.
- ํ ์คํธ์ ๋๋ฒ๊น : ์ปค์คํ ํ ๊ณผ ์ปดํฌ๋ํธ ๋ณ๋ก ๋จ์ ํ ์คํธ๋ฅผ ์์ฑํ์ฌ, ๋ก์ง ๋ณ๊ฒฝ ์ ๋ฐ์ํ ์ ์๋ ๋ฌธ์ ๋ฅผ ๋ฏธ์ฐ์ ๋ฐฉ์งํ ์ ์์ต๋๋ค. Jest, React Testing Library ๋ฑ์ ํ์ฉํ์ฌ ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ๋์ด๋ ๊ฒ์ด ์ข์ต๋๋ค.
๊ฒฐ๋ก
๋ฆฌ์กํธ Hooks๋ฅผ ํ์ฉํ ์ํ ๊ด๋ฆฌ์ ์ปดํฌ๋ํธ ์ค๊ณ๋ ํจ์ํ ์ปดํฌ๋ํธ์ ์ฅ์ ์ ๊ทน๋ํํ์ฌ, ์ฝ๋์ ๊ฐ๋ ์ฑ๊ณผ ์ฌ์ฌ์ฉ์ฑ์ ํฅ์์ํต๋๋ค. useState, useEffect, useContext์ ๊ฐ์ ๊ธฐ๋ณธ ํ ์ ํตํด ๊ฐ๋ณ ์ปดํฌ๋ํธ์ ์ํ์ ์ฌ์ด๋ ์ดํํธ๋ฅผ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ , ์ปค์คํ ํ ์ ํตํด ๊ณตํต ๋ก์ง์ ์บก์ํํจ์ผ๋ก์จ, ๋ณต์กํ ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ์ ์ง๋ณด์์ ํ์ฅ์ฑ์ด ๋ฐ์ด๋ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
์ด๋ฒ ํฌ์คํ ์์๋ ๊ธฐ๋ณธ์ ์ธ ์นด์ดํฐ, ๋ฐ์ดํฐ ํจ์นญ, ์ ์ญ ์ํ ๊ณต์ ์ฌ๋ก๋ฅผ ์ดํด๋ณด์์ผ๋ฉฐ, ์๋์ฐ ํฌ๊ธฐ๋ฅผ ์ถ์ ํ๋ ์ปค์คํ ํ ์์ ๋ฅผ ํตํด ์ค๋ฌด์์ ํ์ฉ ๊ฐ๋ฅํ ๋ค์ํ ๋ฐฉ๋ฒ๋ค์ ์๊ฐํ์์ต๋๋ค. ์ด๋ฌํ ์ ๊ทผ ๋ฐฉ์์ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ์์ ๋ชจ๋ํ, ์ฌ์ฌ์ฉ์ฑ, ๊ทธ๋ฆฌ๊ณ ํ ์คํธ ์ฉ์ด์ฑ์ ๋ชจ๋ ๋ง์กฑ์ํค๋ ํจ๊ณผ์ ์ธ ์ ๋ต์ ๋๋ค.
์์ผ๋ก ํ๋ก์ ํธ๋ฅผ ์งํํ ๋, ์์์ ์๊ฐํ Hooks ์ฌ์ฉ๋ฒ๊ณผ ์ปค์คํ ํ ์ ์ ์ฌ๋ก๋ฅผ ์ฐธ๊ณ ํ์ฌ ๋ณด๋ค ๊น๋ํ๊ณ ์ ์ง๋ณด์๊ฐ ์ฌ์ด ์ฝ๋๋ฅผ ์์ฑํ์๊ธธ ๋ฐ๋๋๋ค. ์ด๋ฅผ ํตํด ์ฌ์ฉ์ ๊ฒฝํ๊ณผ ๊ฐ๋ฐ ํจ์จ์ฑ์ ๋์์ ๋์ด๋ ํ์ ์ ์ธ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌํํ ์ ์์ ๊ฒ์ ๋๋ค.
๋๊ธ