이번 학습 단계의 핵심 두 가지
- 화면을 다시 그릴 필요가 없는 값을 기억할 때 useRef를 쓸 수 있다.
- 특정 DOM 요소에 직접 접근할 때도 useRef를 쓴다.
Step 0. useRef
먼저 State와 비교해보자.
const [count, setCount] = useState(0);
State는 값이 바뀌면
State 변경
↓
컴포넌트 다시 렌더링
↓
화면 업데이트
가 된다.
반면 useRef는 값이 바뀌어도 렌더링을 발생시키지 않는다.
const countRef = useRef(0);
그리고 값은
countRef.current
에 들어 있다.
Step 1. 단순한 useRef
import { useRef } from "react";
function App() {
const numberRef = useRef(0);
function handleClick() {
numberRef.current = numberRef.current + 1;
console.log(numberRef.current);
}
return <button onClick={handleClick}>클릭</button>;
}
export default App;
버튼을 누르면
Console에서는
1 → 2 → 3 → 4
처럼 값이 변한다.
하지만 화면은 바뀌지 않는다.
왜냐하면
numberRef.current = numberRef.current + 1;
은 State 변경이 아니기 때문이다.
State와 Ref의 차이를 보면
State
→ 값을 기억
→ 변경되면 다시 렌더링
Ref
→ 값을 기억
→ 변경되어도 다시 렌더링하지 않음
즉, 값이 바뀌었을 때 화면도 바뀌어야 한다면 보통 State이고 반대로 화면과 직접 관계없는 내부 값이라면 Ref가 선택지가 될 수 있다.
Step 2. useRef의 주 사용처
바로 DOM 요소에 접근하기이다.
예를 들어
<input />
에 자동으로 커서를 넣고 싶다고 해보자.
JavaScript만을 사용하는 경우에는 DOM 요소에 직접 접근하기 위해
const input = document.querySelector("input");
input.focus();
처럼 한다.
React에서는 해당 input을 어떻게 가져올까?
useRef를 사용한다.
Step 3. input에 포커스 주기
다음 코드를 작성해보자.
src/App.jsx
import { useRef } from "react";
function App() {
const inputRef = useRef(null);
function handleFocus() {
inputRef.current.focus();
}
return (
<div>
<input ref={inputRef} />
<button onClick={handleFocus}>
입력창 포커스
</button>
</div>
);
}
export default App;
버튼을 누르면 입력창에 커서가 들어간다.
살펴보면
먼저 import 한다.
import { useRef } from "react";
그리고 Ref 객체를 하나 생성한다.
const inputRef = useRef(null);
그다음 원하는 DOM 요소와 연결한다.
<input ref={inputRef} />
그러면 React가 이 실제 input DOM 요소를
inputRef.current
에 넣어준다.
즉,
inputRef.current → 실제 input DOM 요소
이다.
Step 4. current는 왜 붙을까?
useRef()를 호출하면 대략 이런 형태의 객체를 받는다.
{
current: null
}
처음에는 아직 DOM 요소가 연결되지 않았으므로
inputRef.current === null
이다.
React가 <input>을 실제 DOM에 연결하고 나면
inputRef.current
가 실제 input 요소를 가리키게 된다.
처음
inputRef
└─ current: null
렌더링 후
inputRef
└─ current: 실제 input DOM
Step 5. useState와 useRef를 직접 비교해보자.
다음 코드를 실행해보자.
import { useRef, useState } from "react";
function App() {
const [stateCount, setStateCount] = useState(0);
const refCount = useRef(0);
function increaseState() {
setStateCount(stateCount + 1);
}
function increaseRef() {
refCount.current = refCount.current + 1;
console.log(refCount.current);
}
return (
<div>
<p>State: {stateCount}</p>
<p>Ref: {refCount.current}</p>
<button onClick={increaseState}>
State +1
</button>
<button onClick={increaseRef}>
Ref +1
</button>
</div>
);
}
export default App;
State +1을 누르면 화면이 바로 바뀐다.
그러나 Ref +1은 눌러도 Console 숫자는 증가하지만 화면의 Ref: 값은 바로 갱신되지 않는다.
왜냐하면
refCount.current = refCount.current + 1;
은 다시 렌더링을 발생시키지 않기 때문이다. Ref: 값은 다른 요인에 의해 다시 렌더링이 발생하는 경우에 갱신된다.
따라서 화면에 보여줄 값은 Ref로 관리하면 안 된다.
반면 화면을 다시 그릴 필요 없이 기억만 해야 하는 값이라면 Ref가 후보가 될 수 있다.
예를 들어,
DOM 요소
타이머 ID
이전 값 저장
렌더링과 무관한 mutable 값
같은 경우이다.
Step 6. useState와 useRef를 사용하는 판단 기준
값이 바뀌면 화면도 바뀌어야 한다
→ useState
값을 기억해야 하지만
바뀌어도 화면을 다시 그릴 필요가 없다
→ useRef
그리고 DOM에 직접 접근해야 한다면
DOM 접근
→ useRef
실습
다음 동작을 하는 웹 페이지를 만들어보자.
[React 검색 ]
[검색어 지우기]
[입력창 포커스]
검색어 지우기 버튼은 State를 사용하고,
입력창 포커스 버튼은 Ref를 사용한다.
기본 골격은 다음과 같다.
import { useRef, useState } from "react";
function App() {
const [keyword, setKeyword] = useState("");
const inputRef = useRef(null);
function handleClear() {
// 작성
}
function handleFocus() {
// 작성
}
return (
<div>
<input
ref={inputRef}
value={keyword}
onChange={(event) => setKeyword(event.target.value)}
/>
<button onClick={handleClear}>검색어 지우기</button>
<button onClick={handleFocus}>입력창 포커스</button>
</div>
);
}
export default App;
답안
function handleClear() {
setKeyword("");
}
function handleFocus() {
inputRef.current.focus();
}'개발 > React' 카테고리의 다른 글
| [React] DOM이란 무엇인가? (0) | 2026.09.28 |
|---|---|
| [React] 컴포넌트 합성과 children (0) | 2026.09.27 |
| [React] useEffect (0) | 2026.09.24 |
| [React] 컴포넌트 분리와 State 끌어올리기 (0) | 2026.09.22 |
| [React] Todo List 만들기 실습 (2) [배열 항목 수정] (0) | 2026.09.20 |