관리 메뉴

거니의 velog

(13) ref:DOM 에 이름 달기 1 본문

React_리액트 시작

(13) ref:DOM 에 이름 달기 1

Unlimited00 2023. 11. 30. 16:52

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

* DOM 요소의 id

<div id="my-element"></div>

* 특정 DOM 요소에 어떤 작업을 해야 할 때 이렇게 요소에 id를 달면 CSS에서 특정 id에 특정 스타일을 적용하거나 자바스크립트에서 해당 id를 가진 요소를 찾아서 작업할 수 있다. 우리가 다루는 리액트 프로젝트에서 사용하는 public/index.html 파일에도 id가 root인 div 요소가 있다.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta name="description" content="Web site created using create-react-app"/>
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

* 그리고 src/index.js 파일 중에는 id가 root인 요소에 리액트 컴포넌트를 렌더링하라는 코드가 있다.

const root = ReactDOM.createRoot(document.getElementById('root'));

* 이렇게 HTML에서 id를 사용하여 DOM에 이름을 다는 것처럼 리액트 프로젝트 내부에서 DOM에 이름을 다는 방법이 있다. 바로 ref (reference 의 줄임말) 개념이다.

* 리액트 컴포넌트 안에서는 id를 사용하면 안 되나요?

- 리액트 컴포넌트 안에서도 id를 사용할 수 있다. JSX 안에서 DOM에 id를 달면 해당 DOM을 렌더링할 때 그대로 전달한다.
  하지만 특수한 경우가 아니면 사용을 권장하진 않는다. 예를 들어 같은 컴포넌트를 여러 번 사용한다고 가정해 보자.
  HTML에서 DOM의 id는 유일(unique)해야 하는데, 이런 상황에서는 중복 id를 가진 DOM이 여러 개 생기니 잘못된 사용이다.
  
  ref는 전역적으로 작동하지 않고 컴포넌트 내부에서만 작동하기 때문에 이런 문제가 생기지 않는다.
  
  대부분은 id를 사용하지 않고도 원하는 기능을 구현할 수 있지만, 다른 라이브러리나 프레임워크와 함께 id를 사용해야
  하는 상황이 발생할 수 있다. 이런 상황에서는 컴포넌트를 만들 때마다 id 뒷부분에 추가 텍스트를 붙여서
  (예:button01 button02 button03 ... ) 중복 id가 발생하는 것을 방지해야 한다.

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

* 먼저 ref는 어떤 상황에서 사용해야 하는지 제대로 짚고 넘어가 보자. 일단 특정 DOM에 작업을 해야 할 때 ref를 사용한다는 것은 이미 파악했다. 하지만 대체 '어떤' 작업을 할 때 ref를 사용해야 할까?
* 정답은 'DOM을 꼭 직접적으로 건드려야 할 때' 이다. 예를 들어 일반 순수 자바스크립트 및 jQuery로 만든 웹 사이트에서 input을 검증할 때는 다음과 같이 특정 id를 가진 input 클래스를 설정해 준다.
https://jsbin.com/?html,output

JS Bin

Sample of the bin:

jsbin.com

<!DOCTYPE html>
<html>

    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width">
        <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 type="button" onclick="validate()">Validate</button>
    </body>

</html>

* 하지만 리액트에서 이런 작업은 굳이 DOM에 접근하지 않아도 state로 구현할 수 있다. 잘 이해가 되지 않을 것이나, 앞으로 작성할 예제 코드를 확인해 보면 감이 올 것이다. 리액트 컴포넌트에서 state를 사용하여 제시한 기능을 한번 구현해 보자.
* 이 장에서는 클래스형 컴포넌트에서 ref를 사용하는 방법을 알아보자. 함수형 컴포넌트에서 ref를 사용하려면 Hooks를 사용해야 하기 때문에 이후에 Hooks를 배우면서 알아볼 것이다.
* 이번 실습은 다음 흐름으로 진행한다.


(1) 예제 컴포넌트 생성

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

[ValidationSample.css]

.success {
    background-color: lightgreen;
}

.failure {
    background-color: lightcoral;
}

[ValidationSample.js]

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;

* input에서는 onChange 이벤트가 발생하면 handleChange를 호출하여 state의 password 값을 업데이트하게 했다. button에서는 onClick 이벤트가 발생하면 handleButtonClick을 호출하여 clicked 값을 참으로 설정했고, validated 값을 검증 결과로 설정했다.
* input의 className 값은 버튼을 누르기 전에는 비어 있는 문자열을 전달하며, 버튼을 누른 후에는 검증 결과에 따라 success 값 또는 failure 값을 설정한다. 그리고 이 값에 따라 input 색상이 초록색 또는 빨간색으로 나타난다.


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

* App 컴포넌트에서 ValidationSample 컴포넌트를 불러와 렌더링해 보자. 그 과정에서, App 컴포넌트를 함수형 컴포넌트에서 클래스형 컴포넌트로 전환해 주자. 우리가 추후 App 컴포넌트에서 ref를 사용할 것이기 때문에 이렇게 미리 클래스형 컴포넌트로 작성해 줄 것이다.

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

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

export default App;

* 코드를 저장하고, 다음 결과물이 나타나는지 확인해 보자.

0000 이외의 비밀번호를 누른 경우
0000을 입력하고 검증하기 버튼을 누른 경우

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

* 앞 예제에서는 state를 사용하여 우리에게 필요한 기능을 구현했지만, 가끔 state 만으로 해결할 수 없는 기능이 있다. 어떤 상황인지 알아보자.

- 특정 input에 포커스 주기

- 스크롤 박스 조작하기

- Canvas 요소에 그림 그리기 등

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

'React_리액트 시작' 카테고리의 다른 글

(15) 컴포넌트 반복 1  (0) 2023.12.01
(14) ref:DOM 에 이름 달기 2  (0) 2023.11.30
(12) 이벤트 핸들링 2  (0) 2023.11.30
(11) 이벤트 핸들링 1  (2) 2023.11.30
(10) 컴포넌트 3  (0) 2023.11.29