React 컴포넌트 내 DOM 접근하기

ref, forwardRef 사용법


들어가며

React 컴포넌트 내부에 있는 엘리먼트를 화면별로 다르게 컨트롤 해줘야하는 상황들이 발생합니다. 인풋 요소에 자동 포커스를 넣어주거나 체크박스 상태에 따른 화면 구성이라든지 특정 버튼은 화면 전체 스크롤(최상위 혹은 최하위)이 되는 기능을 구현해야 할 때, React에서 엘리먼트를 접근하는 방법인 Ref와 부모 컴포넌트에서 자식 컴포넌트로 전달해서 사용하는 forwawrdRef 에 대해 소개합니다.


Ref

Ref는 render 메서드에서 생성된 DOM 노드나 React 엘리먼트에 접근하는 방법을 제공합니다. 자바스크립트의 querySelector나 getElementByID를 사용하지만, React에서는 Ref로 선택할 수 있습니다.

querySelector와 같은 DOM API를 이용하는 방법은 React에서 라이프사이클에 따라 해당 요소가 제대로 갖고 오지 못할 수도 있어서 사용을 지양하고 Ref의 활용을 추천하는데요.

Ref를 생성하는 방법은 다음과 같습니다.

  1. createRef 함수로 ref 생성하기
class Form extends React.Component {
  constructor(props) {
    super(props);

    this.inputText = React.createRef();
  }

  componentDidMount() {
    this.inputText.current.focus();
  }

  render() {
    return (
      <React.Fragment>
        <label for="name">Name: </label>
        <input id="name" ref={this.inputText} />
      </React.Fragment>
    );
  }
}

[codepen]에서 보기

  1. callback 함수로 ref 생성하기

    ref 속성에 callback 함수로 input 엘리먼트를 반환하여 저장하는 함수를 전달합니다.

class Form extends React.Component {
  constructor(props) {
    super(props);

    this.inputText;
  }

  componentDidMount() {
    this.inputText.focus();
  }

  render() {
    return (
      <React.Fragment>
        <label htmlFor="name">Name: </label>
        <input id="name" ref={(inputEl) => (this.inputText = inputEl)} />
      </React.Fragment>
    );
  }
}

[codepen]에서 보기

  1. Hook API를 이용하여 ref 생성하기
const Form = () => {
  const inputText = React.useRef();

  // componentDidMount 와 유사
  React.useEffect(() => {
    inputText.current.focus();
  }, []);

  return (
    <React.Fragment>
      <label htmlFor="name">Name: </label>
      <input id="name" ref={inputText} />
    </React.Fragment>
  );
};

[codepen]에서 보기


ForwardRef

ref 전달은 컴포넌트를 통해 자식 중 하나에 ref를 자동으로 전달하는 기법입니다. 특히 재사용할 수 있는 컴포넌트 라이브러리와 같은 어떤 컴포넌트에서는 유용할 수 있습니다.

React는 재사용을 위해 컴포넌트 정의하는데 화면에 따라 컴포넌트 내 엘리먼트를 조작하고 싶을 때, 부모 컴포넌트에서 자식 컴포넌트에 ref를 전달하여 사용할 수 있습니다. forwardRef 함수로 자식 컴포넌트에 ref를 컴포넌트의 인자로 받아서 엘리먼트를 지정하여 선택할 수 있습니다.

const ChildComponent = React.forwardRef((props, receiveRef) => {
  return (
    <>
      <label htmlFor="name">Name: </label>
      <input id="name" type="text" ref={receiveRef} />
    </>
  );
});

const ParentComponent = () => {
  const pageRef = React.useRef(null);

  React.useEffect(() => {
    pageRef.current.focus();
  }, [pageRef]);

  return (
    <div className="wrap">
      <ChildComponent ref={pageRef} />
    </div>
  );
};

[codepen]에서 보기


 마치며

현재 진행하는 프로젝트 중반을 지나고 나서 forwardRef 개념을 알게 되어 되어 아쉬움이 남는데요. 다음 프로젝트에서 공통 정의할 때 유추할 수 있는 기능들…, 인풋 포커싱 기능이라든지, 버튼의 이벤트 부여와 같이 화면에서 컴포넌트별 기능을 추가할 가능성이 큰 컴포넌트의 경우, 함께 작성하면 더 좋지 않을까 스스로 잊어버리지 않기 위해 작성했는데요. 앞으로 React 환경에서 작업하고 있는 분들에게 도움이 되는 글이었음 좋겠습니다.

읽어주셔서 감사합니다.

참고문서


추천 글