useRef는 함수 컴포넌트에서 ref를 쉽게 사용할 수 있도록 만들어주는 훅이다.

useRef의 사용 이유는 다음과 같다.

컴포넌트가 다시 랜더링 되어도 동일한 값을 얻고 싶을 때

<aside> 💡 컴포넌트가 리렌더링 되어도 값을 잃어버리지 않고, 동시에 값이 변화되었다고 해서 state처럼 컴포넌트를 리렌더링 해주고 싶지 않을 때

</aside>

만약 내가 input에 이름을 입력하는 등의 수정을 할 때, 얼마나 이 컴포넌트의 렌더링이 자주 일어나는지를 알고 싶다면 어떻게 해야 할까?

컴포넌트 내부 let 변수 사용

가장 먼저 떠오르는 방법은 let 변수 하나를 이용해 횟수를 세는 것이다.

useEffect를 사용해서 렌더링이 될 때마다 count가 바뀌도록 코드를 짜 보자.

import React from "react";

function RefCounter() {

  let count=0

  useEffect(() => {
    count += 1
  });

  console.log(count)

  return (
    <>
      <input value={name} onChange={e => setName(e.target.value)} />
      <div>My name is {name}</div>
      <div>I rendered {count} times</div>
    </>
  );
}

export default RefCounter;

근데 이상하다. input에 값을 입력하는데도 count가 0에 멈춰 있다. 그 이유는 리렌더링 되어 컴포넌트 함수가 다시 호출이 된다는 것은 함수 내부의 변수들이 모두 다시 초기화가 되고 함수의 모든 로직이 다시 실행된다는 것을 의미하기 때문이다.

물론 let 변수를 컴포넌트 바깥에서 전역 변수로 선언할 수도 있지만 우리는 좀 더 우아한 방법을 안다.

바로 useState를 사용하는 것이다.

useState 사용

import React, { useState, useEffect } from "react";

function RefCounter() {
  const [name, setName] = useState("");
  const [renderCount, setRenderCount] = useState(0);

  useEffect(() => {
    setRenderCount((prevRendeCount) => prevRendeCount + 1);
  });

  return (
    <>
      <input value={name} onChange={e => setName(e.target.value)} />
      <div>My name is {name}</div>
      <div>I rendered {renderCount} times</div>
    </>
  );
}

export default RefCounter;

그런데 renderCount의 값이 멈추지 않고 계속 늘어난다. 난 뭐 한 것도 없는데!

이 방법의 문제는, useState를 사용함으로써 무한 루프에 빠질 수 있다는 것이다. 내가 렌더링이 되면 useEffect안의 setRenderCount가 실행된다. 이는 상태 renderCount를 바꾼다. 상태가 바뀌었으므로 이 state를 소유하고 있는 컴포넌트 RefCounter가 또 렌더링이 된다. 그럼 또 useEffect가 불리고 state가 바뀌고 또 렌더링이 되고....

무한 루프에 빠질 수 있다. 이 문제의 원인은 state가 바뀔 때마다 컴포넌트가 리렌더링 된다는 것에 있다. 그렇다면 리렌더링에 영향을 주지 않고, 또 영향도 받지 않는 변수를 사용하는 방법은 없을까?

바로 그게 useRef이다.