Front-End

ref : DOM에 이름 달기 본문

리액트

ref : DOM에 이름 달기

jeongsso 2023. 2. 23. 13:12

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

// DOM 요소의 id
<div id = "my-element"></div>

특정 DOM 요소에 어떤 작업을 해야 할 때 요소에 id를 달면

CSS에서 특정 id에 특정 스타일을 적용하거나

자바스크립트에서 해당 id를 가진 요소를 찾아서 작업할 수 있다.

 

 

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

리액트 프로젝트 내부에서 DOM에 이름을 다는 방법이 있다.

ref(reference의 줄임말) 개념입니다.

 

 

리액트 안에서도 id를 사용할 수 있지만, 특수한 경우가 아니면 권장하지 않습니다.

ref는 전역으로 작동하지 않고 컴포넌트 내부에서만 작동해서 중복 id 값처럼 오류가 일어나지 않습니다.

 

 

 

 

-    1.  ref는 어떤 상황에서 사용해야 할까?    -

특정 DOM에 작업을 할 때 ref를 사용한다는 건 알았다.

어떤 작업일까!!

 

 

정답은

'DOM을 꼭 직접적으로 건드려야 할 때'

 

 

예를 들어 일반 순수 자바스크립트 및  jQuery로 만든 웹 사이트에서 input을 검증할 때는

다음과 같이 특정 id를 가진 input에 클래스를 설정해 준다.

<html>
  <head>
    <meta charest = "utf-8">
    <meta name = "viewport" content = "width = device-width">
    <title> Example </title>
    <style>
      .success {
        background-color : green;
      }
      
      .failure {
        background-color : red;
      }
    </style>
    <scipt>
      function validate() {
        var input = document.getElementById('password');
        input.className = '';
        if(input.value === '0000') {
          input.className = 'success';
        } else {
          input.className = 'failur';
        }
      }
    </scipt>
  </head>
  <body>
    <input type = "password" id = "password"></input>
    <button onclick = "validate()"> Validate </button>
  </body>
</html>

하지만 리액트에서 굳이 DOM에 접근하지 않아도 state로 구현할 수 있습니다.

 

리액트 컴포넌트에서 state를 사용하여 제시한 기능을 한번 알아보겠습니다.

 

 

 

=   (1)  예제 컴포넌트 생성   =

src 디렉터리 안에 ValidationSample.css와 ValidationSample.js 파일을 만들어 보자.

// 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, 
    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;

input에서는 onchange 이벤트가 발생하면

handleChange를 호출하여 state의 password 값을 업데이트하게 했습니다.

button에서는 onClick 이벤트가 발생하면 handleButtonClick을 호출하여 Clicked 값을 참으로 설정했고,

validated 값을 검증 결과로 설정했습니다.

 

input의 className 값은 버튼을 누르기 전에는 비어 있는 문자열을 전달하며,

버튼을 누른 후에는 검증 결과에 따라 success 값 또는 failure 값을 설정합니다.

그리고 이 값에 따라 input 색상이 초록색 또는 빨간색으로 나타납니다.

 

 

 

 

=   (2)  App 컴포넌트에서 예제 컴포넌트 렌더링   =

App컴포넌트에서 ValidationSample 컴포넌트를 불러와 렌더링해 보겠습니다.

그 과정에서 App컴포넌트를 함수 컴포넌트에서 클래스형 컴포넌트로 전환해 보자.

우리가 추후 App 컴포넌트에서 ref를 사용할 것이기 때문에 이렇게 미리 클래스형 컴포넌트로 작성해 주겠습니다.

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

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

export default App;

 

 

 

=   (3)  DOM을 꼭 사용해야 하는 상황   =

앞 예제에서는 state를 사용하여 우리에게 필요한 기능을 구현했지만, 

가끔 state만으로 해결할 수 없는 기능이 있습니다. 

어떤 상황인지 알아봅시다!!

 

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

 

이때는 어쩔 수 없이 DOM에 직접적으로 접근해야 하는데, 이를 위해 바로 ref를 사용합니다.

 

 

 

 

 

 


-    2.  ref 사용    -

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

 

 

=   (1)  콜백 함수를 통한 ref 설정   =

ref를 만드는 가장 기본적인 방법콜백 함수를 사용하는 것입니다.

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

콜백 함수는 ref 값을 파라미터로 전달받습니다.

그리고 함수 내부에서 파라미터로 받은 ref를 컴포넌트의 멤버 변수로 설정해 줍니다.

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

이렇게 하면 앞으로 this.input은 input 요소의 DOM을 가리킵니다.

ref의 이름은 원하는 것으로 자유롭게 지정할 수 있습니다.

DOM 타입과 관계없이 this.superman = ref처럼 마음대로 지정합니다.

 

 

 

 

 

=   (2)  createRef를 통한 ref 설정   =

ref을 만드는 또 다른 방법은 리액트에 내장되어 있는 createRef라는 함수를 사용하는 것입니다.
이 함수를 사용해서 만들면 더 적은 코드로 쉽게 사용할 수 있습니다. 

