상세 컨텐츠

본문 제목

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

21-22/21-22 리액트 스타터 -1

by Kimpeep 2021. 11. 22. 13:00

본문

728x90

일반적으로 HTML에서 DOM에 이름을 달 때는 id를 사용합니다.

id를 달면, 1. CSS에서 특정 id에 스타일 적용하기 2. 자바스크립트에서 특정 id를 작업하기. 두 가지가 모두 가능합니다.

리액트 프로젝트 내부에서 이와 같은 행동을 하기 위해서는, reference의 줄임말 ref를 사용합니다.

5.1 ref는 어떤 상황에서 사용해야 할까?

'DOM을 꼭 직접 건드려야 할 때' 사용합니다.

.success {
    background-color: lightgreen;
}

.failure {
    background-color: lightcoral;
}

ValidationSample.css

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

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

    handleChange = (e) => { //onChange 이벤트가 발생하면
        this.setState({ //state의 password 값 업데이트
            password: e.target.value
        });
    }

    handleButtonClick = () => { //Button에서 onClick 이벤트가 발생하면
        this.setState({ //검증 결과에 따라 success / failure 설정
            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;

ValidationSample.js

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

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

export default App;

App.js //App 컴포넌트를 함수형에서 클래스형 컴포넌트로 전환해 주었습니다. 이후 App 컴포넌트에서 ref를 사용하기 위함입니다.

실행되었을 때의 초기 화면
success / failure 화면

위 예시는 state를 사용하여 기능을 구현하였지만, state로 해결할 수 없는 기능이 있기도 합니다.

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

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

 

5.2 ref 사용

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

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

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

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

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

위와 같이 코드를 작성하면 앞으로 this.input input 요소의 DOM을 가리킵니다. ref 이름은 자유롭게 지정할 수 있습니다.

2. createRef를 통한 ref 설정

import React, { Component } from 'react';

class RefSample extends Component {
	input = React.createRef(); //멤버 변수로 React.createRef를 담은 뒤, ref props에 요소로 넣기
	
	handleFocus = () => {
		this.input.current.focus(); //ref를 설정해 준 DOM에 접근
	} 

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

export default RefSample;

createRef 사용 예시

 

createRef를 사용하여 ref를 만드려면, 컴포넌트 내부에서 멤버 변수로 React.createRef()를 가져야 합니다. 그리고 해당 변수를 ref를 달고자 하는 요소에 ref props로 넣어야 ref 설정이 완료됩니다.

콜백 함수와 다른 점은 handleFoucus에서 보이는 .current를 넣어 주어야 한다는 것입니다. createRef에서 DOM에 접근하기 위해서는 this.input.current를 조회해야 합니다.

3. 적용

콜백 함수를 이용하여 앞서 작성한 ValidationSample 컴포넌트에 ref를 달아 보겠습니다.

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

ValidationSample.js에서 input 요소를 위와 같이 변경합니다.

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

ValidationSample.js에서 버튼 onClick 이벤트 코드를 위와 같이 수정합니다.

검증하기를 누른 화면

검증하기를 누르면 포커스가 input으로 넘어가는 것을 확인할 수 있습니다.

 

5.3 컴포넌트에 ref 달기

1. 사용법

리액트는 컴포넌트에도 ref를 달 수 있는데, 이는 컴포넌트 내부의 DOM을 컴포넌트 외부에서 사용할 때 이용합니다.

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

DOM에 ref를 다는 방법과 동일하며, ref를 단 후에는 MyComponent의 메서드와 멤버 변수, 즉 내부의 ref에도 접근할 수 있게 됩니다.

 

2. 컴포넌트 초기 설정

class ScrollBox extends Component {
    render() {
        const style = {
            border: '1px solid black',
            height: '300px',
            width: '300px',
            overflow: 'auto',
            position: 'relative'
        };

        const innerStyle = {
            width: '100%',
            height: '650px',
            background: 'linear-gradient(white, black)'
        }

        return (
            <div 
                style={style}
                ref={(ref) => {this.box=ref}}>
                <div style={innerStyle}/>
            </div>
        );
    }
}

export default ScrollBox;

ScrollBox.js

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

class App extends Component {
  render() {
    return (
      <div>
        <ScrollBox/>
      </div>
    );
  }
}

export default App;

App.js

실행 화면

3. 컴포넌트에 메서드 생성

자바스크립트에서 스크롤 바를 내릴 때에는, DOM 노드가 가진 다음 값들을 사용합니다.

  • scrollTop : 세로 스크롤바 위치(0~350)
  • scrollHeight : 스크롤이 있는 박스 안의 div 높이(650)
  • clientHeight : 스크롤이 있는 박스의 높이(300)

즉, 스크롤바를 가장 아래로 내리려면 scrollHeight - clientHeight를 하면 됩니다.

import React, { Component } from 'react';

class ScrollBox extends Component {

    scrollToBottom = () => {
        const { scrollHeight, clientHeight } = this.box;
        /* 
        앞 코드에서는 비구조화 할당 문법을 사용했으며, 아래 코드와 같은 의미입니다.
            const scrollHeight = this.box.scrollHeight;
            const clientHeight = this.box.clientHeight;
        */
       this.box.scrollTop = scrollHeight - clientHeight;
    }
    render() {
        const style = {
            border: '1px solid black',
            height: '300px',
            width: '300px',
            overflow: 'auto',
            position: 'relative'
        };

        const innerStyle = {
            width: '100%',
            height: '650px',
            background: 'linear-gradient(white, black)'
        }

        return (
            <div 
                style={style}
                ref={(ref) => {this.box=ref}}>
                <div style={innerStyle}/>
            </div>
        );
    }
}

export default ScrollBox;

ScrollBox.js에 scrollToBottom 메서드 생성

4. 컴포넌트에 ref 달고 내부 메서드 사용

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

class App extends Component {
  render() {
    return (
      <div>
        <ScrollBox ref={(ref)=>this.scrollBox=ref}/>
        <button onClick={()=>this.scrollBox.scrollToBottom()}>
          맨 밑으로 
        </button>
      </div>
    );
  }
}

export default App;

App.js 수정 -> ScrollBox 컴포넌트의 scrollToBottom 메서드 실행

버튼 누르기 전 / 버튼 누른 후

이때 조심해야 할 점이 있습니다.

onClick = {this.scrollBox.scrollBottom} 과 같은 형식으로 작성해도 틀린 것은 아닌데, 컴포넌트가 처음 렌더링될 때는 this.scrollBox의 값이 undefined이므로 this.scrollBox.scrollBottom의 값을 읽어올 때 오류가 발생할 수 있습니다.

화살표 함수 문법을 사용하여 새로운 함수를 만들고, 그 내부에서 this.scrollBox.scrollBottom 메서드를 실행하면, 버튼을 누를 때 this.scrollBox.scrollBottom의 값을 읽어 와서 실행하므로 오류가 발생하지 않습니다.

 

Quiz

1. 리액트에서 DOM에 이름을 달기 위해 id 대신 사용하는 것은 000이다.

2. ref를 사용하는 방법에는 00 00와 000000000, 두 가지가 있다. 

3. 특정 컴포넌트 내부의 메서드 및 멤버 변수에 접근할 때, Example = {this.example.Exam}과 같은 방식으로 작성하면 오류가 발생한다. 이 오류를 발생하지 않게 하려면 000 00 00을 사용하면 된다.

 

 

 

Corner React Starter #1

Editor PEEP

728x90

관련글 더보기