
useRef는 렌더링에 필요하지 않은 값을 참조할 수 있게 해주는 React Hook입니다.
const ref = useRef(initialValue)
ref 를 선언하려면 컴포넌트의 최상위 수준에서 useRef를 호출하세요.
import { useRef } from 'react';
function MyComponent() {
const intervalRef = useRef(0);
const inputRef = useRef(null);
// ...
initialValue: ref 객체의 current 속성을 초기에 지정하려는 값입니다. 모든 타입의 값이 될 수 있습니다. 이 argument는 초기 렌더링 후에는 무시됩니다.useRef는 단일 속성을 가진 객체를 반환합니다.
current: 처음에는 전달한 initialValue 값으로 설정됩니다. 나중에 다른 것으로 설정할 수 있습니다. ref 객체를 JSX 노드의 ref 속성으로 React에 전달하면 React는 current 속성을 설정합니다.다음 렌더링에서는 useRef가 동일한 객체를 반환합니다.
ref.current 속성을 변경할 수 있습니다. 상태와 달리 mutable 합니다. 그러나 렌더링에 사용되는 객체(예: 상태의 일부)를 보유하는 경우 해당 객체를 변경하면 안 됩니다.ref.current 속성을 변경하면 React는 컴포넌트를 리-렌더링하지 않습니다. ref는 일반 JavaScript 객체이기 때문에 React는 언제 변경되는지 인식하지 못합니다.ref.current를 쓰거나 읽지 마십시오. 이로 인해 구성 요소의 동작을 예측할 수 없게 됩니다.하나 이상의 ref를 선언하려면 컴포넌트의 최상위 수준에서 useRef를 호출하세요.
import { useRef } from 'react';
function Stopwatch() {
const intervalRef = useRef(0);
// ...
useRef는 처음에 사용자가 제공한 초기 값으로 설정된 단일 current 속성을 사용하여 ref 객체를 반환합니다.
다음 렌더링에서는 useRef가 동일한 객체를 반환합니다. current 속성을 변경하여 정보를 저장하고 나중에 읽을 수 있습니다. 이는 state 를 생각나게 할 수 있지만 중요한 차이점이 있습니다.
ref를 변경해도 다시 렌더링이 실행되지 않습니다. 이는 refs가 컴포넌트의 시각적 출력에 영향을 주지 않는 정보를 저장하는 데 적합하다는 것을 의미합니다. 예를 들어 interval ID를 저장하고 나중에 검색해야 하는 경우 이를 참조에 넣을 수 있습니다. ref 내부의 값을 업데이트하려면 current 속성을 수동으로 변경해야 합니다.
function handleStartClick() {
const intervalId = setInterval(() => {
// ...
}, 1000);
intervalRef.current = intervalId;
}
나중에 해당 interval 지우기를 호출할 수 있도록 ref에서 해당 interval ID를 읽을 수 있습니다.
function handleStopClick() {
const intervalId = intervalRef.current;
clearInterval(intervalId);
}
ref를 사용하면 다음을 보장할 수 있습니다.
refs를 변경해도 다시 렌더링이 실행되지 않으므로 refs는 화면에 표시하려는 정보를 저장하는 데 적합하지 않습니다. 대신에 state를 사용하세요. useRef와 useState 중에서 선택하는 방법에 대해 자세히 알아보세요.
아래 예에서는 state와 refs의 조합을 사용합니다. startTime과 now는 모두 렌더링에 사용되므로 상태 변수입니다. 그러나 버튼을 누를 때 간격을 중지할 수 있도록 interval ID도 보유해야 합니다. interval ID는 렌더링에 사용되지 않으므로 ref에 보관하고 수동으로 업데이트하는 것이 적절합니다.
import { useState, useRef } from 'react';
export default function Stopwatch() {
const [startTime, setStartTime] = useState(null);
const [now, setNow] = useState(null);
const intervalRef = useRef(null);
function handleStart() {
setStartTime(Date.now());
setNow(Date.now());
clearInterval(intervalRef.current);
intervalRef.current = setInterval(() => {
setNow(Date.now());
}, 10);
}
function handleStop() {
clearInterval(intervalRef.current);
}
let secondsPassed = 0;
if (startTime != null && now != null) {
secondsPassed = (now - startTime) / 1000;
}
return (
<>
<h1>Time passed: {secondsPassed.toFixed(3)}</h1>
<button onClick={handleStart}>
Start
</button>
<button onClick={handleStop}>
Stop
</button>
</>
);
}

❗️ 렌더링 중에는 ref.current를 쓰거나 읽지 마십시오.
React는 컴포넌트의 본문이 순수한 함수처럼 동작할 것으로 기대합니다.
렌더링 중에 ref를 읽거나 쓰면 이러한 기대가 깨집니다.
function MyComponent() {
// ...
// 🚩 Don't write a ref during rendering
myRef.current = 123;
// ...
// 🚩 Don't read a ref during rendering
return <h1>{myOtherRef.current}</h1>;
}
대신 이벤트 핸들러나 effects에서 refs를 읽거나 쓸 수 있습니다.
function MyComponent() {
// ...
useEffect(() => {
// ✅ You can read or write refs in effects
myRef.current = 123;
});
// ...
function handleClick() {
// ✅ You can read or write refs in event handlers
doSomething(myOtherRef.current);
}
// ...
}
렌더링 중에 무언가를 읽거나 써야 한다면 대신 상태를 사용하세요.
이러한 규칙을 어기면 컴포넌트가 계속 작동할 수 있지만 React에 추가하는 대부분의 최신 기능은 이러한 기대에 의존합니다. 컴포넌트를 순수하게 유지하는 방법에 대해 자세히 알아보세요.
DOM을 조작하기 위해 ref를 사용하는 것이 특히 일반적입니다. React에는 이를 위한 기본 지원이 있습니다.
먼저 초기 값이 null인 ref 객체를 선언합니다.
import { useRef } from 'react';
function MyComponent() {
const inputRef = useRef(null);
// ...
그런 다음 ref 객체를 조작하려는 DOM 노드의 JSX에 ref 속성으로 전달합니다.
// ...
return <input ref={inputRef} />;
React가 DOM 노드를 생성하여 화면에 표시한 후, React는 ref 객체의 current 속성을 해당 DOM 노드로 설정합니다. 이제 <input>의 DOM 노드에 액세스하고 focus()와 같은 메서드를 호출할 수 있습니다.
function handleClick() {
inputRef.current.focus();
}
React는 노드가 화면에서 제거되면 현재 속성을 다시 null로 설정합니다. refs을 사용하여 DOM을 조작하는 방법에 대해 자세히 알아보세요.
'Focus the input' 버튼을 클릭하면 input이 focus 되는 간단한 예시네요.
import { useRef } from 'react';
export default function Form() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
<input ref={inputRef} />
<button onClick={handleClick}>
Focus the input
</button>
</>
);
}

이 예에서는 버튼을 클릭하면 이미지가 스크롤되어 표시됩니다. list DOM 노드에 대한 ref를 사용한 다음 DOM querySelectorAll API를 호출하여 스크롤하려는 이미지를 찾습니다.
import { useRef } from 'react';
export default function CatFriends() {
const listRef = useRef(null);
function scrollToIndex(index) {
const listNode = listRef.current;
// This line assumes a particular DOM structure:
const imgNode = listNode.querySelectorAll('li > img')[index];
imgNode.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'center'
});
}
return (
<>
<nav>
<button onClick={() => scrollToIndex(0)}>
Tom
</button>
<button onClick={() => scrollToIndex(1)}>
Maru
</button>
<button onClick={() => scrollToIndex(2)}>
Jellylorum
</button>
</nav>
<div>
<ul ref={listRef}>
<li>
<img
src="https://placekitten.com/g/200/200"
alt="Tom"
/>
</li>
<li>
<img
src="https://placekitten.com/g/300/200"
alt="Maru"
/>
</li>
<li>
<img
src="https://placekitten.com/g/250/200"
alt="Jellylorum"
/>
</li>
</ul>
</div>
</>
);
}

