리액트 훅(React Hook) 이해하기

2025. 1. 19. 18:08·FRONTEND/React

리액트를 처음 배우면서 "리액트 훅"이라는 용어를 자주 접하게 되었다.

더불어, Zustand를 공부할 때도 "훅"을 사용해야 한다는 이야기를 들으니 ..

이 개념에 대해 좀 더 깊이 공부할 필요성을 느꼈다.

그래서 이번 글에서는 리액트 훅, 사용자 정의 훅, 훅 작성 규칙 등을 자세히 살펴보려고 한다.


1. 리액트 훅(Hook)이란?

리액트에서 컴포넌트는 화면을 구성하는 단위로,

상태(state)를 가지고 있으며 이 상태가 바뀔 때마다 화면도 자동으로 업데이트된다.

 

리액트 훅은 함수형 컴포넌트에서 (1) 상태를 관리하고, (2) 부수 효과를 처리할 수 있도록 도와주는 특별한 함수이다.

클래스형 컴포넌트를 사용하지 않고도 상태를 관리하고, API 호출과 같은 작업을 처리할 수 있다.

 

리액트에서 자주 사용하는 훅은 useState, useEffect, useContext 등이 있다.


2. 리액트에서 가장 많이 사용되는 훅들

2-1. useState : 상태 관리

  • 컴포넌트 내에서 상태를 관리할 수 있게 해주는 훅
    ex) 버튼 클릭 시 카운트가 증가하는 기능
import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);  // 상태 변수와 그 값을 변경하는 함수

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}
  • useState(0)에서 0은 초기값이다.
  • count는 현재 상태를 나타내고, setCount는 상태를 변경하는 함수이다.

 

2-2. useEffect : 부수 효과 처리

  • 부수 효과를 처리할 때 사용
    부수 효과란, 함수나 컴포넌트 내에서 발생하는 주된 동작 외의 추가적인 동작을 의미(ex API 호출, 타이머 설정 등)
    ex) 데이터 요청이나 이벤트 리스너 추가 등 렌더링 외에 필요한 작업을 할 때 사용
import React, { useState, useEffect } from 'react';

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const timer = setInterval(() => {
      setSeconds(prev => prev + 1);
    }, 1000);

    return () => clearInterval(timer);  // 타이머 정리
  }, []);  // 빈 배열: 첫 렌더링 때만 실행

  return <p>Timer: {seconds} seconds</p>;
}
  • useEffect는 컴포넌트가 처음 렌더링될 때 타이머를 시작한다.
  • 컴포넌트가 사라질 때 타이머를 정리하는 작업도 return문에서 처리한다.

 

2-3. useContext : 전역 상태 관리

  • 여러 컴포넌트에서 공통된 데이터를 관리하고 공유하는 데 유용
import React, { createContext, useContext, useState } from 'react';

const CountContext = createContext();  // 전역 상태를 위한 Context

function CountProvider({ children }) {
  const [count, setCount] = useState(0);

  return (
    <CountContext.Provider value={{ count, setCount }}>
      {children}
    </CountContext.Provider>
  );
}

function Display() {
  const { count } = useContext(CountContext);  // Context에서 상태 가져오기
  return <p>Current Count: {count}</p>;
}

function Increment() {
  const { setCount } = useContext(CountContext);  // 상태 변경 함수 사용
  return <button onClick={() => setCount(prev => prev + 1)}>Increment</button>;
}

function App() {
  return (
    <CountProvider>
      <Display />
      <Increment />
    </CountProvider>
  );
}
  • CountContext를 통해 상태를 공유하며, useContext로 쉽게 접근할 수 있다.

3. Zustand와 리액트 훅

  • Zustand는 리액트에서 전역 상태를 관리하는 라이브러리
  • 리액트의 useState 훅을 사용하면 컴포넌트 내부에서만 상태를 관리할 수 있지만, Zustand는 상태를 여러 컴포넌트에서 공유할 수 있게 해 준다.
import create from 'zustand';

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}));

function Counter() {
  const { count, increment } = useStore();  // Zustand 훅 사용
  return (
    <div>
      <p>{count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}
  • useStore 훅을 사용해 전역 상태를 관리하고, 상태를 다른 컴포넌트에서 쉽게 공유할 수 있다.

4. 사용자 정의 훅(Custom Hook)

  • 리액트 훅을 활용하여 공통된 로직을 재사용할 수 있다.
    이것을 사용자 정의 훅이라 하며, 여러 컴포넌트에서 필요한 로직을 공유할 때 유용하다.

ex) useWindowWidth : 화면의 너비를 추적하는 로직을 재사용 가능한 훅

import { useState, useEffect } from 'react';

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handleResize);

    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return width;
}

function App() {
  const width = useWindowWidth();  // 사용자 정의 훅 사용
  return <h1>Window width: {width}px</h1>;
}

 


5. 리액트 훅 작성 규칙 (Rules of Hooks)

리액트 훅을 사용할 때는 몇 가지 중요한 규칙을 반드시 지켜야 한다.

이 규칙들을 지키지 않으면 훅이 예상대로 작동하지 않거나 오류가 발생할 수 있다.

 

1. 컴포넌트 내부에서만 호출

-> 훅은 함수형 컴포넌트 안에서만 호출해야 한다.

 

- 리액트 훅은 함수형 컴포넌트의 최상위 레벨에서 호출되어야 한다.

- 클래스형 컴포넌트나 일반 자바스크립트 함수 안에서는 훅을 사용할 수 없다.

- 훅을 사용하려면 반드시 리액트의 함수형 컴포넌트 내부에서 호출해야 한다.

 

