리액트 useRef

hyun·2023년 1월 3일
1

React Hooks

목록 보기
5/5
post-thumbnail

이 포스트에서 다룰 것

이번 포스트에서는 리액트 훅 시리즈인 useRef에 대해서 공부한다. 왜냐하면 내일 업무에서 useRef를 써야 하기 때문이다🥲.. useRef를 사용해 슬라이더(prev/next 버튼)을 만들어 볼 건데, 무사히 완성되면 관련 내용도 정리해서 포스팅해 볼 계획이다.

useRef는 리액트에서 DOM을 제어할 수 있는 쿼리셀렉터와 같은 역할을 한다 정도로만 알고 있었는데, 이번 기회에 활용법과 응용 예시를 자세히 공부하면 좋겠다.

useRef란?

함수형 컴포넌트에서 useRef를 부르면 ref 오브젝트를 반환해준다.

const ref = useRef(value);

ref 오브젝트는 {current: value} 이렇게 생겼다. 우리가 인자로 넣어준 초깃값valueref 안에 있는 current에 저장이 된다. ref 오브젝트는 수정이 가능하기 때문에 원하는 값으로 바꿀 수 있다.

반환된 ref는 컴포넌트의 전 생애 주기동안 유지된다. 그 말은, 컴포넌트가 계속해서 렌더링이 되어도 언마운트되기 전까지는 값을 유지한다는 뜻이다.

언제 사용될까?

저장공간

state와 비슷하게 무언가를 저장해두는 저장공간으로 사용이 된다. state를 변경하면 컴포넌트가 자동으로 재렌더링이 된다. 함수형 컴포넌트는 재렌더링이 되면 함수가 다시 불려지는 것이기 때문에, 내부에 있는 모든 변수들이 초기화가 된다. 이러한 함수형 컴포넌트의 특징 때문에 원하지 않는 렌더링이 일어날 때마다 퍼포먼스가 떨어질 가능성이 있고, 변수 안의 값을 유지하기 위한 조치를 취해야 한다.

useRef를 저장공간으로 사용한다면 ref 안의 값을 변경해도 렌더링이 일어나지 않고 변수들의 값이 유지된다. 마찬가지로, state가 변경되어 렌더링이 일어나도 ref의 값은 유지된다. 그렇기 때문에, 변경 시 렌더링을 발생시키지 않아야 하는 경우에 유용하게 사용될 수 있다.

DOM 요소에 접근

ref를 통해 DOM 요소에 접근, 조작할 수 있다. 예를 들면 input 요소를 선택하지 않아도 focus를 준다거나 할 때 활용할 수 있다. 바닐라 자바스크립트의 document.querySelector() 역할을 해 준다.

const inputRef = useRef(value);
<input ref={inputRef} />

useRef 변수를 우리가 접근하고자 하는 요소의 ref 속성으로 넣어주기만 하면 된다.

useRef에 대해 간단히 알아봤으니, 이제 예제를 통해 활용법을 익혀보자.

예제 1. stateref의 차이

import React, { useState } from 'react'; 

function App() {
  const [count, setCount] = useState(0);
  const addCountState = () => {
    setCount(count+1);
  }

  console.log("렌더링...🎨");


  return (
    <div> 
      <p>state: {count}</p>
      <button onClick={addCountState}>add state</button>
    </div>
  );
}

export default App;


화면에 count state를 보여주고, 버튼을 눌렀을 때 count를 1씩 증가시키는 앱을 만들었다. 그리고 재렌더링을 확인하기 위해 콘솔을 찍어보았다.

이 간단한 앱에 useRef를 이용한 변수를 하나 만들어 보자.

import React, { useState, useRef } from 'react'; 

function App() {
  const [count, setCount] = useState(0);
  const countRef = useRef(0); 

  const addCountState = () => {
    setCount(count+1);
  }

  console.log(countRef);
  console.log("렌더링...🎨");


  return (
    <div> 
      <p>state: {count}</p>
      <button onClick={addCountState}>add state</button>
    </div>
  );
}

export default App;

useRef를 import하는 것을 잊지 말고, countRef라는 변수에 useRef를 하나 만들어 보았다. 그리고 무엇이 담겨있는지 콘솔로 찍어보았다.

개념 공부에서 이야기했던 대로 {current: 초깃값}이 찍힌다. 초깃값을 0으로 했으므로 {current: 0}이 되겠다. 그래서 우리가 ref에 접근하고 싶다면 countRef.current 이렇게 오브젝트의 값에 접근하는 방법과 똑같이 하면 되겠다.

import React, { useState, useRef } from 'react'; 

function App() {
  const [count, setCount] = useState(0);
  const countRef = useRef(0); 

  const addCountState = () => {
    setCount(count+1);
  }

  const addCountRef = () => {
    countRef.current = countRef.current + 1;
  }

  console.log(countRef);
  console.log("렌더링...🎨");


  return (
    <div> 
      <p>state: {count}</p>
      <p>Ref: {countRef.current}</p>
      <button onClick={addCountState}> add state </button>
      <button onClick={addCountRef}> add Ref </button>
    </div>
  );
}

export default App;

