dev.syw

반복되는 로직을 훅으로 뽑아내 재사용하기.

커스텀 훅 만들기

여러 컴포넌트에서 같은 상태 로직이 반복된다면, 그 로직을 커스텀 훅으로 뽑아낼 수 있습니다. 컴포넌트는 깔끔해지고 로직은 한 곳에서 관리됩니다.

학습 목표

  • 커스텀 훅으로 로직을 재사용하는 이유를 안다.
  • use 접두사 규칙과 훅의 규칙을 지킬 수 있다.
  • useToggle, useLocalStorage 같은 실용 훅을 만들 수 있다.
  • 커스텀 훅이 state를 공유하지 않는다는 점을 이해한다.

커스텀 훅이란?

커스텀 훅은 그저 use 로 시작하는 일반 함수이며, 내부에서 다른 훅(useState, useEffect 등)을 호출할 수 있습니다. 컴포넌트에서 떼어낸 "재사용 가능한 로직 묶음"입니다.

import { useState } from 'react';

function useCounter(initial = 0) {
  const [count, setCount] = useState(initial);
  const increment = () => setCount((c) => c + 1);
  const decrement = () => setCount((c) => c - 1);
  const reset = () => setCount(initial);
  return { count, increment, decrement, reset };
}

// 사용
function Counter() {
  const { count, increment, reset } = useCounter(0);
  return (
    <>
      <p>{count}</p>
      <button onClick={increment}>+1</button>
      <button onClick={reset}>리셋</button>
    </>
  );
}

use 접두사 규칙

이름이 use 로 시작해야 React가 "이건 훅"이라고 인식해 규칙 검사와 린트를 적용합니다.

💡 TIP — 내부에서 훅을 하나도 안 쓰는 그냥 유틸 함수라면 use 를 붙이지 마세요. 반대로 훅을 쓴다면 반드시 use 로 시작하세요.

useToggle 예제

불리언을 켜고 끄는 흔한 패턴입니다.

import { useState, useCallback } from 'react';

function useToggle(initial = false) {
  const [on, setOn] = useState(initial);
  const toggle = useCallback(() => setOn((v) => !v), []);
  return [on, toggle];
}

function Panel() {
  const [open, toggleOpen] = useToggle();
  return (
    <>
      <button onClick={toggleOpen}>{open ? '닫기' : '열기'}</button>
      {open && <div>패널 내용</div>}
    </>
  );
}

useLocalStorage 예제

state를 브라우저 localStorage 와 동기화해, 새로고침해도 값이 남게 합니다.

import { useState, useEffect } from 'react';

function useLocalStorage(key, initial) {
  const [value, setValue] = useState(() => {
    const saved = localStorage.getItem(key);
    return saved !== null ? JSON.parse(saved) : initial; // 지연 초기화
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue];
}

// 사용 — useState와 똑같은 모양!
function Settings() {
  const [name, setName] = useLocalStorage('name', '');
  return <input value={name} onChange={(e) => setName(e.target.value)} />;
}
  • 첫 렌더에서만 localStorage 를 읽도록 지연 초기화(함수 형태)를 썼습니다.
  • 값이 바뀔 때마다 effect가 저장합니다.

훅의 규칙

커스텀 훅이든 내장 훅이든 다음 두 규칙을 반드시 지켜야 합니다.

  1. 최상위에서만 호출if, 반복문, 중첩 함수 안에서 호출하면 안 됩니다. 호출 순서가 매 렌더 같아야 React가 state를 올바르게 연결합니다.
  2. React 함수 안에서만 호출 — 컴포넌트나 다른 커스텀 훅 안에서만. 일반 함수에서는 안 됩니다.
// ❌ 조건 안에서 훅 호출
if (loggedIn) {
  const [x, setX] = useState(0);
}

// ✅ 최상위에서 호출하고, 조건은 안에서
const [x, setX] = useState(0);
if (loggedIn) { /* x 사용 */ }

⚠️ 주의 — 같은 커스텀 훅을 두 컴포넌트가 써도 state는 공유되지 않습니다. 각 컴포넌트가 자기만의 독립된 state를 갖습니다. 상태를 공유하려면 Context나 부모로 끌어올리세요.

요약

  • 커스텀 훅 = use 로 시작하고 내부에서 훅을 쓰는 함수.
  • 반복되는 상태 로직을 뽑아 컴포넌트를 깔끔하게 만든다.
  • useToggle, useLocalStorage 처럼 useState 와 같은 사용감을 만들 수 있다.
  • 훅은 최상위에서, React 함수 안에서만 호출한다.
  • 커스텀 훅은 로직을 공유할 뿐 state는 공유하지 않는다.

연습문제

  1. 마우스 위치 { x, y } 를 추적하는 useMousePosition 훅을 만드세요. (이벤트 구독·해제 포함)
  2. useTogglesetTrue, setFalse 도 추가해 반환하세요.
  3. 창 너비를 알려주는 useWindowWidth 훅을 만들고 리사이즈에 반응하게 하세요.
  4. useLocalStorage 를 사용해 다크모드 on/off 상태를 새로고침 후에도 유지하세요.

힌트 — 1·3번은 useEffect 에서 이벤트 등록 후 클린업으로 해제. 4번은 useLocalStorage('dark', false).

💡 연습문제 풀이

불러오는 중…

함께 보면 좋은 자료

댓글 0

React.js” 강좌에 대한 댓글입니다.

댓글을 작성하려면 로그인이 필요합니다.