[React][리액트를 다루는 기술] ref에 대해 알아보자

uddi·2023년 5월 10일
0

React

목록 보기
8/16

서론

일반 HTML에서 DOM 요소에 이름을 달 때는 id를 사용한다.
많은 사람들이 아래와 같은 구조가 익숙할 것이다.

<div id=“my-element”></div>

이렇게 특정 DOM 요소에 어떤 작업을 해야 할 때 id를 달면 CSS에서 특정 id에 스타일을 적용하거나 JS에서 해당 id를 가진 요소를 찾아 자유롭게 작업할 수 있다.

리액트에서도 HTML에서 id를 사용해 DOM에 이름을 다는 것처럼 ref(reference)를 사용하면 리액트에서도 같은 작업을 할 수 있다.

리액트 컴포넌트에서 id를 사용해도 될까?

리액트 컴포넌트에서도 id를 사용할 수는 있지만 특수한 경우가 아니면 사용을 권장하지 않는다

JSX 안에서 DOM에 id를 달면 해당 DOM을 렌더링할 때 그대로 전달되는데, 같은 컴포넌트를 여러 번 사용하는경우 중복 id를 가진 DOM이 여러 개 생기게 된다.

하지만 ref는 전역적으로 작동하지도 않고 컴포넌트 내부에서만 작동하기 때문에 이런 문제가 발생하지 않는다!

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

앞서 ref는 특정 DOM에 작업을 해야 할 때 사용한다고 얘기했다.
그럼 특정 작업이라는 것은 뭘 의미하는 걸까?

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

다음 예시를 통해 바닐라 JS 및 jQuery로 만든 웹 사이트에서 input을 검증할 때 특정 id를 가진 input에 클래스를 설정해주는 코드를 살펴보자

<script>
  function validate() {
    let input = document.getElementById(‘password’);
    input.className = ‘’;
    if(input.value ===0000) {
      input.className = ‘success’;
    } else {
      input.className = ‘failure’;
    }
  }
</script>

<body>
  <input type=‘password’ id=‘password’></input>
  <button onclick=‘validate()>Validate</button>
</body>

그러나 리액트에서는 state로 구현할 수 있다.

클래스형 컴포넌트에서 ref를 사용하는 방법에 대해 알아보자
함수 컴포넌트에서 사용하려면 Hooks를 사용해야 하므로 추후 Hooks를 배우면서 알아볼 것이다

클래스형 컴포넌트에서 ref 사용하기

첫번째 예제

  1. 예제 컴포넌트를 생성하자
class ValidationSample extends Component {
  state = {
    password: ‘’,
    clicked: false,
    validated: false
  }

  handleChange = (e) => {
    this.setState({
      [assword: 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 onClck={this.handleButtonClick}>검증하기</button>
      </div>
    );
  }
}

컴포넌트를 생성했으면 App 컴포넌트에서 해당 컴포넌트를 렌더링 시켜보자

DOM을 꼭 사용해야 하는 상황들

위 예제에서는 state를 사용해서 필요한 기능을 모두 구현했다. 하지만 가끔 state만으로는 해결할 수 없는 기능이 존재한다.

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

이처럼 몇 가지 상황에서는 DOM에 직접적으로 접근해야 하는데, 이때 사용하는 것이 ref 다

  1. ref를 사용해보자
    ref를 사용하는 방법은 두가지인데 하나씩 알아보도록 하자

콜백 함수를 통한 ref 설정

이 방법은 ref를 만드는 가장 기본적인 방법이다.

사용법은 ref를 달고자 하는 요소에 ref라는 콜백 함수를 props로 전달해 주면 된다.

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

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

이렇게 하면 this.input은 input 요소의 DOM을 가리킨다.
참고로 this.input에서 input 자리는 원하는 이름으로 지정할 수 있다

createRef를 통한 ref 설정

두번째 방법은 리액트에 내장되어 있는 createRef 함수를 사용하는 것이다.

이 함수를 사용해서 만들면 더 적은 코드로 쉽게 사용할 수 있다

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

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

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

이처럼 우선 컴포넌트 내부에서 멤버 변수로 React.createRef()를 담아 준 뒤 사용해야 한다.

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

ref를 설정해 준 DOM에 접근하려면 this.input.current를 조회하면 되는데, 콜백 함수를 사용할 때와 다른 점은 뒷부분에 .current를 넣어 줘야 한다는 것이다.

사용방법이 두 가지인 만큼 사용하기 편한 방법으로 사용하면 된다.

  1. 그럼 아까 생성한 컴포넌트에서 버튼을 한 번 누르면 포커스가 input 쪽으로 이동하도록 코드를 수정해보자
    input 태그에도 ref를 달아줘야 한다는 것을 잊지 말자
handleButtonClick = () => {
    this.setState({
      clicked: true,
      validated: this.state.password ===0000});

    this.input.focus();
  }

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

두번째 예제

ref를 이용하여 컴포넌트 내부 메서드를 호출해보자
리액트에서는 컴포넌트에도 ref를 달 수 있다.

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

컴포넌트에 ref 달기

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

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

이렇게 하면 MyComponent 내부의 메서드 및 멤버 변수에도 접근할 수 있다.
👉 내부의 ref에도 접근 가능하다 ex) myComponent.handleClick, myComponent.input 등..

JS로 스크롤바 내릴 때 사용하면 좋은 DOM 노드 값

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

    스크롤바를 맨 아래로 내리려면 scrollHeight - clientHeight

정리

  • 컴포넌트 내부에서 DOM에 직접 접근해야 할 때 ref 사용
  • 서로 다른 컴포넌트끼리 데이터를 교류할 때 ref를 사용하는 것은 잘못된 것! 👉리덕스 혹은 Context Api 사용
  • 컴포넌트끼리 데이터를 교류할 때는 언제나 부모 <-> 자식 흐름으로 교류해야 함
    함수형 컴포넌트에서는 useRef 사용
profile
거북이는 느리지만 결국 결승선을 통과한다

0개의 댓글