본문 바로가기

개발공부/기술면접준비

[React] useRef가 필요한 상황과 예시

React Hook이란?

리액트 훅은 React v16.8부터 도입된 기능으로, 함수형 컴포넌트에서 상태(state)관리, 생명주기 메서드를 사용하지 않고도 상태와 다른 리액트 기능들을 사용할 수 있게 해주는 기술이다.
Hook은 함수 컴포넌트 내에서 "Hook"함수를 호출하여 리액트의 기능을 사용할 수 있도록 해준다.

  • 상태관리 : 클래스형 컴포넌트에서만 사용되던 state를 함수형 컴포넌트에서도 사용할 수 있게 해준다.
    useState훅을 통해 상태를 관리할 수 있다.
  • 부작용(Side Effect) 처리 : useEffect 훅을 사용하여 컴포넌트의 부작용 로직을 처리할 수 있다.
    데이터 가져오기, 구독설정, DOM조작 등을 수행할 수 있다.
  • 컨텍스트(Context) 사용 : useContext훅을 사용하여 컴포넌트간에 데이터를 공유할 수 있게 해준다.
  • 렌더링 제어 : useMemo, useCallback 훅을 사용하여 성능 최적화를 수행하고 불필요한 렌더링을 방지할 수 있다.

리액트 훅을 사용하면 함수형 컴포넌트가 클래스형 컴포넌트와 비슷한 기능을 제공하며, 코드를 간결하게 유지하고 재사용 가능한 로직을 더 쉽게 작성할 수 있다.

useRef란?

useRef는 리액트에서 사용되는 훅 중 하나로, DOM요소에 접근하거나 리액트 컴포넌트 내부에서 변수를 유지할 때 주로 사용된다.
useRef를 사용하면

  • DOM요소에 접근 : useRef를 사용하여 DOM요소에 접근하고 해당 요소를 조작할 수 있다.
    특정 DOM요소의 높이나 너비를 변경하거나 포커스를 설정할 수 있다.
  • 변수 유지 : useRef를 사용하여 컴포넌트 내에서 변수를 유지할 수 있다. 이 변수는 컴포넌트가 다시 렌더링될 때마다 재설정되지 않으며, 이전 값을 유지한다.
    이러한 특성은 컴포넌트의 렌더링 사이에 데이터를 저장하거나 변경 사항을 추적하는데 유용하다.

useRef를 사용하는 기본적인 방법

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

function MyComponent() {
  // useRef를 사용하여 변수 생성
  const myRef = useRef(null);

  useEffect(() => {
    // 컴포넌트가 렌더링된 후에 실행되는 코드
    // myRef.current를 사용하여 DOM 요소에 접근할 수 있음
    if (myRef.current) {
      myRef.current.focus(); // 예: DOM 요소에 포커스 설정
    }
  }, []);

  return (
    <div>
      <input ref={myRef} type="text" />
    </div>
  );
}

위 예제코드에서 useRef를 사용하여 'myRef'라는 변수를 생성하고, 이 변수를 <input>요소의 ref속성에 연결하여 DOM요소에 접근하고 포커스를 설정한다.
또한 useEffect를 사용하여 컴포넌트가 렌더링된 후에 실행되는 코드를 작성할 수 있다.

useRef는 DOM요소뿐만 아니라 컴포넌트 내에서 어떤 변수든 유지하고 조작하는데 사용될 수 있으며, 컴포넌트의 렌더링과 관련없이 변수를 관리하는데 유용하다.

useRef가 유용하게 사용되는 상황

1. DOM요소에 접근

useRef를 사용하여 특정 DOM요소에 접근하고 조작할 수 있다.
예를 들어 포커스를 설정하거나 스크롤 위치를 조정하는 등의 작업을 수행할 때 사용한다.

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

function FocusInput() {
  const inputRef = useRef(null);

  useEffect(() => {
    // 컴포넌트가 마운트된 후에 input 요소에 포커스 설정
    inputRef.current.focus();
  }, []);

  return <input ref={inputRef} />;
}

위 예제에서는 useRef를 사용하여 inputRef변수를 생성하고 useEffect를 사용하여 컴포넌트가 마운트된 후에 <input>요소에 포커스를 설정한다.

2. 변수 유지

useRef를 사용하여 컴포넌트 내부에서 변수를 유지할 수 있다. 이 변수는 컴포넌트가 리렌더링될 때 재설정되지 않으며, 이전 값을 유지한다.
컴포넌트의 렌더링간에 데이터를 저장하거나 변경사항을 추적하는데 유용하다.

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

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

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

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleIncrement}>Increment</button>
    </div>
  );
}

위 예제코드는 useRef를 사용하여 countRef변수를 생성하고, 이 변수를 사용하여 현재 카운트 값을 변경하고 상태로 업데이트한다.
이렇게하면 렌더링 간에 데이터를 유지할 수 있다.

