본문 바로가기
JavaScript/React

React) ref - DOM에 이름 달기, state를 이용해 기능 구현

by 박채니 2022. 11. 11.
안녕하세요, 코린이의 코딩 학습기 채니 입니다.
[리액트를 다루는 기술]의 책을 참고하여 포스팅한 개인 공부 내용입니다.

 

 

일반 HTML에서 DOM 요소에 이름을 달 때는 id를 사용하지만, 리액트에서는 ref를 사용합니다.

※ 리액트에서 id값이 아닌 ref를 사용하는 이유는?

- 같은 컴포넌트를 여러 번 사용한다고 할 때, id는 유일해야 하는데, 중복 id를 가진 DOM이 여러 개 생길 수 있으니, 문제 발생 원인이 될 수 있음!

 

ref: DOM에 이름 달기

 

ref를 사용해야 하는 상황

 

DOM를 직접적으로 건드려야 할 때 ref를 사용합니다.

예를 들어,

javascript 혹은 jQuery로 만든 웹 사이트는 특정 input 요소를 찾을 때 해당 id값을 가진 input에 클래스를 설정해주곤 합니다.

 

javascript 이용 코드 - 비밀번호 입력 후 버튼 클릭 시 0000일 땐 초록바탕 / 이 외에는 빨간바탕 렌더링

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Example</title>
    <style>
        .success {
            background-color: green;
        }
        .failure {
            background-color: red;
        }
    </style>
    <script>
        function validate() {
            var input = document.getElementById('password');
            input.className = '';

            if(input.value === '0000') {
                input.className = 'success';
            } else {
                input.className = 'failure';
            }
        }
    </script>
</head>
<body>
    <input type="password" id="password" />
    <button onclick="validate()">validate</button>
</body>
</html>

0000 입력 후 버튼 클릭 시

 

하지만 해당 기능을 리액트에서는 위와 같이 DOM에 접근하지 않아도 state로 구현할 수 있습니다.


state를 통해 기능 구현

 

예제 컴포넌트 생성

 

ValidationSample.css

.success {
    background-color: lightgreen;
}
.failure {
    background-color: lightcoral;
}

 

ValidationSample.js

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

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

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

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

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

export default ValidationSample

input에서 onChange 이벤트 발생state의 password 값을 현재 입력 값으로 업데이트 해주고,

button에서 onClick 이벤트 발생clicked를 참으로 변경 / validate 값을 검증 결과로 변경해주었습니다.

input의 className값을 누르기 전엔 비어있는 문자열로, 누른 후엔 검증 결과에 따라 'success' 혹은 'failure'로 설정해주었습니다.

 

 

App 컴포넌트에서 예제 컴포넌트 렌더링

 

App.js

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

class App extends Component {
  render() {
    return (
      <ValidationSample/>
    )
  }
}

export default App;

0000 입력 후 버튼 클릭 시
000 입력 후 버튼 클릭 시

입력 값에 따라 결과물이 제대로 반영 되는 것을 확인할 수 있습니다.

 

 

DOM을 꼭 사용해야 하는 상황은?

 

위 예제에선 state를 통해 기능 구현을 했지만, state만으로 해결 불가한 기능들이 있습니다.

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

이러한 상황 등에는 DOM에 직접 접근해야 하며, 이 때는 ref를 사용합니다.

 


ref 사용

 

방법 ① 콜백 함수를 통한 ref 설정

 

ref를 달고자 하는 요소에 ref라는 콜백 함수를 props로 전달해 줍니다.

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

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

따라서, this.input은 input 요소의 DOM을 가리키게 되며 이 때 이름은 this.superman = ref 같이 맘대로 정할 수 있습니다.

 

 

방법 ② createRef를 통한 ref 설정

 

리액트에 내장 되어 있는 createRef 함수를 사용해 줍니다.

import React, { Component } from "react";

class RefSample extends Component {
    input = React.createRef;

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

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

export default RefSample;

createRef를 사용하여 ref 생성 시,

컴포넌트 내부에서 멤버 변수로 React.createRef()를 담아주어야 합니다. (input = React.createRef)

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

 

추후 ref를 설정한 DOM에 접근하려면 this.input.current를 조회해주면 됩니다. 

 

그렇다면 ref를 이용해 이전에 만들었던 ValidationSample의 코드 중 버튼 클릭 시,

포커스를 input으로 넘어가도록 코드를 작성해보겠습니다.

 

 

input에 ref 달기

 

ValidationSample.js - render() 함수

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

 

 

버튼 onClick 이벤트 코드 수정

 

ValidationSample.js - handleButtonClick 메소드

    handleButtonClick = () => {
        this.setState({
            clicked: true,
            validate: this.state.password === '0000'
        });
        this.input.focus();
    }

this.input이 컴포넌트 내부의 input을 가리키게 되어 일반 DOM을 다루듯이 코드를 작성해 기능을 구현하였습니다.

'검증하기' 버튼을 클릭하면 포커스가 input박스로 넘어가는 것을 확인할 수 있습니다.

 

createRef()를 이용해서도 위와 같은 기능을 구현해낼 수 있죠.

class ValidationSample extends Component {
    input = React.createRef();

    state = {
        password: '',
        clicked: false,
        validate: false
    }

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

    handleButtonClick = () => {
        this.setState({
            clicked: true,
            validate: this.state.password === '0000'
        });
        console.log(this.input.current);
        this.input.current.focus();
    }

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

 

콜백 함수 이용과 달리 this.input.current로 해당 DOM을 찾아오는 것을 확인할 수 있습니다.