Daehyunii's Dev-blog

[리액트를 다루는 기술] - TIL 128 이벤트, ref 본문

✏️ 2022. TIL/December (데브코스)

[리액트를 다루는 기술] - TIL 128 이벤트, ref

Daehyunii 2022. 12. 22. 14:48
리액트의 이벤트 시스템

  리액트의 이벤트 시스템은 웹 브라우저의 HTML 이벤트와 인터페이스가 동일하기 때문에 사용법이 꽤 비슷하다. 다만 주의해야 할 사항이 몇 가지 존재 한다.

  • 이벤트 이름은 카멜 표기법으로 작성한다.
    • 예를 들어 HTML의 onclick은 리액트에서는 onClick으로 작성해야 한다.
  • 이벤트에 실행할 자바스크립트 코드를 전달하는 것이 아니라, 함수 형태의 객체 값을 전달해야 한다.
  • DOM 요소에만 이벤트를 설정할 수 있다.
    • 직접 만든 컴포넌트에 이벤트를 자체적으로 설정할 수 없기 때문에 onClick 이벤트를 전달하려고 한다면 그것은 그냥 이름이 onClick인 props를 전달해 줄 뿐이다.
input 여러 개 다루기

  setState를 여러 개 만들어 상태를 관리하는 방법도 있을 수 있고 관리해야 하는 input에 들어온 값들을 하나의 객체로 만들어 관리하는 방법도 존재할 것이다. input창이 몇 가지 없다면 각 input에 들어온 값들은 useState 하나씩 관리해도 되겠지만 input창이 여러 개라면 아무래도 하나의 객체로 만들어 관리하는 편이 가독성이 좋을 것이다. 그리고 하나의 객체로 값을 관리할 때 유용하게 사용할 수 있는 값이 바로 event.target.name이다. event.target.name은 해당 인풋의 name을 가리킨다. 

 

코드로 확인해 보자

import { useState } from "react";

const Practice = () => {
  const [form, setForm] = useState({
    username: "",
    message: "",
  });

  const { username, message } = form;

  const handleChange = (event) => {
    const nextForm = {
      ...form,
      [event.target.name]: event.target.value,
    };
    setForm(nextForm);
  };

  return (
    <div>
      <div>{username}</div>
      <div>{message}</div>
      <input name="username" onChange={handleChange} />
      <input name="message" onChange={handleChange} />
    </div>
  );
};

export default Practice;

  위의 예시는 내가 급하게 작성한 코드들이기 때문에 예시에 적합한지는 잘 모르겠지만, handleChange 함수부분을 잘 살펴 보면 아주 간단하게 여러 input창이 존재함에도 하나의 이벤트 핸들러와 하나의 state를 통해서 데이터 관리를 할 수 있다. 코드가 굉장히 간결해 지는 점을 확인할 수 있다.

 

ref : DOM에 이름 달기

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

  ref를 사용해야 하는 상황은 한 문장으로 'DOM을 꼭 직접적으로 건드려야 할 때'이다. 바닐라 자바스크립트에서 DOM 요소를 조작하는것과 유사하다. 바닐라 자바스크립트에서는 DOM을 직접 건드리는 경우가 굉장히 많았지만, 리액트에서는 굳이 DOM에 접근하지 않아도 state로 구현할 수 있는 경우가 많다. 

2. DOM을 꼭 사용해야 하는 상황은 언제일까?

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

등 많은 상황이 있고 어쩔 수 없이 DOM에 직접적으로 접근해야 한다. 이런 상황에서 ref를 사용한다.

3. ref 사용하는 방법

  ref를 사용하는 방법은 클래스형과 함수형에 따라 작성 방법에 차이가 있으나 동작 원리는 동일하다. 우선 클래스형 컴포넌트의 경우를 살펴 보자면 크게 2가지로 나눠 작성할 수 있다.

 

1. callback함수를 통해 ref 설정하는 방법

 

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

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

 

2. createRef를 통한 ref 설정

  이 방법은 리액트에 내장되어 있는 createRef 함수를 사용하는 것이다. createRef를 사용하여 ref를 만들려면 우선 컴포넌트 내부에서 멤버 변수로 React.createRef( )를 담아 주어야 한다. 그리고 해당 멤버 변수를 ref를 달고자 하는 요소에 ref props로 넣어 주면 ref 설정이 완료된다.

import { Component } from 'react';

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

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

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

export default Practice;

  참고로 ref를 설정해 준 DOM에 접근하려면 this.input.current로 조회해야 한다.

 

3. 함수형 컴포넌트 ref 달기

 

  함수형 컴포넌트에서 ref를 달기 위해서는 useRef Hook을 사용하면 된다. 사용방법은 createRef와 유사하다.

 

4. 주의사항

  위에서도 언급했지만 컴포넌트 내부에서 DOM에 직접 접근해야 할 때 ref를 사용해야 한다. 먼저 ref를 사용하지 않고도 원하는 기능을 구현할 수 있는지 사전에 고려한 후에 활용하는 습관을 들이면 좋다. 참고로 서로 다른 컴포넌트끼리 데이터를 교류할 때 ref를 사용한다면 이는 잘못된 사용 방법이다. 물론 가능은 하지만 앱 규모가 커지면 스파게티코드가 될 가능성이 높아 유지 보수가 불가능해 질지 모르기 때문이다. 리액트에서 컴포넌트끼리 데이터를 교류할 때는 언제나 데이터를 부모 -> 자식 흐름으로 교류하는 것이 좋은 설계이다.