Hooks 란?
클래스 컴포넌트에서만 가능했던 state(상태관리) 와 lifecycle(라이프사이클) 이 가능하도록 돕는 기능 |
최상위 단계에서만 호출이 가능하며 반복문, 조건문, 중첩된 함수 내부에서 호출하면 됨 |
Hooks 대표적인 종류
useState() | 상태 관리를 위한 가장 기본적인 훅 |
useRef() | 참조(reference)를 생성하고 관리할 수 있는 훅 (DOM 접근, 변수 보존 등) |
useEffect() | 컴포넌트가 렌더링 될 때마다 특정 작업을 수행하도록 설정할 수 있는 훅 |
useMemo() | 메모이제이션을 통해 함수의 리턴 값을 재사용할 수 있게 해주는 훅 |
useCallback() | : 함수를 메모이제이션하여 불필요한 렌더링을 줄이게 해주는 훅 |
useReducer() | 복잡한 컴포넌트 상태 로직을 리듀서 함수를 통해 관리할 수 있는 훅 |
useContext() | 리액트에서 전역적으로 접근 가능한 데이터나 함수를 관리하고, 필요한 컴포넌트에서 그 값을 바로 가져와 사용할 수 있게 도와주는 훅 |
useMemo란?
useMemo에서 Memo란 Memoization을 뜻하며, Memoization이란 기존에 수행한 연산의 결과값을 어딘가에 저장해두고 동일한 입력값이 왔을 때, 재활용한다는 의미임 |
즉, 처음에 계산됐던 결과값을 메모리에 저장해서 컴포넌트가 반복적으로 렌더링 되어도 계속 함수를 호출하지 않고, 이전에 이미 계산된 결과값을 메모리에서 꺼내와 활용함 |
함수를 메모하는 useCallback과는 달리, useMemo는 함수의 결과 값을 메모합니다. |
그렇기 때문에 불필요한 값을 모두 저장해놓으면 성능저하를 불러와 꼭 필요할 때만 사용해야 한다. |
useMemo의 구조
const memo = useMemo(calculateValue, dependencies);
calculateValue
useMemo의 첫 번째 인수로 들어가는 calculateValue는 캐시 하려는 값을 계산하는 함수입니다.
이 함수는 순수 함수여야 하며, 인수를 받지 않아야 하고, 모든 유형의 값을 반환할 수 있어야 합니다.
dependencies
calculateValue에서 참조된 모든 값의 목록입니다.
이 값은 property, state, 컴포넌트 내부에서 직접 선언된 모든 변수와 함수가 포함됩니다.
React는 여기에 담겨 있는 종속성들을 이전 값과 비교하여 변화가 있는지 판단합니다.
Returns
첫 렌더링이라면 인자 없이 함수에서 계산된 값을 반환합니다.
그다음 렌더링부터는 저장되어 있던 값을 반환하거나 함수를 호출하여 새로 계산된 값을 반환합니다.
useMemo의 동작 흐름
1. React는 먼저 현재 종속성과 이전 종속성을 비교하여 종속성의 변경 여부를 확인한다. |
2. 종속성이 변경되지 않았다면 함수를 다시 실행하지 않고, 이전에 메모한 값을 반환함 |
3. 만약 종속성이 변경되었다고 한다면 함수를 실행하고 결과값을 새로 계산하고 그 내용을 저장한다. |
4. 그 다음에는 메모된 값을 반환한다. |
useMemo의 참고 사항
대부분의 계산은 컴퓨터의 연산 속도가 충분하기 때문에 사용하지 않아도 된다. |
하지만 너무 많은 배열을 필터링, 변경하는 경우, 비용이 많이 드는 계산을 수행하는 경우라면 데이터가 변경되지 않았을 때 계산을 수행하지 않는 것이 효율적이다. |
즉, 업데이트 시 불필요한 작업을 생략하기 건너뛰기 위하여 사용하는 장치일 뿐이다. |
useMemo를 사용하면 유용한 경우
1. useMemo에 넣는 계산이 눈에 띄게 느리고 종속성이 거의 변하지 않는 경우 |
2. useMemo로 감싸진 컴포넌트에 값을 전달하는 경우 |
function MyComponent({ propA, propB }) {
const expensiveValue = useMemo(() => {
return propA + propB;
}, [propA, propB]);
return (
<div>{expensiveValue}</div>
);
}
컴포넌트가 리렌더링되면 props가 변경되지 않아도 컴포넌트의 모든 prop이 다시 전달됩니다. |
렌더링이 자주 되는 컴포넌트인데다가 props의 계산도 오래 걸린다면 매우 비효율적일 수 있습니다. |
useMemo를 사용한다면 props의 결과를 메모할 수 있기 때문에 props가 변경되었을 때에만 렌더링이 되기 때문에 불필요한 계산을 줄여 성능을 향상시킬 수 있습니다. |
3. useMemo를 사용한 값이 다른 Hook의 종속성으로 사용될 수 있는 경우 |
function Example() {
const [count, setCount] = useState(0);
const factorial = useMemo(() => {
let fact = 1;
for (let i = 1; i <= count; i++) {
fact = fact * i;
}
return fact;
}, [count]);
useEffect(() => {
console.log(`Factorial of ${count} is ${factorial}`);
}, [factorial]);
return (
<div>
<p>Count: {count}</p>
<p>Factorial: {factorial}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
버튼을 누르면 count의 숫자가 1 증가합니다.
factorial 함수는 useMemo로 감싸져 있고, 종속성 배열에 count가 들어있습니다.
버튼을 눌러서 count의 상태가 변했으므로 factorial 함수가 작동해서 factorial 값을 반환합니다.
useEffect의 종속성 배열에는 factorial 값이 있습니다.
따라서 factorial의 값이 변했으므로 리렌더링을 하고 console.log에 적힌 문자열을 출력합니다.
이렇게 useEffect의 종속성으로 factorial 값을 사용해서 factorial의 값이 변할 때만 실행되도록 사용할 수 있습니다.
useMemo를 사용하면 유용하지 않은 경우
1. 어디까지나 내용을 메모리에 저장해놓는 것이기에 과도한 사용은 메모리 사용량을 늘려, 성능 저하를 불러온다. |
2. 컴포넌트가 리렌더링될 때마다 실앻되는 로직이나 사이드 이펙트에서 사용되어선 안된다. 이때는 useEffect 사용!! |
만약 useMemo 안에 사이드 이펙트를 포함시키면 리렌더링 시마다 그 사이드 이펙트가 실행될 수 있습니다. 이는 리액트의 컴포넌트 리렌더링 원칙에 맞지 않으며, 불필요한 부수적인 작업을 초래할 수 있습니다. |
참고할 만한 블로그
'국비지원 공부 정리 > React' 카테고리의 다른 글
Custom Hooks (0) | 2025.04.01 |
---|---|
useCallback()과 useReducer() (0) | 2025.04.01 |
react-hook-form의 사용법 (1) | 2025.04.01 |
react의 Life Cycle, useEffect (0) | 2025.03.31 |
Ref 란? (0) | 2025.03.31 |