본문 바로가기
컴퓨터 사이언스/TIL 정리

[리액트를 다루는 기술] ref: DOM에 이름 달기

by 메리뉴데이 2022. 8. 14.

HTML에서 id를 사용하여 DOM에 이름을 다는 것처럼

리액트 프로젝트 내부에서 DOM에 이름을 다는 방법이 ref의 개념이다.

리액트 컴포넌트 안에서 id를 사용할 수 있기는 하지만,

같은 컴포넌트를 여러 번 사용할 경우, 중복 id를 가진 DOM이 여러 개 생기는 상황이 되기 때문에 권장하지 않는다.

하지만, ref는 전역적으로 작동하지 않고 컴포넌트 내부에서만 작동하기 때문에 이런 문제가 생기지 않는다. 

다른 라이브러리나 프레임워크와 함께 id를 사용해야 하는 상황이 발생할 경우에는 

컴포넌트를 만들 때마다 id 뒷 부분에 추가 텍스트를 붙여서 (ex. button01, button02, ...) 중복 id가 발생하는 것을 방지해야 한다.

 

ref는 DOM을 꼭 직접적으로 건드려야 할 때 사용한다 !

 

 

import React, { Component } from 'react';
import './ValidationSample.css';

class ValidationSample extends Component {
    state = {
        password: ' ',
        clicked: false,
        validated: false,
    }

     handleChange = (e) => {
         this.setState({
            password: e.target.value
        });
    }

    handleButtonClick = ( ) => {
        this.setState({
            clicked: true,
            validated: this.state.password === '0000'
        })
    }

    render() {
          return (
              <div>
                    <input
                         type="password"
                         value={this.state.password}
                         onChange={this.handleChange}
                         className={this.state.clicked ? (this.state.validated ? 'success' : 'failure') : ' ' }
                      />
                      <button onClick={this.handleButtonClick}>검증하기</button>
                </div>
            );
    }
}

export default ValidationSample;
             

 

여기까지는 state를 사용하여 간단한 검증을 구현하였지만,

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

DOM에 직접 접근해야 하는데, 이 때 ref를 사용한다.

 

ref를 사용하는 방법은 두 가지이다. 

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

: ref를 만드는 가장 기본적인 방법으로, ref를 달고자 하는 요소에 ref라는 콜백 함수를 props로 전달해준다.

이 콜백 함수는 ref 값을 파라미터로 전달받고, 함수 내부에서 파라미터로 받은 ref를 컴포넌트의 멤버 변수로 설정해준다.

 

<input ref={(ref) => {this.input=ref}} />     // this.input은 input 요소의 DOM을 가리킴

 

2. createRef를 통한 ref 설정

: 리액트 내장 함수 createRef를 사용하는 방법으로, 이 함수를 사용하면 더 적은 코드로 쉽게 사용할 수 있다. v16.3부터 도입

createRef를 사용하여 ref를 만들려면 우선 컴포넌트 내부에서 멤버 변수로 React.createRef()를 담아주어야 한다.

그리고, ref를 달고자 하는 요소에 ref props로 넣어 주면 ref 설정이 완료된다.

설정한 뒤에 ref를 설정해 준 DOM에 접근하려면 this.input.current를 조회하면 된다. 

 

input = React.createRef( );

handleFocus = ( ) => {
    this.input.current.focus( );
}

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

 

 

 

컴포넌트에 ref를 달 수도 있다. 컴포넌트 외부에서 컴포넌트 내부의 DOM을 사용하고자 할 때 그렇다.

 

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

이렇게 하면 MyComponent 내부의 메서드 및 멤버 변수에 접근할 수 있다.

  ex. myComponent.handleClick, myComponent.input 등

 

App 컴포넌트에서 ScrollBox에 ref를 달고 버튼을 만들어 누르면, ScrollBox 컴포넌트의 scrollToBottom 메서드를 실행하도록 코드를 작성하겠다.

 

<div>
    <ScrollBox ref={(ref) => this.scrollBox=ref} />
    <button onClick={( ) => this.scrollBox.scrollToBottom( )}>    // 스크롤박스의 메서드
        맨 밑으로
    </button>
</div>

 

문법상으로 onClick= {this.scrollBox.scrollBottom} 같은 형식으로 작성해도 틀린 것은 아니지만, 

컴포넌트가 처음 렌더링될 때는 this.scrollBox 값이 undefined이므로 this.scrollBox.scrollBottom 값을 읽어올 때 오류가 발생하지만, 화살표 함수 문법을 사용해 새로운 함수를 만들고 그 내부에서 this.scrollBox.scrollBottom 메서드를 실행하면, 버튼을 누를 때(이미 한 번 렌더링을 해서 this.scrollBox를 설정한 시점) this.scrollBox.scrollBottom 값을 읽어 와서 실행하므로 오류가 발생하지 않는다.

 

 

컴포넌트끼리 데이터를 교류할 때는 언제나 데이터를 부모 ↔ 자식 흐름으로 교류해야 하고 (리덕스 혹은 Context API를 사용해 효율적으로 교류하는 방법은 익힐 예정) ref를 사용하지 않고도 원하는 기능을 구현할 수 있는지 반드시 고려한 후에 ref를 활용하는 것이 좋다.