react-query 에서 useRef 사용에 관한 질문 모음

Q&A 큐레이션

1. useRef의 작동방식이 궁금합니다

useRef를 통해 dom element에 접근하기도 하고, stale closure문제를 해결하는데에도 사용하는데, 실질적으로 어떻게 작동해서 이게 가능한지 궁금합니다.


답변

안녕하세요! useRef를 통해 만들어지는 것은 JS의 객체와 유사하다고 보면 됩니다. useRef를 통해 만든 값이 일반 객체와 다른점은, 매 렌더링 시 동일한 객체를 제공하는 것입니다. - useRef<T>(initialValue: T): {current: T} - (참고: https://github.com/facebook/react/blob/fd31724d5d41a4df71af55f710b46562874511f9/packages/react-reconciler/src/ReactInternalTypes.js#L388) - (참고: https://ko.reactjs.org/docs/hooks-reference.html#useref) useRef를 통해 받은 값은 useRef가 사용된 컴포넌트의 라이프사이클 동안 사용할 수 있습니다. JSX에 특정 DOM 컴포넌트에 ref={useRef를 통해 받은 값}을 추가하시게 되면, 컴파일러가 JSX를 컴파일하면서 JS로 변환을 시켜주고 해당 JS를 브라우저가 실행하면서 값을 채워넣어주는 것으로 알고있습니다. (참고: https://ko.reactjs.org/docs/jsx-in-depth.html) stale closure의 경우는 useRef의 값이 항상 같은 객체이고 closure안에서는 이 객체를 "참조"하는 형식이기 때문에, 객체의 current 값이 바뀌면 closure 안에서도 바뀐 current 값을 사용할 수 있습니다. (reference 관련 so 글 참고: https://stackoverflow.com/questions/8318357/javascript-pointer-reference-craziness-can-someone-explain-this) 저도 질문에 답변하려고 여러가지 실험들을 해보았는데요. Functional component로 gist를 한번 만들어보았습니다. 저도 헷갈리는 부분이 있긴하지만 확실히 일반 JS 객체 대신 왜 useRef를 사용해야하는 이유를 알게된 것 같네요. (https://gist.github.com/json9512/ef28abf2429c639ccbdd6b48c7c3efe5) 결론은 useRef의 동작 방식이 가능한 이유는 JS에서 객체를 접근할때 참조 형식으로 접근한다는 점과 useRef를 통해 받은 객체가 항상 동일하다는 점 때문인 것 같습니다. 제가 참고했던 아티클들 첨부할게요. - https://dmitripavlutin.com/react-useref-guide/ - https://www.freecodecamp.org/news/react-under-the-hood/ - https://www.smashingmagazine.com/2020/11/react-useref-hook/ - https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Working_with_Objects#%EA%B0%9D%EC%B2%B4_%EA%B0%9C%EC%9A%94 - https://hewonjeong.github.io/deep-dive-how-do-react-hooks-really-work-ko/

이 질문 바로 가기

2. useRef를 이용하는 것과 그냥 변수를 정의하는 것의 차이는 무엇인가요?

useRef를 통해 특정 element를 선택할 수도 있지만 값을 넣어 사용하는 경우도 있다고 하는데 그냥 변수로 사용하는 것과 어떤차이가 있나요? 아래 두 코드의 차이점이 궁금합니다. export default function Test () { const a = useRef(0) } export default function Test () { const a = 0 }


답변

useRef는 DOM element를 저장하는 것 뿐만 아니라 질문주신대로 number를 비롯해 어떤 값이든 저장할 수 있습니다. 이를 저장할 수 있는 배경은 사실 useRef()가 사실 순수 Javascript 객체를 생성하기 때문인데요! 질문에 있는 값이 0이 0 고대로 저장되는 것이 아니라 { current: 0, ... } 같이 객체의 형태로 값이 관리됩니다. 그러면 const a = { current: 0 } 이랑은 무슨 차이가 있느냐고 말씀하실 수 있는데 React 공식 문서의 말을 빌리자면 객체 자체를 생성하는 것의 유일한 차이점은 useRef는 매번 렌더링을 할 때 동일한 ref 객체를 제공한다는 것입니다. const a = 0 이나 const a = { current: 0 } 의 경우는 렌더링될 때마다 값을 초기화 합니다. 따라서 컴포넌트의 라이프사이클동안 관리해야하는 변수에는 적절하지 않습니다. + 마지막 TMI로 useRef에서 생성하는 객체는 heap 영역에 저장되므로 웹앱이 종료되거나 가비지 컬렉팅되기 전까지 참조할 때마다 동일한 메모리 값을 가집니다. 참고. React 공식문서 useRef 섹션 https://ko.reactjs.org/docs/hooks-reference.html#useref

외 1개 답변 보러 가기

3. typescript에서 useRef.current는 null 체크가 필수인가요?

useRef를 이용해서 focus를 하도록 하고 있는데 typescript에서 오류가 납니다. 아래 코드와 같습니다. 동작은 잘 하는데 코드의 밑줄이 신경쓰입니다.


답변

안녕하세요! 결론부터 말씀드리면, inputRef.current의 타입은 TextInput이라는 것이 확실시되어야 해요. focus는 null이 아니라 TextInput이라는 타입에 존재하는 메소드이기 때문입니다. (엄밀하게는 null 체크를 해야한다기보다 TextInput이라는 것이 확실시되어야 한다는 말이 정확해요. 왜냐하면 useRef 선언 시점에 null을 넣지 않고 string이나 number같은 타입을 넣어도 focus를 동작하게 할 수는 있거든요. 사실 아무도 그렇게 쓰지도 않고 별로 중요한 얘기도 아니라, 여기서는 그냥 넘어가겠습니다🙂) 따라서 아래와 같이 사용하시면 될 것 같아요. 1번) if문 사용 useEffect(() => { if (isRead && inputRef.current) { inputRef.current.focus(); } }, []); 2번) optional chaining 사용 useEffect(() => { if (isRead) { inputRef.current?.focus(); } }, []); 3번) Type Assertion(타입 단언) 사용 <- 이 부분이 맨 처음 말씀드린 내용인데, 이렇게 쓰는 게 별로 일반적이지는 않습니다. 1번 또는 2번 방법 중에서 골라 쓰시면 될 거 같아요! useEffect(() => { (inputRef.current as TextInput).focus(); }, []); 추가 설명드리면, 질문자님께서 만드신 코드의 if (isRead && inputRef && inputRef.current)에서 inputRef는 null 체크를 할 필요가 없습니다. 왜냐하면 inputRef에는 useRef 선언 당시 React.RefObject<TextInput>라는 타입의 object를 할당하기 때문이에요. inputRef의 타입: React.RefObject<TextInput> inputRef.current의 타입: TextInput 또는 null 혹시 이게 진짜인지 궁금하시면 아래 예시 코드와 같이 로그를 한 번 찍어보세요! import { useEffect, useRef } from "react"; const App = () => { const inputRef = useRef<HTMLInputElement>(null); console.log("실제 DOM에 마운트되기 전이에요."); console.log(inputRef); console.log(inputRef.current); useEffect(() => { console.log("실제 DOM에 마운트된 후에요."); console.log(inputRef); console.log(inputRef.current); }, []); return <input ref={inputRef} />; }; export default App; 아마 아래처럼 로그가 찍힐겁니다. 실제 DOM에 마운트되기 전이에요. { current: null } null 실제 DOM에 마운트된 후에요. { current: input } <input> inputRef는 마운트 전/후 모두 null인 적이 없고 언제나 current를 속성으로 가지고 있는 object네요! 조금이나마 도움이 되셨길 바라며, 혹시라도 더 궁금하신 점 있으시면 댓글 남겨주세요~

