코딩/리액트

[새싹 성동 2기] React에서 useRef 예제 및 활용법

insu90 2024. 11. 19. 20:13

React의 함수형 컴포넌트에서 useRef는 매우 유용한 훅입니다. 주로 DOM 요소에 직접 접근하거나, 렌더링에 영향을 주지 않는 값을 관리할 때 사용됩니다. 이번에는 useRef의 기본 사용법과 이를 활용한 예제들을 다뤄 보겠습니다.

useRef란 무엇인가?

useRef는 기본적으로 객체를 반환하는 훅입니다. 이 객체는 current라는 속성을 가지며, 이 속성에 값을 저장할 수 있습니다. useRef는 값을 변경해도 컴포넌트가 다시 렌더링되지 않기 때문에, 렌더링에 영향을 주지 않고 값이나 DOM 요소를 계속 추적할 수 있습니다.

useRef의 주요 사용 사례

  1. DOM 요소 접근
    useRef는 함수형 컴포넌트에서 DOM 요소를 직접 접근하고 조작할 수 있게 해줍니다.
  2. 렌더링에 영향을 주지 않는 값 추적
    상태(useState)와 달리 useRef는 값을 변경해도 컴포넌트가 다시 렌더링되지 않으므로, 렌더링을 최적화할 수 있습니다.

1. useRef로 포커스 제어하기

먼저, 숫자를 입력하고 목록에 추가하는 예제를 통해 useRef의 활용법을 살펴보겠습니다.

Average 컴포넌트 (리스트 추가 및 포커스 제어)

import { useRef, useState } from "react";

export default function Average() {
    const [number, setNumber] = useState('');
    const [list, setList] = useState([]);
    
    // 숫자 입력 값 변경 함수
    const changeNumber = e => setNumber(e.target.value);

    // 리스트에 숫자 추가 및 입력창 초기화, 포커스 설정
    const changeList = () => {
        // const newList = [...list, number];
        const newList = list.concat(number);
        setList(newList);
        setNumber(''); // 입력창 초기화
        refNumber.current.focus(); // 입력창에 포커스
    };

    // useRef를 이용해 input에 접근
    const refNumber = useRef();

    return (
        <>
            <input 
                ref={refNumber} 
                type="number" 
                value={number} 
                onChange={changeNumber} 
            />
            <button onClick={changeList}>등록</button>
            <ul>
                {list.map((v, i) => <li key={i}>{v}</li>)}
            </ul>
        </>
    );
}

 

코드 설명

  • useRef를 사용하여 input 요소에 대한 참조를 생성합니다.
  • refNumber.current.focus()로 리스트에 숫자를 추가한 후, 입력창으로 포커스를 자동으로 이동시킵니다.
  • list에 숫자가 추가되면 입력창은 비워지고, 포커스는 다시 input에 이동합니다

2. useRef와 렌더링 횟수 추적

useRef는 값이 변경되어도 컴포넌트를 리렌더링하지 않기 때문에, 렌더링 횟수나 변경된 값을 추적하는 데 유용합니다. 이를 통해, 상태가 변경되더라도 불필요한 렌더링을 방지할 수 있습니다.

useRef로 렌더링 횟수 추적하고 다른 방법과 비교하기.

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

// 지역 변수를 이용해서 렌더링 회수를 체크 ⇒ 렌더링 마다 초기화되어 값을 유지할 수 없음
export const ChangeCountWithLocalVariable = () => {
  const [message, setMessage] = useState('');
  let count = 0;

  console.log("#1", message);

  useEffect(() => {
    console.log("렌더링되었습니다.");
    count++;
  });

  return (
    <>
      <h1>지역 변수를 사용하는 경우</h1>
      <h2>렌더링 회수: {count}</h2>
      <input type="text" value={message} onChange={e => setMessage(e.target.value)} />
    </>
  );
};

// 상태 변수를 이용해서 렌더링 회수를 체크 ⇒ 렌더링 되어도 값은 유지되나, 값이 변경되면 렌더링이 발생생
export const ChangeCountWithStateVariable = () => {
  const [message, setMessage] = useState('');
  const [count, setCount] = useState(0);

  console.log("#2", message);

  useEffect(() => {
    console.log("렌더링되었습니다.");
    setCount(count + 1);
  }, [message]);

  return (
    <>
      <h1>상태 변수를 사용하는 경우</h1>
      <h2>렌더링 회수: {count}</h2>
      <input type="text" value={message} onChange={e => setMessage(e.target.value)} />
    </>
  );
};