add State버튼을 통해 state를 변경할 때마다 렌더링이 되지만, add Ref를 통해 conutRef를 변경할 때는 렌더링이 안 되는 것을 확인할 수 있다. add Ref 버튼을 눌러도 화면에 변화가 없어서 아무 일도 일어나지 않는 것 같지만, countRef의 값은 변경되고 있다. addCountRef 안에서 콘솔을 찍어서 확인해 보면,

  const addCountRef = () => {
    countRef.current = countRef.current + 1;
    console.log(countRef.current); // 추가
  };


이렇게 countRef의 값이 화면에서 확인할 수는 없지만 변경되고 있다는 것을 알 수 있다.

예제 2. 일반 변수와 ref의 차이

import React, { useState, useRef } from 'react'; 

function App() {
  const countRef = useRef(0); 
  let countVar = 0; 

  const addCountRef = () => {
    countRef.current = countRef.current + 1;
    console.log('ref: ', countRef.current);
  }

  const addCountVar = () => {
    countVar = countVar + 1;
    console.log('var: ', countVar);
  }

  return (
    <div> 
      <p>Ref: {countRef.current}</p>
      <p>Var: {countVar}</p>
      <button onClick={addCountRef}> add Ref </button>
      <button onClick={addCountVar}> add Var </button>
    </div>
  );
}

export default App;

이번에는 일반 변수와 useRef의 차이를 알아보기 위해 countRefcountVar을 가지고 비교해 볼 것이다. countRefuseRef, countVarlet을 이용해 만들었다.

두 변수 모두 렌더링을 유발하지 않기 때문에 화면의 변화는 없지만, 콘솔을 통해 값이 변화하고 있다는 사실을 알 수 있다.

그러면 화면을 업데이트 하기 위한state를 하나 추가해 보자.

import React, { useState, useRef } from 'react'; 

function App() {
  const [renderer, setRenderer] = useState(0); 
  const countRef = useRef(0); 
  let countVar = 0; 

  const addCountRef = () => {
    countRef.current = countRef.current + 1;
    console.log('ref: ', countRef.current);
  }

  const addCountVar = () => {
    countVar = countVar + 1;
    console.log('var: ', countVar);
  }

  const makeRender = () => {
    setRenderer(renderer+1);
    console.log('render🎨');
  }

  return (
    <div> 
      <p>Ref: {countRef.current}</p>
      <p>Var: {countVar}</p>
      <button onClick={addCountRef}> add Ref </button>
      <button onClick={addCountVar}> add Var </button>
      <button onClick={makeRender}> render </button>
    </div>
  );
}

export default App;

renderer라는 state를 하나 선언하고, 버튼을 이용해 1씩 증가시켜 컴포넌트 렌더링이 일어나도록 만들었다. 그럼 이 세가지 버튼이 어떻게 동작하는지 눌러보자.

ref는 렌더링이 일어나도 값이 그대로 유지가 되지만, 일반 변수인 countVar는 렌더링 이후 0으로 다시 초기화되는 것을 볼 수 있다. 렌더링이 되도 렌더 이전의 값인 4부터 시작한다. 그 말은, 컴포넌트가 브라우저에 마운트된 순간부터 언마운트될 때까지 같은 값을 유지할 수 있다는 뜻이다.

예제 3. DOM 요소에 접근

import React, { useEffect, useRef } from 'react'; 

function App() {
  return (
    <div> 
      <input type="text" placeholder="ID" />
      <button> 로그인 </button> 
    </div>
  );
}

export default App;


간단한 로그인 Input창과 버튼을 만들었다. 이번 예제에서는 input창을 클릭하지 않아도 자동으로 focus(커서)를 주는 기능을 만들 것이다.

const inputRef = useRef();

useEffect(()=>{
  console.log(inputRef);
}, []);  

먼저 input에 달아줄 inputRef를 만들었다. 그리고 렌더링 후 실행될 useEffect를 만들고 의존성 배열에 빈 배열을 넣어 한 번만 실행되도록 했다.

useRef를 초기화하지 않았기 때문에 undefined가 뜬다. 이 undefined의 자리에 input에 대한 참조값을 넣을 것이다. 그 방법은 다음처럼 간단하다.

<input ref={inputRef} />

이렇게 접근하고자 하는 DOM요소의 ref속성에 useRef가 담긴 변수를 달아주면,
current 자리에 input 오브젝트가 들어간다.

import React, { useEffect, useRef } from 'react'; 

function App() {
  const inputRef = useRef();

  useEffect(()=>{
    console.log(inputRef);
    inputRef.current.focus();
  }, []);

  return (
    <div> 
      <input ref={inputRef} type="text" placeholder="ID" />
      <button> 로그인 </button> 
    </div>
  );
}

export default App;

inputRef.current 안에는 input 요소가 담겨있기 때문에 바로 접근해서 사용 가능하다. useEffect 안에서 focus가 되게끔 만들어뒀으므로 새로고침 후 바로 focus가 될 것이다.

DOM에 가할 수 있는 조작 모두 inputRef.current를 통해 가능하다. 이를테면 input value를 출력하려면 console.log(inputRef.current.value) 이런 식으로 접근해서 value를 가져올 수 있다.

profile
프론트엔드를 공부하고 있습니다.

0개의 댓글