3. 컴포넌트의 상태와 무관한 데이터 저장

useRef로 저장하는 데이터는 컴포넌트의 상태와 무관하므로 상태 변경이 없어도 데이터를 저장하고 사용할 수 있다.
특정 데이터를 리렌더링과 관계없이 보존하려는 경우에 유용하다.

import React, { useRef } from 'react';

function NonStateDataStorage() {
  // useRef를 사용하여 컴포넌트 상태와 무관한 데이터 저장
  const dataRef = useRef([]);

  const handleAddData = () => {
    // 데이터 배열에 새로운 항목 추가
    dataRef.current.push(Math.random());
    console.log('Data:', dataRef.current);
  };

  return (
    <div>
      <button onClick={handleAddData}>Add Data</button>
      <p>Click the button to add random data.</p>
    </div>
  );
}

export default NonStateDataStorage;

위 예제코드는 컴포넌트 상태와는 별도로 데이터를 저장하고 나중에 사용하는 방법을 보여준다.
useRef를 사용하여 dataRef변수를 생성하고, 버튼을 클릭할 때마다 배열에 새로운 무작위 데이터를 추가한다.
dataRef.current를 통해 데이터에 접근하고 변경한다.
이 데이터는 컴포넌트의 렌더링과 상태 변경과는 독립적으로 관리된다.

4. 이전 값과 현재 값 비교

useRef를 사용하여 이전 값을 저장하고, 새로운 값과 비교하여 이전 값과 현재값을 비교하고 싶을때 사용할 수 있다.

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

function CompareValues() {
  const prevValueRef = useRef(null);
  const currentValue = 42;

  useEffect(() => {
    prevValueRef.current = currentValue;
  }, [currentValue]);

  return (
    <div>
      <p>Current Value: {currentValue}</p>
      <p>Previous Value: {prevValueRef.current}</p>
    </div>
  );
}

위 예제코드에서는 useRef를 사용하여 prevValueRef변수를 생성하고, useEffect를 사용하여 currentValue가 변경될때마다 이전 값을 저장한다.
이것을 통해 현재 값과 이전값의 비교를 할 수 있다.

5. 외부 라이브러리와 통합

외부 라이브러리와 통합할 때 useRef를 사용하여 라이브러리에서 반환한 객체나 인스턴스를 저장할 수 있다.
컴포넌트가 리렌더링될 때마다 새로운 인스턴스를 생성하지 않고 이전 인스턴스를 재사용할 수 있다.

import React, { useRef, useEffect } from 'react';
import ChartJS from 'chart.js';

function ChartComponent() {
  const chartRef = useRef(null);

  useEffect(() => {
    const ctx = chartRef.current.getContext('2d');
    new ChartJS(ctx, {
      type: 'bar',
      data: {
        labels: ['A', 'B', 'C'],
        datasets: [{ data: [1, 2, 3] }],
      },
    });
  }, []);

  return <canvas ref={chartRef} />;
}

위 예제코드에서 useRef를 사용하여 chartRef변수를 생성하고, Chart.js와 같은 외부 차트 라이브러리와 통합할때 <canvas>요소에 접근한다.

6. 컴포넌트의 생명 주기와 관계없는 작업

useRef를 사용하면 컴포넌트의 생명 주기와 상관없이 데이터를 유지하고 작업을 수행할 수 있으므로, 특정 생명주기 메서드에 종속되지 않는 작업을 수행할 때 유용하다.

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

function Timer() {
  const timerRef = useRef(null);
  const [count, setCount] = useState(0);
  const [isRunning, setIsRunning] = useState(false);

  const startTimer = () => {
    if (!isRunning) {
      timerRef.current = setInterval(() => {
        setCount((prevCount) => prevCount + 1);
      }, 1000);
      setIsRunning(true);
    }
  };

  const stopTimer = () => {
    if (isRunning) {
      clearInterval(timerRef.current);
      setIsRunning(false);
    }
  };

  const resetTimer = () => {
    clearInterval(timerRef.current);
    setIsRunning(false);
    setCount(0);
  };

  return (
    <div>
      <p>Timer: {count} seconds</p>
      <button onClick={startTimer}>Start</button>
      <button onClick={stopTimer}>Stop</button>
      <button onClick={resetTimer}>Reset</button>
    </div>
  );
}

export default Timer;

위 예제코드에서는 타이머를 시작하고 중지하는 기능을 구현한다.
useRef를 사용하여 timerRef변수를 생성하고, 타이머를 시작하고 중지하며 초기화하는 기능을 구현한다.
timerRef변수는 타이머의 ID를 저장하고 컴포넌트의 생명주기와는 관계없이 타이머를 제어한다.

useRef는 주로 상태를 관리하는 것이 아니라 데이터를 보존하거나 DOM요소에 접근하는 등의 상황에서 사용된다.