--- id: react-custom-hook-patterns title: Custom Hook 작성 패턴 category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [react, hooks, custom-hook, abstraction, vibe-coding] tech_stack: { language: "TypeScript / React 18+", applicable_to: ["Web", "React Native"] } applied_in: [] aliases: [reusable hook, hook composition] related_existing: [Frontend/Custom_Hooks.md, Frontend/Custom-Hooks-Patterns.md] duplicate_check: "기존 두 파일은 폭넓은 개요. 본 문서는 네이밍·반환형·테스트 측면 보강." --- # Custom Hook 작성 패턴 > Custom hook 은 `use` 로 시작하는 함수 + 다른 hook 호출. 핵심은 **이름이 의도를 정확히 말하는가**, **반환형이 사용처에 자연스러운가**, **테스트 가능한가**. ## 📖 핵심 개념 - 이름: `use` (e.g. `useFormSubmit`, `useDebouncedValue`). - 반환형: 한두 개면 tuple, 셋 이상이면 객체. - 부작용은 hook 내부 useEffect 로, 호출자는 dispatcher / state 만 받음. ## 💻 코드 패턴 ```ts // ✅ 단일 값 → 그냥 반환 function useDebouncedValue(value: T, ms: number): T { const [debounced, setDebounced] = useState(value); useEffect(() => { const t = setTimeout(() => setDebounced(value), ms); return () => clearTimeout(t); }, [value, ms]); return debounced; } // ✅ state + setter — tuple function useToggle(initial = false): [boolean, () => void] { const [v, set] = useState(initial); const toggle = useCallback(() => set(x => !x), []); return [v, toggle]; } // ✅ 다중 반환 — 객체 + 명시적 키 function useInfiniteQuery(...): { data: T[]; isLoading: boolean; hasNextPage: boolean; fetchNextPage: () => void; } { ... } ``` ## 🤔 의사결정 기준 | 반환 항목 수 | 형식 | |---|---| | 1 | 그대로 반환 | | 2 | tuple `[value, setter]` (useState 패턴) | | 3+ | 객체 (key 명시로 가독성) | | 다양한 변형이 필요 | discriminated union 으로 상태 표현 | | 추출 기준 | hook | |---|---| | 컴포넌트 간 상태 로직 재사용 | ✅ | | 단일 컴포넌트 내 정리 | ❌ (그냥 함수) | | 외부 시스템 동기화 (websocket, sub) | ✅ | | 순수 변환만 | ❌ (일반 함수) | ## ❌ 안티패턴 - **`use` 가 아닌 이름**: ESLint hooks 룰 깨짐. 일반 함수로 인식. - **조건부 hook 호출**: `if (x) useEffect(...)`. React rules 위반. - **반환 객체 매번 새 reference**: 호출자 deps 무한 trigger. 내부 useMemo / 안정 참조. - **부작용 노출**: useDebounced 가 ref / dispatcher 노출 — 호출자가 hook 내부 모르고 mutate. - **테스트 불가능**: hook 안에서 `Date.now()`, `Math.random()` 직접. 시간/랜덤 주입 가능하게. ## 🤖 LLM 활용 힌트 - LLM에게 "이 컴포넌트에서 X 로직을 hook 으로 추출" 요청 시 위 네이밍 + 반환형 규칙 강조. - 테스트는 `@testing-library/react` 의 `renderHook` 사용. ## 🔗 관련 문서 - [[React_useEffect_Pitfalls]] - [[Pure_Functions_in_Practice]]