[React] ref

Lian Kim·2022년 9월 25일
0

일반 HTML에서는 DOM 요소에 고유한 이름을 달 때 id를 사용한다.
특정 id에 특정 스타일을 적용해야 하거나, 자바스크립트에서 해당 id를 가진 요소를 찾아서 작업해야 할 때 등 대개 특정 DOM 요소에 어떤 작업을 해야 할 때 id를 활용한다.

이런 id의 역할을 리액트에서는 ref가 대신한다.
id 대신 ref를 사용하는 이유는 무엇일까? 또 ref는 어떤 작업을 할 때 사용해야 할까?






ref(reference)란?

ref는 render 메서드에서 생성된 DOM 노드나 리액트 엘리먼트에 접근할 수 있도록 레퍼런스 값을 만들어준다. Ref를 통해서 DOM 노드나 리액트 엘리먼트에 접근하여 값을 얻어낼 수 있다.

ref는 DOM을 꼭 직접적으로 건드려야 할 때 사용한다. state만으로 해결할 수 없는 상황에서 유용하다.

  • 특정 input에 포커스 주기
  • 스크롤 박스 조작하기
  • Canvas 요소에 그림 그리기 등


id 대신 ref?

물론 리액트 컴포넌트 안에서도 id를 사용할 수는 있다. 하지만 HTML에서 DOM의 id는 유일해야 하는데, 같은 컴포넌트를 여러 번 사용하게 되면 중복 id를 가진 DOM이 여러 개 생기게 되어 에러가 발생하기 때문에 사용하지 않는 편이 좋다.

반면, ref는 id와 같이 전역적으로 작동하는 것이 아니라, 컴포넌트 내부에서만 작동하기 때문에 이런 문제가 발생하지 않는다.

대부분은 id를 사용하지 않고도 구현할 수 있지만 사용해야 하는 상황이 발생할 수 있다. 그럴 때는 컴포넌트를 만들 때마다 id 뒷부분에 추가 텍스트를 붙이면(ex. button01, button02, button03 …) 중복 id 발생을 방지할 수 있다.







클래스 컴포넌트에서 ref 사용하기

클래스 컴포넌트에서는 두 가지 방법으로 ref를 사용할 수 있다.

  1. 콜백 함수를 통한 ref 설정
  2. createRef를 통한 ref 설정


콜백 ref

ref를 만드는 가장 기본적인 방법이다.

  1. ref를 달고자 하는 요소에 ref라는 콜백 함수를 props로 전달해준다.
    이 콜백 함수는 파라미터로 ref 값을 전달받고, 그 ref 값으로 컴포넌트의 멤버 변수로 설정해준다.
  ...

  render() {
    return <input ref={(ref) => {this.input=ref}} />;
  }
}
  1. ref의 이름으로 해당 ref를 가진 DOM에 접근할 수 있다.
  this.input.focus();

import React, { Component } from "react";

class RefSample extends Component {
  
  render() {
    return (
      <div>
        {/* ref 달아줄 요소에 콜백 함수를 props로 전달해주기 */}
        <input ref={(ref) => {this.input=ref}}/>
		{/* 접근할 때는 ref 이름으로 조회 */}
        <button onClick={() => this.input.focus()}>Click</button>
      </div>
    );
  }
}

export default RefSample;

ref의 이름은 자유롭게 지정할 수 있다.

  <button ref={(ref) => {this.cat=ref}}>고양이</button>
  <div ref={(ref) => {this.bread=ref}}></div>



createRef

createRef는 리액트 v16.3부터 도입된 내장 함수로, 더 적은 코드로 쉽게 사용 가능하다.
리액트는 컴포넌트가 마운트될 때 current 프로퍼티에 DOM 요소를 할당하고, 컴포넌트가 언마운트될 때 current 프로퍼티에 다시 null을 할당한다.


  1. 컴포넌트 내부 상단에서 멤버 변수로 React.createRef()를 담아주고,
class RefSample extends Class {
  input = React.createRef();

