React에서 useRef 이해하기 (혼란 없이)

발행: (2026년 1월 9일 오전 01:48 GMT+9)
7 min read
원문: Dev.to

Source: Dev.to

React에서 useRef 이해하기

React를 배우고 있다면 useStateuseEffect를 이미 익혔을 가능성이 높습니다. 그런데 useRef를 만나면 갑자기 상황이… 조금 이상하게 느껴질 수 있습니다.

화면을 업데이트하지도 않고, 리렌더도 일으키지 않습니다. 마치 “React 규칙”을 어기는 것처럼 느껴지죠. 비밀은 useRef가 컴포넌트의 개인적인 영구 저장소라는 점입니다.

그럼 useRef가 실제로 무엇을 하는지, 왜 유용한지, 그리고 머리 아프지 않게 어떻게 사용하는지 살펴보겠습니다.

useRef가 실제로 하는 일

핵심적으로 useRef는 다음과 같은 값을 저장할 수 있는 장소를 제공합니다:

  • 렌더 사이에 동일하게 유지됨
  • 언제든지 변경 가능
  • 변경될 때 재렌더링을 일으키지 않음
const myRef = useRef(initialValue);

React는 다음과 같은 객체를 제공합니다:

{ current: initialValue }

중요한 부분은 .current입니다. 값은 여기서 살아 있습니다. React는 이 객체를 한 번만 생성하고 매 렌더마다 같은 객체를 반환합니다.

useRef가 존재하는 이유

당신은 이렇게 궁금할 수도 있습니다: “왜 모든 것에 useState만 쓰지 않나요?” 차이는 가시성에 있습니다.

  • State는 사용자가 볼 수 있는 것(UI)을 위한 것입니다. 상태가 변하면 React가 화면을 다시 그립니다.
  • Refs는 백그라운드에서 기억하고 싶은 것들을 위한 것입니다. ref가 변해도 React는 조용히 유지되며 UI에 아무 변화도 주지 않습니다.

일반적인 ref 사용 사례:

  • DOM 요소
  • 타이머 ID
  • WebSocket 연결
  • 이전 값
  • 플래그 또는 카운터

이를 state에 넣으면 불필요한 재렌더링이 발생합니다. 일반 변수를 사용하면 매 렌더링마다 초기화됩니다. useRef는 렌더링을 트리거하지 않고 메모리를 제공함으로써 이 문제를 정확히 해결합니다.

가장 일반적인 사용법: DOM 접근

This is where most people meet useRef first.

import { useRef } from "react";

function MyComponent() {
  const inputRef = useRef(null);

  return (
    <>
      
       inputRef.current.focus()}>
        Focus
      
    
  );
}

React는 실제 DOM 입력 요소를 inputRef.current에 넣어, focus()와 같은 메서드를 직접 호출할 수 있게 합니다. 이것이 올바른 사용 방법입니다:

  • 입력에 포커스 주기
  • 요소를 화면에 스크롤하기
  • 너비 또는 높이 측정하기
  • 비디오 또는 오디오 요소 제어하기

DOM 노드는 state에 넣어서는 안 되며, refs가 올바른 도구입니다.

렌더 사이에 값 유지하기 (재렌더링 없이)

때때로 무언가를 기억해야 하지만 React가 관여하길 원하지 않을 때가 있습니다. 예시: 컴포넌트가 렌더링된 횟수를 세기.

const renderCount = useRef(0);
renderCount.current += 1;

이 값은:

  • 매 렌더마다 증가합니다
  • 절대 재렌더링을 일으키지 않습니다
  • 절대 초기화되지 않습니다

카운터, 플래그, 캐시된 데이터, 혹은 내부 회계 등에 유용합니다.

이전 값 추적하기

React는 기본적으로 “이전 상태”를 제공하지 않습니다. useRef가 그 빈틈을 메워줍니다.

function MyComponent({ value }) {
  const prevValue = useRef(value);

  useEffect(() => {
    prevValue.current = value;
  }, [value]);

  // now prevValue.current holds the previous `value`
}

value는 현재 값이고, prevValue.current는 이전 값입니다. 이 패턴은 깔끔하고 예측 가능하며 널리 사용됩니다.

외부 객체 저장

React와 전혀 관련 없는 것들이 있습니다:

  • WebSocket 연결
  • AbortController 인스턴스
  • 옵저버
  • 서드파티 라이브러리 객체
function useWebSocket(url) {
  const socketRef = useRef(null);

  useEffect(() => {
    socketRef.current = new WebSocket(url);
    return () => socketRef.current.close();
  }, [url]);

  return socketRef.current;
}

이러한 객체들은 안정적인 저장 위치가 필요합니다; useRef가 이를 제공합니다. 상태에 넣는 것은 설계상의 실수입니다.

useRef가 아닌 것

  • useState를 대체하는 것이 아닙니다.
  • 반응형이 아니며 UI를 업데이트하는 데 사용해서는 안 됩니다.

UI가 값에 의존한다면 state를 사용하세요. React가 해당 값을 알 필요가 없다면 ref를 사용하세요. 이 규칙을 따르면 대부분의 실수를 방지할 수 있습니다.

간단한 정신 모델

useRef를 컴포넌트에 붙어 있는 작은 상자라고 생각해 보세요:

  • React가 컴포넌트를 렌더링합니다.
  • 상자는 렌더링 사이에 동일하게 유지됩니다.
  • 상자 안에 무엇이든 넣을 수 있습니다 (.current).
  • React는 그 상자를 완전히 무시합니다.

그게 전부입니다.

최종 생각

useRef는 복잡하지 않으며—그냥 솔직합니다. React가 관리해서는 안 되는 것들을 저장할 수 있는 장소를 제공합니다. 이를 이해하면 “고급”이라는 느낌이 사라지고 당연하게 느껴집니다. 완전히 이해했을 때, 컴포넌트는 다음과 같이 됩니다:

  • 더 깔끔해짐
  • 더 예측 가능함
  • 이해하기 쉬워짐

그것은 마법이 아니라—올바른 작업에 올바른 도구를 사용하는 것입니다.

Back to Blog

관련 글

더 보기 »

React에서 간단한 Carousel/Slider 만들기

캐러셀 또는 슬라이더는 이미지나 콘텐츠를 하나씩 표시하는 훌륭한 방법입니다. 버튼을 사용하여 이를 탐색할 수 있습니다. 아래는 간단한 구현...