React Hook – UseRef 이해하기


React의 useRef는 컴포넌트의 상태 관리와 DOM 접근을 간단하고 효율적으로 처리할 수 있는 강력한 도구입니다. 이번 포스트에서는 useRef의 기본 개념, 사용 방법, 그리고 활용 사례를 중심으로 설명합니다. 특히, 렌더링 최적화와 상태 관리의 차이점을 이해하고, 실무에서 useRef를 어떻게 활용할 수 있는지 알아보겠습니다.


1. useRef란 무엇인가?

useRef는 React에서 제공하는 Hook 중 하나로, **참조(ref)**를 생성하고 관리하기 위해 사용됩니다. 주로 DOM 요소에 직접 접근하거나, 컴포넌트의 상태와는 독립적으로 값을 저장하고 유지할 때 활용됩니다.

useRef는 React의 상태 관리와는 다르게, 값이 변경되어도 컴포넌트를 다시 렌더링하지 않는 특징이 있습니다. 따라서 렌더링과 관계없이 값을 유지하거나 DOM 요소를 조작해야 할 때 유용합니다.

주요 특징

  • 렌더링을 발생시키지 않음: useRef로 관리하는 값은 변경되어도 컴포넌트를 다시 렌더링하지 않습니다.
  • 컴포넌트 생애주기 동안 값 유지: useRef로 저장된 값은 컴포넌트가 언마운트될 때까지 유지됩니다.
  • DOM 요소 접근: 특정 DOM 요소에 직접 접근하여 조작할 수 있습니다.

2. useRef의 기본 사용법

useRef는 React에서 상태 관리와 DOM 접근을 간단히 처리할 수 있습니다. 기본적인 사용법은 다음과 같습니다:

상태 관리에 useRef 사용하기

import React, { useRef } from "react";

function Counter() {
  const countRef = useRef(0); // 초기값 설정

  const increment = () => {
    countRef.current += 1; // current를 통해 값에 접근
    console.log("Current count:", countRef.current);
  };

  return (
    <div>
      <button onClick={increment}>Increment</button>
      <p>Check the console for the count value!</p>
    </div>
  );
}

export default Counter;

DOM 접근에 useRef 사용하기

import React, { useRef } from "react";

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

  const focusInput = () => {
    inputRef.current.focus(); // input 요소에 포커스
  };

  return (
    <div>
      <input ref={inputRef} type="text" placeholder="Type something..." />
      <button onClick={focusInput}>Focus Input</button>
    </div>
  );
}

export default InputFocus;

3. useRefuseState의 차이점

useRefuseState는 모두 값을 저장할 수 있지만, 다음과 같은 차이점이 있습니다:

특징useRefuseState
렌더링 발생 여부값이 변경되어도 렌더링을 발생시키지 않음값이 변경되면 컴포넌트를 다시 렌더링
주요 사용 목적DOM 접근, 렌더링과 무관한 값 저장UI 업데이트를 위한 상태 관리
값 유지 기간컴포넌트 생애주기 동안 유지상태가 변경될 때마다 새로운 값으로 업데이트

4. useRef를 사용하는 주요 사례

4.1. DOM 요소 접근 및 조작

useRef는 DOM 요소에 직접 접근해야 할 때 유용합니다. 예를 들어, 특정 입력 필드에 포커스를 주거나 스크롤 위치를 제어할 때 사용할 수 있습니다.

function ScrollToTop() {
  const divRef = useRef(null);

  const scrollToTop = () => {
    divRef.current.scrollTop = 0; // 스크롤 위치를 맨 위로 이동
  };

  return (
    <div>
      <div ref={divRef} style={{ height: "200px", overflow: "auto" }}>
        <p>긴 내용...</p>
      </div>
      <button onClick={scrollToTop}>Scroll to Top</button>
    </div>
  );
}

4.2. 렌더링 횟수 추적

컴포넌트가 몇 번 렌더링되었는지 추적할 때 useRef를 사용할 수 있습니다.

function RenderCounter() {
  const renderCount = useRef(1);

  useEffect(() => {
    renderCount.current += 1;
    console.log("Render Count:", renderCount.current);
  });

  return <p>Check the console for render count!</p>;
}

4.3. 이전 값 저장

useRef를 사용하여 이전 상태 값을 저장할 수 있습니다.

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

  useEffect(() => {
    prevCount.current = count; // 현재 값을 이전 값으로 저장
  }, [count]);

  return (
    <div>
      <p>Current Count: {count}</p>
      <p>Previous Count: {prevCount.current}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

4.4. 성능 최적화

useRef는 렌더링과 무관한 값을 저장할 때 유용합니다. 예를 들어, 애니메이션 프레임 ID나 타이머 ID를 저장하여 불필요한 렌더링을 방지할 수 있습니다.

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

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

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

  return (
    <div>
      <button onClick={startTimer}>Start Timer</button>
      <button onClick={stopTimer}>Stop Timer</button>
    </div>
  );
}

