React Custom Hook (컀μ€ν ν )
1. 컀μ€ν ν μ΄λ?
컀μ€ν
ν
(Custom Hook)μ Reactμ λ΄μ₯ ν
(useState, useEffect λ±)μ μ‘°ν©νμ¬ λ°λ³΅λλ μν λ‘μ§μ μ¬μ¬μ© κ°λ₯ν ν¨μλ‘ μΆμΆν κ²μ
λλ€.
ν΅μ¬ κ·μΉ
β’
ν¨μ μ΄λ¦μ λ°λμ useλ‘ μμν΄μΌ ν©λλ€.
β’
λ΄λΆμμ λ€λ₯Έ ν
μ νΈμΆν μ μμ΅λλ€.
β’
μ»΄ν¬λνΈκ° μλ μΌλ° JavaScript ν¨μμ΄μ§λ§, ν
μ κ·μΉμ λ°λ¦
λλ€.
μΌλ° ν¨μ β getUser()
컀μ€ν
ν
β useUser()
Plain Text
볡μ¬
2. μ 컀μ€ν ν μ μ¬μ©νλκ°?
λ¬Έμ μν© | 컀μ€ν
ν
λμ
ν |
μ¬λ¬ μ»΄ν¬λνΈμ λμΌν λ‘μ§ μ€λ³΅ | ν
νλλ‘ κ³΅μ |
μ»΄ν¬λνΈκ° λ무 컀μ§κ³ 볡μ‘ν΄μ§ | λ‘μ§μ λΆλ¦¬νμ¬ μ»΄ν¬λνΈ λ¨μν |
λ‘μ§ ν
μ€νΈκ° μ΄λ €μ | ν
λ¨μλ‘ λ
립 ν
μ€νΈ κ°λ₯ |
λ‘μ§ μμ μ μ¬λ¬ νμΌ λ³κ²½ νμ | ν
νμΌ νλλ§ μμ |
3. κΈ°λ³Έ μμ - useCounter
컀μ€ν ν μμ΄ (μ€λ³΅ μ½λ λ°μ)
// ComponentA.jsx
const [count, setCount] = useState(0);
const increment = () => setCount(c => c + 1);
const decrement = () => setCount(c => c - 1);
const reset = () => setCount(0);
// ComponentB.jsx (λμΌν μ½λ λ°λ³΅)
const [count, setCount] = useState(0);
const increment = () => setCount(c => c + 1);
const decrement = () => setCount(c => c - 1);
const reset = () => setCount(0);
JavaScript
볡μ¬
컀μ€ν ν μΌλ‘ λΆλ¦¬
// hooks/useCounter.js
import { useState } from 'react';
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount(c => c + 1);
const decrement = () => setCount(c => c - 1);
const reset = () => setCount(initialValue);
return { count, increment, decrement, reset };
}
export default useCounter;
JavaScript
볡μ¬
μ¬μ©νλ μ»΄ν¬λνΈ
// ComponentA.jsx
import useCounter from './hooks/useCounter';
function ComponentA() {
const { count, increment, decrement, reset } = useCounter(10);
return (
<div>
<p>μΉ΄μ΄νΈ: {count}</p>
<button onClick={increment}>+1</button>
<button onClick={decrement}>-1</button>
<button onClick={reset}>μ΄κΈ°ν</button>
</div>
);
}
JavaScript
볡μ¬
4. μ€μ© μμ - useFetch
API νΈμΆ λ‘μ§μ μ¬μ¬μ© κ°λ₯νκ² μΆμΆν©λλ€.
// hooks/useFetch.js
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
if (!url) return;
setLoading(true);
setError(null);
fetch(url)
.then(res => {
if (!res.ok) throw new Error('μλ² μ€λ₯: ' + res.status);
return res.json();
})
.then(json => setData(json))
.catch(err => setError(err.message))
.finally(() => setLoading(false));
}, [url]);
return { data, loading, error };
}
export default useFetch;
JavaScript
볡μ¬
μ¬μ© μμ
import useFetch from './hooks/useFetch';
function UserList() {
const { data: users, loading, error } = useFetch('<https://jsonplaceholder.typicode.com/users>');
if (loading) return <p>λ‘λ© μ€...</p>;
if (error) return <p>μλ¬: {error}</p>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
JavaScript
볡μ¬
5. μ€μ© μμ - useLocalStorage
localStorageμ μνλ₯Ό λκΈ°νν©λλ€.
// hooks/useLocalStorage.js
import { useState } from 'react';
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});
const setValue = (value) => {
try {
setStoredValue(value);
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error(error);
}
};
return [storedValue, setValue];
}
export default useLocalStorage;
JavaScript
볡μ¬
μ¬μ© μμ
import useLocalStorage from './hooks/useLocalStorage';
function ThemeToggle() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
νμ¬ ν
λ§: {theme}
</button>
);
}
JavaScript
볡μ¬
6. μ€μ© μμ - useInput
νΌ μ
λ ₯ μνλ₯Ό κ΄λ¦¬ν©λλ€.
// hooks/useInput.js
import { useState } from 'react';
function useInput(initialValue = '') {
const [value, setValue] = useState(initialValue);
const onChange = (e) => setValue(e.target.value);
const reset = () => setValue(initialValue);
return { value, onChange, reset };
}
export default useInput;
JavaScript
볡μ¬
μ¬μ© μμ
import useInput from './hooks/useInput';
function LoginForm() {
const email = useInput('');
const password = useInput('');
const handleSubmit = (e) => {
e.preventDefault();
console.log(email.value, password.value);
email.reset();
password.reset();
};
return (
<form onSubmit={handleSubmit}>
<input type="email" placeholder="μ΄λ©μΌ" {...email} />
<input type="password" placeholder="λΉλ°λ²νΈ" {...password} />
<button type="submit">λ‘κ·ΈμΈ</button>
</form>
);
}
JavaScript
볡μ¬
μ€νλ λ νμ©: {...email}μ value={email.value} onChange={email.onChange}μ λμΌν©λλ€.
7. μ€μ© μμ - useDebounce
μ
λ ₯κ°μ΄ μΌμ μκ° λμ λ³νμ§ μμΌλ©΄ μ μ©ν©λλ€ (κ²μ μ΅μ νμ μ μ©).
// hooks/useDebounce.js
import { useState, useEffect } from 'react';
function useDebounce(value, delay = 500) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => clearTimeout(timer); // ν΄λ¦°μ
}, [value, delay]);
return debouncedValue;
}
export default useDebounce;
JavaScript
볡μ¬
μ¬μ© μμ
import { useState } from 'react';
import useDebounce from './hooks/useDebounce';
function SearchBox() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 500);
// debouncedQueryκ° λ³κ²½λ λλ§ API νΈμΆ
useEffect(() => {
if (debouncedQuery) {
console.log('κ²μ API νΈμΆ:', debouncedQuery);
}
}, [debouncedQuery]);
return (
<input
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="κ²μμ΄ μ
λ ₯..."
/>
);
}
JavaScript
볡μ¬
8. 컀μ€ν ν μμ± κ·μΉ μμ½
κ·μΉ | μ€λͺ
|
use μ λμ¬ | μ΄λ¦μ΄ λ°λμ useλ‘ μμν΄μΌ ν¨ |
ν
μ κ·μΉ μ€μ | μ΅μμ λ 벨μμλ§ νΈμΆ, 쑰건문/λ°λ³΅λ¬Έ λ΄ νΈμΆ κΈμ§ |
νμΌ μμΉ | λ³΄ν΅ src/hooks/ ν΄λμ λͺ¨μ |
λ¨μΌ μ±
μ | νλμ ν
μ νλμ κ΄μ¬μ¬λ§ λ΄λΉ |
λ°νκ° | κ°μ²΄ λλ λ°°μ΄λ‘ νμν κ°κ³Ό ν¨μλ₯Ό λ°ν |
9. ν΄λ ꡬ쑰 κΆμ₯ μμ
src/
βββ components/
β βββ UserList.jsx
β βββ LoginForm.jsx
βββ hooks/
β βββ useCounter.js
β βββ useFetch.js
β βββ useInput.js
β βββ useLocalStorage.js
β βββ useDebounce.js
βββ App.jsx
Plain Text
볡μ¬
10. 컀μ€ν ν vs μΌλ° ν¨μ
κ΅¬λΆ | 컀μ€ν
ν
| μΌλ° ν¨μ |
μ΄λ¦ | useλ‘ μμ | μμ λ‘κ² μλͺ
|
λ΄λΆ ν
μ¬μ© | κ°λ₯ (useState, useEffect λ±) | λΆκ°λ₯ |
μν κ΄λ¦¬ | κ°λ₯ | λΆκ°λ₯ |
μ¬μ΄λ μ΄ννΈ | κ°λ₯ | λΆκ°λ₯ |
νΈμΆ μμΉ | μ»΄ν¬λνΈ λλ λ€λ₯Έ 컀μ€ν
ν
λ΄λΆλ§ | μ΄λμλ κ°λ₯ |
ν΅μ¬ μ 리
컀μ€ν
ν
μ "λ‘μ§μ μ¬μ¬μ©"μ μν λꡬμ
λλ€. UI(JSX)λ μ»΄ν¬λνΈλ‘ λΆλ¦¬νκ³ , μν λ‘μ§μ 컀μ€ν
ν
μΌλ‘ λΆλ¦¬νλ κ²μ΄ Reactμ κΆμ₯ ν¨ν΄μ
λλ€.
μ»΄ν¬λνΈ = UI (JSX) + 컀μ€ν
ν
νΈμΆ
컀μ€ν
ν
= μν λ‘μ§ + μ¬μ΄λ μ΄ννΈ λ‘μ§




