프로그래밍/ReactJS

React Refs 완벽 가이드: DOM 직접 조작, 과연 필요할까요?

shimdh 2025. 10. 11. 00:38
728x90

안녕하세요, 프론트엔드 개발자 여러분! React는 선언적인 방식으로 UI를 구축하게 해주는 강력한 라이브러리입니다. 하지만 때로는 선언적인 방식만으로는 해결하기 어려운 문제에 직면하기도 합니다. 바로 DOM 요소나 클래스 컴포넌트 인스턴스에 직접 접근하여 조작해야 할 때인데요. 이때 우리에게 필요한 것이 바로 Refs(참조) 입니다. 오늘은 React Refs가 무엇인지, 왜 사용해야 하는지, 그리고 어떻게 사용하는지 자세히 알아보겠습니다. Refs를 마스터하면 React 개발이 한층 더 유연해질 거예요!

728x90

Refs, 왜 필요할까요?

React는 가상 DOM을 통해 효율적인 UI 업데이트를 제공하지만, 실제 DOM에 직접적인 접근이 필요한 시나리오들이 존재합니다. Refs는 이러한 상황에서 React의 데이터 흐름을 거치지 않고 DOM 요소나 컴포넌트 인스턴스에 직접 접근할 수 있는 강력한 기능을 제공합니다. 아래에서 Refs의 주요 필요성을 세 가지로 나누어 설명하겠습니다.

1. 직접 접근의 필요성

일반적인 React 개발에서는 상태(state)와 프롭스(props)를 통해 데이터 흐름을 관리하고, 이에 따라 UI가 렌더링되도록 합니다. 하지만 특정 DOM 요소에 대한 직접적인 제어가 필요할 때가 있습니다. 예를 들어, 사용자가 폼 필드에 입력할 준비가 되었을 때 자동으로 텍스트 입력란에 포커스를 맞추거나, 동적으로 생성된 요소의 크기를 즉시 측정해야 하는 경우 등이 대표적입니다. Refs는 이러한 직접적인 상호작용을 가능하게 하여 사용자 경험을 향상시키고 특정 UI 동작을 구현하는 데 필수적인 도구 역할을 합니다. 이는 특히 웹 애플리케이션에서 사용자 편의성을 높이는 데 중요한 역할을 합니다. 예를 들어, 로그인 폼에서 비밀번호 입력 후 자동으로 '로그인' 버튼에 포커스를 주는 기능처럼요.

2. 불필요한 상태 업데이트 방지

React에서 모든 DOM 조작을 상태 및 프롭스 시스템을 통해 처리하려고 하면 불필요한 리렌더링을 유발하여 성능 저하를 초래할 수 있습니다. 예를 들어, 단순히 DOM에서 값을 읽어오는 데만 관심이 있다면 굳이 상태를 업데이트할 필요가 없습니다. Refs를 사용하면 React의 렌더링 주기에 영향을 주지 않으면서 DOM 노드에 직접 접근하여 필요한 정보를 추출할 수 있습니다. 이는 특히 고성능이 요구되는 애플리케이션이나 복잡한 UI 구성 요소에서 큰 장점이 됩니다. 예를 들어, 무한 스크롤 컴포넌트에서 스크롤 위치를 추적하거나, 드래그 앤 드롭 기능을 구현할 때 DOM 요소의 위치를 실시간으로 파악해야 하는 경우 Refs가 매우 효율적입니다. 이렇게 하면 앱의 반응 속도가 훨씬 빨라집니다!

3. 서드파티 라이브러리와의 통합

jQuery와 같이 DOM을 직접 조작하는 서드파티 라이브러리를 React 프로젝트에 통합할 때, Refs는 이러한 요소들에 원활하게 연결될 수 있는 방법을 제공합니다. React는 가상 DOM을 사용하여 효율적으로 UI를 업데이트하지만, 외부 라이브러리는 종종 실제 DOM에 직접 접근하여 작업을 수행합니다. Refs를 사용하면 React 컴포넌트 내에서 서드파티 라이브러리가 조작할 DOM 요소의 참조를 얻을 수 있으며, 이를 통해 React의 제어를 벗어나지 않으면서 외부 라이브러리와 효과적으로 협력할 수 있습니다. 이는 기존의 JavaScript 라이브러리나 위젯을 React 프로젝트에 통합해야 할 때 매우 중요합니다. 예를 들어, Chart.js 같은 차트 라이브러리나 Google Maps 같은 맵 라이브러리가 DOM 요소를 필요로 할 때 Refs를 사용하여 해당 DOM 요소를 전달할 수 있습니다. 이렇게 하면 React와 기존 코드베이스를 부드럽게 연결할 수 있어요.

Refs 생성 방법

React에서 Refs를 생성하는 방법은 크게 세 가지가 있으며, 각 방법은 특정 사용 사례와 컴포넌트 유형(클래스 컴포넌트 또는 함수형 컴포넌트)에 최적화되어 있습니다. 아래에서 하나씩 살펴보겠습니다.

1. React.createRef() 사용하기 (클래스 컴포넌트)

React.createRef()는 주로 클래스 컴포넌트에서 Ref를 생성하고 요소에 연결하고자 할 때 사용됩니다. 이 메서드는 생성자에서 호출되어 Ref 객체를 생성하며, 이 객체는 나중에 Ref가 연결된 DOM 노드나 React 컴포넌트 인스턴스를 저장하는 데 사용됩니다. Ref 객체의 .current 속성을 통해 실제 DOM 요소나 컴포넌트 인스턴스에 접근할 수 있습니다.

