반응형
안녕하세요! 오늘은 React에서 가장 많이 사용되는 Hook 중 하나인 useEffect의 올바른 사용법에 대해 이야기해보려고 합니다.
들어가며 📝
useEffect는 React의 강력한 기능이지만, 때로는 불필요하게 사용되어 성능 저하나 버그를 유발할 수 있습니다.
실제로 많은 개발자들이 컴포넌트의 로직을 useEffect에 넣는 것을 당연하게 생각하곤 하는데요, 과연 그럴까요?
useEffect가 필요하지 않은 실제 사례들 🚫
1. 폼 유효성 검사
// 🔴 불필요한 useEffect 사용
function SignupForm() {
const [email, setEmail] = useState('');
const [isValid, setIsValid] = useState(false);
useEffect(() => {
setIsValid(email.includes('@') && email.includes('.'));
}, [email]);
// ✅ 렌더링 중에 직접 계산
const isValid = email.includes('@') && email.includes('.');
}
2. 계산된 스타일
// 🔴 불필요한 useEffect 사용
function ProductCard({ isOnSale, price }) {
const [cardStyle, setCardStyle] = useState({});
useEffect(() => {
setCardStyle({
backgroundColor: isOnSale ? '#ffebee' : 'white',
color: price > 100 ? 'red' : 'black',
});
}, [isOnSale, price]);
// ✅ 렌더링 중에 직접 계산
const cardStyle = {
backgroundColor: isOnSale ? '#ffebee' : 'white',
color: price > 100 ? 'red' : 'black',
};
}
3. Props 기반 필터링
// 🔴 불필요한 useEffect 사용
function ProductList({ products, category }) {
const [filteredProducts, setFilteredProducts] = useState([]);
useEffect(() => {
setFilteredProducts(
products.filter(product =>
category === 'all' ? true : product.category === category
)
);
}, [products, category]);
// ✅ 렌더링 중에 직접 계산
const filteredProducts = products.filter(product =>
category === 'all' ? true : product.category === category
);
}
4. 로컬 스토리지 동기화
// 🔴 불필요한 useEffect 사용
function UserSettings({ user }) {
const [theme, setTheme] = useState('light');
useEffect(() => {
localStorage.setItem('theme', theme);
}, [theme]);
// ✅ 이벤트 핸들러에서 처리
const handleThemeChange = (newTheme) => {
setTheme(newTheme);
localStorage.setItem('theme', newTheme);
};
}
5. API 응답 후 상태 업데이트
// 🔴 불필요한 useEffect 사용
function OrderComplete({ orderId }) {
const [orderStatus, setOrderStatus] = useState('');
const [showConfetti, setShowConfetti] = useState(false);
useEffect(() => {
if (orderStatus === 'completed') {
setShowConfetti(true);
}
}, [orderStatus]);
// ✅ API 응답 핸들러에서 직접 처리
const handleOrderComplete = async () => {
const status = await checkOrderStatus(orderId);
setOrderStatus(status);
if (status === 'completed') {
setShowConfetti(true);
}
};
}
실제 useEffect가 필요한 경우 ✅
- 외부 시스템과의 동기화
- WebSocket 연결
- 브라우저 API 구독
- 애널리틱스 이벤트 발송
// ✅ 외부 시스템 연결에는 useEffect 사용이 적절
function ChatRoom() {
useEffect(() => {
const connection = createConnection();
connection.connect();
return () => connection.disconnect();
}, []);
}
- 데이터 페칭(React Query를 추천합니다!)
// 🔴 useEffect를 사용한 데이터 페칭
function SearchResults({ query }) {
const [results, setResults] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
let ignore = false;
async function fetchData() {
setIsLoading(true);
try {
const data = await fetchResults(query);
if (!ignore) {
setResults(data);
}
} catch (err) {
if (!ignore) {
setError(err);
}
} finally {
setIsLoading(false);
}
}
fetchData();
return () => {
ignore = true;
};
}, [query]);
if (isLoading) return <div>로딩 중...</div>;
if (error) return <div>에러 발생!</div>;
return <div>{/* 결과 표시 */}</div>;
}
// ✅ React Query를 사용한 데이터 페칭
function SearchResults({ query }) {
const { data, isLoading, error } = useQuery(
['search', query],
() => fetchResults(query),
{
enabled: !!query,
}
);
if (isLoading) return <div>로딩 중...</div>;
if (error) return <div>에러 발생!</div>;
return <div>{/* 결과 표시 */}</div>;
}
마무리 🎯
useEffect는 분명 유용한 도구이지만, 모든 문제의 해결책은 아닙니다. 다음을 기억하세요:
- 렌더링 중에 계산할 수 있는 것은 useEffect 대신 직접 계산하세요.
- 이벤트 핸들러에서 처리할 수 있는 로직은 useEffect 밖으로 빼세요.
- 실제로 외부 시스템과 동기화가 필요한 경우에만 useEffect를 사용하세요.
- 데이터 페칭은 React Query와 같은 전문화된 라이브러리를 사용하세요.
참고 문서 📚
반응형