리액트를 사용해 개발을 하다 보면, 'useState' 나 'useEffect' 같은 훅을 많이 사용하게 된다.
하지만 저 둘에 비해 'useRef' 훅은 자주 사용하지 않았었는데
이번에 동적인 랜딩 페이지를 구현하면서 사용하게 되었다.
사용해 본 김에 확실히 정리하여 다음번에 사용할 때 안 까먹기 위해 글로써 정리해보려 한다.
📕 1. useRef 란?
리액트 공식문서에 따르면, useRef는 렌더링에 필요하지 않은 값을 참조할 수 있는 React 훅이라고 설명되어 있다.
여기서 "렌더링에 필요하지 않은 값"이 무슨 말인지 내가 이해한 방식대로 설명해 보겠다.
예를 들어 + 버튼을 누르면 숫자가 1씩 증가하는 버튼이 있다고 상상해 보자.
이 버튼을 눌렀을 때 사용자가 1씩 증가하는 것을 직접적으로 확인할 수 있게 하려면,
버튼을 누를 때마다 '렌더링'을 통해 숫자를 1이 증가한 숫자로 바꿔줘야 한다.
하지만 1씩 증가하는 변수를 관리 하면서도 사용자에게까지 굳이 보여줄 필요가 없는 경우가 있다면,
매번 바뀐 숫자를 렌더링 하는 것은 불필요하다. 그럴 때 useRef를 사용해 변수를 관리하는 것이라고 이해했다.
그러니깐 다시 요약하면, useRef는 "즉시 렌더링을 할 필요가 없는 값"을 참조하는 훅이라고 이해하면 될 것 같다.
여기까지 읽으면 이해가 부족할 수도 있는데, 밑에 적을 useRef의 사용 방법을 보면서 이해가 되면 좋겠다.
📕 2. 그렇다면 useRef를 왜 사용할까?
useRef는 보통 다음과 같은 2개 용도에 사용된다.
1. 재렌더링 없이 데이터 저장하기
2. DOM 요소에 접근하기
✅ 2-1. 재렌더링 없이 데이터 저장하기.
먼저 위에서 먼저 설명했듯이, useRef는 변수를 관리하는 데에 쓰일 수 있다.
특히 화면에 즉시 반영할 필요가 없는 변수를 관리하는데 유용하다.
여기까지 읽다 보면,
그거 변수 useState나 그냥 let이나 const 같은 변수를 통해 관리하면 되는 거 아닌가? 하는 생각이 들 텐데
이 글을 읽는 우리는 개발자니깐~~! 코드를 통해 설명을 돕도록 하겠다.
먼저, let 변수와 useState, 그리고 useRef를 비교하기 위해
각각 변수를 만들고 각각 변수와 연결된 버튼을 만들어
버튼을 누르면 해당되는 변수들이 +1 되게 만들어놨다.
먼저, useState를 통해 변수를 +1씩 증가시키는 모습을 보면
변수를 +1씩 증가할 때마다 UI가 바로 바뀌는 것을 확인할 수 있다.
왜냐하면 useState에서는 상태를 업데이트할 때마다 컴포넌트가 재렌더링되기 때문인데,
이렇게 컴포넌트가 바로바로 재렌더링 되는 것은 사용자 측면에서는 변하는 값을 즉시 확인할 수 있다는 장점이 된다.
하지만, 상태가 변할 때마다 컴포넌트를 재렌더링하기 때문에 성능 측면에서는 좋지 않다는 단점이 되기도 한다.
그래서 변수가 변경될 때마다 사용자가 확인해야만 하는 상황이 아니라면 useState는 성능의 저하를 가져오는 방법이 되기도 한다. 이런 문제점을 해결하기 위해서는 보통 useState가 아닌 var, let, const를 사용한 변수를 사용하는 방법을 떠올리게 된다.
이번에는 let을 사용해 변수를 실시간으로 변경시켜 보겠다.
버튼을 누를 때마다 변숫값을 console.log로 확인할 수 있게 해 놨는데
위 gif처럼 버튼을 누를 때마다 실제로 letCount라는 변수가 +1씩 증가하는 것을 확인할 수 있다.
하지만 let을 사용하게 되면 치명적인 단점이 존재한다.
바로, 컴포넌트가 다른 이유로 재렌더링 되는 과정에서 컴포넌트 내부의 변수들이 초기화된다는 점이다.
아무리 버튼을 통해 변수의 값을 증가시켜도 컴포넌트가 재렌더링 되면 값이 초기화되버린다.
그렇기 때문에 만약 useState와 같이 재렌더링을 불러오는 훅들과 함께 사용하면, 변숫값이 초기화되어 개발자가 의도하는 대로 동작하지 않게 된다.
요약하면 useState는 재렌더링을 유발하고, let과 같은 변수는 재렌더링시 변수가 초기화된다.
이러한 각각의 단점들을 보완한 것이 useRef인데, 재렌더링이 필요 없는 상황에서 변수를 유지하기 위해 주로 사용한다.
gif를 자세히 보면 useRef의 변수를 +1씩 증가시켜도 재렌더링되지 않기 때문에
화면상의 값이 변하지 않는 것을 알 수 있다.
하지만 중간중간에 useState에 담긴 변수를 +1 하면 컴포넌트가 재렌더링되기 때문에
useRef 객체에 저장된 current 값이 갱신되게 된다.
let에 담긴 변수를 재렌더링하면 값이 초기화되는 거에 비해 useRef를 사용하면 재렌더링 되어도 값이 유지가 된다.
이렇듯 useRef는 재렌더링을 하지 않기 때문에 useState에 비해 성능 측면에서 이득을 볼 수 있다는 장점이 있다.
즉, 리액트 컴포넌트의 라이프 사이클이 끝날 때까지 변수의 값을 유지함과 동시에,
UI를 다시 그릴 필요가 없을 때 사용하는 것이 useRef라고 이해하면 될 것 같다.
✅ 2-2. DOM 요소에 접근하기
useRef는 위에서 언급한 저장 공간으로서의 기능 외에도 DOM 요소에 접근하는 기능을 제공한다.
위의 코드처럼 inputRef라는 이름의 useRef 객체를 생성하고,
input 창에 ref={inputRef}를 넣음으로써 DOM에 있는 인풋 요소를 선택하게 된다.
console.log를 통해 inputRef 변수를 찍어보면,
ref 안에 current값에 input을 지정하고 있음을 알 수 있다.
그리고 '인풋창 포커스'를 누르면 현재 DOM에서 선택한 요소인 인풋창에 포커스가 생기는 것을 확인할 수 있다.
이렇듯, useRef는 DOM에 있는 요소를 선택해 애니메이션을 주거나 UI 요소를 다룰 때 사용하게 된다.
내가 진행 중인 프로젝트에서는 화살표 버튼을 눌렀을 때, 특정 스크롤로 이동하는 기능과
특정 스크롤로 이동했을 때 사진들이 보여지는 애니메이션을 구현하기 위해 useRef를 사용했는데,
이에 대한 내용은 다음 번에 작성할 "랜딩페이지 구현하기"에서 더 자세하게 작성하도록 하겠다.
📕3. 느낀 점
리액트를 사용하며 useRef를 쓸 일이 그렇게 많지 않아서 얕게 이해하고 있었는데,
이번 기회에 좀 더 자세하게 이해할 수 있었고
자주 사용하지 않는 useMemo와 같은 다른 리액트 훅들도 정리해야겠다는 생각이 든 좋은 계기가 되었다 !
'🎬Project' 카테고리의 다른 글
[Project] Vercel 배포시에 이미지 렌더링 속도 저하 문제를 해결한 경험 (5) | 2024.06.24 |
---|---|
[Next.js] useRef를 활용하여 동적인 랜딩 페이지 구현하기 (0) | 2024.05.06 |
[Next.js] 외부 API KEY값 노출 문제 해결하기 (0) | 2024.04.18 |
[Next.js] AWS S3와 next/image를 사용해 이미지 최적화를 진행한 경험 (0) | 2024.04.13 |
PWA(프로그래시브 웹 앱) (0) | 2023.10.15 |