Logo

[React] ref로 HTML 엘리먼트에 접근/제어하기

React로 웹 애플리케이션을 개발하다 보면 간혹 React 컴포넌트가 아닌 HTML 엘리먼트에 직접 접근해서 DOM API를 이용해서 제어해야 할 필요가 있는데요. 이번 포스팅에서는 이럴 때 유용하게 사용할 수 있는 React의 독특한 prop인 ref 에 대해서 알아보도록 하겠습니다.

ref prop

React의 ref prop은 HTML 엘리먼트의 레퍼런스를 변수에 저장하기 위해서 사용합니다.

예를 들어, 다음과 같이 <input> 엘리먼트에 ref prop으로 inputRef라는 변수를 넘기게 되면, 우리는 이 inputRef 객체의 current 속성을 통해서 <input> 엘리먼트에 접근할 수 있고, DOM API를 이용하여 제어할 수 있습니다.

<input ref={inputRef} />

useRef

ref prop에는 React API를 이용해서 생성한 current 속성을 갖는 특정 형태의 객체만을 할당할 수 있는데요. 클래스 기반 컴포넌트를 에서는 React.createRef() 함수를, 함수형 컴포넌트에서는 useRef() 훅(hook) 함수를 사용하여 이 객체를 생성할 수 있습니다.

본 포스팅에서는 함수형 컴포넌트를 선호하는 최근 트랜드에 맞게 useRef() 훅(hook) 함수를 사용하여 예제 코드를 작성하겠습니다.

예제 1. input 엘리먼트 제어

ref prop은 여러 가지 HTML 엘리먼트 중에서도 <input>을 제어할 때 많이 사용됩니다.

예를 들어, 버튼을 클릭했을 때, 비활성화(disabled)되어 있던 입력란을 활성화시키며 포커스(focus)를 이동시키는 React 컴포넌트를 작성해보겠습니다.

useRef() 훅(hook) 함수를 사용하여 inputRef 객체를 생성한 후, <input> 엘리먼트의 ref prop에 넘기고 있습니다. 이렇게 해주면 inputRef 객체의 current 속성에는 <input> 엘리먼트의 레퍼런스가 저장됩니다. 따라서 <button> 엘리먼트의 클릭(click) 이벤트 핸들러에서는 inputRef.current로 간단하게 <input> 엘리먼트를 제어할 수 있습니다.

import React, { useRef } from "react";

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

  function handleFocus() {
    inputRef.current.disabled = false;
    inputRef.current.focus();
  }

  return (
    <>
      <input disabled type="text" ref={inputRef} />
      <button onClick={handleFocus}>활성화</button>
    </>
  );
}

마찬가지 방법으로 입력란을 원래대로 비활성화 시켜주는 버튼도 어렵지 않게 추가로 구현할 수 있습니다.

import React, { useRef } from "react";

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

  function handleFocus() {
    inputRef.current.disabled = false;
    inputRef.current.focus();
  }

  function handleReset() {
    inputRef.current.disabled = true;
    inputRef.current.value = "";
  }

  return (
    <>
      <input disabled type="text" ref={inputRef} />
      <button onClick={handleFocus}>활성화</button>
      <button onClick={handleReset}>초기화</button>
    </>
  );
}

예제 2. audio 엘리먼트 제어

ref prop이 사용되는 또 다른 사례로 audio 엘리먼트 제어를 들 수 있는데요.

예를 들어, 음악 파일의 재생하거나 중지할 수 있는 컴포넌트를 작성해보겠습니다.

import React, { useRef } from "react";
import music from "./music.mp3";

function Player() {
  const audioRef = useRef(null);

  const handlePlay = () => {
    audioRef.current.play();
  };

  const handlePause = () => {
    audioRef.current.pause();
  };

  return (
    <>
      <figure>
        <figcaption>Eyes on You (Sting) - Network 415:</figcaption>
        <audio src={music} ref={audioRef}>
          Your browser does not support the
          <code>audio</code> element.
        </audio>
      </figure>
      <button onClick={handlePlay}>재생</button>
      <button onClick={handlePause}>중지</button>
    </>
  );
}

useRef() 훅(hook) 함수로 audioRef 객체를 생성한 후, <audio> 엘리먼트의 ref prop에 설정해주었습니다. 따라서, 이벤트 핸들러 함수 내에서는 audioRef.current를 통해 <audio> 엘리먼트의 play()pause() 함수를 호출할 수 있습니다.

전체 코드

본 포스팅에서 작성한 코드는 아래에서 직접 확인하고 실행해볼 수 있습니다.

마치면서

이상으로 React에서 ref prop를 사용하여 어떻게 HTML 엘리먼트에 직접 접근할 수 있는지에 대해서 알아보았습니다.

ref prop 관련해서 한 가지 주의할 점은 HTML 엘리먼트에 직접 제어하는 것은 JQuery 시절에 주로 쓰이던 imperative(명령형) 방식의 웹 프로그래밍이라는 것입니다. Declarative(선언형) 프로그래밍 패러다임을 기반으로 하는 React를 포함한 모던 자바스크립트 라이브러리에서는 반드시 필요한 경우가 아니라면 이러한 접근 방식은 지양하는 것이 좋습니다.