728x90
안녕하세요.
오늘은 useRef를 컴포넌트의 리렌더링(re-rendering)을 유발하지 않는 값 저장소로 활용하는 예제를 만들어 보겠습니다.
useRef
useState와 useRef는 둘 다 무언가를 '기억'하지만, 그 성격이 완전히 다릅니다.
이 차이를 setInterval 타이머 예제로 확실하게 알아보겠습니다.
- useState
- 값이 바뀌면 ( setCount(1) ) 즉시 리렌더링이 예약됩니다.
- UI에 보여야 하는 값을 저장합니다.
- useRef
- myRef.current = '...' 처럼 값을 바꿔도, React는 전혀 신경 쓰지 않습니다. (리렌더링 X)
- UI와는 상관없는 내부 관리용 값을 저장할 때 씁니다.
예제 - setInterval 타이머 만들기
화면에 보이는 초(second): 0, 1, 2... 는 useState여야 합니다.
setInterval을 실행했을 때 반환되는 타이머 ID: 123 같은 숫자는 useRef여야 합니다.
왜 타이머 ID를 useState에 저장하면 안 될까요?
setTimerId(123)을 호출하는 순간, 불필요한 리렌더링이 발생하기 때문입니다.
import React, { useState, useRef, useEffect } from 'react';
import './App.css';
function App() {
// count가 바뀌면 화면이 리렌더링됩니다.
const [count, setCount] = useState(0);
// intervalRef.current 값은 바뀌어도 리렌더링되지 않습니다.
const intervalRef = useRef(null);
// 컴포넌트가 사라질 때(unmount) 타이머를 정리(cleanup)합니다.
// (메모리 누수 방지를 위한 중요한 습관입니다)
useEffect(() => {
// 이 return 함수는 컴포넌트가 사라질 때 호출됩니다.
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, []); // 의존성 배열 []: 컴포넌트가 처음 마운트될 때 1번만 실행
// '시작' 버튼 핸들러
const handleStart = () => {
// 이미 타이머가 실행 중이라면 (intervalRef.current에 값이 있다면)
// 중복 실행을 방지합니다.
if (intervalRef.current !== null) {
return;
}
// 1초(1000ms)마다 count state를 1씩 올립니다.
// intervalRef.current에 setInterval이 반환한 ID를 "조용히" 저장합니다.
intervalRef.current = setInterval(() => {
// setCount는 리렌더링을 유발합니다 (전광판 변경!)
setCount(prevCount => prevCount + 1);
}, 1000);
console.log('타이머 시작, ID:', intervalRef.current);
};
// '정지' 버튼 핸들러
const handleStop = () => {
if (intervalRef.current === null) {
return;
}
// intervalRef.current에 "조용히" 저장해 둔 ID를 사용해 타이머를 정지합니다.
clearInterval(intervalRef.current);
intervalRef.current = null; // ID를 null로 리셋 (정지 상태 표시)
console.log('타이머 정지!');
};
// '초기화' 버튼 핸들러
const handleReset = () => {
handleStop(); // 1. 일단 타이머를 멈추고
setCount(0); // 2. 화면에 보이는 숫자를 0으로 리셋 (리렌더링!)
};
return (
<div className="App">
<header className="App-header">
<h1>useRef로 타이머 만들기</h1>
<p style={{ fontSize: '3rem' }}>{count}초</p>
<div style={{ display: 'flex', gap: '10px' }}>
<button onClick={handleStart}>시작</button>
<button onClick={handleStop}>정지</button>
<button onClick={handleReset}>초기화</button>
</div>
</header>
</div>
);
}
export default App;
결과
'React' 카테고리의 다른 글
| [React] useRef로 페이지 로딩 시 input에 자동 포커스(focus) 주기 (0) | 2025.10.23 |
|---|---|
| [React] 바깥 클릭 시 닫히는 드롭다운 (useOnClickOutside 훅) (1) | 2025.09.03 |
| [React] Tailwind v4로 웹페이지 다크모드 설정하기 (클래스 기반) (0) | 2025.09.02 |
| [React] navigator.share + clipboard로 공유 버튼 간단 구현하기 (0) | 2025.08.25 |
| [React] LottieFiles 애니메이션 적용하기 (0) | 2025.08.21 |