[React Hook 완전 정복] Step 4: customHook

Suyo·2025년 6월 20일
1

React 애플리케이션을 만들다 보면, 여러 컴포넌트에서 비슷한 로직이 반복되는 경우가 많다.
예를 들어 입력 값 제어, 타이머 관리, 로컬 스토리지 접근, API 상태 관리 등은 다양한 컴포넌트에서 공유되는 공통된 패턴이다.

React는 이러한 로직을 커스텀 Hook(Custom Hook)으로 추상화할 수 있도록 지원한다.
이번 글에서는 커스텀 Hook의 개념부터 작성법, 실전 예제까지 알아본다.


커스텀 Hook이란?

커스텀 Hook은 React의 기본 Hook(useState, useEffect, 등)을 사용해 만든, 재사용 가능한 함수다.
이 함수는 반드시 use로 시작하는 이름을 가지며, 내부에서 하나 이상의 Hook을 사용할 수 있다.

function useCustomFeature() {
  const [value, setValue] = useState(0);
  // 기타 로직 ...
  return [value, setValue];
}

커스텀 Hook은 UI가 아닌 로직만을 담당하며, 함수형 컴포넌트처럼 동작하지만 JSX를 반환하지 않는다.


커스텀 Hook을 사용하는 이유

  • 로직의 재사용성 향상
  • 컴포넌트 코드 분리와 가독성 향상
  • 팀원 간 기능 추상화 및 일관성 유지
  • Hook의 조합을 통해 더 복잡한 기능도 단순화 가능

기본 예제: 입력 값 관리 훅 만들기

여러 입력 필드에서 useState를 반복하는 대신, 커스텀 훅으로 추상화할 수 있다.

// useInput.js
import { useState } from 'react';

function useInput(initialValue = "") {
  const [value, setValue] = useState(initialValue);
  const onChange = (e) => setValue(e.target.value);
  const reset = () => setValue("");
  return { value, onChange, reset };
}

export default useInput;

사용 예시:

import useInput from './useInput';

function LoginForm() {
  const username = useInput();
  const password = useInput();

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log("입력값:", username.value, password.value);
    username.reset();
    password.reset();
  };

  return (
    <form onSubmit={handleSubmit}>
      <input {...username} placeholder="아이디" />
      <input {...password} type="password" placeholder="비밀번호" />
      <button>로그인</button>
    </form>
  );
}

커스텀 Hook 작성 원칙

이름은 use로 시작해야 한다

→ React는 이 네이밍 규칙을 기반으로 Hook을 식별하고 동작 순서를 관리한다.

다른 Hook과 마찬가지로 최상위 레벨에서만 호출해야 한다

→ 조건문, 루프, 중첩 함수 내에서 호출하면 안 된다.

JSX를 반환하지 않는다

→ 커스텀 Hook은 로직만 담당하는 함수이며, UI는 일반 컴포넌트에서 처리한다.


실무 예제

API 상태 관리 Hook

import { useEffect, useState } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let ignore = false;
    setLoading(true);
    fetch(url)
      .then((res) => res.json())
      .then((result) => {
        if (!ignore) {
          setData(result);
          setLoading(false);
        }
      });

    return () => {
      ignore = true;
    };
  }, [url]);

  return { data, loading };
}

사용 예시:

function PostList() {
  const { data: posts, loading } = useFetch("https://jsonplaceholder.typicode.com/posts");

  if (loading) return <p>로딩 중...</p>;

  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

로컬 스토리지 Hook

import { useState, useEffect } from 'react';

function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    const stored = localStorage.getItem(key);
    return stored ? JSON.parse(stored) : initialValue;
  });

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

  return [value, setValue];
}

사용 예시:

function ThemeToggle() {
  const [darkMode, setDarkMode] = useLocalStorage("darkMode", false);

  return (
    <button onClick={() => setDarkMode(!darkMode)}>
      {darkMode ? "라이트 모드" : "다크 모드"}
    </button>
  );
}

핵심 정리

  1. 커스텀 Hook은 Hook 기반 로직을 재사용 가능한 함수로 추상화한 것이다.
  2. 이름은 use로 시작해야 하며, JSX를 반환하지 않는다.
  3. 여러 Hook들을 조합하여 복잡한 동작을 하나의 커스텀 Hook으로 묶을 수 있다.
  4. 반복적인 상태 관리, API 처리, 이벤트 등록 등 비즈니스 로직을 재사용성 있게 구성할 수 있다.
  5. 커스텀 Hook은 로직의 분리컴포넌트 단순화에 핵심적인 역할을 한다.

마무리

React에서 커스텀 Hook은 로직을 모듈화하고 팀 개발 효율을 높이기 위한 가장 강력한 도구 중 하나다. 복잡한 컴포넌트일수록 로직을 커스텀 Hook으로 분리하면 코드가 훨씬 읽기 쉬워지고, 유지보수성이 향상된다.
앞으로는 하나의 기능을 구현할 때마다 "이걸 커스텀 Hook으로 만들 수 있을까?"를 고민해보자. 작은 반복도 훅으로 추상화하는 습관이 점점 더 탄탄한 리액트 실력을 만들어줄 것이다.


참고 자료

profile
Mee-

0개의 댓글