리액트는 기본적으로 가상 DOM(Virtual DOM)을 사용하기 때문에 DOM 조작으로 인한 브라우저 렌더링 과정 리소스를 최소화한다는 장점을 가지고 사용한다. 따라서 가능하면 리액트에서 DOM을 직접 건드리는 일은 지양해야 한다. 하지만 개발을 하다 보면 DOM을 직접 건드려야 하는 상황이 생기게 된다. 이럴 때 사용하는 개념이 ref(reference의 줄임말)이다.
우리가 일반적으로 어떤 컴포넌트에서 조작이 일어날 때 DOM을 변화시키는 경우 state를 사용한다. 함수형 컴포넌트에서는 useState를 사용할 수 있다. 하지만 state만 가지고 해결할 수 없는 경우도 존재한다. 예를 들면
- 특정 DOM 노드에 접근하여 포커스, 미디어 재생 등을 제어하거나 사이즈를 얻어올 때
- 스크롤 박스 조작하기 등 애니메이션을 직접 실행시킬 때
- Canvas 요소에 그림 그릴 때
- 서드 파티 라이브러리를 사용할 때
- 자식의 state에 부모가 접근할 때
- state로 제어하지 않는 비제어 컴포넌트를 사용할 때
이렇게 어쩔 수 없이 DOM을 직접 건드려야 할 때 ref를 사용한다. ref를 사용할 때 크게 두 가지 방법이 있는데, 첫 번째는 콜백 함수를 사용하는 것이고, 두 번째는 createRef를 사용하는 것이다.
<input ref={(ref) => {this.input=ref}} />
ref를 만드는 가장 기본적인 방법은 콜백 함수를 사용하는 것이다. ref를 달고자 하는 요소에 ref라는 콜백 함수를 props로 전달해 주면 된다. 위의 예제의 경우 this.input은 input 요소의 DOM을 가리킨다. 여기서 ref의 이름은 마음대로 지정해 줄 수 있다.
콜백 ref를 사용할 때는 ref 어트리뷰트에 React.createRef()를 통해 생성된 ref를 전달하는 대신, 함수를 전달한다. 전달된 함수는 다른 곳에 저장되고 접근될 수 있는 리액트 요소나 DOM 노드를 인자로 받는다.
인라인 함수로 콜백 ref를 선언했다면 ref 콜백은 업데이트 과정에서 한 번은 null로, 그 다음에는 DOM 요소로, 총 두 번 호출된다. 이러한 현상은 매 렌더링마다 ref 콜백의 새 인스턴스가 생성되므로 리액트가 이전에 사용된 ref를 제거하고 새 ref를 설정해야 하기 때문에 일어난다. 이 현상은 ref 콜백을 클래스에 바인딩된 메서드로 선언하여 해결할 수 있다. 많은 경우에는 이 현상은 문제가 되진 않는다.
ref를 만드는 또 다른 방법은 리액트에 내장된 createRef 함수를 이용하는 것이다. 우선 컴포넌트 내부에서 멤버 변수로 React.createRef()를 담아 주어야 한다. 그리고 해당 멤버 변수를 ref를 달고자 하는 요소에 ref props로 넣어주면 된다. 설정한 뒤 나중에 ref를 설정해 준 DOM에 접근하려면 this.input.current를 조회하면 된다. 콜백 함수를 사용할 때와 다른 점은 이렇게 뒷부분에 .current를 넣어주면 된다는 점이다.
이와 같이 컴포넌트에서 ref를 사용하면, 버튼을 클릭한 후 포커스가 다시 input 요소로 가는 것을 확인할 수 있게 된다. createRef는 따로 콜백을 만들지 않아서 코드가 간단해 진다는 장점이 있다.
DOM에 ref를 다는 것처럼, 컴포넌트에도 ref를 사용할 수가 있다. 아래의 예제에서 한 번 확인해 보도록 하자.
이 예제에서는 하단의 '맨 밑으로'라고 적힌 버튼을 누르면, ScrollBox 컴포넌트에 ref가 설정이 되어, 이 컴포넌트 내부의 scrollToButtom 메서드를 자동으로 실행한다. 이 때 onClick={this.scrollBox.scrollBottom} 으로 적으면 컴포넌트가 처음 렌더링이 될 때 this.scrollBox가 undefined 이므로 this.scrollBox.scrollToBottom 값을 가져오는 과정에서 오류가 발생한다. 따라서 화살표 함수 문법을 사용하여 새로운 함수를 만들고 그 안에서 this.scrollBox.scrollToBottom 메서드를 실행하면 버튼을 누를 때 이미 한 번 this.scrollBox를 설정한 시점이므로 오류가 발생하지 않는다.
함수형 컴포넌트에서는 useRef라는 훅을 사용한다. 사용법은 createRef과 비슷하다.
이번 포스팅에서 컴포넌트 내부에서 DOM을 직접 조작해야 할 때 ref를 사용해야 한다는 점에 대해 알아보았다. ref는 가능하면 사용하지 않는 것이 좋다. 왜냐하면 ref로 DOM을 직접 조작하지 않는 것이 리액트가 가진 장점 중 하나이기 때문이다. 그럼에도 불구하고 구현해야 하는 기능이 반드시 DOM을 조작해야 하는 경우라면, 잘 알고 잘 쓰길 바란다!
참고자료
'Web Frontend Developer' 카테고리의 다른 글
MPA와 SPA, SSR과 CSR, 그리고 SEO (0) | 2020.08.13 |
---|---|
React의 컴포넌트 라이프사이클 (0) | 2020.08.10 |
[React] 클래스형 컴포넌트 vs 함수형 컴포넌트 (2) | 2020.07.01 |
React와 TypeScript로 간단한 뉴스 어플리케이션 만들기 (feat. redux, redux-saga) (0) | 2020.06.25 |
Babel이란 무엇인가? (2) | 2020.06.22 |