React Learn - Ref 를 이용하여 값을 Referencing 하자

ChoiYongHyeun·2024년 2월 26일
1

리액트

목록 보기
10/31
post-thumbnail

리액트 공식문서 중 Escape Hatches 의 첫 번째 강을 읽고 내 마음대로 정리하는 내용

Referencing Values with Refs


리액트에서 컴포넌트를 정의 할 때

컴포넌트 내부의 변수는 const , let , var 등과 같은 변수 선언문 혹은 useState , useReducer 등을 이용해

선언해주었다.

이 때 state 형태로 선언되는 변수들은 값이 변하게 되면 리렌더링이 되기 때문에

값이 변하지 않는 값을 선언하기 위해선 변수 선언문인 const , let , var 등을 이용해야 한다.

그런데 문득 생각이 든다.

컴포넌트가 재호출될 때 마다 const , let , var 과 같은 변수 선언문이 항상 다시 선언될 텐데
(어차피 선언 될 때 마다 다른 지역 환경이기 때문에 const 라고 하더라도 재할당이 된다)

만약 선언하고자 하는 변수가 선언하는데까지 오래 걸리는 변수라면 ?

컴포넌트가 재호출 될 때 마다 변수를 선언하는데 오랜 시간이 걸릴 것이다.

컴포넌트 외부에서 변수를 선언해두고 캐싱해두고 싶은 마음이 굴뚝 같아진다.

그 ~ 때 ~ 사용하는 것이 바로 Ref 이다.


useRef 컴포넌트에 추가하기

useRef 는 컴포넌트 내부에서만 사용 가능하다.

컴포넌트 외부에서 선언된 useRef 는 컴파일 에러를 야기한다.

useRef 로 생성된 Ref 객체는 current 프로퍼티에 값을 저장하고 있다.

스톱워치 예제를 통해 useRef 가 필요한 경우를 생각해보기

기억해야 하는 값을 let 변수 선언문로 선언했을 때

스톱워치를 만들어보았다.

이 때 스톱워치 기능을 할 setIntervalid 값을 IntervalId 라는 변수에 선언해주고 Stop 버튼이 눌리면

IntervalId 를 이용해 clearInterval 을 하여 스톱워치를 멈추게 하도록 handleStop 메소드를 선언해주었다.

하지만 이미지를 보면 작동 안하는 모습을 볼 수 있다.

그 이유는 값의 할당은 re-render 이후의 다른 지역환경에서 일어나기 때문이다.

IntervalId 의 값은 re-render 이후 어떤 id 값을 갖는 값으로 변경되지만

컴포넌트는 setInterval 에 의해 지속적으로 setNow 를 호출, 컴포넌트를 호출하며 계속하여 매번 다른 지역 환경을 만들어낸다.

해당 지역 환경에서는 IntervalId 의 값은 여전히 undefined 이다.

컴포넌트가 재호출되어 지역환경이 달라졌기 때문이다.

handleStart 이벤트가 호출된 초기의 지역환경에서의 IntervalId 값은 id 값이 지정되었을 것이다.

그럼 state 로 설정해두면 좀 괜찮을까 ?

기억해야 하는 값을 useState로 선언했을 때

state 로 설정하면 작동은 잘 한다.

다만 state 란 시시각각 변하는 변수들을 담기 위한 Hooks 인데 변하지 않는 값을 state Hooks 를 이용하려고 하니

어색하고 목적에 맞지 않는다.

또한 useRef 값이 변할 때 마다 리렌더링이 되는 것을 방지하기 위해 useRef 가 존재한다고 하였으니 useRef 를 써보자

기억해야 하는 값을 useRef 로 선언했을 때

이전 변수 선언문인 let 으로 선언했을 때 문제가 발생했던 이유를 생각해보자

handleStop 내에서 선언된 지역변수 intervalId 의 경우 값이 변하더라도 (let)

상태가 변한 후의 값을 할당받는게 아니라 상태가 변하기 전의 값을 받고 있는 것이 문제였다.

useRef 를 이용하면 컴포넌트가 재호출되어 새로운 지역환경이 되더라도

값이 가장 마지막으로 변경된 시점의 값을 current 프로퍼티에 담고 있기 때문에 잘 작동한다.


useRef vs useState

useRef useState
Returns { current: initialValue } [value, setValue]
Re-render Doesn’t trigger re-render when you change it. Triggers re-render when you change it.
Mutability Mutable—you can modify and update current’s value outside of the rendering process. ”Immutable”—you must use the state setting function to modify state variables to queue a re-render.
Rendering You shouldn’t read (or write) the current value during rendering. You can read state at any time. However, each render has its own snapshot of state which does not change.

