상세 컨텐츠

본문 제목

React Hook 기본 정리 / Custom Hook

Front/React

by Chan.94 2025. 2. 5. 14:03

본문

반응형

React Hook의 등장배경

리액트 컴포넌트는 함수형 컴포넌트와 클래스형 컴포넌트로 나뉜다.

함수형 컴포넌트에 Hook이 나오기 전까지는, 일반적으로 클래스형 컴포넌트를 많이 사용했다. 

하지만 클래스형 컴포넌트에서 상태(state)를 사용하고, 생명주기 메서드를 사용하는 방식은 많은 문제들과 불편함을 가지고 있었다. 

React Hook은 클래스형 컴포넌트의 단점을 보완하여, 함수형 컴포넌트를 사용할 수 있도록 한다.

 

React Hook

리액트 16.8 버전에 새로 도입된 기능으로, 함수형 컴포넌트에서 사용되는 몇 가지 기술들을 일컫는다. 

리액트 훅은 함수형 컴포넌트가 클래스형 컴포넌트의 기능을 사용할 수 있도록 해주며 대표적인 예로는 useState, useEffect 등이 존재한다.

 

React Hook 사용 규칙

  • 최상위에서만 Hook을 호출해야한다.
    • 반복문이나 조건문 혹은 중첩된 함수 내에서 Hook을 호출하면 안 된다. 리액트 훅은 호출되는 순서에 의존하기 때문에 조건문이나 반복문 안에서 실행하게 될 경우 해당 부분을 건너뛰는 일이 발생할 수도 있기 때문에 순서가 꼬여 버그가 발생할 수 있다.
    • 이 규칙을 따르면 useState와 useEffect가 여러 번 호출되는 경우에도 Hook의 상태를 올바르게 유지할 수 있게 된다.
  • Hook은 React 함수 내에서만 호출해야 한다.
    • 일반적인 javascript 함수에서 호출하면 안 된다
    • 함수형 컴포넌트나 custom hook에서는 호출 가능하다.

useState

useState는 상태를 관리하는 Hook으로 클래스형 컴포넌트에서만 사용 가능하던 State를 함수형 컴포넌트에서도 사용 가능 하도록 한 대표적인 기능이다.

 

선언방법

const [변수명, 상태를 업데이트 할 함수명] = useState(초기값)

useState로 선언된 변수는 상태(state)가 변할 때마다 페이지를 리렌더링 시키게 된다. 

  • 현재 상태를 나타내는 state값과 이 상태를 변경하는 setState값을 한 쌍으로 제공한다.
  • state는 초기값을 설정할 수 있으며, 초기값은 첫 렌더링 때 한번 사용된다.
  • state는 객체일 필요 없이 문자열, 숫자, boolean, 배열, null, 객체 등의 여러 가지 다양한 값을 넣을 수 있다.

 

useState Example

import React, { useState } from 'react';

