먼저 뜻이 어디서 파생되었는지 알아보았다. reference의 준말이라고 한다. 한국말로는 참조, 참고 정도의 뜻을 지닌다고 보면된다.
일반적으로 HTML에서 DOM요소에 이름을 달때는 id라는 고유값을 사용한다. 이처럼 리액트에서도 DOM요소에 이름표를 붙이는 방법이 있는데 이를 ref라고 부른다.
Ref는 사실 일반 객체이다. Ref를 console.log로 찍어보면 {current: null}
current 프로퍼티 하나를 가진 객체가 나타나고 React는 이 객체를 통해 DOM에 직접적인 접근을 가능하게 해준다.
HTML에서 DOM요소에 이름을 달때 쓰이는 id는 유일해야 하기때문에 컴포넌트 재사용을 한다면 중복될 가능성이 농후하다. 반면에 Ref는 전역적으로 작동하지 않고 컴포넌트 내부에서만 작동한다.
React에서 state로만 해결할 수 없고, DOM을 반드시 직접 건드려야할때 사용한다.
ex) 특정 input에 포커스 주기, 스크롤 박스조작, 애니메이션을 직접적으로 실행시킬때,서드 파티 DOM 라이브러리를 React와 같이 사용할 때
위의 경우들을 비제어컴포넌트를 제어할때 라고 일축할 수 있을것 같다. React 시스템 안에서 제어하지 않고, 순수 JS를 이용해 제어하는 컴포넌트를 비제어 컴포넌트라고 한다. 말이 조금 어렵지만, React가 제어하지 않는 컴포넌트라고 생각하면 될거 같다. 비제어 컴포넌트를 순수 JS로 제어하기 위해서 DOM 요소를 담는 역할을 Ref가 하는것이다.
React 공식문서에서는 위의 경우가 아닌 선언적으로 해결될 수 있는 문제에서는 ref 사용을 지양하고 있다.
그냥 DOM요소를 반환해주면 좋을 텐데 왜 createRef/useRef는 왜 객체를 반환하고 current 프로퍼티로 DOM 요소를 전달할까?
먼저 React가 가상 돔을 기반으로 작동하는 라이브러리라는 사실을 생각해봐야 합니다.
컴포넌트가 마운트될때 React는 current 프로퍼티에 DOM 엘리먼트를 대입하고, 컴포넌트의 마운트가 해제될때 current 프로퍼티를 다시 null로 돌려놓습니다. ref를 수정하는 작업은 componentDidMount 또는 componentDidUpdate 생명주기 메서드가 호출되기 전에 이루어집니다.
실제 DOM에 React 노드가 렌더될 때가지 Ref가 가리키는 DOM 요소의 주소값은 확정된 것이 아니다.우리가 Ref에 접근할 수 있는 시점은 React노드가 실제로 DOM에 반영되는 시점부터이다.
useref는 초깃값을 따로 설정할 수 있고 null값으로 해놓으면 라이프사이클상에서는 componentDidMount 그 이전에는 null이 current 프로퍼티에 담깁니다.그리고 가상 DOM이 변경될 때 실제 DOM의 요소도 변경되는 경우가 있기 때문에 DOM이 업데이트되는 경우(componentDidUpdate)도 ref의 current 값이 변경되게 됩니다.
이처럼 유동적이기에 React는 객체를 반환해 current 프로퍼티의 값을 계속해서 수정합니다.
Ref는 특정 DOM 요소를 가져올때 더 신뢰할 만하기 때문이다. 라이플사이클에 따라 DOM 요소를 가져오지 못하는 경우가 있는데, 이런 예측하지 못한 상황으로 인해 DOM 요소를 가져오지 못한다면 로직에 따라 큰 결함으로 이어질 수 있다.
또한 컴포넌트가 하나가 아닌 여러개가 생성되는 경우 우리는 id나 class로 특정해서 원하는 DOM 요소를 가져올수 있을것인가? 의문을 던진다면 쉽지 않을것이다. DOM 요소를 특정할 수 있도록 관심 영역을 특정 컴포넌트로 제한하는 역할을 Ref가 할 수 있다.
클래스형 컴포넌트에서는 createRef를 사용할 수 있지만 함수형 컴포넌트에서는 사용하지 않는다. 정확히 말하자면 사용할 수는 있지만 함수형 컴포넌트는 상태가 바뀔때마다 새롭게 호출되기에 ref가 가리키는 DOM요소가 re-render 되는 것과 상관없이 새로는 ref 객체가 계속 만들어지게 되고 useRef는 이름에서 알 수 있듯이 hook으로 함수 호출에 관계없이 state를 유지한다. 그래서 함수형 컴포넌트를 사용할때는 useRef를 쓰자!