
리액트 앱 프로젝트를 진행하면서 useRef를 사용한 여러 기능들을 구현하고 있다. 슬라이드 배너, 클릭 위치에 따라 다른 위치에 뜨는 팝업창 등 여러 곳에 사용하긴 했는데 사실 gpt가 도와주고 구글링이 도와줘서 사용할 수 있었던 것이지 내가 아는 것이 아니었다. 그냥 써야 돼서 쓰는 것과 제대로 알고 적재적소에 쓰는 것은 다르다.
따라서 오늘은 useRef란 무엇인지 어떨 때 사용해야 효율적인지에 대해 알아보도록 하겠다.
useRef는 .current 프로퍼티로 전달된 인자(initialValue)로 초기화된 변경 가능한 ref 객체를 반환한다. 반환된 객체는 컴포넌트의 전 생명주기를 통해 유지된다.
const 변수명 = useRef(초기값);
➡️ 이러한 결과 값으로 {current: 초기값}을 지닌 객체가 반환된다. 다음 렌더링에서 useRef는 동일한 객체를 반환한다.
useRef에서 기억할 것은 이러한 current라는 키 값을 지닌 프로퍼티가 생성되고 값에 어떤 변경을 줄 때도 current를 이용해서 한다는 점이다.
ref.current 프로퍼티를 변경해도 React는 컴포넌트를 리렌더링하지 않는다.
<input ref={변수명}/>
import { useRef } from 'react';
export default function Counter() {
let ref = useRef(0);
function handleClick() {
ref.current = ref.current + 1;
alert('You clicked ' + ref.current + ' times!');
}
return (
<button onClick={handleClick}>
// {ref.current}
Click me!
</button>
);
}
{ref.current} 를 표시하면 클릭 시 번호가 업데이트 되지 않는다.ref.current를 설정해도 리렌더링을 촉발하지 않기 때문이다.❗주의사항
1.ref.current는 state와 달리 변이할 수 있다. 그러나 렌더링에 사용되는 객체를 포함하는 경우 변이해서는 안된다.
2. 초기화를 제외하고는 렌더링 중에ref.current를 쓰거나 읽으면 안된다. 이벤트 핸들러(함수)나 useEffect를 통해 하자!
3.ref를 변경해도 리렌더링이 되지 않기 때문에 시각적 출력에 영향을 미치지 않는 정보를 저장하는 데 적합하다.
4. 리렌더링이 되지 않기에 화면에 즉각적으로 반영되는 정보를 저장하는 데에는 적합하지 않다.
ref를 사용하여 DOM을 조작하는 것이 일반적이다.null인 ref 객체를 선언한다.import { useRef } from 'react';
function MyComponent() {
const inputRef = useRef(null);
// ...
ref 객체 속성으로 조작하려는 DOM 노드의 JSX에 전달한다. // ...
return <input ref={inputRef} />;
ref 객체의 current를 DOM 노드로 설정한다.<input>에 접근하여 focus()와 같은 메서드를 호출할 수 있다. function handleClick() {
inputRef.current.focus();
}
노드가 화면에서 제거되면 React는 current를 다시 null로 설정한다.
import { useRef } from 'react';
export default function Form() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
<input ref={inputRef} />
<button onClick={handleClick}>
Focus the input
</button>
</>
);
}
import { useRef } from 'react';
export default function CatFriends() {
const listRef = useRef(null);
function scrollToIndex(index) {
const listNode = listRef.current;
// 다음 코드는 특정 DOM 구조를 가정합니다:
const imgNode = listNode.querySelectorAll('li > img')[index];
imgNode.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'center'
});
}
return (
<>
<nav>
<button onClick={() => scrollToIndex(0)}>
Neo
</button>
<button onClick={() => scrollToIndex(1)}>
Millie
</button>
<button onClick={() => scrollToIndex(2)}>
Bella
</button>
</nav>
<div>
<ul ref={listRef}>
<li>
<img
src="https://placecats.com/neo/300/200"
alt="Neo"
/>
</li>
<li>
<img
src="https://placecats.com/millie/200/200"
alt="Millie"
/>
</li>
<li>
<img
src="https://placecats.com/bella/199/200"
alt="Bella"
/>
</li>
</ul>
</div>
</>
);
}
| 메서드 | 용도 | 언제 사용하는가 |
|---|---|---|
focus() | 포커스 설정 | 폼 제출 시 특정 인풋에 자동 포커스 줄 때 (예: 로그인 첫 입력창) |
blur() | 포커스 해제 | 사용자가 입력 후 포커스를 제거하고 싶을 때 |
scrollIntoView() | 요소로 스크롤 이동 | 특정 섹션으로 부드럽게 스크롤할 때 (ex: 메뉴 클릭 → 해당 섹션 이동) |
scrollTo(x, y) | 절대 위치로 스크롤 | 특정 좌표로 스크롤할 때 (ex: 최상단 이동 등) |
scrollBy(x, y) | 상대 위치로 스크롤 | 현재 위치 기준으로 스크롤할 때 (ex: 100px 아래로 이동) |
classList.add() | 클래스 추가 | 특정 스타일을 동적으로 추가할 때 (ex: 에러 표시 등) |
classList.remove() | 클래스 제거 | 특정 스타일을 제거하고 싶을 때 (ex: 에러 해제 등) |
classList.toggle() | 클래스 토글 | 클릭 시 스타일을 켜고 끄고 싶을 때 (ex: 아코디언 메뉴) |
getAttribute() | 속성 값 가져오기 | 요소의 특정 속성 값을 읽어야 할 때 (예: data-id) |
setAttribute() | 속성 값 설정 | 동적으로 속성을 바꿔야 할 때 (예: aria-expanded 조작) |
removeAttribute() | 속성 제거 | 속성이 더 이상 필요 없을 때 제거 (예: disabled 해제 등) |
getBoundingClientRect() | 요소 위치/크기 계산 | 뷰포트 내에서 위치 측정할 때 (예: 툴팁 위치 계산 등) |
element.style.property = value | 인라인 스타일 변경 | 동적으로 스타일을 적용할 때 (ex: display: 'none') |
오늘은 useRef 훅에 대해서 알아보았다. focus, scroll 등 DOM을 조작하거나 리렌더링 없이 값을 저장할 때 사용한다고 생각할 수 있다. 주로 input 창의 focus 또는 스크롤 조작에 많이 사용할 수 있을 것 같다. 앞으로도 자유자재로 잘 사용할 수 있도록 노력해봐야겠다.
꾸준한 모습 멋져요!