리액트 up 7. 커스텀훅 구조와 원리

Posted by : on

Category : react-up


훅이란

훅이란 클래스 컴포넌트에서 사용되던 state와 생명주기 관련 기능 등을 함수 컴포넌트에서 쓸 수 있게 하기 위해 만들어졌다.

React 16.8 버전에서 도입된 이 기능은 use로 시작하는 함수들로, useState, useEffect, useContext 등이 대표적인 예다.

훅을 사용하면 함수 컴포넌트에서도 상태 관리와 부수 효과를 처리할 수 있어 코드의 재사용성과 구성을 더 효율적으로 만들 수 있다.

Custom hook만들기 및 주의점

커스텀 훅은 React의 내장 훅을 활용하여 만드는 재사용 가능한 함수. 특별한 API가 아니라 함수의 이름이 use로 시작하는 JavaScript 함수.

hooks라는 폴더를 만들어서 모아놓는것이 일반적!

function useCustomHook(initialValue) {
  const [state, setState] = useState(initialValue);

  useEffect(() => {
    // 원하는 로직 구현
    return () => {
      // 정리(cleanup) 함수
    };
  }, [dependencies]);

  // 필요한 함수 정의
  const handleChange = () => {
    // 상태 변경 로직
  };

  return { state, handleChange }; // 외부에 노출할 값과 함수 반환
}

주의점

  1. 이름은 반드시 ‘use’로 시작
  • React의 규칙을 따르고 lint 도구가 제대로 작동하게한다. 즉 use로 시작 안하면 최적ㅘ가 안되거나 렌더링 우선순의가 잘못처리 될 수 있다.
  1. 훅 호출은 컴포넌트나 다른 훅 내부에서만 해야 한다.
  • 일반 함수나 조건문, 반복문 내부에서 직접 호출하면 안된다
  1. 의존성 배열 관리에 주의해야 한
  • 불필요한 렌더링이나 무한 루프를 방지하기 위해 의존성 배열을 올바르게 설정해야한다
  1. 관심사 분리를 잘 해야 한다
  • 하나의 커스텀 훅이 너무 많은 일을 하지 않도록 해야한다
언제 Custom hook을 만들어야할까

커스텀 훅은 이런 상황에서 만들어야 한다:

  1. 반복되는 로직이 여러 컴포넌트에 있을 때
  • 비슷한 로직이 여러 곳에 중복된다? 커스텀 훅으로 만들어라.
  1. 복잡한 상태 로직을 분리해야 할 때
  • 컴포넌트가 너무 복잡해졌다? 로직을 훅으로 분리해라.
  1. 외부 데이터와 연결할 때
  • API 호출? 웹소켓? 로컬 스토리지? 커스텀 훅으로 만들어라.
  1. 브라우저 API를 사용할 때
  • 위치 정보? 미디어 쿼리? 이벤트? 훅으로 감싸라.
  1. 애니메이션, 타이머, 인터벌 등을 관리할 때
  • 정리(cleanup)가 필요한 로직이다? 커스텀 훅이 해결책이다.

예시 useLocalStorage: 로컬 스토리지 관리? 이 훅이 필요. useFetch: API 요청? 이 훅으로 단순화. useMediaQuery: 반응형 필요할때 useAuth: 인증 로직 훅으로 관리. useTheme: 테마 전환. 커스텀 훅은 코드 중복을 없애고 컴포넌트를 간결하게 만든다. 복잡한 로직이 있다면 커스텀 훅으로 만들어라!

참고로 useFetch같은 경우에는 많이사용해서 전형적인 소스가 존재한다
// hooks/useFetch.js
import { useState, useEffect } from "react";

function useFetch(url, options = {}) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [refetchIndex, setRefetchIndex] = useState(0);

  // 데이터 다시 가져오기 함수
  const refetch = () => {
    setLoading(true);
    setRefetchIndex((prev) => prev + 1);
  };

  useEffect(() => {
    // AbortController로 요청 취소 가능하게 만들기
    const controller = new AbortController();
    const signal = controller.signal;

    const fetchData = async () => {
      setLoading(true);

      try {
        const response = await fetch(url, {
          ...options,
          signal,
        });

        if (!response.ok) {
          throw new Error(`에러 발생! 상태 코드: ${response.status}`);
        }

        const result = await response.json();
        setData(result);
        setError(null);
      } catch (err) {
        if (err.name !== "AbortError") {
          setError(err.message);
          setData(null);
        }
      } finally {
        setLoading(false);
      }
    };

    fetchData();
    s;
    // 클린업 함수: 컴포넌트 언마운트나 의존성 변경 시 요청 취소
    return () => {
      controller.abort();
    };
  }, [url, refetchIndex, JSON.stringify(options)]);

  return { data, loading, error, refetch };
}

export default useFetch;
s;

About 유재석
유재석

개발자 유재석 입니다. Web Developer.

Email : jaeseok9405@gmail.com

Website : https://github.com/yoo94