---
title: useRef
---
`useRef`는 렌더링에 필요하지 않은 값을 참조할 수 있는 React Hook입니다.
```js
const ref = useRef(initialValue)
```
---
## 레퍼런스 {/*reference*/}
### `useRef(initialValue)` {/*useref*/}
컴포넌트의 최상위 레벨에서 `useRef`를 호출하여 [ref](/learn/referencing-values-with-refs)를 선언합니다.
```js
import { useRef } from 'react';
function MyComponent() {
const intervalRef = useRef(0);
const inputRef = useRef(null);
// ...
```
[아래에서 더 많은 예시를 확인하세요.](#usage)
#### 매개변수 {/*parameters*/}
* `initialValue`: ref 객체의 `current`프로퍼티 초기 설정값입니다. 여기에는 어떤 유형의 값이든 지정할 수 있습니다. 이 인자는 초기 렌더링 이후부터는 무시됩니다.
#### 반환값 {/*returns*/}
`useRef`는 단일 프로퍼티를 가진 객체를 반환합니다:
* `current`: 처음에는 전달한 `initialValue`로 설정됩니다. 나중에 다른 값으로 바꿀 수 있습니다. ref 객체를 JSX 노드의 `ref`어트리뷰트로 React에 전달하면 React는 `current`프로퍼티를 설정합니다.
다음 렌더링에서 `useRef`는 동일한 객체를 반환합니다.
#### 주의 사항 {/*caveats*/}
* `ref.current` 프로퍼티는 state와 달리 변이할 수 있습니다. 그러나 렌더링에 사용되는 객체(예: state의 일부)를 포함하는 경우 해당 객체를 변이해서는 안 됩니다.
* `ref.current` 프로퍼티를 변경해도 React는 컴포넌트를 다시 렌더링하지 않습니다. ref는 일반 JavaScript 객체이기 때문에 React는 사용자가 언제 변경했는지 알지 못합니다.
* [초기화](#avoiding-recreating-the-ref-contents)를 제외하고는 렌더링 중에 `ref.current`를 쓰거나 *읽지* 마세요. 이렇게 하면 컴포넌트의 동작을 예측할 수 없게 됩니다.
* Strict Mode에서 React는 **컴포넌트 함수를 두 번 호출하여** [의도하지 않은 변경을 찾을 수 있도록 돕습니다.](/reference/react/useState#my-initializer-or-updater-function-runs-twice) 이는 개발 환경 전용 동작이며 Production 환경에는 영향을 미치지 않습니다. 각 ref 객체는 두 번 생성되고 그중 하나는 버려집니다. 컴포넌트 함수가 순수하다면(그래야만 합니다), 컴포넌트의 로직에 영향을 미치지 않습니다.
---
## 사용법 {/*usage*/}
### ref로 값 참조하기 {/*referencing-a-value-with-a-ref*/}
컴포넌트의 최상위 레벨에서 `useRef`를 호출하여 하나 이상의 [ref](/learn/referencing-values-with-refs)를 선언합니다.
```js [[1, 4, "intervalRef"], [3, 4, "0"]]
import { useRef } from 'react';
function Stopwatch() {
const intervalRef = useRef(0);
// ...
```
`useRef`는 처음에 제공한 초기값으로 설정된 단일 `current` 프로퍼티가 있는 ref 객체를 반환합니다.
다음 렌더링에서 `useRef`는 동일한 객체를 반환합니다. 정보를 저장하고 나중에 읽을 수 있도록 `current` 속성을 변경할 수 있습니다. [state](/reference/react/useState)가 떠오를 수 있지만, 둘 사이에는 중요한 차이점이 있습니다.
**ref를 변경해도 리렌더링을 촉발하지 않습니다.** 즉 ref는 컴포넌트의 시각적 출력에 영향을 미치지 않는 정보를 저장하는 데 적합합니다. 예를 들어 [interval ID](https://developer.mozilla.org/en-US/docs/Web/API/setInterval)를 저장했다가 나중에 불러와야 하는 경우 ref에 넣을 수 있습니다. ref 내부의 값을 업데이트하려면 `current` 프로퍼티를 수동으로 변경해야 합니다:
```js [[2, 5, "intervalRef.current"]]
function handleStartClick() {
const intervalId = setInterval(() => {
// ...
}, 1000);
intervalRef.current = intervalId;
}
```
나중에 ref에서 해당 interval ID를 읽어 [해당 interval을 취소](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval)할 수 있습니다:
```js [[2, 2, "intervalRef.current"]]
function handleStopClick() {
const intervalId = intervalRef.current;
clearInterval(intervalId);
}
```
ref를 사용하면 다음을 보장합니다:
- (렌더링할 때마다 재설정되는 일반 변수와 달리) 리렌더링 사이에 **정보를 저장**할 수 있습니다.
- (리렌더링을 촉발하는 state 변수와 달리) 변경해도 **리렌더링을 촉발하지 않습니다.**
- (정보가 공유되는 외부 변수와 달리) 각각의 컴포넌트에 **로컬로 저장됩니다.**
ref를 변경해도 다시 렌더링되지 않으므로 화면에 표시되는 정보를 저장하는 데는 ref가 적합하지 않습니다. 대신 state를 사용하세요. 더 자세한 내용은 [`useRef`와 `useState` 중 선택하기](/learn/referencing-values-with-refs#differences-between-refs-and-state)에서 확인하세요.
#### counter 클릭하기 {/*click-counter*/}
이 컴포넌트는 ref를 사용하여 버튼이 클릭된 횟수를 추적합니다. 클릭 횟수는 이벤트 핸들러에서만 읽고 쓰기 때문에 여기서는 state 대신 ref를 사용해도 괜찮습니다.
```js
import { useRef } from 'react';
export default function Counter() {
let ref = useRef(0);
function handleClick() {
ref.current = ref.current + 1;
alert('You clicked ' + ref.current + ' times!');
}
return (
);
}
```
JSX에 `{ref.current}`를 표시하면 클릭 시 번호가 업데이트되지 않습니다. `ref.current`를 설정해도 리렌더링을 촉발하지 않기 때문입니다. 렌더링에 사용하는 정보는 ref가 아닌 state여야 합니다.
#### 스톱워치 {/*a-stopwatch*/}
예시에서는 state와 ref의 조합을 사용합니다. `startTime`과 `now`는 모두 렌더링에 사용되기 때문에 state 변수입니다. 그러나 버튼을 누를 때 interval을 멈출 수 있게 하기 위해선 [interval ID](https://developer.mozilla.org/en-US/docs/Web/API/setInterval)도 보유해야 합니다. interval ID는 렌더링에 사용되지 않으므로 ref에 보관하고 수동으로 업데이트하는 것이 적절합니다.
```js
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 (
<>
Time passed: {secondsPassed.toFixed(3)}
>
);
}
```
**렌더링 중에는 `ref.current`를 쓰거나 _읽지_ 마세요.**
React는 컴포넌트의 본문이 [순수 함수처럼 동작하기](/learn/keeping-components-pure)를 기대합니다:
- 입력값들([props](/learn/passing-props-to-a-component), [state](/learn/state-a-components-memory), [context](/learn/passing-data-deeply-with-context))이 동일하면 완전히 동일한 JSX를 반환해야 합니다.
- 다른 순서나 다른 인수를 사용하여 호출해도 다른 호출의 결과에 영향을 미치지 않아야 합니다.
**렌더링 중에** ref를 읽거나 쓰면 이러한 기대가 깨집니다.
```js {3-4,6-7}
function MyComponent() {
// ...
// 🚩 Don't write a ref during rendering
myRef.current = 123;
// ...
// 🚩 Don't read a ref during rendering
return
{myOtherRef.current}
;
}
```
**대신 이벤트 핸들러나 Effect에서** ref를 읽거나 쓸 수 있습니다.
```js {4-5,9-10}
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);
}
// ...
}
```
렌더링 중에 무언가를 읽거나 [써야](/reference/react/useState#storing-information-from-previous-renders)*만* 하는 경우, 대신 [state를 사용](/reference/react/useState)하세요.
컴포넌트는 이러한 규칙을 어기더라도 여전히 작동할 수도 있지만, React에 추가되는 대부분의 새로운 기능들은 이러한 기대에 의존합니다. 자세한 내용은 [컴포넌트를 순수하게 유지하기](/learn/keeping-components-pure#where-you-_can_-cause-side-effects)에서 확인하세요.
---
### ref로 DOM 조작하기 {/*manipulating-the-dom-with-a-ref*/}
ref를 사용하여 [DOM](https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API)을 조작하는 것은 특히 일반적입니다. React에는 이를 위한 기본 지원이 있습니다.
먼저 초기값이 `null`인 ref 객체를 선언하세요:
```js [[1, 4, "inputRef"], [3, 4, "null"]]
import { useRef } from 'react';
function MyComponent() {
const inputRef = useRef(null);
// ...
```
그런 다음 ref 객체를 `ref` 속성으로 조작하려는 DOM 노드의 JSX에 전달하세요:
```js [[1, 2, "inputRef"]]
// ...
return ;
```
React가 DOM 노드를 생성하고 화면에 그린 후, React는 ref 객체의 `current`프로퍼티를 DOM 노드로 설정합니다. 이제 DOM 노드 `` 접근해 [`focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus)와 같은 메서드를 호출할 수 있습니다.
```js [[2, 2, "inputRef.current"]]
function handleClick() {
inputRef.current.focus();
}
```
노드가 화면에서 제거되면 React는 `current` 프로퍼티를 다시 `null`로 설정합니다.
자세한 내용은 [ref로 DOM 조작하기](/learn/manipulating-the-dom-with-refs)에서 알아보세요.
#### 텍스트 input에 초점 맞추기 {/*focusing-a-text-input*/}
이 예시에서는 버튼을 클릭하면 입력에 초점이 맞춰집니다.
```js
import { useRef } from 'react';
export default function Form() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
>
);
}
```
#### 이미지 스크롤하기 {/*scrolling-an-image-into-view*/}
이 예시에서는 버튼을 클릭하면 이미지가 스크롤됩니다. 목록 DOM 노드에 대한 ref를 사용한 다음 DOM [`querySelectorAll`](https://developer.mozilla.org/ko/docs/Web/API/Document/querySelectorAll) API를 호출하여 스크롤하려는 이미지를 찾습니다.
```js
import { useRef } from 'react';
export default function CatFriends() {
const listRef = useRef(null);
function scrollToIndex(index) {
const listNode = listRef.current;
// 다음 코드는 특정 DOM 구조를 가정합니다:
const imgNode = listNode.querySelectorAll('li > img')[index];
imgNode.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'center'
});
}
return (
<>
>
);
}
```
```css
div {
width: 100%;
overflow: hidden;
}
nav {
text-align: center;
}
button {
margin: .25rem;
}
ul,
li {
list-style: none;
white-space: nowrap;
}
li {
display: inline;
padding: 0.5rem;
}
```
#### 비디오 재생 및 정지하기 {/*playing-and-pausing-a-video*/}
이 예시에서는 ref를 사용하여 `
---
### ref 콘텐츠 재생성 피하기 {/*avoiding-recreating-the-ref-contents*/}
React는 초기에 ref 값을 한 번 저장하고, 다음 렌더링부터는 이를 무시합니다.
```js
function Video() {
const playerRef = useRef(new VideoPlayer());
// ...
```
`new VideoPlayer()`의 결과는 초기 렌더링에만 사용되지만, 호출 자체는 이후의 모든 렌더링에서도 여전히 계속 이뤄집니다. 이는 값비싼 객체를 생성하는 경우 낭비일 수 있습니다.
이 문제를 해결하려면 대신 다음과 같이 ref를 초기화할 수 있습니다:
```js
function Video() {
const playerRef = useRef(null);
if (playerRef.current === null) {
playerRef.current = new VideoPlayer();
}
// ...
```
일반적으로 렌더링 중에 `ref.current`를 쓰거나 읽는 것은 허용되지 않습니다. 하지만 이 경우에는 결과가 항상 동일하고 초기화 중에만 조건이 실행되므로 충분히 예측할 수 있으므로 괜찮습니다.
#### `useRef`를 초기화할 때 null 검사를 피하는 방법 {/*how-to-avoid-null-checks-when-initializing-use-ref-later*/}
타입 검사기를 사용하면서 항상 `null`을 검사하고 싶지 않다면 다음과 같은 패턴을 대신 사용해 볼 수 있습니다:
```js
function Video() {
const playerRef = useRef(null);
function getPlayer() {
if (playerRef.current !== null) {
return playerRef.current;
}
const player = new VideoPlayer();
playerRef.current = player;
return player;
}
// ...
```
여기서 `playerRef` 자체는 nullable합니다. 하지만 타입 검사기에 `getPlayer()`가 `null`을 반환하는 경우가 없다는 것을 확신시킬 수 있어야 합니다. 그런 다음 이벤트 핸들러에서 `getPlayer()`를 사용하십시오.
---
## 문제 해결 {/*troubleshooting*/}
### 커스텀 컴포넌트에 대한 ref를 얻을 수 없습니다 {/*i-cant-get-a-ref-to-a-custom-component*/}
컴포넌트에 `ref`를 전달하고자 다음과 같이 하면:
```js
const inputRef = useRef(null);
return ;
```
다음과 같은 오류가 발생할 것입니다:
경고: 함수 컴포넌트에는 ref를 지정할 수 없습니다. 이 ref에 접근하려는 시도는 실패합니다. React.forwardRef()를 사용하려고 하셨나요?
기본적으로 컴포넌트는 내부의 DOM 노드에 대한 ref를 외부로 노출하지 않습니다.
이 문제를 해결하려면 ref를 가져오고자 하는 컴포넌트를 찾으세요:
```js
export default function MyInput({ value, onChange }) {
return (
);
}
```
그런 다음 아래와 같이 [`forwardRef`](/reference/react/forwardRef)로 감싸세요:
```js {3,8}
import { forwardRef } from 'react';
const MyInput = forwardRef(({ value, onChange }, ref) => {
return (
);
});
export default MyInput;
```
그러면 부모 컴포넌트가 ref를 가져올 수 있습니다.
자세한 내용은 [다른 컴포넌트의 DOM 노드에 접근하기](/learn/manipulating-the-dom-with-refs#accessing-another-components-dom-nodes)에서 확인하세요.