import { 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;

createrRef를 사용하여 ref를 만들려면

우선 컴포넌트 내부에서 멤버 변수로 React.createRef()을 남아 주어야 합니다.

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

 

 

설정한 뒤 나중에 ref를 설정해 준 DOM에 접근하려면 this.input.current를 조회하면 됩니다.
콜백 함수를 사용할 때와 다른 집은 이렇게 뒷부분에 .current를 넣어 주어야 한다는 것입니다.
지금까지 콜백 함수 혹은 createRef를 사용하여 ref를 만드는 방법을 배워 보았습니다.

 

앞으로 두 가지 방법 중에서 편한 방법을 사용하세요.
이번 장에서는 주로 콜백 함수를 사용하는 방식으로 ref를 다루어 보겠습니다.

 

 

 

 


-    3.  컴포넌트에 ref 달기    -

리액트에서는 컴포넌트에도 ref를 달 수 있습니다.

이 방법은 주로 컴포넌트 내부에 있는 DOM을 컴포넌트 외부에서 사용할 때 씁니다

컴포넌트에 ref를 다는 방법은 DOM에 ref를 다는 방법과 똑같습니다.

 

 

=   (1)  사용법   =

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

 

•  이렇게 하면 JeongSeo 내부의 메서드 및 멤버 변수에도 접근할 수 있습니다.

즉, 내부의 ref에도 접근할 수 있습니다(예: JeongSeo.handleclick, JeongSeo.input 등)


•  이번에는 스크를 박스가 있는 컴포넌트를 하나 만들고, 

스크롤바를 아래로 내리는 작업을 부모 컴포넌트에서 실행해 보겠습니다.

 

 

 

=   (2)  컴포넌트 초기 설정   =

먼저 ScrollBox 라는 컴포넌트 파일을 만들어봅시다.

JSX의 인라인 스타일링 문법으로 스크롤 박스를 만들어주세요.

그다음에는 최상위 DOM에 ref를 달아 주세요.

 

 

=   (2 - 1)  컴포넌트 파일 생성   =

// ScrollBox.js
import { Component } from 'react';

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;

 

 

 

=   (2 - 2)  App 컴포넌트에서 스크롤 박스 컴포넌트 렌더링   =

기존 ValidationSample을 지우고, 방금 만든 SerollBox 컴포넌트를 렌더링해 주세요.

// App.js
import { Component } from 'react';
import ScrollBox from './ScrollBox';

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

export default App;

 

 

 

 

=   (3)  컴포넌트에 메서드 생성   =

컴포넌트에 스크롤바를 맨 아래쪽으로 내리는 메서드를 만들겠습니다.

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

 

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

 

스크롤바를 맨 아래쪽으로 내리려면 scrolIHeight에서 clientheight를 빼면 되겠지요?

//ScrollBox.js
import { Component } from 'react';

class ScrollBox extends Component {
  scrollToBottom = () => {
    const { scrollHeight, clientHeight } = this.box;
    /* 앞 코드에는 비구조화 할당 문법을 사용했습니다.
     다음 코드와 같은 의미입니다.
     const scrollheight = this.box.scrollHeight;
     const clientHeight = this.box.cliengHeight;
    */
    this.box.scrollTop = scrollHeight - clientHeight;
  }
  
  render() {
    ( ... )
}

export default ScrollBox;

 

scrollToBottom 메서드의 첫 번째 줄에서는 ES6의 비구조화 할당 문법을 사용했습니다.

 

이렇게 만든 메서드는 부모 컴포트인 App 컴포넌트에서 ScrollBox에 ref를 달면 사용할 수 있습니다.

 

 

 

 

 

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

그럼 APP컴포트에서 ScrollBox에 ref를 달고 버튼을 만들어 누르면,

ScrollBox 컴포넌트의 scrollToBottom 메서드를 실행하도록 코드를 작성하겠습니다.

// App.js
import { 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;

여기서 주의할 점이 하나 있습니다.

 

문법상으로는 onclick = {this. scrollBox.scrollToBottom) 같은 형식으로 작성해도 틀린 것은 아닙니다.

하지만 컴포넌트가 처음 렌더링될 때는 this.ScrollBox 값이 undefined이므로

this.scrollBox.ScrollToBottom 값을 읽어 오는 과정에서 오류가 발생합니다.

 

 

화살표 함수 문법을 사용하여 아예 새로운 함수를 만들고

그 내부에서 this.ScrollBox.scrollToBottom 메서드를 실행하면,

버튼을 누를 때(이미 한 번 렌더링을 해서 this.ScrollBox를 설정한 시점)

this.ScrollBox.scrollToBottom 값을 읽어 와서 실행하므로 오류가 발생하지 않습니다.

 

 

마무리.

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

먼저 ref를 사용하지 않고도 원하는 기능을 구현할 수 있는지 고려한 후 사용하는 것이 좋다.

 

주의할 점은 컴포넌트끼리 데이터를 교류할 때는 언제나 데이터를 부모 ↔ 자식 흐름으로 교류해야 한다.

함수에서는 useRef라는 Hook 함수를 이용하는데 createRef와 유사하니 추후 알아보자!

 

 

 

 

 

 

'리액트' 카테고리의 다른 글

컴포넌트의 라이프사이클 메서드  (0) 2023.02.28
컴포넌트 반복  (0) 2023.02.27
이벤트 핸들링  (0) 2023.02.20
컴포넌트  (0) 2023.02.18
리액트의 첫 시작 / JSX  (3) 2023.02.17
Comments