I turn coffee into code ☕ → 💻 자세히보기

개발/React

React 훅(useRef, useMemo, useCallback) 정리

xuwon 2025. 8. 13. 09:56

내가 헷갈리는 훅들인 useRef, useMemo, useCallback에 대해서 정리하려고 한다.

useRef

1. 무엇을 기억? : 변경 가능한 값 혹은 DOM 요소의 참조
2. 렌더링에 영향? : 값이 바뀌어도 컴포넌트는 다시 렌더링되지 않음
3. 렌더와 상관없이 값을 저장할 때 사용 (ex. 타이머 ID, 이전 상태값 저장) / DOM 엘리먼트에 직접 접근할 때 (ref 연결)

import { useRef } from "react";

function Timer() {
  const timerId = useRef(null);

  const startTimer = () => {
    timerId.current = setInterval(() => {
      console.log("Tick");
    }, 1000);
  };

  const stopTimer = () => {
    clearInterval(timerId.current);
  };

  return (
    <>
      <button onClick={startTimer}>시작</button>
      <button onClick={stopTimer}>멈춤</button>
    </>
  );
}


타이머 ID 저장

- setInterval이나 setTimeout을 쓸 때, 타이머를 멈추거나 초기화하려면 타이머 ID 필요

const timerId = setInterval(() => {
  console.log("Tick");
}, 1000);

- 이 timerId는 렌더링과 상관없이 계속 유지되어야 하므로 useRef에 저장!
- 즉, 컴포넌트가 렌더링돼도 새 타이머가 계속 만들어지는 것을 막기 위함이다.


이전 상태값 저장
- 상태가 바뀔 때 이전 상태와 비교하거나, 상태 변경 전에 값을 기억해야 할 때 사용

const [count, setCount] = useState(0);
const prevCount = useRef(0);

useEffect(() => {
  if (count !== prevCount.current) {
    console.log("count가 바뀜:", count);
    prevCount.current = count; // 이전 값을 업데이트
  }
}, [count]);

- count가 변할 때마다 useEffect가 실행되고 리렌더링 됨. (count - 0 -> 1)
- 하지만 prevCount는 리렌더링의 영향을 받지 않으므로 prevCount -> 0
- if (count !== prevCount.current) 를 만족하며 prevCount -> count -> 1로 업데이트
- 즉, prevCount는 리렌더링 전의 count 값을 가리키고 있다가, 리렌더링 이후에 새 값으로 업데이트


useMemo

1. 무엇을 기억? : 함수 결과값 (계산 결과)
2. 렌더링에 영향? : 직접 렌더를 일으키지는 않지만, 의존성이 바뀌면 다시 계산됨
3. 무거운 계산 결과를 메모리에 저장해두고 재사용 / 렌더마다 같은 계산 반복하는 낭비 방지

import { useMemo, useState } from "react";

function ExpensiveCalculation({ number }) {
  const squared = useMemo(() => {
    console.log("계산 중...");
    return number * number;
  }, [number]);

  return <div>제곱: {squared}</div>;
}

useCallback

1. 무엇을 기억? : 함수 자체 (함수의 참조)
2. 렌더링에 영향? : 직접 렌더를 일으키지 않으며, 의존성이 바뀌면 새 함수 생성
3. 자식 컴포넌트에 함수를 props로 넘길 때 불필요한 리렌더 방지 / 이벤트 핸들러 같은 함수 재생성 비용 줄이기

import { useCallback, useState } from "react";

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

  const handleClick = useCallback(() => {
    setCount((c) => c + 1);
  }, []);

  return <button onClick={handleClick}>클릭 {count}회</button>;
}


자식 컴포넌트에 함수를 props로 넘길 때
- 함수 참조가 바뀌게 될 경우(부모 컴포넌트 리렌더링) 자식 컴포넌트도 불필요하게 리렌더링 될 수 있다.
- useCallback을 사용하면 부모가 리렌더돼도 같은 함수 객체를 자식에게 넘기기 때문에 불필요한 자식 리렌더링을 막아준다.


 

Hook 기억하는 대상 렌더링에 영향 의존성 배열 존재 주용도
useRef 변경 가능한 값, DOM 참조 없음 없음 렌더와 상관없는 값 저장, DOM 접근
useMemo 함수 실행 결과 값 의존성 바뀔 때 재계산 있음 무거운 계산 결과 캐싱
useCallback 함수 참조 의존성 바뀔 때 새 함수 생성 있음 함수 재생성 방지, 자식 리렌더 방지