개요
얼마 전에 라이브 코딩테스트를 치는데 부끄럽게도 풀지 못한 문제가 있었다.
심지어는 Next.js 책을 읽고 있는데 동일한 유형의 예제 코드가 보여서
실수하지 않고자 배운 점을 글로 남긴다.
내용
#1. useRef()란
Reference의 약자로 Ref이라는 단어를 포함하고 있는 React 제공 훅이다.
이름에서도 알 수 있듯이 어떤 대상을 참조하기 위한 객체를 생성하는 훅이며,
대표적으로 HTML의 DOM Element를 참조할 때 사용한다.
HTML과 JavaScript에 익숙하면 getByElementId() 메서드나
querySelect(), querySelectAll() 메서드를 많이 사용해 봤을 것이다.
이러한 메서드들은 DOM Element의 참조를 가져와 JavaScript에서 직접 DOM 조작을 가능케 한다.
useRef() 역시 React 환경에서 DOM Element의 참조 객체를 생성하여
직접적인 DOM 접근을 가능하게 한다는 점에서 비슷한 역할을 한다.
#2. useRef()만의 추가 특징
useRef()는 DOM Element 참조뿐만 아니라 리렌더링이 발생해도 값이 유지되는 특징이 있다.
React에서는 state가 변경되면 컴포넌트가 리렌더링 되는데, 모든 값이 리렌더링의 영향을 받아야 하는 것은 아니다.
어떤 값은 화면을 다시 그릴 필요 없이 단순히 “기억”만 하고 있으면 되는 경우도 있다.
import { useRef, useState } from 'react';
export default function RefPersistExample() {
const clickCountRef = useRef(0);
const [renderCount, setRenderCount] = useState(0);
const handleClick = () => {
clickCountRef.current += 1;
console.log('누적 클릭 수:', clickCountRef.current);
};
return (
<>
<button onClick={handleClick}>ref 값 증가</button>
<button onClick={() => setRenderCount(v => v + 1)}>
강제 리렌더링
</button>
<p>현재 renderCount: {renderCount}</p>
</>
);
}
위 코드에서 clickCountRef.current 값은 리렌더링 여부와 상관없이 계속 유지된다.
state = UI 변경을 위한 값
ref = 리렌더링과 무관하게 유지할 값
#3. 이 글을 쓰게 된 이유
useRef()를 사용하면 DOM Element를 직접 참조할 수 있다.
문제는 DOM Element가 생성되지 않았을 때
ref 객체가 이미 존재한다고 착각하고 접근하면 에러가 발생한다는 점이다.
import { useEffect, useRef, useState } from 'react';
export default function MountTimingExample() {
const [ready, setReady] = useState(false);
const ref = useRef<HTMLDivElement | null>(null);
useEffect(() => {
console.log('effect 실행:', ref.current); // null
}, []);
return (
<>
<button onClick={() => setReady(true)}>mount</button>
{ready && <div ref={ref}>DOM</div>}
</>
);
}
위의 코드에서 ready 상태가 false이기 때문에 div 태그가 생성되지 않았다.
초기화도 되지 않은 div 태그를 ref에 할당했다고 착각해서 접근하려고 하면 에러가 발생하는 것은 자연스러운 흐름이다.
따라서 아래와 같이 사용해서 null 참조 에러가 발생하는 걸 막아야 한다.
useEffect(() => {
if (!ref.current) return; // early return
console.log('effect 실행:', ref.current);
}, []);
이걸 당시에는 몰라서 하필이면..
정리
이번 경험을 통해 useRef를 사용할 때 중요한 점을 다시 정리하게 됐다.
- useRef는 DOM Element를 참조할 수 있는 훅이다.
- 하지만 DOM이 생성되기 전에는 ref.current는 null이다.
- 특히 조건부 렌더링 상황에서는 DOM이 존재하지 않을 수 있다.
- 따라서 ref.current에 접근할 때는 반드시 null 체크를 해야 한다.
지금 돌아보면 React의 렌더링 타이밍을 제대로 이해하지 못했던 것이 원인이라 생각된다.
다시는 잊지 말도록 하고 혹시나 비슷한 실수를 하는 누군가에게도 도움이 되면 좋겠다.
'Web' 카테고리의 다른 글
| [Next.js] 스트리밍(Streaming)과 스켈레톤(Skeleton) - loading.txs, Suspense (0) | 2026.02.20 |
|---|---|
| [Next.js] App Router에서 error.tsx를 쓰다가 try/catch로 돌아온 이유 (0) | 2026.02.16 |
| [Next.js] App Router 데이터 fetching 구조와 에러 핸들링 (0) | 2026.02.16 |