import React from 'react';

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    // Ref 생성
    this.myInput = React.createRef();
  }

  focusInput = () => {
    // Ref를 사용하여 input에 포커스
    this.myInput.current.focus();
  };

  render() {
    return (
      <div>
        <input type="text" ref={this.myInput} />
        <button onClick={this.focusInput}>Focus Input</button>
      </div>
    );
  }
}

export default MyComponent;

위 예시에서 myInput이라는 Ref는 React.createRef()를 통해 초기화되며, input 필드는 ref={this.myInput} 속성을 통해 이 Ref를 사용하도록 연결됩니다. 버튼을 클릭하면 focusInput 메서드가 호출되어 this.myInput.current.focus()를 통해 input 요소에 직접 포커스를 맞춥니다. 이 방식은 React의 상태를 변경하지 않고 직접적인 DOM 조작이 필요할 때 매우 유용합니다.

2. 콜백 Refs 사용하기 (클래스 및 함수형 컴포넌트)

콜백 Refs는 Ref가 설정되고 정리되는 방식을 직접 정의할 수 있도록 하여 더 많은 유연성을 제공합니다. Ref 속성에 함수를 전달함으로써 React는 DOM 노드나 컴포넌트 인스턴스가 마운트되거나 언마운트될 때마다 해당 함수를 호출합니다. 마운트 시에는 DOM 요소 또는 컴포넌트 인스턴스가 함수의 인수로 전달되고, 언마운트 시에는 null이 전달됩니다.

import React from 'react';

class MyComponent extends React.Component {
  myElement = null;

  setRef = (element) => {
    this.myElement = element;
  };

  alertValue = () => {
    if (this.myElement) {
      alert(this.myElement.value);
    }
  };

  render() {
    return (
      <div>
        <input type="text" ref={this.setRef} />
        <button onClick={this.alertValue}>Alert Value</button>
      </div>
    );
  }
}

export default MyComponent;

이 예시에서는 input 요소가 렌더링되면 setRef 함수가 호출되고, element 인수에 input DOM 요소의 참조가 전달되어 this.myElement에 저장됩니다. alertValue 메서드에서는 this.myElement에 저장된 input 요소에 직접 접근하여 value 속성을 읽어 경고창으로 표시합니다. 콜백 기반 Ref는 특정 DOM 속성을 읽거나 직접 조작해야 할 때 매우 유용합니다. 함수형 컴포넌트에서도 동일하게 사용할 수 있어요.

3. useRef Hook 사용하기 (함수형 컴포넌트)

useRef 훅은 함수형 컴포넌트용으로 설계되었으며, 클래스 기반 Refs와 유사한 기능을 제공하지만 훅의 원칙을 더 잘 따릅니다. useRef.current 속성을 가진 변경 가능한 Ref 객체를 반환합니다. 이 객체는 컴포넌트의 전체 생명 주기 동안 유지되며, 렌더링 사이에 값을 "기억"할 수 있습니다.

import React, { useRef } from 'react';

const MyFunctionalComponent = () => {
  const myInput = useRef(null);

  const focusInput = () => {
    if (myInput.current) {
      myInput.current.focus();
    }
  };

  return (
    <div>
      <input type="text" ref={myInput} />
      <button onClick={focusInput}>Focus Input</button>
    </div>
  );
};

export default MyFunctionalComponent;

함수형 컴포넌트에서 useRef(null) 훅을 사용하여 Ref(myInput)를 생성하고 input 요소에 ref={myInput} 속성을 통해 연결합니다. 클래스 컴포넌트의 예제와 마찬가지로, 버튼을 클릭하면 focusInput 함수가 호출되어 myInput.current를 통해 텍스트 input에 접근하여 포커스를 맞춥니다. useRef는 함수형 컴포넌트에서 Ref를 사용할 수 있게 하여, 클래스 컴포넌트에서 제공되던 직접적인 DOM 조작 기능을 함수형 컴포넌트에서도 자연스럽게 사용할 수 있도록 합니다.

Refs 사용 시 주의점

Refs를 사용할 때는 몇 가지 주의할 점이 있습니다. 먼저, Refs는 React의 선언적 패러다임을 위반할 수 있으므로 최소한으로 사용하세요. 상태나 프롭스로 해결 가능한 문제라면 Refs 대신 그쪽을 우선 고려하는 게 좋습니다. 또한, Refs는 렌더링 후에만 유효하니 useEffect나 이벤트 핸들러 안에서 접근하는 게 안전합니다. 마지막으로, 클래스 컴포넌트에서 createRef를 사용할 때는 바인딩을 잊지 마세요!

결론

Refs는 애플리케이션의 유연성을 높여주는 강력한 도구지만, 남용하면 코드의 유지보수성을 해칠 수 있습니다. 선언적 방식이 React의 철학이니, Refs는 정말 "직접 조작"이 필요한 순간에만 활용하세요. 이 가이드를 통해 Refs를 효과적으로 사용하시면 더 나은 React 개발자가 되실 거예요! 질문이 있으시면 댓글로 남겨주세요. 다음 포스트에서 또 만나요! 😊

728x90