React 컀μ€ν ν μ νμ©νμ¬ μ»΄ν¬λνΈ λ‘μ§μ μΆμΆνκ³ μ¬μ¬μ©νμ¬ μ½λ μ μ§ λ³΄μμ±, ν μ€νΈ μ©μ΄μ± λ° μ λ°μ μΈ μ ν리μΌμ΄μ μν€ν μ²λ₯Ό κ°μ νλ λ°©λ²μ μμ보μΈμ.
React 컀μ€ν ν : μ¬μ¬μ©μ±μ μν μ»΄ν¬λνΈ λ‘μ§ μΆμΆνκΈ°
React ν μ React μ»΄ν¬λνΈλ₯Ό μμ±νλ λ°©μμ νλͺ μ μΌμΌμΌ°μΌλ©°, μν λ° μ¬μ΄λ μ΄ννΈλ₯Ό κ΄λ¦¬νλ λμ± μ°μνκ³ ν¨μ¨μ μΈ λ°©λ²μ μ 곡ν©λλ€. λ€μν ν μ€μμ 컀μ€ν ν μ μ»΄ν¬λνΈ λ‘μ§μ μΆμΆνκ³ μ¬μ¬μ©νκΈ° μν κ°λ ₯ν λκ΅¬λ‘ λκ°μ λνλ λλ€. μ΄ κΈμ React 컀μ€ν ν μ μ΄ν΄νκ³ κ΅¬ννκΈ° μν ν¬κ΄μ μΈ κ°μ΄λλ₯Ό μ 곡νμ¬, λ μ μ§ λ³΄μ κ°λ₯νκ³ ν μ€νΈνκΈ° μ¬μ°λ©° νμ₯ κ°λ₯ν μ ν리μΌμ΄μ μ ꡬμΆν μ μλλ‘ μ§μν©λλ€.
React 컀μ€ν ν μ΄λ 무μμΈκ°μ?
λ³Έμ§μ μΌλ‘ 컀μ€ν ν μ "use"λ‘ μμνλ μ΄λ¦μ κ°μ§ JavaScript ν¨μμ΄λ©° λ€λ₯Έ ν μ νΈμΆν μ μμ΅λλ€. μ΄λ₯Ό ν΅ν΄ μ»΄ν¬λνΈ λ‘μ§μ μ¬μ¬μ© κ°λ₯ν ν¨μλ‘ μΆμΆνμ¬ μ½λ μ€λ³΅μ μ κ±°νκ³ λ κΉλν μ»΄ν¬λνΈ κ΅¬μ‘°λ₯Ό μ΄μ§ν μ μμ΅λλ€. μΌλ° React μ»΄ν¬λνΈμ λ¬λ¦¬ 컀μ€ν ν μ UIλ₯Ό λ λλ§νμ§ μμΌλ©°, λ¨μν λ‘μ§μ μΊ‘μνν©λλ€.
컀μ€ν ν μ React μν λ° λΌμ΄νμ¬μ΄ν΄ κΈ°λ₯μ μ κ·Όν μ μλ μ¬μ¬μ© κ°λ₯ν ν¨μλΌκ³ μκ°νμΈμ. μ΄λ κ³ μ°¨ μ»΄ν¬λνΈ(HOC)λ λ λ νλ‘μ€(Render Props)λ₯Ό μ¬μ©νμ§ μκ³ λ μ¬λ¬ μ»΄ν¬λνΈ κ°μ μν μ μ₯ λ‘μ§μ 곡μ νλ νμμ μΈ λ°©λ²μ λλ€. κ³ μ°¨ μ»΄ν¬λνΈλ λ λ νλ‘μ€λ μ’ μ’ μ½κΈ° μ΄λ ΅κ³ μ μ§ λ³΄μνκΈ° μ΄λ €μ΄ μ½λλ‘ μ΄μ΄μ§ μ μμ΅λλ€.
μ 컀μ€ν ν μ μ¬μ©ν΄μΌ ν κΉμ?
컀μ€ν ν μ μ¬μ©νλ©΄ λ€μκ³Ό κ°μ μλ§μ μ΄μ μ μ»μ μ μμ΅λλ€:
- μ¬μ¬μ©μ±: λ‘μ§μ ν λ² μμ±νκ³ μ¬λ¬ μ»΄ν¬λνΈμμ μ¬μ¬μ©ν μ μμ΅λλ€. μ΄λ μ½λ μ€λ³΅μ ν¬κ² μ€μ΄κ³ μ ν리μΌμ΄μ μ λ μ μ§ λ³΄μ κ°λ₯νκ² λ§λλλ€.
- ν₯μλ μ½λ ꡬμ±: 볡μ‘ν λ‘μ§μ 컀μ€ν ν μΌλ‘ μΆμΆνλ©΄ μ»΄ν¬λνΈκ° κΉλν΄μ Έ μ½κ³ μ΄ν΄νκΈ° μ¬μμ§λλ€. μ»΄ν¬λνΈλ ν΅μ¬ λ λλ§ μ± μμ λ μ§μ€νκ² λ©λλ€.
- ν₯μλ ν μ€νΈ μ©μ΄μ±: 컀μ€ν ν μ μ½κ² λ 립μ μΌλ‘ ν μ€νΈν μ μμ΅λλ€. μ»΄ν¬λνΈλ₯Ό λ λλ§νμ§ μκ³ λ ν μ λ‘μ§μ ν μ€νΈν μ μμ΄ λ κ²¬κ³ νκ³ μ λ’°ν μ μλ ν μ€νΈλ₯Ό μνν μ μμ΅λλ€.
- μ μ§ λ³΄μμ± ν₯μ: λ‘μ§μ΄ λ³κ²½λ λ, μ¬μ©λλ λͺ¨λ μ»΄ν¬λνΈκ° μλ λ¨ ν κ³³ β 컀μ€ν ν β μμλ§ μ λ°μ΄νΈνλ©΄ λ©λλ€.
- μμ©κ΅¬ μ½λ κ°μ: 컀μ€ν ν μ μΌλ°μ μΈ ν¨ν΄κ³Ό λ°λ³΅μ μΈ μμ μ μΊ‘μννμ¬ μ»΄ν¬λνΈμ μμ±ν΄μΌ νλ μμ©κ΅¬ μ½λμ μμ μ€μΌ μ μμ΅λλ€.
첫 λ²μ§Έ 컀μ€ν ν λ§λ€κΈ°
μ€μ©μ μΈ μμ(APIμμ λ°μ΄ν° κ°μ Έμ€κΈ°)λ₯Ό ν΅ν΄ 컀μ€ν ν μ μμ± λ° μ¬μ©λ²μ μ΄ν΄λ³΄κ² μ΅λλ€.
μμ: useFetch - λ°μ΄ν° κ°μ Έμ€κΈ° ν
React μ ν리μΌμ΄μ
μμ λ€μν APIλ‘λΆν° λ°μ΄ν°λ₯Ό μμ£Ό κ°μ ΈμμΌ νλ€κ³ μμν΄ λ³΄μΈμ. κ° μ»΄ν¬λνΈμμ λ°μ΄ν° κ°μ Έμ€κΈ° λ‘μ§μ λ°λ³΅νλ λμ , useFetch ν
μ λ§λ€ μ μμ΅λλ€.
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const abortController = new AbortController();
const signal = abortController.signal;
const fetchData = async () => {
setLoading(true);
try {
const response = await fetch(url, { signal: signal });
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const json = await response.json();
setData(json);
setError(null); // Clear any previous errors
} catch (error) {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
} else {
setError(error);
}
setData(null); // Clear any previous data
} finally {
setLoading(false);
}
};
fetchData();
return () => {
abortController.abort(); // Cleanup function to abort the fetch on unmount or URL change
};
}, [url]); // Re-run effect when the URL changes
return { data, loading, error };
}
export default useFetch;
μ€λͺ :
- μν λ³μ: μ΄ ν
μ
useStateλ₯Ό μ¬μ©νμ¬ λ°μ΄ν°, λ‘λ© μν λ° μ€λ₯ μνλ₯Ό κ΄λ¦¬ν©λλ€. - useEffect:
useEffectν μurlpropμ΄ λ³κ²½λ λ λ°μ΄ν° κ°μ Έμ€κΈ°λ₯Ό μνν©λλ€. - μ€λ₯ μ²λ¦¬: μ΄ ν μ λ°μ΄ν° κ°μ Έμ€κΈ° μμ μ€ λ°μν μ μλ μ μ¬μ μΈ μ€λ₯λ₯Ό μ²λ¦¬νλ λ‘μ§μ ν¬ν¨ν©λλ€. μλ΅μ΄ μ±κ³΅μ μΈμ§ νμΈνκΈ° μν΄ μν μ½λλ₯Ό νμΈν©λλ€.
- λ‘λ© μν:
loadingμνλ λ°μ΄ν°κ° μμ§ λ‘λ μ€μΈμ§ μ¬λΆλ₯Ό λνλ΄λ λ° μ¬μ©λ©λλ€. - AbortController: μ»΄ν¬λνΈκ° λ§μ΄νΈ ν΄μ λκ±°λ URLμ΄ λ³κ²½λ κ²½μ° κ°μ Έμ€κΈ° μμ²μ μ·¨μνκΈ° μν΄ AbortController APIλ₯Ό μ¬μ©ν©λλ€. μ΄λ λ©λͺ¨λ¦¬ λμλ₯Ό λ°©μ§ν©λλ€.
- λ°ν κ°: μ΄ ν
μ
data,loading,errorμνλ₯Ό ν¬ν¨νλ κ°μ²΄λ₯Ό λ°νν©λλ€.
μ»΄ν¬λνΈμμ useFetch ν
μ¬μ©νκΈ°
μ΄μ React μ»΄ν¬λνΈμμ μ΄ μ»€μ€ν ν μ μ¬μ©νλ λ°©λ²μ μ΄ν΄λ³΄κ² μ΅λλ€:
import React from 'react';
import useFetch from './useFetch';
function UserList() {
const { data: users, loading, error } = useFetch('https://jsonplaceholder.typicode.com/users');
if (loading) return <p>Loading users...</p>;
if (error) return <p>Error: {error.message}</p>;
if (!users) return <p>No users found.</p>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name} ({user.email})</li>
))}
</ul>
);
}
export default UserList;
μ€λͺ :
- μ»΄ν¬λνΈλ
useFetchν μ μν¬νΈν©λλ€. - API URLκ³Ό ν¨κ» ν μ νΈμΆν©λλ€.
- λ°νλ κ°μ²΄λ₯Ό ꡬ쑰 λΆν΄νμ¬
data(usersλ‘ μ΄λ¦ λ³κ²½),loading,errorμνμ μ κ·Όν©λλ€. loadingλ°errorμνμ λ°λΌ λ€λ₯Έ μ½ν μΈ λ₯Ό 쑰건λΆλ‘ λ λλ§ν©λλ€.- λ°μ΄ν°κ° μ¬μ© κ°λ₯ν κ²½μ°, μ¬μ©μ λͺ©λ‘μ λ λλ§ν©λλ€.
κ³ κΈ μ»€μ€ν ν ν¨ν΄
λ¨μν λ°μ΄ν° κ°μ Έμ€κΈ°λ₯Ό λμ΄, 컀μ€ν ν μ λ 볡μ‘ν λ‘μ§μ μΊ‘μννλ λ° μ¬μ©λ μ μμ΅λλ€. λ€μμ λͺ κ°μ§ κ³ κΈ ν¨ν΄μ λλ€:
1. useReducerλ₯Ό μ¬μ©ν μν κ΄λ¦¬
λ 볡μ‘ν μν κ΄λ¦¬ μλ리μ€μμλ 컀μ€ν
ν
κ³Ό useReducerλ₯Ό κ²°ν©ν μ μμ΅λλ€. μ΄λ₯Ό ν΅ν΄ μν μ νμ λ μμΈ‘ κ°λ₯νκ³ μ²΄κ³μ μΈ λ°©μμΌλ‘ κ΄λ¦¬ν μ μμ΅λλ€.
import { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function useCounter() {
const [state, dispatch] = useReducer(reducer, initialState);
const increment = () => dispatch({ type: 'increment' });
const decrement = () => dispatch({ type: 'decrement' });
return { count: state.count, increment, decrement };
}
export default useCounter;
μ¬μ©λ²:
import React from 'react';
import useCounter from './useCounter';
function Counter() {
const { count, increment, decrement } = useCounter();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
export default Counter;
2. useContextλ₯Ό μ¬μ©ν 컨ν
μ€νΈ ν΅ν©
컀μ€ν
ν
μ React Contextμ λν μ κ·Όμ λ¨μννλ λ°λ μ¬μ©λ μ μμ΅λλ€. μ»΄ν¬λνΈμμ useContextλ₯Ό μ§μ μ¬μ©νλ λμ , 컨ν
μ€νΈ μ κ·Ό λ‘μ§μ μΊ‘μννλ 컀μ€ν
ν
μ λ§λ€ μ μμ΅λλ€.
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext'; // Assuming you have a ThemeContext
function useTheme() {
return useContext(ThemeContext);
}
export default useTheme;
μ¬μ©λ²:
import React from 'react';
import useTheme from './useTheme';
function MyComponent() {
const { theme, toggleTheme } = useTheme();
return (
<div style={{ backgroundColor: theme.background, color: theme.color }}>
<p>This is my component.</p>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
}
export default MyComponent;
3. λλ°μ΄μ± λ° μ€λ‘νλ§
λλ°μ΄μ±(Debouncing)κ³Ό μ€λ‘νλ§(Throttling)μ ν¨μκ° μ€νλλ μλλ₯Ό μ μ΄νλ λ° μ¬μ©λλ κΈ°μ μ λλ€. 컀μ€ν ν μ μ΄λ¬ν λ‘μ§μ μΊ‘μννλ λ° μ¬μ©λ μ μμ΄, μ΄λ²€νΈ νΈλ€λ¬μ μ΄λ¬ν κΈ°μ μ μ½κ² μ μ©ν μ μμ΅λλ€.
import { useState, useEffect, useRef } from 'react';
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
export default useDebounce;
μ¬μ©λ²:
import React, { useState } from 'react';
import useDebounce from './useDebounce';
function SearchInput() {
const [searchValue, setSearchValue] = useState('');
const debouncedSearchValue = useDebounce(searchValue, 500); // Debounce for 500ms
useEffect(() => {
// Perform search with debouncedSearchValue
console.log('Searching for:', debouncedSearchValue);
// Replace console.log with your actual search logic
}, [debouncedSearchValue]);
const handleChange = (event) => {
setSearchValue(event.target.value);
};
return (
<input
type="text"
value={searchValue}
onChange={handleChange}
placeholder="Search..."
/>
);
}
export default SearchInput;
컀μ€ν ν μμ±μ μν λͺ¨λ² μ¬λ‘
컀μ€ν ν μ΄ ν¨κ³Όμ μ΄κ³ μ μ§ λ³΄μ κ°λ₯νλλ‘ νλ €λ©΄ λ€μ λͺ¨λ² μ¬λ‘λ₯Ό λ°λ₯΄μμμ€:
- "use"λ‘ μμ: νμ 컀μ€ν ν μ μ΄λ¦μ "use" μ λμ¬λ‘ μμνμμμ€. μ΄ κ·μΉμ Reactμ ν΄λΉ ν¨μκ° ν μ κ·μΉμ λ°λ₯΄λ©° ν¨μν μ»΄ν¬λνΈ λ΄μμ μ¬μ©λ μ μμμ μ립λλ€.
- μ΄μ μ μ§: κ° μ»€μ€ν ν μ λͺ ννκ³ κ΅¬μ²΄μ μΈ λͺ©μ μ κ°μ ΈμΌ ν©λλ€. λ무 λ§μ μ± μμ μ²λ¦¬νλ μ§λμΉκ² 볡μ‘ν ν μμ±μ νΌνμμμ€.
- μ μ©ν κ° λ°ν: ν μ μ¬μ©νλ μ»΄ν¬λνΈκ° νμλ‘ νλ λͺ¨λ κ°κ³Ό ν¨μλ₯Ό ν¬ν¨νλ κ°μ²΄λ₯Ό λ°ννμμμ€. μ΄λ ν μ λ μ μ°νκ³ μ¬μ¬μ© κ°λ₯νκ² λ§λλλ€.
- μ€λ₯λ₯Ό μ μμ μΌλ‘ μ²λ¦¬: μ»΄ν¬λνΈμμ μμμΉ λͺ»ν λμμ λ°©μ§νκΈ° μν΄ μ»€μ€ν ν μ μ€λ₯ μ²λ¦¬λ₯Ό ν¬ν¨νμμμ€.
- μ 리(Cleanup) κ³ λ €:
useEffectμ μ 리 ν¨μλ₯Ό μ¬μ©νμ¬ λ©λͺ¨λ¦¬ λμλ₯Ό λ°©μ§νκ³ μ μ ν 리μμ€ κ΄λ¦¬λ₯Ό 보μ₯νμμμ€. μ΄λ ꡬλ , νμ΄λ¨Έ λλ μ΄λ²€νΈ 리μ€λλ₯Ό λ€λ£° λ νΉν μ€μν©λλ€. - ν μ€νΈ μμ±: 컀μ€ν ν μ΄ μμλλ‘ μλνλμ§ νμΈνκΈ° μν΄ λ 립μ μΌλ‘ μ² μ ν ν μ€νΈνμμμ€.
- ν λ¬Έμν: 컀μ€ν ν μ λͺ©μ , μ¬μ©λ² λ° μ μ¬μ μΈ μ ν μ¬νμ μ€λͺ νλ λͺ νν λ¬Έμλ₯Ό μ 곡νμμμ€.
κΈλ‘λ² κ³ λ € μ¬ν
κΈλ‘λ² μ¬μ©μλ₯Ό μν μ ν리μΌμ΄μ μ κ°λ°ν λ λ€μ μ¬νμ μΌλμ λμμμ€:
- κ΅μ ν(i18n) λ° νμ§ν(l10n): 컀μ€ν
ν
μ΄ μ¬μ©μμκ² νμλλ ν
μ€νΈ λλ λ°μ΄ν°λ₯Ό λ€λ£¨λ κ²½μ°, λ€μν μΈμ΄ λ° μ§μμ λ§κ² κ΅μ ν λ° νμ§νλλ λ°©λ²μ κ³ λ €νμμμ€. μ΄λ
react-intlλλi18nextμ κ°μ λΌμ΄λΈλ¬λ¦¬ μ¬μ©μ ν¬ν¨ν μ μμ΅λλ€. - λ μ§ λ° μκ° νμ: μ μΈκ³μμ μ¬μ©λλ λ€μν λ μ§ λ° μκ° νμμ κ³ λ €νμμμ€. κ° μ¬μ©μμκ² λ μ§μ μκ°μ΄ μ¬λ°λ₯΄κ² νμλλλ‘ μ μ ν νμ μ§μ ν¨μ λλ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©νμμμ€.
- ν΅ν νμ: λ§μ°¬κ°μ§λ‘, λ€μν μ§μμ λ§κ² ν΅ν νμμ μ μ νκ² μ²λ¦¬νμμμ€.
- μ κ·Όμ±(a11y): 컀μ€ν ν μ΄ μ ν리μΌμ΄μ μ μ κ·Όμ±μ λΆμ μ μΈ μν₯μ λ―ΈμΉμ§ μλλ‘ νμμμ€. μ₯μ κ° μλ μ¬μ©μλ₯Ό κ³ λ €νκ³ μ κ·Όμ± λͺ¨λ² μ¬λ‘λ₯Ό λ°λ₯΄μμμ€.
- μ±λ₯: νΉν 볡μ‘ν λ‘μ§μ΄λ λκ·λͺ¨ λ°μ΄ν° μΈνΈλ₯Ό λ€λ£° λ 컀μ€ν ν μ μ μ¬μ μΈ μ±λ₯ μν₯μ μ μνμμμ€. λ€μν μμΉμ λ€νΈμν¬ μλλ₯Ό κ°μ§ μ¬μ©μμκ² μ μλνλλ‘ μ½λλ₯Ό μ΅μ ννμμμ€.
μμ: 컀μ€ν ν μ μ¬μ©ν κ΅μ νλ λ μ§ νμ μ§μ
import { useState, useEffect } from 'react';
import { DateTimeFormat } from 'intl';
function useFormattedDate(date, locale) {
const [formattedDate, setFormattedDate] = useState('');
useEffect(() => {
try {
const formatter = new DateTimeFormat(locale, {
year: 'numeric',
month: 'long',
day: 'numeric',
});
setFormattedDate(formatter.format(date));
} catch (error) {
console.error('Error formatting date:', error);
setFormattedDate('Invalid Date');
}
}, [date, locale]);
return formattedDate;
}
export default useFormattedDate;
μ¬μ©λ²:
import React from 'react';
import useFormattedDate from './useFormattedDate';
function MyComponent() {
const today = new Date();
const enDate = useFormattedDate(today, 'en-US');
const frDate = useFormattedDate(today, 'fr-FR');
const deDate = useFormattedDate(today, 'de-DE');
return (
<div>
<p>US Date: {enDate}</p>
<p>French Date: {frDate}</p>
<p>German Date: {deDate}</p>
</div>
);
}
export default MyComponent;
κ²°λ‘
React 컀μ€ν ν μ μ»΄ν¬λνΈ λ‘μ§μ μΆμΆνκ³ μ¬μ¬μ©νκΈ° μν κ°λ ₯ν λ©μ»€λμ¦μ λλ€. 컀μ€ν ν μ νμ©νλ©΄ λ κΉλνκ³ , μ μ§ λ³΄μνκΈ° μ¬μ°λ©°, ν μ€νΈ κ°λ₯ν μ½λλ₯Ό μμ±ν μ μμ΅λλ€. Reactμ μλ ¨λ μλ‘ μ»€μ€ν ν μ λ§μ€ν°νλ κ²μ 볡μ‘νκ³ νμ₯ κ°λ₯ν μ ν리μΌμ΄μ μ ꡬμΆνλ λ₯λ ₯μ ν¬κ² ν₯μμν¬ κ²μ λλ€. λ€μν μ¬μ©μμκ² ν¨κ³Όμ μ΄κ³ μ κ·Ό κ°λ₯νλλ‘ μ»€μ€ν ν μ κ°λ°ν λ λͺ¨λ² μ¬λ‘λ₯Ό λ°λ₯΄κ³ κΈλ‘λ² μμλ₯Ό κ³ λ €νλ κ²μ μμ§ λ§μμμ€. 컀μ€ν ν μ νμ λ°μλ€μ΄κ³ React κ°λ° κΈ°μ μ ν λ¨κ³ λμ΄μ¬λ¦¬μΈμ!