2025 / 04 / 02
오늘 수업 시간에는 React 기본 훅 중 하나인 useRef에 대해 배웠습니다.
Html에서 사용하는 label의 for 속성과 비슷한 역할을 하는 것 같습니다.
동작하는 방식이 배우면서 살짝 헷갈렸는데! 이해하기 쉽게 정리해보겠습니다.
- React에서 "참조"를 저장하는 기본 훅입니다.
- 기본적으로 DOM이나 어떤 값이 변하더라도 렌더링하지 않습니다.
- 해당 값이나 객체를 기억하게 하는 용도로 사용됩니다.
- 값이 변경돼도 컴포넌트를 리렌더링하지 않습니다.
- 주로 DOM Element에 접근할 때 사용합니다.
- 값을 저장하고 싶지만 렌더링하고 싶지 않은 경우에 유용합니다.
- useRef는 값이 변경되어도 컴포넌트를 리렌더링하지 않습니다.
- 리렌더링이 이루어지지 않기 때문에 성능 최적화가 필요한 경우 유용합니다.
- ex) 렌더링을 하지 않으면서 값이나 DOM 상태를 추척하고 싶을 때 효과적입니다.
const renderCount = useRef(0); renderCount.current += 1; console.log(renderCount.current); // 변경되지만 리렌더링은 되지 않음
- useRef는 React에서 관리되는 DOM Element에 직접 접근할 수 있게 해줍니다.
- ref 속성을 사용하여 DOM을 직접 제어하고, useRef로 해당 참조를 저장할 수 있습니다.
const inputRef = useRef(null); inputRef.current.focus(); // 직접 DOM에 접근하여 포커스를 줄 수 있음
- 값이 변해도 렌더링하지 않으므로 컴포넌트의 상태를 바꾸지 않고 값을 유지할 수 있습니다.
- 렌더링 성능에 영향을 주지 않으면서 상태를 추적할 수 있습니다.
const prevValue = useRef(); prevValue.current = currentValue; // 렌더링 없이 값 추적 ```
- useRef로 저장된 값은 변경되더라도 UI에 자동으로 반영되지 않습니다.
- UI 업데이트와 관련된 값을 관리하는 데 적합하지 않습니다.
- UI 상태를 관리하고자 할 때는 useState를 사용해야 합니다.
- useRef의 current 속성을 직접 수정하는 것은 안전하지 않을 수 있습니다.
- current 값이 변경되더라도 React가 이를 감지하지 않습니다.
- 특정 상황에서 예기치 않은 버그나 동기화 문제가 발생할 수 있습니다.
- 상태와 함께 사용해야 할 때는, useState나 useEffect를 적절히 사용하는 것이 더 안전합니다.
- useRef는 렌더링을 발생시키지 않는 값을 관리하는 데 유용합니다.
- UI를 업데이트하고자 할 때는 useState를 사용하는 것이 맞습니다.
- useRef로 DOM Element를 참조할 때, 컴포넌트가 언마운트되거나 DOM 엘리먼트가 제거되면 해당 참조는 null이 될 수 있기 때문에 이를 안전하게 처리해야 합니다.
- ex) inputRef.current가 null인 경우에는 null 체크를 통해 에러를 방지할 수 있습니다.
const inputRef = useRef(null); if (inputRef.current) { inputRef.current.focus(); }
- useRef의 간단한 사용 예시입니다.
import { useRef } from "react"; import "./App.css"; function App() { console.log("App실행"); const inputRef = useRef(null); const handleFocuse = () => { inputRef.current.focus(); // input 엘리먼트에 포커스를 줌 inputRef.current.value = "안녕"; // input 값 변경 }; return ( <div> <h2>useref 연습</h2> <input type="text" placeholder="여기에 입력하세요!" ref={inputRef} /> <button onClick={handleFocuse}>입력창에 포커스 추가</button> </div> ); } export default App;
- inputRef는 useRef 훅을 통해 생성된 참조 객체입니다.
- 초기값은 null로 설정되며, input Element를 참조하기 위한 준비입니다.
- inputRef는 DOM 요소를 직접 다루기 위해 사용됩니다.
- 실제 DOM 요소는 inputRef.current를 통해 접근할 수 있습니다.
const inputRef = useRef(null);
- handleFocuse 함수는 버튼 클릭 시 실행되는 함수입니다.
- inputRef.current를 사용하여 input Element에 접근하고 있습니다.
- inputRef.current.focus( )는 input Element에 포커스를 주는 명령입니다.
- inputRef.current.value = "안녕"은 input의 값을 "안녕"으로 변경합니다.
const handleFocuse = () => { inputRef.current.focus(); // input 엘리먼트에 포커스를 줌 inputRef.current.value = "안녕"; // input 값 변경 };
- input 엘리먼트에 ref={inputRef}가 붙어 있습니다.
- ref 속성은 inputRef 객체에 연결되며, input Element를 참조할 수 있습니다.
- 버튼은 클릭 시 handleFocuse 함수를 실행하며, input에 포커스를 주고 값을 변경합니다.
<input type="text" placeholder="여기에 입력하세요!" ref={inputRef} /> <button onClick={handleFocuse}>입력창에 포커스 추가</button>
1) 초기 상태
- 처음 화면에는 input 필드와 "입력창에 포커스 추가" 버튼이 나타납니다.
2) 버튼 클릭 시
- 사용자가 "입력창에 포커스 추가" 버튼을 클릭하면, handleFocuse 함수가 실행됩니다.
- handleFocuse 함수는
inputRef.current.focus()로 input에 포커스를 주고,inputRef.current.value = "안녕"으로 input의 값을 "안녕"으로 변경합니다.
3) useRef의 동작
- inputRef는 input 요소를 참조 ->
inputRef.current로 요소에 직접 접근 가능합니다.inputRef.current를 통해 값이나 DOM을 직접 다루지만, 이 값의 변경으로 인해 React 컴포넌트가 리렌더링되지는 않습니다.
useState
- 값이 변경되면 컴포넌트가 리렌더링됩니다.
- 주로 화면(UI)과 관련된 상태를 저장할 때 사용됩니다.
useRef
- 값이 변경되더라도 컴포넌트가 리렌더링되지 않습니다.
- 주로 DOM Element에 직접 접근하거나 렌더링에 영향을 주지 않는 값들을 저장할 때 사용됩니다.
| 특징 | useState | useRef |
|---|---|---|
| 목적 | 컴포넌트의 상태(state)를 관리할 때 사용 | DOM 엘리먼트나 렌더링 없이 값을 참조할 때 사용 |
| 렌더링 | 값이 변경되면 컴포넌트를 리렌더링함 | 값이 변경되어도 컴포넌트를 리렌더링하지 않음 |
| 용도 | UI와 직접적으로 연결되는 값들 | DOM 엘리먼트에 접근하거나, 렌더링에 영향을 주지 않는 값들 |
| 값 접근 | 값은 state 변수로 접근 (setState로 변경) | 값은 ref.current로 접근 |
| 초기값 | useState(value) 형식으로 설정 | useRef(value) 형식으로 설정 |
| 사용 예시 | 사용자와의 상호작용에 따라 UI 업데이트가 필요할 때 | DOM 조작, 값 변경이 UI에 영향을 주지 않아야 할 때 |
App 컴포넌트 (파일 선택기)
import { useRef } from "react"; import "./index.css"; import Timer from "./Timer"; function App() { const inputRef = useRef(null); // 1 const showFile = () => { // 2 inputRef.current.click(); // 3 console.log(inputRef.current); }; return ( <div id="app"> <p>Please select an image</p> <p> <input data-testid="file-picker" type="file" accept="image/*" ref={inputRef} // 4 /> <button onClick={showFile}>Pick Image</button> // 5 </p> <Timer /> </div> ); } export default App;
- useRef는 DOM Element에 접근하기 위해 사용됩니다.
- inputRef는 input 태그를 참조하기 위한 변수입니다.
- useRef의 초기 값은 null로 설정됩니다.
- 이 null 값은 DOM Element를 가리키는 참조로 업데이트됩니다.
const inputRef = useRef(null);
- showFile 함수는 버튼 클릭 시 실행되는 함수입니다.
inputRef.current.click( )을 호출하여, 파일 선택 창을 열도록 합니다.- 즉, 사용자가 버튼을 클릭하면, input 요소가 클릭된 것처럼 동작하여 파일 선택 창이 열립니다.
const showFile = () => { inputRef.current.click(); console.log(inputRef.current); };
inputRef.current는 실제 DOM 요소인<input>태그를 참조합니다.- inputRef.current.click( )을 사용하여, 코드에서 직접 클릭 이벤트를 발생시킵니다.
- input 엘리먼트를 시각적으로 클릭하지 않고도 파일 선택 창을 열 수 있습니다.
- input 요소에 ref 속성을 통해 inputRef를 연결합니다.
inputRef.current가 해당 input 요소를 참조하게 됩니다.<input data-testid="file-picker" type="file" accept="image/*" ref={inputRef} />
- "Pick Image" 버튼을 클릭하면 showFile 함수가 실행됩니다.
- 이를 통해 사용자는 파일 선택 창을 띄울 수 있게 됩니다.
<button onClick={showFile}>Pick Image</button>
Timer 컴포넌트 (타이머)
import { useRef, useState } from "react"; export default function Timer() { const [seconds, setSeconds] = useState(0); // 1) const timeRef = useRef(null); // 2) const startTimer = () => { // 3) if (timeRef.current !== null) return; timeRef.current = setInterval(() => { setSeconds((prev) => prev + 1); }, 1000); }; const stopTimer = () => { // 4) clearInterval(timeRef.current); timeRef.current = null; }; const resetTimer = () => { // 5) stopTimer(); setSeconds(0); }; return ( <div> <h2>타이머 : {seconds}초</h2> <button onClick={startTimer}>시작</button> <button onClick={stopTimer}>정지</button> <button onClick={resetTimer}>리셋</button> </div> ); }
useState(0)로 초 단위 카운트 관리
- seconds와 setSeconds는 타이머에서 경과된 시간을 관리하는 상태입니다.
- setSeconds를 사용하여 초를 증가시킵니다.
const [seconds, setSeconds] = useState(0);
timeRef로 타이머 ID 관리
- timeRef는 useRef(null)을 사용하여 타이머의 ID를 저장합니다.
- useRef는 상태 업데이트와 상관없이 값을 유지합니다.
- 타이머 ID를 렌더링 없이 추적할 수 있습니다.
const timeRef = useRef(null);
- startTimer 함수는 타이머를 시작하는 함수입니다.
- 이미 실행 중일 경우(timeRef.current가 null이 아닌 경우), 재실행을 방지합니다.
- 실행 중이 아니라면, setInterval을 호출하고 timeRef.current에 타이머 ID를 저장합니다.
- setInterval을 사용하여 1초마다 seconds 상태를 1씩 증가시킵니다.
- timeRef.current에 저장된 값으로 나중에 타이머를 멈출 수 있게 합니다.
const startTimer = () => { if (timeRef.current !== null) return; timeRef.current = setInterval(() => { setSeconds((prev) => prev + 1); }, 1000); };
- stopTimer 함수는 타이머를 멈추는 함수입니다.
- clearInterval(timeRef.current)로 타이머를 멈출 수 있습니다.
- timeRef.current를 null로 설정하여 타이머 상태를 초기화합니다.
const stopTimer = () => { clearInterval(timeRef.current); timeRef.current = null; };
- resetTimer 함수는 타이머를 정지하고, seconds를 0으로 리셋하는 함수입니다.
- stopTimer를 호출하여 타이머를 멈추고, seconds를 0으로 설정합니다.
const resetTimer = () => { stopTimer(); setSeconds(0); };
60일차 후기
- 참조한다는 개념이 좀 이해하기 어려웠던 것 같습니다.
- useRef를 사용하면 렌더링이 일어나지 않고 값이 변하지 않는데 사용할 일이 있을까..? 의문이었지만, 해당 개념에 대한 실습을 해보고나서 사용자 경험을 개선하기 위해 어느 정도는 있으면 좋을 것 같아서 필요하다고 생각하게 되었습니다. (ง ´▽` )ว
- useRef의 기본 값은 null이라고 했는데, null 값 말고 다른 값이 들어가는 경우가 있는지 궁금증이 생기게 되었습니다. 근데 생각해보면 렌더링이 일어나지 않고, 상태 값이 바뀌는 것도 아니라서 다른 값을 넣어주는 일은 별로 없을 것 같긴합니다...ㅎㅎ
- 딱 기초적인 부분만 이해한 것 같아서 사용해보면서 감을 익혀야할 것 같습니다.
- 오랜만에 setInterval 함수를 사용해보니까.. 복습을 해야할 것 같습니다. ૮ ๑´ᯅ`๑ ა