Hooks
컴포넌트가 드롭다운 열림 상태를 기억해야 하거나, 렌더링 후 서버와 동기화하기 위해 코드를 실행해야 할 때가 있습니다. 둘 다 Hook의 역할입니다. 여러분은 이미 두 개를 사용했습니다: useState는 컴포넌트에 메모리를 주고, useEffect는 렌더링 후 코드를 실행해서 React 밖의 세상과 연결합니다. 이 둘이 가장 많이 쓰이는 Hook이고, 나머지 built-in Hook들도 같은 패턴을 따릅니다. 컴포넌트 내에서 함수를 호출하면 React의 특정 기능에 연결되는 것입니다. 그 함수의 이름은 항상 use로 시작하는데, 이 접두사 자체가 Hook의 정의입니다.
자주 쓰이는 built-in Hook들
처음부터 모든 Hook을 알아야 하는 건 아니지만, 필요할 때 올바른 도구를 인식할 수 있도록 어떤 Hook들이 있는지 아는 것이 도움됩니다. 자주 나오는 것들입니다:
- **
useRef**는 렌더링을 일으키지 않으면서도 렌더링을 거쳐 남아있는 가변 값을 보관합니다. 또한 input에 포커스를 맞추는 것처럼 DOM 엘리먼트에 직접 접근해야 할 때도 이 Hook을 사용합니다. - **
useContext**는 상위 provider로부터 공유된 데이터를 읽어서, 깊이 중첩된 컴포넌트가 모든 단계마다 prop을 전달받지 않고도 값에 접근할 수 있게 합니다. - **
useReducer**는 reducer 함수를 통해 state를 관리하는데, 각 업데이트는 현재 state에서 다음 state로의 전환을 나타냅니다. state 변화가 복잡해져서 단순한useState하나로는 부족할 때 좋습니다. - **
useMemo**는 계산된 값을 렌더링 사이에 캐시했다가, 입력값이 바뀔 때만 다시 계산합니다. - **
useCallback**은 함수에도 같은 작업을 합니다. 매번 새로운 함수 인스턴스를 만드는 대신 렌더링 사이에 같은 함수를 돌려줍니다.
useMemo와 useCallback은 실제 성능 이야기가 있을 때 더 자세히 다룹니다. Refs와 DOM에서 useRef를 자세히 봅니다. useContext와 useReducer는 이 장에서 가장 깊이 있게 다루니, 필요할 때 여기로 돌아오세요. 지금은 패턴만 기억하세요: Hook은 특정 React 기능으로 들어가는 통로이고, 이름을 보면 Hook임을 알 수 있습니다.
Hook의 규칙
Hook에는 두 가지 규칙이 있으며, React는 이를 따를 것을 기대합니다.
첫째, Hook을 컴포넌트의 최상위 레벨에서만 호출하세요. 조건문, 반복문, 중첩 함수 안에서 호출하면 안 됩니다. 컴포넌트가 사용하는 모든 Hook은 매 렌더링마다, 같은 순서로, 매번 실행되어야 합니다.
function Profile({ isLoggedIn }) {
// 틀렸음: 이 Hook은 때때로만 실행됨
if (isLoggedIn) {
const [name, setName] = useState('')
}
// 맞음: 최상위 레벨에서 조건 없이 호출
const [name, setName] = useState('')
// ...그 다음 로직 안에서 isLoggedIn을 사용
}둘째, Hook은 React 함수 컴포넌트나 다른 custom hook에서만 호출하세요. 일반 JavaScript 함수는 Hook을 사용할 수 없습니다. React가 state를 붙일 컴포넌트 렌더링이 없기 때문입니다.
자신만의 Hook 만들기
여러 컴포넌트가 브라우저 창의 너비를 알아야 한다고 가정해봅시다. 같은 useState와 useEffect 코드를 각 컴포넌트에 복사하는 대신, 한 번만 패키징할 수 있습니다:
import { useState, useEffect } from 'react'
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth)
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth)
window.addEventListener('resize', handleResize)
return () => window.removeEventListener('resize', handleResize)
}, [])
return width
}이제 어떤 컴포넌트든 const width = useWindowWidth()를 호출하면 창이 리사이즈될 때 업데이트되는 값을 얻습니다. useWindowWidth는 custom hook입니다: 다른 Hook들을 호출하는 함수 말입니다. 그게 전부인 패턴이고, 함수이기 때문에 상태를 가진 로직에 이름을 붙이고, 한 곳에 두고, 여러 컴포넌트에서 재사용할 수 있습니다. use 접두사는 React의 도구와 코드를 읽는 다음 사람이 이것이 Hook임을 알 수 있게 합니다. Custom Hook은 이미 있는 Hook들을 묶어서 재사용 가능하게 만드는 것일 뿐, React에 새로운 능력을 더하지는 않습니다.
use로 시작하는 함수이고, 컴포넌트를 메모리나 effects 같은 React 기능에 연결합니다. 이미 useState와 useEffect를 만났고, 나머지도 같은 모양입니다. 기억할 핵심은: Hook을 컴포넌트의 맨 위에서만 호출하고, if나 반복문 안에서는 절대 호출하지 않으면, 매 렌더링마다 같은 방식으로 실행됩니다. 다음: React로 생각하기. 컴포넌트, prop, state, effect가 디자인을 UI로 바꾸는 과정으로 어떻게 함께 작동하는지 봅니다.