// ref 변수를 이용해서 렌더링 회수를 체크 ⇒ 값이 변경되어도 렌더링되지 않고, 계속해서 값이 유지됨
export const ChangeCountWithRefVariable = () => {
  const [message, setMessage] = useState('');
  const count = useRef(0);

  console.log("#3", message);

  useEffect(() => {
    console.log("렌더링되었습니다.");
    count.current++;
  });

  return (
    <>
      <h1>ref 변수를 사용하는 경우</h1>
      <h2>렌더링 회수: {count.current}</h2>
      <input type="text" value={message} onChange={e => setMessage(e.target.value)} />
    </>
  );
};

이 때 app.js는 아래와 같습니다.

import React from "react";
import {ChangeCountWithLocalVariable, 
  ChangeCountWithStateVariable, 
  ChangeCountWithRefVariable} from './Average';

function App() {
  return (
    <>
      <ChangeCountWithLocalVariable />
      <ChangeCountWithStateVariable />
      <ChangeCountWithRefVariable />
    </>
  );
}

export default App;

 

코드 설명

  • count는 useRef를 사용하여 렌더링 횟수를 추적하는 데 사용됩니다.
  • useEffect 훅을 이용해 컴포넌트가 렌더링될 때마다 count.current 값을 증가시킵니다.
  • 이 방식은 렌더링 횟수를 추적하면서도, count 값이 변경되어도 컴포넌트가 리렌더링되지 않음을 확인할 수 있습니다.

3. useRef로 타이머 관리하기

타이머 기능을 구현하면서, useRef를 사용하여 setInterval의 ID를 추적하고 이를 통해 타이머의 시작 및 중지 기능을 제어할 수 있습니다.

타이머 컴포넌트 구현

import { useRef, useState } from "react";

export const ChangeCountWithRefVariable = () => {
  const [count, setCount] = useState(0);
  const intervalId = useRef(0);

  console.log(`렌더링 .... count: ${count}, intervalId: ${intervalId.current}`);

  const startCount = () => {
    intervalId.current = setInterval(() => setCount(count => count + 1), 1000);
    console.log(`카운트 시작 ... intervalId: ${intervalId.current}`);
  };

  const stopCount = () => {
    clearInterval(intervalId.current);
    console.log(`카운트 중지 ... intervalId: ${intervalId.current}`);
  };

  return (
    <>
      <h1>카운트: {count}</h1>
      <button onClick={startCount}>카운트 시작</button>
      <button onClick={stopCount}>카운트 중지</button>
    </>
  );
};

export const ChangeCountWithLocalVariable = () => {
  const [count, setCount] = useState(0);
  let intervalId = 0;

  console.log(`렌더링 .... count: ${count}, intervalId: ${intervalId}`);

  const startCount = () => {
    intervalId = setInterval(() => setCount(count => count + 1), 1000);
    console.log(`카운트 시작 ... intervalId: ${intervalId}`);
  };

  const stopCount = () => {
    clearInterval(intervalId);
    console.log(`카운트 중지 ... intervalId: ${intervalId}`);
  };


  return (
    <>
      <h1>카운트: {count}</h1>
      <button onClick={startCount}>카운트 시작</button>
      <button onClick={stopCount}>카운트 중지</button>
    </>
  );
};

코드 설명

  • intervalId는 useRef를 사용하여 setInterval 함수의 ID를 추적합니다.
  • startCount 함수에서 타이머를 시작하고, stopCount 함수에서 타이머를 중지합니다.
  • useRef로 intervalId를 관리하면, intervalId가 변경되어도 컴포넌트가 리렌더링되지 않으므로, 불필요한 렌더링을 방지할 수 있습니다.
  • 위 내용은 ref로 작성한 내용만이 제대로 스탑이 작동하는 것을 볼 수 있습니다.

useRef는 React에서 매우 유용한 훅으로, DOM 요소에 접근하거나 렌더링에 영향을 주지 않는 값을 추적할 때 사용됩니다. 특히 DOM을 제어하거나 타이머, 이전 상태 추적 등 다양한 기능에서 유용하게 활용할 수 있습니다

 

 

*생성형 AI 활용한 클라우드&보안 전문가 양성캠프 과정의 교육내용 정리 자료입니다.