Ref
Ref는 React가 렌더 사이에 유지해 주는 mutable한 보관 상자입니다. 이를 통해 실제 DOM 노드를 직접 가리키거나, 화면에 바로 반영될 필요가 없는 값을 기억할 수 있습니다.
▶아키텍처 다이어그램
🔗 관계 다이어그램점선 애니메이션은 데이터 또는 요청의 흐름 방향을 나타냅니다
모든 값을 state에 넣으면 필요하지 않은 재렌더가 늘고, focus 이동이나 스크롤 제어처럼 실제 DOM 노드에 명령해야 하는 작업은 state만으로 자연스럽게 표현하기 어렵습니다. 선언형 렌더링이 기본이더라도, 일부 작업은 여전히 명령형 접근을 필요로 합니다.
React는 가능한 한 UI를 선언형으로 다루게 만들었지만, 브라우저 API와 외부 라이브러리는 여전히 명령형 모델이 많습니다. 그래서 화면 계산과는 별도로 노드 참조나 mutable 값을 붙잡아 둘 통로가 필요했고, ref가 그 역할을 맡게 됐습니다.
useRef를 호출하면 current 프로퍼티를 가진 객체가 반환되고, 이 객체는 렌더가 반복돼도 같은 참조를 유지합니다. DOM 요소에 ref를 연결하면 React가 커밋 시점에 current에 실제 노드를 넣어 줍니다. 일반 값 저장에도 쓸 수 있지만, current를 바꾼다고 자동으로 화면이 다시 그려지지는 않습니다.
DOM 노드를 직접 가리켜 focus 제어하기
import { useRef } from "react";
export function SearchInput() {
const inputRef = useRef<HTMLInputElement | null>(null);
function handleFocus() {
inputRef.current?.focus();
}
return (
<>
<input ref={inputRef} />
<button onClick={handleFocus}>검색창으로 이동</button>
</>
);
}ref는 커밋 이후 실제 DOM 노드에 닿는 손잡이 역할을 하므로, focus 같은 명령형 작업을 자연스럽게 연결할 수 있습니다.
화면과 무관한 mutable 값은 ref에 둘 수 있습니다
import { useRef } from "react";
export function DragSurface() {
const dragStartX = useRef<number | null>(null);
function handleMouseDown(event: React.MouseEvent<HTMLDivElement>) {
dragStartX.current = event.clientX;
}
return <div onMouseDown={handleMouseDown}>드래그 시작</div>;
}드래그 시작 좌표처럼 기억은 해야 하지만 즉시 다시 그릴 필요는 없는 값은 ref에 두면 재렌더 없이 유지할 수 있습니다.
Ref와 state는 둘 다 값을 기억하지만 의도가 다릅니다. state는 화면을 다시 계산하기 위한 값이고, ref는 렌더에 참여하지 않는 값이나 DOM 참조를 위한 도구입니다. 또한 Virtual DOM이 React의 기본 갱신 경로라면, ref는 그 경로 밖에서 직접 다루는 예외 통로에 가깝습니다.
실무에서는 input autofocus, 드래그 시작 좌표 보관, 이전 props 기억, 외부 차트 라이브러리 인스턴스 유지 같은 장면에서 ref가 자주 쓰입니다. 다만 ref를 state 대체품처럼 넓게 쓰기 시작하면 화면과 값이 어긋나기 쉬우므로, 렌더링에 영향을 줘야 하는 값인지부터 먼저 확인하는 습관이 중요합니다.