외 2개 답변 보러 가기

4. input form 데이터 땡겨올 때 useRef를 많이 사용하지 않는 이유가 있나요?

회원가입 폼에 input에 useRef로 값 땡겨오면 값이 변할 때 재랜더링이 없어서 좋을 것 같은데 깃헙 다른 분들 코드 보면 거의 Hooks으로 useInput 써서 하는데 무슨 이유로 더 간단한 useRef를 안 쓰고 기존 onChage 형식으로 하는 건가요?


답변

보통 React가 직접 상태 제어를 하는 방식을 제어 컴포넌트(Controlled Components)라고 부르며, 브라우저의 DOM이 상태 제어를 하도록 자연스럽게 두는 방식을 비제어 컴포넌트(Uncontrolled Components)라고 합니다. React의 `useState()` hook나 `useState()`를 내부적으로 호출하는 `useInput()`과 같은 커스텀 hook 함수를 사용하면 제어 컴포넌트가 되며, React의 `userRef()` hook을 사용하면 비제어 컴포넌트가 되지요. `useRef()` hook를 사용하면 굳이 상태 저장 변수와 상태 변경 함수가 짝으로 필요가 없으며 `<input>` 요소에 `ref` prop만 넘겨주면 되서 개발이 좀 더 간편하게 느껴질 수 있습니다. 또한 말씀하신 것처럼 상태가 변할 때 마다 다시 랜더링이 일어나지 않기 때문에 성능상 이점도 있는 것도 사실이죠. (하지만 React가 얼마나 빠른지를 생각해보면 이 부분은 논쟁의 여지가 있기도 합니다.) 하지만 React의 공식 문서를 보시면 아래와 같이 대부분의 경우에는 양식(form) UI를 구현할 때 제어 컴포넌트를 사용하라고 권장하고 있습니다. > In most cases, we recommend using controlled components to implement forms. In a controlled component, form data is handled by a React component. The alternative is uncontrolled components, where form data is handled by the DOM itself. (출처: https://reactjs.org/docs/uncontrolled-components.html) 양식 UI를 구현할 때 React를 통해서 상태 관리를 하면 브라우저의 DOM을 통해서는 어려운 좀 더 섬세한 상태 제어가 가능하고 입력값 검증이 용이해집니다. 예를 들어 유효하지 않은 입력 값에 대해서 시각적인 피드백을 주거나, 하나의 입력 값이 다른 입력 값에 영향을 줄 수 있는 경우를 생각해볼 수 있겠습니다. 이러한 기능을 `useRef()`을 사용해서 구현하는 것이 아예 불가능하지는 않겠지만, `useState()`을 사용하면 상대적으로 훨씬 더 쉽게 구현할 수 있습니다. 게다가 이러한 경우에는 어차피 다시 랜더링을 해줘야하기 때문에 오히려 자동으로 랜더링이 일어나지 않는 부분이 단점으로 작용할 수 있습니다. 따라서 이렇게 "알아서 반응하는" React의 장점을 극대화하시려면 일반적으로 `useState()`의 사용을 먼저 고려하시는 것이 유리하며, `useRef()`는 사실 양식 UI를 구현할 때 보다는 다른 좀 더 제한적인 상황에서 빛을 발휘하는 경우가 많습니다. 이 부분에 대해서는 제가 따로 정리한 블로그 포스팅을 아래에 공유드리오니 한 번 읽어보시면 도움이 될 것 같습니다. - React Hooks: useRef 사용법: https://www.daleseo.com/react-hooks-use-ref/ - React로 비제어 양식 UI를 만드는 방법: https://www.daleseo.com/react-uncontrolled-components/

이 질문 바로 가기

지금 가입하면 모든 질문의 답변을 볼 수 있어요!

현직자들의 명쾌한 답변을 얻을 수 있어요.

또는

이미 회원이신가요?

키워드로 질문 모아보기

실무, 커리어 고민이 있다면

새로운 질문 올리기

지금 가입하면 모든 질문의 답변을 볼 수 있어요!