본문 바로가기
JS/React 강의

[React] 23. 커스텀 훅(Custom Hook)을 만들어 보자

by GiraffePark 2024. 2. 7.

안녕하세요. 박기린입니다.

리액트에는 다양한 React Hooks가 존재합니다. 대표적으로 useState, useEffect, useReducer가 있고, 여러 개의 use로 시작되는 훅들이 존재합니다.

이번에는 공식적으로 제공하는 훅 뿐만 아니라, 직접 Custom Hook을 제작하는 방법에 대해 알아보겠습니다.

 

 

 


Custom Hook이란?

Custom Hook이란, 함수는 함수인데 React Hook (ex : useState) 이나 React State를 사용해서 함수를 만드는 것을 의미합니다.

함수 안에서 useState()나 useEffect()와 같은 것을 사용해서, 여러 컴포넌트에서 재사용이 가능한 함수를 만듭니다.

 

예를 들어, useState()는 원래 리액트 컴포넌트 함수 내부에서만 사용할 수 있었는데, Custom Hook을 제작함으로써 독립적인 함수파일에서도 사용할 수 있게 됩니다.

 

 

 


Custom Hook 파일 만들기

src 폴더 내부에 hooks라는 폴더를 만듭니다. 이 안에 커스텀 훅들을 담은 JS 파일들을 저장합니다.

 

React Hook은 useState나 useEffect처럼 'use'라는 키워드로 시작합니다. 필수적으로 지켜야만 하는 규칙입니다. 리액트 엔진은 use 키워드를 보고, 이 함수가 커스텀 훅인지를 알아냅니다. 그래서 이 함수가 React Hook과 같은 방식으로 사용되게끔 보장해줍니다.

 

'use' 방식의 장점 : 만약 커스텀 훅이 '리액트에서 실제 작동하는 hook의 방식'에 위배될 경우 경고를 보내줍니다. -> 커스텀 훅이 잘못된 곳에 사용된다면 경고를 표시합니다.

 

 

 

 

 


예제 : Custom Hook 만들기

두 개의 컴포넌트가 있습니다. 하나는 1초마다 1을 더하고, 하나는 1초마다 1을 뺍니다.

 

 

 

1초마다 1이 추가되는 컴포넌트 / 1이 빼지는 컴포넌트

위 두 컴포넌트로 각각 구성이 되어 있는데, 두 코드에는 공통점이 있습니다.

 

 

 

  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCounter((prevCounter) => prevCounter - 1); // or '+ 1'
    }, 1000);

위 코드들이 중복으로 사용된다는 점입니다. 단지 setCounter에서 1을 빼냐 더하냐의 차이만 있을 뿐입니다. 

지금부터 이 중복되는 부분을 하나의 커스텀 Hook에 담아서 사용해보겠습니다.

 

 

 

 

useCounter라는 커스텀 훅을 담을 파일을 생성합니다.

 

 

 

// use-counter.js
import { useState, useEffect } from "react";

const useCounter = () => {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCounter((prevCounter) => prevCounter + 1);
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  return counter;
}

export default useCounter

아까 중복된 부분의 코드를 그대로 use-counter.js에 복붙합니다.

그리고 counter state를 return 하여, 이 커스텀 훅을 사용하는 컴포넌트가 counter state에 접근할 수 있도록 지정합니다.

 

 

 

 

// ForwardConter.js
import useCounter from "../hooks/use-counter";
import Card from "./Card";

const ForwardCounter = () => {
  const counter = useCounter();
  return <Card>{counter}</Card>;
};

export default ForwardCounter;

useCounter 훅을 사용할 컴포넌트에는 아래와 같은 단계를 진행합니다.

1. 커스텀 훅(useCounter)을 import 합니다.
2. 커스텀 훅이 반환할 값을 담아줄 변수/상수(counter)를 지정합니다.
3. 그 상수(counter)에 커스텀 훅(useCounter)를 할당합니다.

 

커스텀 훅을 사용하니 코드가 훨씬 간결해졌습니다.

 

 

 

 

 

 

이제 컴포넌트에 따라서 1을 더할지 뺄지를 결정해주는 기능을 구현해야 합니다. 방법은 간단합니다. hook 함수가 인수를 전달받도록 지정해주면 됩니다.

 

 

함수를 전달받거나,

 

 

특정 값을 전달받아서, 때에 따라 적절한 결과값을 낼 수 있도록 유도하면 됩니다.

 

 

 

 

const useCounter = (forward = true) => {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      if (forward) {
        setCounter((prevCounter) => prevCounter + 1);
      } else {
        setCounter((prevCounter) => prevCounter - 1);
      }
    }, 1000);

    return () => clearInterval(interval);
  }, [forward]);

  return counter;
};

export default useCounter;

useCounter에는 forward라는 Bool 타입 값을 받도록 지정했습니다.

default 값으로 true를 지정해놨고, 만약 False를 전달받으면 1씩 빼는 기능을 구현하였습니다.

 

 

 

 

const BackwardCounter = () => {
  const counter = useCounter(false);
  return <Card>{counter}</Card>;
};

export default BackwardCounter;

이제 useCounter()에 false를 전달하여, 매 초마다 1씩 빼는 컴포넌트도 구현하였습니다.

 

 

 

 

 

 

 

 

반응형