4.5. 외부 라이브러리와의 통합

useRef는 외부 라이브러리와 React를 통합할 때도 자주 사용됩니다. 예를 들어, 차트 라이브러리나 캔버스 API와 같은 DOM 기반 라이브러리를 사용할 때 useRef를 통해 DOM 요소를 참조할 수 있습니다.

import { useEffect, useRef } from "react";

function CanvasExample() {
  const canvasRef = useRef(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    const ctx = canvas.getContext("2d");
    ctx.fillStyle = "blue";
    ctx.fillRect(0, 0, 100, 100);
  }, []);

  return <canvas ref={canvasRef} width={200} height={200}></canvas>;
}

5. useRef의 장점과 주의점

장점

  1. DOM 요소에 직접 접근 가능
    useRef를 사용하면 React의 선언적 방식과 함께 특정 DOM 요소에 직접 접근할 수 있습니다. 예를 들어, 포커스 설정, 스크롤 위치 조정, 또는 Canvas와 같은 DOM 요소를 조작할 때 유용합니다.
  2. 렌더링 없이 값 유지
    useRef는 컴포넌트가 다시 렌더링되더라도 값이 초기화되지 않고 유지됩니다. 상태(useState)와는 다르게 값이 변경되어도 컴포넌트를 다시 렌더링하지 않으므로, 불필요한 렌더링을 방지할 수 있습니다.
  3. 타이머, 이벤트 핸들러 등 외부 값 관리
    useRef는 타이머 ID, 이전 상태 값, 또는 이벤트 핸들러와 같은 값을 저장하고 관리하는 데 적합합니다. 이를 통해 상태와는 독립적으로 외부 값을 안전하게 관리할 수 있습니다.
  4. 초기화 비용 절감
    useRef는 초기화 시점에만 값을 설정하며, 이후에는 값이 유지되므로 초기화 비용이 높은 작업을 반복적으로 수행하지 않아도 됩니다.
  5. React의 상태 관리와 분리
    useRef는 React의 상태 관리와 별개로 동작하므로, 상태를 변경하지 않고도 값을 저장하거나 참조할 수 있습니다. 이는 상태 변경으로 인해 발생하는 불필요한 렌더링을 방지하는 데 유용합니다.

주의점

  1. 값 변경 시 렌더링되지 않음
    useRef의 값이 변경되어도 컴포넌트는 다시 렌더링되지 않습니다. 따라서, 값 변경에 따라 UI를 업데이트해야 하는 경우에는 useState를 사용해야 합니다
  2. DOM 조작은 최소화해야 함
    React는 선언적 UI를 지향하므로, useRef를 사용한 DOM 조작은 꼭 필요한 경우에만 사용해야 합니다. 과도한 DOM 조작은 React의 상태 관리와 충돌하거나, 코드의 유지보수성을 떨어뜨릴 수 있습니다.
  3. 초기값 설정 주의
    useRef는 초기값을 설정하지 않으면 undefined를 반환합니다. 따라서, 초기값이 필요한 경우 명시적으로 설정해야 합니다.
  4. 클린업 처리 필요
    useRef를 사용해 타이머나 이벤트 핸들러를 관리할 경우, 컴포넌트가 언마운트될 때 이를 정리하지 않으면 메모리 누수나 예기치 않은 동작이 발생할 수 있습니다.
  5. React의 상태 관리와 혼동 주의
    useRef는 상태 관리가 아닌 참조를 위한 도구입니다. 상태 변경과 관련된 작업에는 반드시 useState를 사용해야 하며, useRef를 잘못 사용하면 React의 상태 관리 흐름을 방해할 수 있습니다.

6. 결론

useRef는 React에서 상태 관리와 DOM 접근을 효율적으로 처리할 수 있는 강력한 도구입니다. 렌더링과 무관한 값을 저장하거나 DOM 요소를 조작해야 할 때 유용하며, 성능 최적화에도 큰 도움을 줍니다. 실무에서는 DOM 접근, 렌더링 횟수 추적, 이전 값 저장, 외부 라이브러리 통합 등 다양한 상황에서 활용할 수 있습니다. useRef를 적절히 활용하면 React 애플리케이션의 성능과 유지보수성을 크게 향상시킬 수 있습니다.

jeewoo jung 아바타