Ch.22 React 심화 — 상태 관리 패턴
커스텀 훅 — 로직 재사용
커스텀 훅의 개념과 네이밍 규칙을 이해한다useFetch 커스텀 훅을 만들 수 있다실전에서 유용한 커스텀 훅 패턴을 안다
같은 fetch 코드를 10번 복사하고 있다면?
여러 컴포넌트에서 API를 호출할 때마다 useState, useEffect, loading, error 관련 코드를 반복 작성합니다.
반복되는 로직을 한 번만 작성하고 재사용하는 방법은?
커스텀 훅 — use로 시작하는 함수에 로직을 추출하면 어디서든 재사용할 수 있습니다.
article
핵심 내용
use로 시작하는 함수 = 커스텀 훅입니다
데이터, 로딩, 에러를 하나의 훅으로 묶습니다
// hooks/use-fetch.ts
import { useState, useEffect } from "react";
export function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
setLoading(true);
setError(null);
fetch(url)
.then((res) => {
if (!res.ok) throw new Error("요청 실패");
return res.json();
})
.then(setData)
.catch((e) => setError(e.message))
.finally(() => setLoading(false));
}, [url]);
return { data, loading, error };
}// 사용 — 3줄이면 API 호출 완료!
function UserProfile({ userId }: { userId: string }) {
const { data, loading, error } = useFetch<User>(
`/api/users/${userId}`
);
if (loading) return <p>로딩 중...</p>;
if (error) return <p>에러: {error}</p>;
if (!data) return null;
return <h1>{data.name}</h1>;
}
// 다른 컴포넌트에서도 동일하게!
function ProductList() {
const { data: products, loading } = useFetch<Product[]>(
"/api/products"
);
// ...
}자주 쓰는 패턴을 커스텀 훅으로 만들어두세요
// useLocalStorage — 로컬스토리지 동기화
function useLocalStorage<T>(key: string, initial: T) {
const [value, setValue] = useState<T>(() => {
const saved = localStorage.getItem(key);
return saved ? JSON.parse(saved) : initial;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue] as const;
}
// 사용
const [theme, setTheme] = useLocalStorage("theme", "light");// useDebounce — 연속 입력 방지
function useDebounce<T>(value: T, delay = 300): T {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debounced;
}
// 사용 — 검색어 입력 시 300ms 후에만 API 호출
function Search() {
const [query, setQuery] = useState("");
const debouncedQuery = useDebounce(query, 300);
const { data } = useFetch(
`/api/search?q=${debouncedQuery}`
);
// ...
}// useMediaQuery — 반응형 감지
function useMediaQuery(query: string): boolean {
const [matches, setMatches] = useState(false);
useEffect(() => {
const media = window.matchMedia(query);
setMatches(media.matches);
const listener = (e: MediaQueryListEvent) =>
setMatches(e.matches);
media.addEventListener("change", listener);
return () => media.removeEventListener("change", listener);
}, [query]);
return matches;
}
// 사용
const isMobile = useMediaQuery("(max-width: 768px)");
return isMobile ? <MobileNav /> : <DesktopNav />;바이브 코더 팁 AI에게 "useWindowSize 커스텀 훅 만들어줘"라고 요청하세요. 결과 코드를 읽고 이해할 수 있으면, 수정도 자유자재입니다.
커스텀 훅의 필수 네이밍 규칙은?
커스텀 훅 안에서는 useState, useEffect 등 다른 훅을 사용할 수 없다
useDebounce의 주요 사용 사례는?
key
핵심 용어
📛
이름은 use로 시작
useUser, useFetch, useTheme 등
🧩
내부에서 훅 사용 가능
useState, useEffect 등 React 훅을 자유롭게 조합
📤
값을 반환
상태, 함수, 객체 등 필요한 것을 반환
♻️
재사용
여러 컴포넌트에서 import해서 사용
edit_note
정리 노트
커스텀 훅 핵심 정리
커스텀 훅 기본
- 커스텀 훅
- use로 시작하는 함수로, 반복되는 로직을 재사용 가능하게 추출
- 규칙
- 반드시 use 접두사 사용, 최상위에서만 호출, 조건문 안에서 호출 금지
- 장점
- 컴포넌트를 깔끔하게 유지하고 로직 테스트가 쉬워짐
자주 쓰는 패턴
- useLocalStorage
- 로컬스토리지 읽기/쓰기를 자동 동기화하는 훅
- useDebounce
- 연속 입력 시 마지막 입력 후 일정 시간 뒤 실행하는 훅
★
같은 로직이 2개 이상의 컴포넌트에서 반복되면 커스텀 훅으로 추출할 시점입니다.
퀴즈와 인터랙션으로 더 깊이 학습하세요
play_circle인터랙티브 레슨 시작