React
Hooks
JavaScript

React Hooks 완벽 가이드

2024. 1. 20.
15분
블로그 포스트

Modern Blog

웹 개발 전문가

React Hooks 완벽 가이드

React Hooks는 함수형 컴포넌트에서 상태와 생명주기를 관리할 수 있게 해주는 기능입니다.

기본 Hooks

useState

가장 기본적인 Hook으로 상태를 관리합니다:

import { useState } from "react";

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

  return (
    <div>
      <p>카운트: {count}</p>
      <button onClick={() => setCount(count + 1)}>증가</button>
    </div>
  );
}

useEffect

컴포넌트의 생명주기와 관련된 작업을 처리합니다:

import { useState, useEffect } from "react";

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchUser() {
      try {
        const response = await fetch(`/api/users/${userId}`);
        const userData = await response.json();
        setUser(userData);
      } catch (error) {
        console.error("사용자 정보를 가져오는데 실패했습니다:", error);
      } finally {
        setLoading(false);
      }
    }

    fetchUser();
  }, [userId]); // userId가 변경될 때만 실행

  if (loading) return <div>로딩 중...</div>;
  if (!user) return <div>사용자를 찾을 수 없습니다.</div>;

  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}

커스텀 Hooks

자주 사용하는 로직을 재사용 가능한 Hook으로 만들 수 있습니다:

import { useState, useEffect } from "react";

function useLocalStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });

  const setValue = (value) => {
    try {
      const valueToStore =
        value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.error(error);
    }
  };

  return [storedValue, setValue];
}

// 사용 예시
function ThemeToggle() {
  const [theme, setTheme] = useLocalStorage("theme", "light");

  return (
    <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
      현재 테마: {theme}
    </button>
  );
}

고급 Hooks

useReducer

복잡한 상태 로직을 관리할 때 사용합니다:

import { useReducer } from "react";

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    case "reset":
      return { count: 0 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>카운트: {state.count}</p>
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
      <button onClick={() => dispatch({ type: "reset" })}>리셋</button>
    </div>
  );
}

useCallback과 useMemo

성능 최적화를 위한 Hook들입니다:

import { useState, useCallback, useMemo } from "react";

function ExpensiveComponent({ items, onItemClick }) {
  // items가 변경될 때만 계산
  const processedItems = useMemo(() => {
    return items.map((item) => ({
      ...item,
      processed: item.value * 2,
    }));
  }, [items]);

  // onItemClick이 변경되지 않는 한 함수 재생성 방지
  const handleClick = useCallback(
    (item) => {
      onItemClick(item);
    },
    [onItemClick]
  );

  return (
    <div>
      {processedItems.map((item) => (
        <div key={item.id} onClick={() => handleClick(item)}>
          {item.name}: {item.processed}
        </div>
      ))}
    </div>
  );
}

베스트 프랙티스

1. Hook 규칙 준수

  • Hook은 항상 컴포넌트의 최상위 레벨에서 호출
  • 조건문이나 반복문 안에서 Hook 호출 금지
  • React 함수 컴포넌트나 커스텀 Hook에서만 Hook 호출

2. 의존성 배열 관리

useEffect의 의존성 배열을 올바르게 관리하세요:

// ❌ 잘못된 예시
useEffect(() => {
  fetchData();
}, []); // 의존성이 누락됨

// ✅ 올바른 예시
useEffect(() => {
  fetchData();
}, [fetchData]); // fetchData가 변경될 때마다 실행

3. 커스텀 Hook 분리

복잡한 로직은 커스텀 Hook으로 분리하세요:

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

  useEffect(() => {
    async function fetchData() {
      try {
        setLoading(true);
        const response = await fetch(url);
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    }

    fetchData();
  }, [url]);

  return { data, loading, error };
}

마무리

React Hooks는 함수형 컴포넌트를 더욱 강력하게 만들어주는 기능입니다. 올바른 사용법을 익히고 베스트 프랙티스를 따르면 깔끔하고 유지보수하기 쉬운 코드를 작성할 수 있습니다.

React 공식 문서에서 더 자세한 내용을 확인할 수 있습니다.