React를 공부하면서, React 애플리케이션을 만들 때 DOM을 직접 조작하는 것은 지양해야한다고 배웠습니다. 하지만, 개발을 하다보면 DOM을 직접 건드려야하는 상황이 발생하기도 합니다. 이럴 때 사용할 수 있는 것이 바로 useRef 라는 Hook 함수입니다.
지금까지 React를 공부하면서 익히셨던 내용을 돌이켜보면, React만 가지고 거의 대부분의 프론트엔드 요구사항을 구현할 수 있었습니다. DOM 지식이 필요 없다고 생각하실지도 모르겠습니다. 하지만 React로 모든 개발 요구 사항을 충족할 수는 없습니다. 아래와 같이 DOM 엘리먼트의 주소값을 활용해야 하는 경우 특히 그렇습니다.
- focus
- text selection
- media playback
- 애니메이션 적용
- d3.js, greensock 등 DOM 기반 라이브러리 활용
React는 이런 예외적인 상황에서 useRef 로 DOM 노드, 엘리먼트, 그리고 React 컴포넌트 주소값을 참조할 수 있습니다. 아래 예시 코드처럼 작성하시면 주소값을 활용할 수 있습니다.
const 주소값을_담는_그릇 = useRef(참조자료형) // 이제 주소값을_담는_그릇 변수에 어떤 주소값이든 담을 수 있습니다. return ( <div> <input ref={주소값을_담는_그릇} type="text" /> {/* React에서 사용 가능한 ref라는 속성에 주소값을_담는_그릇을 값으로 할당하면*/} {/* 주소값을_담는_그릇 변수에는 input DOM 엘리먼트의 주소가 담깁니다. */} {/* 향후 다른 컴포넌트에서 input DOM 엘리먼트를 활용할 수 있습니다. */} </div>);
이 주소값은 컴포넌트가 re-render 되더라도 바뀌지 않습니다. 이 특성을 활용하여 아래의 제한된 상황에서 useRef 를 활용할 수 있습니다.
function TextInputWithFocusButton() { const inputEl = useRef(null); const onButtonClick = () => { inputEl.current.focus(); }; return ( <> <input ref={inputEl} type="text" /> <button onClick={onButtonClick}>Focus the input</button> </>); }
제시된 상황 제외한 대부분의 경우 기본 React 문법을 벗어나 useRef 를 남용하는 것은 부적절하고, React의 특징이자 장점인 선언형 프로그래밍 원칙과 배치되기 때문에, 조심해서 사용해야 합니다.
예제 1 : focus
import React, { useRef } from "react";
const Focus = () => {
const firstRef = useRef(null);
const secondRef = useRef(null);
const thirdRef = useRef(null);
//~Ref : 주소값을_담는_그릇
//null : 참조자료형
const handleInput = (event) => {
console.log(event.key, event);
if (event.key === "Enter") { /
if (event.target === firstRef.current) {
secondRef.current.focus();
event.target.value = "";
} else if (event.target === secondRef.current) {
thirdRef.current.focus();
event.target.value = "";
} else if (event.target === thirdRef.current) {
firstRef.current.focus();
event.target.value = "";
} else {
return;
}
}
};
return (
<div>
<h1>타자연습</h1>
<h3>각 단어를 바르게 입력하고 엔터를 누르세요.</h3>
<div>
<label>hello </label>
//onKeyUp : 키보드의 키를 눌렀을 때
<input ref={firstRef} onKeyUp={handleInput} />
</div>
<div>
<label>world </label>
<input ref={secondRef} onKeyUp={handleInput} />
</div>
<div>
<label>codestates </label>
<input ref={thirdRef} onKeyUp={handleInput} />
</div>
</div>
);
};
export default Focus;
예제 2 : media playback
import { useRef } from "react";
export default function App() {
const videoRef = useRef(null);
//videoRef : 주소값을 담을 그릇
const playVideo = () => {
videoRef.current.play();
console.log(videoRef.current);
};
const pauseVideo = () => {
videoRef.current.pause();
videoRef.current.remove();
};
return (
<div className="App">
<div>
<button onClick={playVideo}>Play</button>
<button onClick={pauseVideo}>Pause</button>
</div>
<video ref={videoRef} width="320" height="240" controls>
<source
type="video/mp4"
src="https://player.vimeo.com/external/544643152.sd.mp4?s=7dbf132a4774254dde51f4f9baabbd92f6941282&profile_id=165"
/>
</video>
</div>
);
}