useState 와의 차이를 보면 useRef 는 객체 형태를 반환하면서

re-render 를 트리거 하지 않는다는 점에서 useState 와 차이가 존재한다.

또한 useRef 는 따로 setterFunc 없이 직접적으로 변경이 가능한데 이는

렌더링 프로세스와 독립적으로 변경이 가능하다는 점이 useState 와 큰 차이점이다.

useState 는 렌더링 이후에 값이 변경되도록 설정되어있다.

하지만 useRef 의 경우에는 렌더링 프로세스와는 별개로 작동한다.

이로 인해 useState 의 경우에는 렌더링 되는 화면과 현재의 state 값이 동기화되지만

값이 정확히 같지는 않다. 이전에 말했듯 state 는 렌더링 이후 값이 업데이트 된다.

useRef 의 값의 경우에는 렌더링 되는 화면과 관련없이 변경이 가능하다.

이는 useState 는 항상 값이 변경되면 렌더링을 일으키지만, useRef 는 그렇지 않다는 점에서 기인한다.

useRef 는 어떤 원리로 동작할까

리액트에서는 더욱 최적화된 형태로 빌트인 되어 있지만 공식문서에서는 다음처럼 보아도 이해 할 수 있을 것이라고 한다.

useRef 의 경우 useState 를 이용해 ref 객체를 생성하고 setter function 은 사용하지 않은 채 state 값을 변경시키도록 한다.

이를 통해 렌더링 프로세스와 독립적으로 변경 가능하게 만들어둔다.

또한 useState 의 로직을 통해 만들어졌기 때문에 컴포넌트가 새롭게 호출되더라도

useState 의 특성으로 인해 useRefCustom 지역변수 내에서 반환되는 ref 객체는 이전에 생성해둔 ref 객체를 받기 때문에 유지가 가능하다.


언제 useRef 를 쓰는게 좋을까 ?

useRef 는 주로 컴포넌트 렌더링 로직과 상관 없는 변수들을 담는데 이용하는 것이 좋다.

when your component needs to "step outside"

리액트 공식문서에서는 컴포넌트와 관련 없는 것들을 useRef 를 이용하기를 권장한다.

그 이유는 렌더링 로직과 독립적으로 이동하여 값이 변경되더라도 렌더링을 요구하지 않으며

리렌더링 되어 새롭게 컴포넌트가 호출되더라도 값을 저장 할 수 있는 것은 useRef 이기 때문이다.

전역 변수에 선언해두어도 렌더링 로직과 별개로 사용 할 수 있지만
React 를 쓰는 동안에는 React Hook 을 이용하는 것이 성능상이나 가독성 상에서 좋을 것이다.

다음과 같은 상황에서 자주 사용한다고 한다.

  • Storing timeoutID
    이전 예시에서 보았듯이 setTimeout , setIntervalid 들은 호출환경이 달라지면 값을 추적 할 수 없지만 ref 를 이용하여 값을 저장하는게 가능했다.
  • Stroing and manipulating DOM ELEMENT
    DOM 객체를 useRef 에 저장해둬 다음 페이지러 넘어가는 등의 행위를 할 수 있다고 한다. 일종의 노드 캐싱일까 ? 이 부분은 더 챕터가 나아가면 공부 할 수 있을 것이라 생각한다.
  • Storing other objects that aren’t necessary to calculate the JSX.
    기억이 필요한 웬만한 변수는 useRef 로 하도록 하자

useRef 를 잘 이용하기 위한 실전 팁

useRefescape hatch 처럼 사용해라

useRefstep outside 에 접근 할 수 있도록 도와주는 hook 이라고 했다.

React 의 로직이 아닌, 다른 시스템이나 브라우저의 API 를 이용하고자 할 때

useRef 를 적절하게 이용하면 외부 환경의 기능들과 React 의 기능들을 조합해 적절하게 사용 할 수 있을 것이다.

useRef 는 값의 변화를 추적하는데 어려움이 있다

useState 의 경우에는 값이 변하면 바뀐 값에 따라 렌더링이 되기 때문에

값의 변화를 추적하는게 쉽다.

하지만 useRef 는 값이 변하더라도 렌더링이 되지 않기 때문에 값의 변화를 추적하는 것이 어렵다.

이에 값의 변화를 엄밀하게 추적해야 하는 경우에는 useState 를 이용해주도록 하자

The only exception to this is code like if (!ref.current) ref.current = new Thing() which only sets the ref once during the first render.

유일한 예외 사항은 처음 렌더링 될 때 값이 존재하는지를 확인하고 없다면 값을 재할당 하는 경우이다.

profile
빨리 가는 유일한 방법은 제대로 가는 것이다

0개의 댓글