관리 메뉴

꿈꾸는 개발자

useRef로 값 참조하기 (feat. React)-그래서 언제 쓰라고?? 본문

React.js

useRef로 값 참조하기 (feat. React)-그래서 언제 쓰라고??

rickysin 2023. 6. 15. 20:38

도입

원티드 6월 강의를 수강하면서 useRef로 값을 참조하고 관리하는 코드를 접했다. 강사님에 따르면 제어/비제어 컴포넌트의 사용을 고려할 때 useState를 사용할 것인지 아니면 useRef로 값을 관리할건지 상황에 따라서 나뉠 수 있다고 했다. 물론 둘의 차이점은 이해가 됐다. 하지만, 비제어 방식의 경우 단순히 렌더링 횟수를 줄이고, 개발자가 trigger할 수 있다는 이유만으로 사용하는지 궁금하기도 하고 개념이 조금 모호한 것 같아서...이전에 작성했던 글과 강의에서 학습한 자료, 그리고 최신에 업데이트 된 react 공식문서와 함께 정리하고자 한다. 

 

useRef에 대한 이해가 부족하시면 여기에 들어가서 추가적으로 학습하는 것을 권장한다. 


React 공식문서가 리뉴얼 되기 이전에는 "신뢰 가능한 단일 출처"를 언급하면서 비제어보단 제어 컴포넌트의 사용을 적극 권장했다. 하지만, useRef로 상태를 관리하는 코드들도 점차 많이 보이기도 하고, 공식문서에도 한 파트를 나눠 다루고 있는 만큼 비제어 컴포넌트에 대한 인식도 바뀐 것처럼 보인다. 

(제어/비제어 컴포넌트에 대해 이전에 작성했던 글)

위 내용은 원티드 강의에서 제시된 제어/비제어 컴포넌트의 장단점을 담고 있다. 비제어 컴포넌트는 정말 특수한 경우를 제외하고 사용이 제한되는 것처럼 보인다.


공식문서 참고(리뉴얼)

리뉴얼된 리액트 공식문서에 따르면 "특정 정보를 리-렌더링 없이 기억하고 싶다면 useRef를 사용하라고 말하고 있다". 

 

공식문서에선 useRef로 상태 관리를 하는 하나의 예로 아래 코드를 제시하고 있다. 

 

import { useState, useRef } from 'react';

export default function Stopwatch() {
  const [startTime, setStartTime] = useState(null);
  const [now, setNow] = useState(null);
  const intervalRef = useRef(null);

  function handleStart() {
    setStartTime(Date.now());
    setNow(Date.now());

    clearInterval(intervalRef.current);
    intervalRef.current = setInterval(() => {
      setNow(Date.now());
    }, 10);
  }

  function handleStop() {
    clearInterval(intervalRef.current);
  }

  let secondsPassed = 0;
  if (startTime != null && now != null) {
    secondsPassed = (now - startTime) / 1000;
  }

  return (
    <>
      <h1>Time passed: {secondsPassed.toFixed(3)}</h1>
      <button onClick={handleStart}>
        Start
      </button>
      <button onClick={handleStop}>
        Stop
      </button>
    </>
  );
}

위 코드는 secondsPassed를 현재 시간에서 시작 시작을 뺴서 경과된 시간을 표시하는 간단한 코드이다.  "Stop" 버튼을 누르면 interval를 취소해서 now 상태의 업데이트를 정지해야 한다. 그러기 위해서는 clearInterval로 간단하게 해결할 수 있지만, 그러기 위해서는 interval ID를 aurgment로 줘야 한다. interval ID의 경우 렌더링 될 때 필요한 값은 아니기 때문에 useRef를 통해 관리하는 것이다

 

따라서, 리 렌더링이 필요한 값이면 useState를 사용하고 아닐 경우 useRef를 사용해서 관리하는 것이 더 효율적일 수도 있다고 공식문에서는 말하고 있다. 

 

하나의 예를 더 들어서 아래와 같은 경우에선 useRef를 사용하게 되면 re-rendering이 절대 발생하지 않기 때문에 화면에 click 횟수가 올라갈 일은 절대로 없게 된다. 따라서 아래와 같은 상황에선 useRef보단 useState로 관리하는 것이 타당하다.

import { useRef } from 'react';

export default function Counter() {
  let countRef = useRef(0);

  function handleClick() {
    // This doesn't re-render the component!
    countRef.current = countRef.current + 1;
  }

  return (
    <button onClick={handleClick}>
      You clicked {countRef.current} times
    </button>
  );
}

 

useRef를 사용하는 경우 

  • 이전 공식문서처럼 여러 경우가 있겠지만, 결론적으로 렌더링 로직에 영향을 주지는 않는 상황에선 refs를 선택해도 무방하다.

그렇다면 원티드 강의에서 말하는 useRef를 사용하는 경우는 어떠한 경우인가? 아직 실무를 접해보지 못해 쉽게 말할 순 없지만, 로그인 등 즉각적으로 값을 관리하고 "항상 단일 진실의 원칙"을 유지해야 하는 유효성 검사 등을 하는 경우 useState를 통해 제어적으로 관리를 하고 그 외 단순 값을 전달하는 경우라면 useRef를 적절히 사용해서 렌더링 횟수를 감소시켜 전체적인 성능 향상을 꾀하는 게 더 바람직할 수도 있다. 일단 이론적인 접근은 이러하고 관련 코도들을 조금 더 많이 확인하고 적극적으로 사용하는 것이 좋을 것 같다......

 

느낀 점: 

확실히 이전 공식문서에서는 비제어 컴포넌트 사용에 대해서 굉장히 부정적인 느낌이었는데.. 리뉴얼 된 공식문서에서는 여러 파트로 ref로 사용법을 다루는 만큼 그 용법이 점차 늘어나는 것 같다. 같은 기술이더라도 시간이 지남에 따라 그 범용성이 다를 수 있다는 사실을 다시 한 번 깨달으면서 본 글을 마무리하겠다.....