React 개발을 하다 보면, 컴포넌트의 상태 관리로는 해결하기 어려운 순간이 찾아옵니다. 예를 들어, DOM 요소를 직접 조작해야 하거나, 렌더링을 유발하지 않고 값을 저장해야 할 때 말입니다. 이때 빛을 발하는 도구가 바로 Refs와 useRef 훅입니다. React의 선언적 패러다임을 유지하면서도 명령형 접근을 가능하게 해주는 이 강력한 기능을 이번 포스팅에서 깊이 파헤쳐 보겠습니다. 초보자부터 중급 개발자까지, useRef를 완벽히 마스터하고 React 코드를 더 효율적으로 작성하는 팁을 공유할게요!
Refs는 무엇이며, 왜 필요한가요?
React는 선언적(Declarative) 방식으로 UI를 구축하도록 설계되었습니다. 상태와 props를 통해 UI가 자동으로 업데이트되죠. 하지만 때로는 명령형(Imperative) 접근이 불가피합니다. 예를 들어:
- 입력 필드에 자동 포커스를 주기
- 비디오나 오디오 미디어 재생을 제어하기
- D3.js나 Chart.js 같은 서드 파티 라이브러리와 연동하기
이런 시나리오에서 Refs가 등장합니다. Refs는 DOM 요소나 React 컴포넌트 인스턴스에 직접 접근할 수 있게 해주는 메커니즘입니다. 클래스 컴포넌트에서는 React.createRef()를 사용했지만, 현대적인 함수형 컴포넌트에서는 useRef 훅이 훨씬 간편하고 직관적입니다. useRef는 React 16.8부터 도입된 Hooks의 일원으로, 함수형 컴포넌트의 생명 주기를 존중하면서도 유연성을 더해줍니다.
useRef 훅의 핵심 개념 3가지
useRef 훅은 단순한 Ref 생성을 넘어, React 개발의 다양한 문제를 해결하는 다재다능한 도구입니다. 다음 세 가지 핵심 개념을 이해하면 useRef를 자유자재로 다룰 수 있을 거예요.
1. DOM 요소에 대한 직접 접근
useRef의 가장 흔한 용도는 DOM 노드에 직접 접근하는 것입니다. React의 가상 DOM 흐름을 벗어나 실제 브라우저 DOM을 조작할 때 필수적입니다.
- 기본 DOM 노드 접근: input, div 등의 요소에 접근해
.focus(),.scrollIntoView(), 또는clientWidth같은 속성을 읽을 수 있습니다. - 서드 파티 라이브러리 연동: DOM을 직접 다루는 라이브러리(예: GSAP 애니메이션, Three.js 3D 렌더링)와 React를 결합할 때 Ref가 브릿지 역할을 합니다.
간단한 예시로, 컴포넌트 마운트 시 입력 필드에 자동 포커스를 주는 코드를 보죠:
import React, { useEffect, useRef } from 'react';
function FocusInput() {
const inputRef = useRef(null); // Ref 생성, 초기값은 null
useEffect(() => {
// 컴포넌트가 마운트될 때 입력 요소에 포커스를 맞춥니다.
if (inputRef.current) {
inputRef.current.focus(); // Ref의 .current 속성을 통해 DOM 요소에 접근
}
}, []); // 빈 의존성 배열: 마운트 시 한 번만 실행
return (
<input
ref={inputRef}
type="text"
placeholder="여기에 포커스!"
/>
);
}
이 코드를 실행하면 컴포넌트가 로드되자마자 커서가 입력 필드에 깜빡이며 타이핑 준비가 됩니다. 클릭 한 번 없이 사용자 경험을 업그레이드하는 마법 같은 순간이죠!
2. 가변 객체로서의 Ref
useRef가 반환하는 객체는 컴포넌트의 전체 생명 주기 동안 유지됩니다. 이 객체의 .current 속성은 어떤 값이든 저장할 수 있으며, 가장 큰 장점은 값 변경 시 리렌더링이 발생하지 않는 점입니다.
- 렌더링을 유발하지 않는 데이터 저장: useState와 달리 UI 업데이트를 트리거하지 않아, 타이머 ID, 이전 props 값, WebSocket 인스턴스 등을 안전하게 보관할 수 있습니다.
- 컴포넌트 외부 데이터 관리: 상태에 포함되지 않으면서도 컴포넌트 인스턴스에 묶인 데이터를 유연하게 다룹니다.
이 개념은 특히 복잡한 애플리케이션에서 메모리 누수나 불필요한 재계산을 방지하는 데 유용합니다. Ref는 "컴포넌트의 비밀 저장소"처럼 작동하죠.
3. 불필요한 렌더링 방지
Ref의 .current 변경이 리렌더링을 유발하지 않는다는 점은 성능 최적화의 핵심입니다. 자주 변하지만 UI에 직접 영향을 주지 않는 값(예: 내부 카운터, 캐시 데이터)을 저장할 때 이상적입니다.
useState를 쓰면 매 업데이트마다 전체 컴포넌트가 리렌더링되지만, useRef는 이를 피할 수 있어 불필요한 DOM 재조작을 줄입니다.
실제 예시로, 카운터의 이전 값을 추적하는 컴포넌트를 보세요:
import React, { useState, useEffect, useRef } from 'react';
function TimerComponent() {
const [count, setCount] = useState(0);
const prevCount = useRef(count); // 초기값으로 현재 count 설정
useEffect(() => {
prevCount.current = count; // count 변경 시 이전 값으로 업데이트 (리렌더링 없음)
}); // 의존성 배열 없음: 모든 렌더링 후 실행
return (
<div>
<h1>현재 카운트: {count}</h1>
<h2>이전 카운트: {prevCount.current}</h2>
<button onClick={() => setCount(count + 1)}>증가</button>
</div>
);
}
여기서 prevCount.current 업데이트는 조용히 일어나며, count 상태 변경에만 리렌더링이 발생합니다. 결과적으로 더 부드럽고 효율적인 앱이 됩니다!
결론: 언제 useRef를 사용해야 할까요?
useRef는 React의 선언적 세계를 보완하는 명령형의 강력한 무기입니다. 다음 상황에서 적극 활용하세요:
- DOM 직접 접근 필요 시: 포커스 관리, 스크롤 제어, 미디어 플레이어 등.
- 변경 가능하지만 리렌더링 피할 값 저장 시: 타이머 ID, 이전 상태, 외부 라이브러리 객체.
- 성능 최적화: 자주 업데이트되는 비-UI 데이터로 인한 오버헤드 줄이기.
useRef를 마스터하면 코드가 더 깔끔해지고, 앱 성능이 눈에 띄게 향상됩니다. React 개발자라면 Hooks 중에서도 useRef를 "숨은 조력자"로 삼아보세요. 다음 포스팅에서는 useImperativeHandle과 같은 고급 Ref 패턴을 다룰 예정이니 기대해주세요!
'프로그래밍 > ReactJS' 카테고리의 다른 글
| React 프래그먼트: DOM 오버헤드를 줄이고 성능을 향상시키는 비결 (0) | 2025.10.12 |
|---|---|
| React 프래그먼트: 깔끔한 DOM 구조와 최적화된 성능을 위한 필수 패턴 (0) | 2025.10.11 |
| React Refs 완벽 가이드: DOM 직접 조작, 두 가지 방법으로 마스터하기! (0) | 2025.10.11 |
| React Refs 완벽 가이드: DOM 직접 조작, 과연 필요할까요? (0) | 2025.10.11 |
| React 폼 완벽 가이드: 제어/비제어 컴포넌트부터 유효성 검사까지 (1) | 2025.10.11 |