function Example() {
  // "count"라는 새 상태 변수를 선언합니다
  const [count, setCount] = useState(0);

  // 버튼을 클릭할 때마다 기존의 state 기본값인 0에 1이 더해진다.
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

useEffect

useEffect는 화면이 렌더링 될 때마다, 특정 작업을 실행할 수 있도록 하는 Hook으로 일반적으로 sideEffect의 처리를 위해서 사용된다고 말한다.

sideEffect란?
컴포넌트가 화면에 렌더링 된 이후에 비동기로 처리되어야 하는 부수적인 효과들을 말한다.

 

선언방법

useEffect( () => {실행시킬 함수}, 의존성 배열)

의존성 배열에 따라 렌더링의 횟수를 조절할 수 있다.

  • 생략
    리렌더링시마다 함수 실행
  • []
    최초 렌더링될 때 한 번만 함수 실행
  • [상태값 1, 상태값 2..]
    선언한 상태값들이 업데이트될 때만 실행

 

useEffect Example

import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

useMemo

컴포넌트의 성능을 최적화하는 데 사용한다

 

  • 계산 비용이 높은 연산 결과의 재사용
    복잡한 계산을 수행하는 함수의 결과를 저장하고 필요할 때 재사용한다.
  • 참조 동일성 유지
    객체나 배열과 같은 복잡한 데이터 타입의 불필요한 재생성을 방지하여 자식 컴포넌트의 불필요한 재렌더링을 막는다.

선언 방법

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
  • 첫 번째 인자 → 계산을 수행하는 함수
  • 두 번째 인자 → 의존성 배열
    이후 렌더링에서는 의존성 배열의 값이 변하지 않는다면 이전에 계산한 값을 재사용한다.

 

사용 시 주의할 점

  • 불필요한 사용은 오히려 성능을 저하시킬 수 있다
  • useMemo를 사용할 때 의존성 배열을 설정하는 것은 매우 중요하다
    • 빈 배열([])을 사용하면 컴포넌트 마운트 시에만 계산이 수행되고 이후에는 항상 같은 값을 반환한다
    • 메모이제이션된 값의 계산에 사용되는 변수나 상태만 의존성 배열에 포함시켜야 한다.
    • 의존성 배열에 포함된 값들이 자주 변경되면 useMemo의 효과가 감소한다. 따라서 가능한 한 변경 빈도가 낮은 값들을 선택해야 한다.

 

useMemo은 어떻게 동작하는가

  1. useMemo를 사용하면 React는 먼저 현재 종속성과 이전 종속성을 비교하여 종속성이 변경되었는지 확인
  2. 종속성이 변경되지 않았다면, React는 함수를 다시 실행하지 않고 이전에 메모한 값을 반환
  3. 종속성이 변경되었다면, 함수를 실행하고 값을 새로 계산하고 메모한다. 그다음, 이 메모된 값을 반환

useMemo Example

import React, { useEffect, useState, useMemo } from 'react';

function UseMemoExample() {
    const [localStates, setLocalStates] = useState({
        number1: 0,
        number2: 0
    });

    const fn_localStateChange = (e, stateName) => {
        setLocalStates(prev => ({...prev,[stateName]: e.target.value}));
    };

    const doubleNumber = useMemo(() => {
        console.log('Calculating doubleNumber');
        const result = slowFunction(localStates.number1);
        setLocalStates(prev => ({...prev, number2: result}));
        return result;
    }, [localStates.number1]);

    function slowFunction(num) {
        console.log('Calling slow function');
        for (let i = 0; i <= 1000000000; i++) { }
        return num * 2;
    }

    return (
        <div>
            <input
                type="number"
                value={localStates.number1}
                onChange={e => fn_localStateChange(e, 'number1')}
            />
            <div>Double: {doubleNumber}</div>
        </div>
    );
}

export default UseMemoExample;

 


useContext

컨텍스트는 컴포넌트 트리 전체에 전역적으로 데이터를 전달할 수 있다.

 

사용방법

1. Context 생성

const MyContext = React.createContext

createContext 함수를 호출하여 새로운 Context 객체를 만든다.

 

2. Provider로 값 제공

<MyContext.Provider value={/* 공유할 값 */}>
	{/* 자식 컴포넌트 */} 
</MyContext.Provider>

생성된 Context 객체는 Provider 컴포넌트를 갖고 있다. 이 컴포넌트는 하위 컴포넌트들에게 컨텍스트 값을 전달한다.
Provider의 value가 변경되면 해당 Context를 사용하는 모든 하위 컴포넌트가 리렌더링 된다

 

3. useContext로 값 사용

const value = React.useContext(MyContext);

useContext를 사용하여 함수 컴포넌트 내에서 컨텍스트 값을 구독할 수 있다.

 

특징

  • 전역 상태 관리
    앱 전체에서 사용할 수 있는 전역 상태를 만들 수 있다
  • props 드릴링 방지
    여러 단계의 컴포넌트를 거치지 않고도 직접 데이터에 접근할 수 있어 props 드릴링 문제를 해결한다
  • 간편한 사용
    createContext로 Context를 생성하고, Provider로 값을 제공한 후, useContext로 값을 사용할 수 있다

useContext Example

import React, { createContext, useContext, useState } from "react";

// AuthContext 생성
const AuthContext = createContext();

// AuthProvider로 값 제공
export const AuthProvider = ({ children }) => {
    const [user, setUser] = useState(null); // 사용자 정보 상태 (null: 로그아웃 상태)

    // 로그인 함수
    const login = (userData) => {
        setUser(userData); // 사용자 정보 설정
    };

    // 로그아웃 함수
    const logout = () => {
        setUser(null); // 사용자 정보 제거
    };

    return (
        <AuthContext.Provider value={{ user, login, logout }}>
            {children}
        </AuthContext.Provider>
    );
};

// Custom Hook for AuthContext
export const useAuth = () => useContext(AuthContext);

 

Context 여러 개 사용할 경우

CombinedProvider를 생성하여 여러 개의 Context Provider를 하나로 결합하여 사용할 수 있게 해주는 패턴을 적용

import React from "react";
import { AuthProvider } from "./AuthContext";
import { ModalProvider } from "./ModalContext";

const CombinedProvider = ({ children }) => {
    return (
        <ModalProvider>
            <AuthProvider>
                {children}
            </AuthProvider>
        </ModalProvider>
    );
};

export default CombinedProvider;
import React, { useEffect } from 'react';
import CombinedProvider from './context/CombinedProvider';

const App = () => {

  return (
    <CombinedProvider>
      <YourComponents />
    </CombinedProvider>
  );
}

export default App;

Custom Hook

  • 리액트의 기본 훅들을 조합하여 재사용 가능한 상태 로직을 만드는 방식
  • 커스텀 훅을 사용하면 컴포넌트에서 반복되는 로직을 분리하여 중복을 줄이고, 코드의 재사용성을 높일 수 있다
  • 커스텀 훅은 일반 함수처럼 정의되지만, 이름은 use로 시작해야 한다 이를 통해 개발자들은 해당 함수가 리액트의 훅 규칙을 따르는 커스텀 훅임을 쉽게 인지할 수 있다

Custom Hook Example

import { useState, useEffect } from 'react';

function useLocalState(initialState, onStateChange) {
    const [localStates, setLocalStates] = useState(initialState);

    useEffect(() => {
        if (onStateChange) {
            onStateChange(localStates);
        }
    }, [localStates, onStateChange]);

    const handleLocalStateChange = (value, stateName) => {
        setLocalStates(prev => ({ ...prev, [stateName]: value }));
    };

    return [localStates, handleLocalStateChange, setLocalStates];
}

export default useLocalState;
  • 배열 형태로 반환할 경우
    • 대괄호 []를 사용하여 순서대로 값을 할당
    • 인덱스 번호가 중요하며, 순서가 유지되어야 함
    • 반환된 값의 이름을 자유롭게 지정할 수 있음
    • 주로 반환하는 항목의 수가 적거나 같은 컴포넌트에서 여러 번 사용할 때 선호
  • 객체 형태로 반환할 경우
    • 중괄호 {}를 사용하여 구조 분해 할당으로 값을 가져옴
    • 프로퍼티 이름을 기준으로 값을 가져오며, export 된 이름을 그대로 사용해야 함
    • 필요한 경우 이름을 변경하여 사용할 수 있음
    • 주로 반환하는 항목의 수가 많거나 같은 컴포넌트에서 한 번만 사용할 때 선호

 

Custom Hook 일괄 Export 적용시키기

일괄 export를 적용하여  import 할 때 경로를 간결하게 작성할 수 있으며 모든 커스텀 훅을 한 곳에서 관리할 수 있다.

 

index.js 생성

import useLocalState from "./useLocalState";
import useCustomHook1 from "./useCustomHook1";
import useCustomHook2 from "./useCustomHook2";
import useCustomHook3 from "./useCustomHook3";
...

export {
    useLocalState,
    useCustomHook1,
    useCustomHook2,
    useCustomHook3,
    ...
}

 

커스텀 훅 import

import { useLocalState, useCustomHook1, useCustomHook2 } from '../hooks';

 


마무리

앞에서 그냥 사용했던 useState, useEffect와 같은 Hook들이 어떤것인지 정리가 되었을 것이라 생각한다.

 

이전글

다음글

 

 

반응형

'Front > React' 카테고리의 다른 글

React axios 모듈화  (0) 2025.02.07
React-Router-Dom 정리  (0) 2025.02.06
React CORS 설정 - 프록시 (http-proxy-middleware)  (7) 2025.01.31
React 설치 및 실행 / 프로젝트 구조 정리  (3) 2025.01.30
React 기본 개념 정리  (5) 2025.01.23

관련글 더보기

댓글 영역

>