

컴포넌트 내부의 변수로써 일반적인 값들을 저장할 수 있는 Ref는 State와 비슷하지만, 값이 변경되어도 컴포넌트가 리렌더링되지 않습니다. 따라서 렌더링에 영향을 미치고 싶지 않은 요소를 변수를 생성할 때 사용하며, 추가적으로 컴포넌트가 렌더링하는 특정 돔(DOM) 요소들을 직접 접근하고 조작할 수 있습니다.
const refObj = useRef();
console.log(refObj); //{current: undefined}
console.log(refObj.current); //undefined
Ref 객체는 단순히 현재 값이 얼마인지 저장된 간단한 요소입니다. 값을 불러오거나 변경할 때는 변수와 마찬가지로 .current를 사용합니다.
useRef는 useState와 같이 새로운 Reference 객체를 생성하는 기능입니다. 따라서 useRef을 먼저 react로부터 불러옵니다.
import { useRef, useState } from "react";
이전에 useState에서 사용했던 입력 폼에 ref 기능을 더해봅니다. input 에 무언가를 입력할 때마다, 얼마나 입력했는지 갯수를 세는 countRef 입니다.
const Register = () => {
const [input, setInput] = useState({
name: "",
gender: "",
});
const countRef = useRef(0);
const onChange = (e) => {
countRef.current++;
console.log(countRef.current);
setInput({
...input,
[e.target.name]: e.target.value,
});
};
return (
<div>
<div>
<input
name="name"
value={input.name}
onChange={onChange}
placeholder={"이름"}
/>
</div>
<div>
<input
name="birth"
value={input.birth}
onChange={onChange}
type="date"
/>
</div>
</div>
);
};
아래와 같이 리렌더링이 되지 않으면서 숫자를 입력할 때마다 console의 값이 증가합니다. 이와 같이 리렌더링되지 않는 변수를 조작할 때 사용하기 용이합니다.

❗ 그렇다면 일반 객체와 뭐가 다를까?
이전에
state글에서 일반 객체 대신state를 사용하는 이유는,state는 자바스크립트 객체와 달리 즉각 화면에 변화가 반영되기 때문이라고 했습니다. 그렇다면 리렌더링이 되지 않는ref객체는 왜 사용하는 걸까요?const Register = () => { (...) let count = 0; const onChange = (e) => { count++; console.log(count); (...)위와 같이
ref객체를 모두 일반 객체로 바꾸어 보았습니다. 그다음 콘솔에 똑같이 출력해보니, 이전과 다르게 값이 증가하는 게 아닌 똑같은 1이 계속하여 출력되는 것을 볼 수 있습니다.
그 이유는 생각보다 간단합니다.<input>객체에 입력할 경우onChange이벤트 핸들러가 실행되어setInput함수가 함께 실행되고, 그렇게 되면state값이 변경되어Register컴포넌트가 리렌더링됩니다.Register컴포넌트에서let count = 0으로 변수를 선언하고 있기 때문에, 결국count는input이 바귈 때마다 0으로 리셋되는 셈입니다.
그런데,useRef혹은useState로 만든 특수한 객체들은 리액트 내부적으로 컴퍼넌트가 리렌더링 된다고 해도 다시 리셋이 되지 않습니다. 그렇기 때문에countRef의 값은 리셋되지 않고 계속 증가할 수 있는 것입니다.
여기서 한 가지 더, 그렇다면count를 Register 컴포넌트 밖으로 전역 변수로써 선언하면 되지 않을까요? 결과적으로, 문제 없이 동작합니다. 하지만 같은 변수를 사용하는 컴포넌트가 두 개 이상 있을 경우 두 함수가 같은 변수를 공유하여 원하는 결과가 나오지 않을 수 있기 때문에 권고하지 않고 있습니다.
ref 객체를 사용하면 돔 요소에 접근하여 style를 바꾸거나 강조 등의 효과를 줄 수 있습니다.
아래처럼 <input> 에 사용자가 입력을 하고 작성 완료 버튼을 눌렀을 때, 입력창이 비어 있다면 해당 칸을 focus 하도록 만드려고 합니다.
이렇게 특정 DOM 요소에 포커스를 주려면 return 문 안에 있는 <input> 태그에 접근할 수 있어야 하는데, ref 를 사용하면 <input> 태그가 렌더링하는 DOM 요소가 레퍼런스 오브젝트 객체에 저장이 됩니다.
import { useRef, useState } from "react";
function Body() {
const [text, setText] = useState("");
const textRef = useRef();
const handleOnChange = (e) => {
setText(e.target.value);
};
const handleOnClick = () => {
if (input.name === ""){
console.log(inputRef.current); //<input value>
inputRef.current.focus();
};
return (
<div>
<input ref={textRef} value={text} onChange={handleOnChange} />
<button onClick={handleOnClick}>작성 완료</button>
</div>
);
}
위와 같이 inputRef의 current 값을 콘솔에 찍어보면 <input> 요소가 나오게 되며, 따라서 inputRef의 current 값에 focus를 주면 원하는대로 효과가 나타나게 됩니다.
🔗참고 자료
한 입 크기로 잘라먹는 리액트