[리액트 훅 가이드 2편] DOM을 내 맘대로 조작해보자 (feat. `useRef` & `forwardRef`)
kwangmook.dev·2026. 05. 31.·2 min read
DOM을 직접 만질 때 자주 쓰는 useRef랑, 부모 ref를 자식으로 안전하게 넘길 때 쓰는 forwardRef를 정리해봤다.
useRef로 DOM 직접 가리키기useRef를 렌더링 없는 저장소로 쓰는 패턴forwardRef로 ref 전달 문제 해결하기
1. DOM을 직접 가리키는 useRef
리액트를 쓰다 보면 아래 같은 순간이 온다.
- 특정 input에 자동 포커스 주기
- 특정 영역 스크롤 이동시키기
- 비디오/오디오 DOM 메서드 호출하기
이때 쓰는 훅이 useRef다.
import { useRef } from "react";
export default function MyComponent() {
const inputRef = useRef<HTMLInputElement>(null);
const onClick = () => {
inputRef.current?.focus();
};
return (
<>
<button onClick={onClick}>포커스</button>
<input ref={inputRef} />
</>
);
}
- 조작할 DOM에
ref를 연결하고 ref.current로 실제 DOM에 접근한다.
1-1. useRef를 "렌더링 없는 저장소"로 쓰는 패턴
이번에 다시 정리하면서 useRef의 진짜 강점을 또 느꼈다.
값이 바뀌어도 리렌더를 발생시키지 않는다는 점이다.
그래서 아래 같은 값은 useState보다 useRef가 맞을 때가 있다.
- 타이머 ID
- 이전 값 스냅샷
- 재생/일시정지 같은 내부 플래그
- 디바운스/스로틀 제어 플래그
예시:
import { useRef, useState } from "react";
export default function Player() {
const pausedRef = useRef(false);
const [label, setLabel] = useState("재생 중");
const togglePause = () => {
pausedRef.current = !pausedRef.current;
setLabel(pausedRef.current ? "일시정지" : "재생 중");
};
return <button onClick={togglePause}>{label}</button>;
}
pausedRef.current를 바꾸는 것 자체는 리렌더를 만들지 않는다.
화면에 보여줄 값만 별도로 state로 관리하면 필요한 리렌더만 만들 수 있다.
1-2. 타입스크립트에서 헷갈렸던 포인트
처음엔 useRef<HTMLInputElement>(null)만 써도 되는 줄 알았는데, 용도에 따라 의미가 꽤 다르다.
- DOM ref 용도:
useRef<HTMLInputElement>(null)
이전/다음 포스트
댓글 남기기
댓글 0개
첫 댓글을 남겨보세요.