useRef: 렌더링 없이 값을 기억하는 방법

useRef는 두 가지 용도로 사용됩니다. 첫째는 DOM 요소에 직접 접근하는 것, 둘째는 렌더링을 일으키지 않으면서 값을 유지하는 것입니다.

ref 객체 이해하기

useRef{ current: 초기값 } 형태의 객체를 반환합니다.

const ref = useRef(0);
console.log(ref); // { current: 0 }
console.log(ref.current); // 0

이 객체의 핵심 특징은 두 가지입니다.

  1. ref.current를 변경해도 컴포넌트가 리렌더링되지 않습니다
  2. 리렌더링이 발생해도 ref.current의 값이 유지됩니다

DOM 요소에 직접 접근하기

React는 기본적으로 DOM을 직접 건드리지 않고 추상화를 통해 관리합니다. 하지만 input에 포커스를 주거나, 스크롤 위치를 측정하는 등 DOM에 직접 접근해야 할 때가 있습니다.

function SearchInput() {
  const inputRef = useRef(null);
 
  function handleClick() {
    inputRef.current.focus(); // DOM 요소에 직접 접근
  }
 
  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={handleClick}>검색창 포커스</button>
    </>
  );
}

JSX의 ref 속성에 ref 객체를 넘기면, React가 해당 DOM 요소를 ref.current에 자동으로 연결해줍니다.

렌더링 없이 값 유지하기

타이머 ID, 이전 값, 플래그 같은 정보를 화면에 보여줄 필요는 없지만 기억해야 할 때 ref를 사용합니다.

function Timer() {
  const [count, setCount] = useState(0);
  const timerIdRef = useRef(null); // 타이머 ID 저장
 
  function startTimer() {
    timerIdRef.current = setInterval(() => {
      setCount(prev => prev + 1);
    }, 1000);
  }
 
  function stopTimer() {
    clearInterval(timerIdRef.current); // 저장해둔 ID로 정지
  }
 
  return (
    <div>
      <p>{count}초</p>
      <button onClick={startTimer}>시작</button>
      <button onClick={stopTimer}>정지</button>
    </div>
  );
}

timerIdRef.current를 바꿔도 렌더링이 발생하지 않으므로, 타이머 ID 같은 내부 정보를 저장하기에 적합합니다.

ref vs state: 무엇을 써야 할까?

비교 항목useRefuseState
값 변경 시 리렌더링발생하지 않음발생함
화면에 표시적합하지 않음적합함
주요 용도DOM 접근, 내부 값 추적UI에 반영할 상태

화면에 보여줘야 하는 값useState, 내부적으로만 필요한 값useRef를 사용하세요.

function ClickTracker() {
  const [displayCount, setDisplayCount] = useState(0); // 화면에 표시
  const totalClicksRef = useRef(0); // 내부 추적용 (렌더링 불필요)
 
  function handleClick() {
    totalClicksRef.current += 1; // 렌더링 없이 증가
    if (totalClicksRef.current % 10 === 0) {
      setDisplayCount(totalClicksRef.current); // 10번마다 화면 업데이트
    }
  }
 
  return <button onClick={handleClick}>클릭 수: {displayCount}</button>;
}

이전 값 기억하기

useEffect와 함께 사용하면 이전 렌더링의 값을 기억할 수 있습니다.

function PreviousValue({ value }) {
  const prevValueRef = useRef();
 
  useEffect(() => {
    prevValueRef.current = value; // 렌더링 후 현재 값을 저장
  });
 
  const prevValue = prevValueRef.current; // 렌더링 시점에는 이전 값
 
  return (
    <div>
      현재: {value}, 이전: {prevValue}
    </div>
  );
}

정리

  • useRef{ current: 값 } 형태의 객체를 반환합니다
  • ref.current 변경은 리렌더링을 유발하지 않습니다
  • DOM 요소에 ref 속성으로 연결하면 해당 DOM에 직접 접근할 수 있습니다
  • 화면에 반영할 필요 없는 내부 값을 저장할 때 유용합니다