useRef란?
React 공식 문서에서는 useRef
를 "렌더링에 필요하지 않은 값을 참조할 수 있게 해주는 React Hook"이라고 정의하고 있습니다.
Vue.js에 익숙한 저는 처음에 '굳이 훅을 사용하지 않고도 const
나 let
을 통해 변수를 선언하면 되는 것 아닌가?'라는 의문이 들었습니다. 그런데 왜 React에서는 useRef
라는 Hook을 따로 설계했을까요? 그 이유를 알아보겠습니다.
React에서 useRef
가 필요한 이유
React는 컴포넌트가 리렌더링될 때마다 상태와 변수를 다시 초기화하는 특징이 있습니다. 그러나 일부 값은 리렌더링과 상관없이 계속 유지되어야 하는 경우가 있습니다. 예를 들어, DOM 요소에 직접 접근하거나, UI 업데이트와는 무관하게 특정 값을 추적해야 할 때 useRef
를 사용합니다. 단순히 let
이나 const
를 사용해 변수를 선언하면, 리렌더링 시마다 그 값이 다시 초기화되기 때문에 이러한 상황에 적합하지 않습니다.
useRef
는 리렌더링이 발생해도 내부적으로 값을 유지할 수 있도록 설계되었습니다. React의 내부 구현 코드(2024-10-13 기준)를 살펴보면memoizedState
라는 메커니즘을 통해 useRef
로 저장된 값이 리렌더링 간에도 유지된다는 것을 알 수 있습니다.
// packages/react-reconciler/src/ReactFiberHooks.js
function mountRef<T>(initialValue: T): {current: T} {
const hook = mountWorkInProgressHook();
const ref = {current: initialValue};
hook.memoizedState = ref;
return ref;
}
memoizedState
는 리렌더링 시에도 Hook의 상태를 유지하는 역할을 합니다. useRef
뿐만 아니라 useState
나 useEffect
같은 다른 Hook들도 이 구조를 통해 리렌더링이 되어도 이전 상태를 유지하도록 보장됩니다. 즉, memoizedState
에 저장된 값은 리렌더링이 발생하더라도 초기화되지 않으며, 계속해서 유지됩니다.
useRef와 일반 변수(let)의 차이
다음 코드 예시는 useRef
와 let
변수가 리렌더링에서 어떻게 다르게 동작하는지를 보여줍니다.
export default function Count() {
const useRefCount = useRef(0);
const handleUseRefCount = () => {
useRefCount.current += 1;
console.log(useRefCount);
}
let count = 0;
const handleCount = () => {
count += 1;
console.log(count);
}
const [trigger, setTrigger] = useState(false);
return (
<div>
<div>
useRef count: { useRefCount.current }
</div>
<button onClick={handleUseRefCount}>useRef Count plus</button>
<div>
count: {count}
</div>
<button onClick={handleCount}>Count plus</button>
<button onClick={() => setTrigger(!trigger)}>rerendering Trigger</button>
</div>
);
}
이 코드를 실행하면, useRef Count plus
버튼과 Count plus
버튼을 눌렀을 때 콘솔에는 값이 증가하지만 UI 상에는 값이 변하지 않는 것을 볼 수 있습니다. 하지만 Rerender Trigger
버튼을 눌러 리렌더링을 발생시키면, useRefCount
는 증가한 값을 유지하는 반면, count
는 다시 0으로 초기화됩니다.
useRefCount.current
: 리렌더링이 발생해도 증가한 값을 유지.let count
: 리렌더링 시 초기화됨.
이는 useRef
의 상태가 memoizedState
에 저장되어 리렌더링 중에도 값이 유지되는 반면, let count
는 컴포넌트가 다시 렌더링되면서 초기화되기 때문입니다.
memoizedState의 역할
memoizedState
는 각 Hook의 상태를 기억하고, 리렌더링이 발생하더라도 그 상태를 유지하는 중요한 역할을 합니다. useRef
를 통해 관리되는 값은 렌더링에 영향을 주지 않으면서도 리렌더링 간 값을 계속 유지하기 때문에, 상태가 UI 업데이트와는 무관하지만 리렌더링 간에도 저장이 필요한 경우에 매우 유용합니다.
반면 let
이나 const
로 선언된 변수는 리렌더링 시 컴포넌트가 다시 실행되면서 초기화되므로 값을 유지할 수 없습니다. 결국 memoizedState
덕분에 React는 리렌더링 시에도 Hook의 상태를 안정적으로 관리할 수 있게 됩니다.
결론
결론적으로, useRef
는 렌더링에는 영향을 미치지 않으면서도 리렌더링이 발생해도 내부적으로 값을 유지할 수 있는 Hook입니다. 이를 통해 DOM 요소에 접근하거나, 컴포넌트의 상태와는 별개로 값을 유지해야 하는 경우에 매우 효과적으로 사용할 수 있습니다. 예를 들어:
- HTML 요소에 직접 접근해야 할 때
- 타이머 ID, 폼 입력값 추적 등 렌더링과 무관한 값을 저장해야 할 때
이와 같은 상황에서 useRef
를 적절히 활용할 수 있습니다.
'React' 카테고리의 다른 글
React-Router 기본 원리 알아보기 (0) | 2024.11.18 |
---|---|
useCallback 알아보기 (2) | 2024.10.14 |
useState 깊게 분석하기 (6) | 2024.10.10 |