이 예제에서는 ref를 사용하여 <video> DOM 노드에서 play() 및 Pause()를 호출합니다.
import { useState, useRef } from 'react';
export default function VideoPlayer() {
const [isPlaying, setIsPlaying] = useState(false);
const ref = useRef(null);
function handleClick() {
const nextIsPlaying = !isPlaying;
setIsPlaying(nextIsPlaying);
if (nextIsPlaying) {
ref.current.play();
} else {
ref.current.pause();
}
}
return (
<>
<button onClick={handleClick}>
{isPlaying ? 'Pause' : 'Play'}
</button>
<video
width="250"
ref={ref}
onPlay={() => setIsPlaying(true)}
onPause={() => setIsPlaying(false)}
>
<source
src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
type="video/mp4"
/>
</video>
</>
);
}
때로는 상위 구성 요소가 구성 요소 내부의 DOM을 조작하도록 할 수도 있습니다. 예를 들어 MyInput 구성 요소를 작성 중이지만 부모가 input(부모가 액세스할 수 없음)에 집중할 수 있기를 원할 수 있습니다. useRef의 조합을 사용하여 input을 보관하고 forwardRef을 사용해 상위 구성 요소에 노출할 수 있습니다. 여기에서 자세한 연습을 읽어보세요.
import { forwardRef, useRef } from 'react';
const MyInput = forwardRef((props, ref) => {
return <input {...props} ref={ref} />;
});
export default function Form() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
<MyInput ref={inputRef} />
<button onClick={handleClick}>
Focus the input
</button>
</>
);
}
React는 초기 참조 값을 한 번 저장하고 다음 렌더링에서는 이를 무시합니다.
function Video() {
const playerRef = useRef(new VideoPlayer());
// ...
new VideoPlayer()의 결과는 초기 렌더링에만 사용되지만 여전히 모든 렌더링에서 이 함수를 호출합니다. 값비싼 객체를 생성한다면 이는 낭비일 수 있습니다.
이 문제를 해결하려면 대신 다음과 같이 ref를 초기화할 수 있습니다. (오홋...)
function Video() {
const playerRef = useRef(null);
if (playerRef.current === null) {
playerRef.current = new VideoPlayer();
}
// ..
일반적으로 렌더링 중에는 ref.current를 쓰거나 읽는 것이 허용되지 않습니다. 하지만 이 경우에는 결과가 항상 동일하고 조건이 초기화 중에만 실행되므로 완전히 예측 가능하기 때문에 괜찮습니다.
다음과 같이 자신의 컴포넌트에 ref를 전달하려고 하면:
const inputRef = useRef(null);
return <MyInput ref={inputRef} />;
이러한 에러를 볼 수 있을 겁니다.

기본적으로 자신의 구성 요소는 내부 DOM 노드에 refs를 노출하지 않습니다. 이 문제를 해결하려면 ref를 가져오려는 구성요소를 찾으세요.
export default function MyInput({ value, onChange }) {
return (
<input
value={value}
onChange={onChange}
/>
);
}
그리고 나서 이 컴포넌트를 forwardRef로 감싸세요.
import { forwardRef } from 'react';
const MyInput = forwardRef(({ value, onChange }, ref) => {
return (
<input
value={value}
onChange={onChange}
ref={ref}
/>
);
});
export default MyInput;
그런 다음 상위 구성 요소는 해당 구성 요소에 대한 참조를 얻을 수 있습니다. 다른 구성 요소의 DOM 노드에 액세스하는 방법에 대해 자세히 알아보세요.
useRef에 대해 한번 리마인딩하는 시간이 되었네요. "useRef는 렌더링에 필요하지 않은 값을 참조할 수 있게 해주는 React Hook입니다." 그리고 그 쓰임새에 따라 두가지 경우가 있죠.
사용시 주의할 점으로는 forwardRef 를 사용하는 경우가 있겠네요.