0. React에는 왜 Hooks가 있을까? (공통 글)
리액트는 라이브러리이다.
기존에 HTML, CSS, Javascript로만 구성된 프로젝트에서 JSX를 해석해 줄 Babel만 있으면 부분적으로 도입할 수 있다는 이야기다.
처음에 리액트는 클래스형 컴포넌트가 많이 쓰였다. 여러 가지 이유가 있었지만, 함수형 컴포넌트는 내부적으로 상태를 가지고 있을 수 없기 때문이었다.
Q. 왜 함수형 컴포넌트는 상태를 저장할 수 없나요?
A. 우리가 무언가를 '저장'한다고 생각해봅시다. 변수를 선언하지 않고 저장하는 방법이 있을까요?
없습니다. 클래스는 그 자체가 객체로 태어나면서 변수 안에 데이터를 가지고 있을 수 있습니다. 하지만 함수는 어떤가요? 함수는 실행형 문구입니다. 그냥 한 번 '실행'되고 끝나는 것이죠. 무언가를 저장할 수 없습니다.
이런 단점을 보완하기 위해서 2019년 페이스북은 리액트 16.8 버전에 Hooks를 도입한다.
Hooks는 함수형 컴포넌트에서 상태를 보존할 수 있는 방법을 제공했고, 그 외에도 컴포넌트의 생애주기(life-cycle)에서 단계별로 여러 가지 기능을 수행할 수 있도록 방법을 제공했다.
리액트에서 제공하는 Hooks는 기본적으로 3가지(useState, useEffect, useContext)이며, 그 외에는 10가지 추가 Hooks가 있고 라이브러리 형태로 2가지를 더 제공한다.
1. useRef 설명
useRef는 컴포넌트가 유지되는 동안에 변하지 않았으면 하는 값을 저장합니다.
state와는 다릅니다.
1. State가 변할 경우에는 컴포넌트가 렌더링되면서 컴포넌트 내부 변수들이 모두 초기화됩니다.
2. 하지만 Ref안의 값을 바꿔도 리액트는 컴포넌트를 다시 렌더링하지 않습니다.
즉, 해당 Ref의 값은 화면이 다시 그려지지 않지만 유지가 된다는 뜻입니다.
간단한 예시코드를 통해서 살펴봅시다.
useState를 통해 관리하는 state는 해당 상태가 변하면 화면을 다시 그린다고 했습니다. (렌더링)
useRef는 그렇지 않고요. 이 차이점을 잘 염두하고 따라오시면 됩니다.
import React, { useEffect, useRef, useState } from "react";
import "./App.css";
function App() {
const [count, setCount] = useState(0);
const countRef = useRef(0);
console.log(">> 렌더링 >>");
const upCountState = () => {
setCount(count + 1);
};
const upCountRef = () => {
countRef.current = countRef.current + 1;
console.log("Ref >> ", countRef.current);
};
return (
<>
<p>State: {count}</p>
<p>Ref: {countRef.current}</p>
<button onClick={upCountState}>State UP</button>
<button onClick={upCountRef}>Ref UP</button>
</>
);
}
export default App;
useRef는 내부의 current라는 변수를 통해 현재 가지고 있는 값을 알 수 있습니다.
실제로 countRef 자체의 값을 출력해보면, 내부에 current라는 값만 있는 것을 알 수 있습니다.
해당 예시코드는 이렇게 화면이 나옵니다.
(색상과 배열은 제가 Vite를 통해서 빌드했기 때문에 이렇게 보입니다.)
(색상과 배열은 제가 Vite를 통해서 빌드했기 때문에 이렇게 보입니다.)
여기서 State UP을 누르면 화면은 계속 그려지면서 숫자가 올라가며,
Ref UP을 누르면 화면은 변하지 않지만 console에 값은 계속 찍힙니다.
Ref UP을 누르다가 State UP을 누르면 한 번에 Ref의 숫자가 확 늘어서 찍히는 것을 알 수 있는데,
이것은 화면이 그려지지 않다가 다시 그려지면서 가지고 있던 값을 출력해서 그렇습니다.
2. 두 번째 사용 예시코드
두 번째 사용 예시로 Ref를 통해 DOM에 접근하는 것이 있습니다.
예제 코드를 통해 알아봅시다.
import React, { useRef } from "react";
import "./App.css";
function App() {
const inputElement = useRef();
const focusInput = () => {
inputElement.current.focus();
};
return (
<>
<input type="text" ref={inputElement} />
<button onClick={focusInput}>Focus Input</button>
</>
);
}
export default App;
이렇게 작성하면 아래와 같이 뜹니다.
코드의 흐름을 볼까요?
리액트에서 제공하는 ref라는 변수가 있습니다.
<input type="text" ref={inputElement} />
요 부분입니다.
ref의 값에 useRef()를 받은 변수를 넣으면, 그 변수를 통해 해당 요소를 제어할 수 있습니다.
지금 우리가 제어할 수 있는 요소는 input 태그이죠?
input 태그는 HTMLElement이기 때문에 focus 메서드를 지원합니다.
참고: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus
useRef는 current라는 변수를 통해 해당 요소에 접근합니다.
순서를 다시 살펴봅시다.
1. 내가 접근하고 싶은 태그에 ref라는 변수를 선언하고
2. 거기에 useRef를 받아온 변수를 넣는다.
2. useRef를 받아온 변수.current.내가사용하고싶은메서드 <-- 이렇게 사용해서 해당 Element를 컨트롤한다.
이렇게 사용하게 됩니다.