  ...
}

  1. 해당 멤버 변수를 ref로 달고자 하는 요소에 ref props로 넣어주면 ref 설정이 완료된다.
  ...

  render() {
    return <input ref={this.input} />;
  }
}

  1. 설정 후에 해당 ref를 가진 DOM에 접근하려면, 뒷부분에 .current를 붙여서 조회한다.
    (DOM 대한 참조 값은 ref의 current 프로퍼티에 담기게 된다)
  this.input.current.focus();


import React, { Component } from "react";

class RefSample extends Component {
  // 멤버 변수로 React.createRef() 담아주기
  input = React.createRef();

  render() {
    return (
      <div>
        {/* ref 달아줄 요소에 ref props로 넣어주기 */}
        <input ref={this.input} />
        {/* 접근할 때는 멤버 변수 뒷부분에 .current 붙여주기 */}
        <button onClick={() => this.input.current.focus()}>Click</button>
      </div>
    );
  }
}

export default RefSample;



컴포넌트에 ref 달기

DOM에 달 때와 동일한 방법으로 컴포넌트에도 ref를 달 수 있다.
주로 컴포넌트 내부에 있는 DOM을 컴포넌트 외부에서 사용할 때 쓴다.

  <MyComponent ref={(ref) => {this.myComponent=ref}} />

외부 컴포넌트에서, 컴포넌트 내부의 메서드 및 멤버 변수에도 접근할 수 있다.
⇒ 즉, 내부의 ref에도 접근 가능 (ex. myComponent.handleClick, myComponent.input 등)

  <MyComponent ref={(ref) => {this.myComponent=ref}} />
  <button onClick={() => this.myComponent.handleClick()} />






함수 컴포넌트에서 ref 사용하기

함수 컴포넌트에서 ref를 사용하려면 useRef라는 Hook 함수를 사용해야 한다.
useRef의 사용법은 React.createRef와 유사하다.

리렌더링 시 클래스형 컴포넌트는 render 함수만 다시 실행하지만, 함수형 컴포넌트는 함수 자체를 다시 실행한다. 이 때문에 함수형 컴포넌트에서 createRef를 사용하면 ref 값이 초기화되어서 원하는 값을 얻지 못하기 때문에 리액트의 Hook인 useRef를 사용하는 것이다.


  1. 리액트에서 useRef를 import 한다.
import { useRef } from 'react';

  1. useRef를 사용하여 Ref 객체를 만든다.
const input = useRef();

  1. 해당 변수를 ref로 달고자 하는 요소에 ref props로 넣어주면 ref 설정이 완료된다.
<input ref={input} />

  1. 설정 후에 해당 ref를 가진 DOM에 접근하려면, 뒷부분에 .current를 붙여서 조회한다.
    (DOM 대한 참조 값은 ref의 current 프로퍼티에 담기게 된다)
<button onClick={() => input.current.focus()}>Click</button>


import React, { useRef } from "react";

const RefSample = () => {
  const input = useRef();

  return (
    <div>
      <input ref={input} />
      <button onClick={() => input.current.focus()}>Click</button>
    </div>
  );
};

export default RefSample;






정리

컴포넌트 내부에서 DOM에 직접 접근해야 할 때 ref를 사용한다.

클래스형 컴포넌트에서는 콜백 ref나 createRef라는 내장 함수를 통해 ref를 설정할 수 있고,
함수형 컴포넌트에서는 useRef라는 Hook을 통해 ref를 설정할 수 있다.


주의
서로 다른 컴포넌트끼리 데이터 교류할 때 ref를 사용하는 것은 옳지 않다. 앱 규모가 커지면 구조가 꼬여버려서 유지 보수가 어려워진다. 컴포넌트들끼리 데이터를 교류할 때는 언제나 데이터를 부모 ↔ 자식 흐름으로 교류해야 한다. (리덕스 혹은 Context API 등 사용)







참고 자료
김민준, 『리액트를 다루는 기술』, 길벗
React Ref와 DOM

0개의 댓글