잘못된 예시

function notAComponent() {
  const [state, setState] = useState(0);  // 오류 발생 !! 함수형 컴포넌트 외부에서 훅을 사용했기 때문에 오류가 발생한다.
}

 

올바른 예시

function MyComponent() {
  const [state, setState] = useState(0);  // 컴포넌트 내부에서 훅을 호출해야 한다.
  return <div>{state}</div>;
}

 

2. 최상위에서만 호출

-> 조건문, 반복문, 또는 중첩 함수 안에서 훅을 호출하지 말고, 컴포넌트의 최상위 레벨에서만 호출해야 한다.

 

- 리액트 훅은 반복문, 조건문, 중첩된 함수 안에서 호출해서는 안 된다.

- 훅은 렌더링 되는 컴포넌트의 매 렌더링마다 동일한 순서로 호출되어야 하므로, 조건에 따라 훅을 호출하면 렌더링 간에 훅 호출 순서가 달라져서 오류가 발생할 수 있다.

 

잘못된 예시

function Counter({ count }) {
  if (count > 0) {
    const [state, setState] = useState(0);  // 조건문 안에서 훅을 호출하면 안 된다.
  }
  return <div>{count}</div>;
}

 

올바른 예시

function Counter({ count }) {
  const [state, setState] = useState(0);  // 최상위에서 호출해야 한다.

  if (count > 0) {
    // 조건문 안에서 상태를 사용할 수 있다.
  }
  return <div>{count}</div>;
}

 

3. use로 시작

-> 리액트 훅은 모두 use로 시작해야 한다.


- 리액트에서 제공하는 내장 훅과 사용자 정의 훅은 모두 use로 시작해야 한다.

- 이것은 훅이 리액트의 특별한 함수임을 구분할 수 있게 해주는 규칙이다.

- ex) useState, useEffect, useContext 등은 리액트에서 제공하는 훅

- 사용자가 정의한 훅도 use로 시작해야 하며, 이를 통해 리액트가 해당 함수가 훅임을 인식하고 올바르게 처리할 수 있다.

 

잘못된 예시

function myCustomHook() {  // 사용자 정의 훅 이름은 'use'로 시작해야 한다.
  const [state, setState] = useState(0);
  return [state, setState];
}

 

올바른 예시

function useCustomHook() {  // 사용자 정의 훅 이름은 'use'로 시작해야 한다.
  const [state, setState] = useState(0);
  return [state, setState];
}

 

훅 규칙을 지켜야 하는 이유

이 규칙들은 리액트의 렌더링 사이클에 맞추어 훅이 일관되게 호출되도록 보장한다.

 

예를 들어, 훅은 컴포넌트가 렌더링 될 때마다 같은 순서로 호출되어야 하기 때문에
조건문이나 반복문 안에서 훅을 호출하면 리액트가 훅의 호출 순서를 추적할 수 없게 되어 예기치 않은 동작을 초래할 수 있다.

 

따라서 훅을 사용할 때는 항상 위의 규칙들을 지켜야만 리액트가 올바르게 동작하고,
예상대로 상태 변경 및 부수 효과 처리가 이루어질 수 있다.


결론

1. 리액트 훅은 상태 관리와 부수 효과 처리 작업을 매우 간편하게 해 준다.

2. useState, useEffect, useContext와 같은 기본 훅을 잘 활용하면 리액트 컴포넌트를 더 쉽게 만들 수 있다.

3. Zustand와 같은 라이브러리 역시 훅을 활용해 전역 상태를 쉽게 관리하고 공유할 수 있게 도와준다.

 

=> 리액트 훅을 제대로 이해하고 사용하면 더 깔끔하고 효율적인 코드를 작성할 수 있다.

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

리액트 훅 | useState에 대해 알아보기  (4) 2025.08.08
부모 요소 패딩 값에 영향을 받지 않고 전체 너비 구분선 그리기  (0) 2025.04.04
모던 리액트 Deep Dive | 1.1 자바스크립트와 동등 비교  (0) 2025.03.09
리액트 컴포넌트 기반 구조 이해하기(WITH 재사용성, 유지보수성)  (0) 2025.01.05
React 프로젝트 생성 with Vite & Tailwind  (2) 2024.12.26
'FRONTEND/React' 카테고리의 다른 글
  • 부모 요소 패딩 값에 영향을 받지 않고 전체 너비 구분선 그리기
  • 모던 리액트 Deep Dive | 1.1 자바스크립트와 동등 비교
  • 리액트 컴포넌트 기반 구조 이해하기(WITH 재사용성, 유지보수성)
  • React 프로젝트 생성 with Vite & Tailwind
uoaheu
uoaheu
uoaheu 님의 블로그 입니다.
  • uoaheu
    uoaheu 님의 블로그
    uoaheu
  • 전체
    오늘
    어제
    • 분류 전체보기 (50)
      • 알고리즘 (7)
      • CS (9)
      • FRONTEND (9)
        • React (12)
        • Kotlin (1)
        • JS (5)
        • HTML (2)
      • SQL (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    boj25418
    토스 uiux
    mysql 피벗테이블
    리액트usestate
    BFS
    mysql
    이더넷프레임
    토스분석
    유레카3기
    toss 분석
    엘지유플러스유레카프론트엔드
    mysql로 피벗테이블만들기
    토스 앱 분석
    toss uiux
    혼자서 공부하는 네트워크
    useactionstate
    알고리즘
    멀티캠퍼스it부트캠프
    부트캠프후기
    백준1926번
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
uoaheu
리액트 훅(React Hook) 이해하기
상단으로

티스토리툴바