일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- DIGITALHANARO
- 버전생성프로세스
- github
- 프론트엔드배포
- 웅진씽크빅
- 디지털하나로입학식
- 프로젝트캠프
- udemy
- 디지털하나로
- 맥북백틱
- 맥북백틱입력
- `
- 개발자교육과정
- 디지털취업
- 하나은행
- 스나이퍼팩토리
- 네이버로그인창만들기
- 부트캠프
- 유데미
- 취준생
- 미래내일일경험
- Next.js
- 프론트엔드개발자양성과정
- kdt
- 깃허브 레포지토리와 로컬 코드 연결하기
- 배포
- 디지털교육
- 백틱
- s3
- Today
- Total
Land of Joe
메모이제이션과 상태 관리 (useCallback, useMemo, React.memo, useReducer, ContextAPI) 본문
메모이제이션과 상태 관리 (useCallback, useMemo, React.memo, useReducer, ContextAPI)
Arendt 2024. 7. 25. 22:37
React를 사용하다 보면 성능 최적화와 효율적인 상태 관리가 중요하다는 것을 알게 된다.
메모이제이션과 관련된 다양한 Hook과 Context API, 그리고 useReducer에 대해 알아보고자 한다.
1. 메모이제이션 (Memoization)
동일한 계산을 반복하지 않도록, 함수 호출의 결과를 저장하여 성능을 최적화하는 기법을 의미한다.
메모이제이션을 하기 위한 방법으로는 세 가지가 있다.
- 콜백 함수를 메모이제이션 하는 useCallback 훅
- 데이터 값을 메모이제이션 하는 useMemo 훅
- 컴포넌트를 메모이제이션 하는 React.memo 함수
💡 useCallback, useMemo 훅이 사용된 경우,
의존성 배열(Dependency Array)에 의해서만 각각 함수와 값의 리랜더링을 진행하는데
이때 의미 없는 메모이제이션이 되지 않게 주의해야한다.
1.1 useCallback
useCallback은 주어진 콜백 함수를 메모이제이션하여, 의존성이 변경되지 않는 한 동일한 함수를 반환한다.
이는 주로 자식 컴포넌트에 콜백 함수를 전달할 때 유용하다.
✅ useCallback( ( ) => { 메모이제이션 대상 함수 }, [ 의존성 배열 ] )
import React, { useState, useCallback } from "react";
const functionSet = new Set();
const App = () => {
const [count, setCount] = useState(0);
// 함수의 메모이제이션
const increment = useCallback(() => {
// 이 함수 내부에
// 굉장히 비용이 많이 발생하는 코드가 정의 되어 있다면
setCount((prev) => prev + 1);
}, []);
functionSet.add(increment);
console.log(functionSet);
return (
<>
<div>count : {count}</div>
<button onClick={increment}>증가</button>
</>
);
};
export default App;
메모이제이션 되면 외부의 영향을 받지 않는다.
내부에 있는 변수나 데이터가, 메모이제이션 되었을 당시의 값을 유지함을 기억해야 한다.
1.2 useMemo
useMemo는 주어진 계산 결과를 메모이제이션하여, 의존성이 변경되지 않는 한 동일한 값을 반환한다.
이는 주로 무거운 계산을 메모이제이션할 때 유용하다.
✅ useMemo( ( ) => { 메모이제이션 대상 값 }, [ 의존성 배열 ] )
import React, { useState, useMemo } from 'react';
const ExpensiveComponent = ({ number }) => {
const computeExpensiveValue = (num) => {
console.log('Computing expensive value');
return num * 2;
};
const expensiveValue = useMemo(() => computeExpensiveValue(number), [number]);
return <div>Expensive Value: {expensiveValue}</div>;
};
const ParentComponent = () => {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<ExpensiveComponent number={count} />
</div>
);
};
export default ParentComponent;
두 번째 매개변수인 의존성 배열 값을 비워두면 다시는 메모이제이션 하지 않겠다는 것을 의미한다.
한 번 메모이제이션 해두면 그 다음부턴 메모제이션된 값을 사용하기 때문에 속도가 빨라짐을 확인할 수 있다.
💡 메모이제이션은 코드적으로 고비용이다.
메모이제이션 할 만한 가치가 있는지 고민해야 한다.
반복문으로 출력되는 것만 메모이제이션 하는 것이 좋지 않을지!
1.3 React.memo
React.memo는 고차 컴포넌트로, 컴포넌트의 props가 변경되지 않으면 재렌더링을 방지한다.
(+) 고차 컴포넌트(Higher-Order Component, HOC)는 컴포넌트를 인수로 받아 새로운 컴포넌트를 반환하는 함수이다.
컴포넌트의 재사용 가능한 로직을 추출하고, 이를 여러 컴포넌트에서 공유할 수 있게 해준다.
✅ React.memo( 메모이제이션 대상 컴포넌트명 )
import React, { useState } from 'react';
// 리스트 아이템 컴포넌트
const ListItem = ({ item }) => {
console.log('ListItem rendered:', item);
return <li>{item}</li>;
};
// React.memo를 사용하여 리스트 아이템 컴포넌트를 메모이제이션
const MemoizedListItem = React.memo(ListItem);
const ListComponent = () => {
const [items, setItems] = useState(['Apple', 'Banana', 'Orange']);
const [newItem, setNewItem] = useState('');
const [count, setCount] = useState(0);
const addItem = () => {
setItems([...items, newItem]);
setNewItem('');
};
return (
<div>
<h2>Fruits List</h2>
<ul>
{items.map((item, index) => (
<MemoizedListItem key={index} item={item} />
))}
</ul>
<input
type="text"
value={newItem}
onChange={(e) => setNewItem(e.target.value)}
placeholder="Add new item"
/>
<button onClick={addItem}>Add Item</button>
<button onClick={() => setCount(count + 1)}>Increment Count</button>
<p>Count: {count}</p>
</div>
);
};
export default ListComponent;
- ListItem 컴포넌트가 메모이제이션 되었다.
- MemoizedListItem 컴포넌트는 props가 변경되지 않으면 재렌더링되지 않는다. 따라서, 리스트에 새로운 아이템을 추가할 때만 새로 추가된 아이템이 렌더링된다.
- Increment Count 버튼을 클릭해도 MemoizedListItem 컴포넌트는 재렌더링되지 않기 때문에 성능 최적화에 도움이 된다.
2. useReducer
하나의 상태값(useState)을 단 하나의 함수(reducer)로 편하게 관리하고자 하는 목적이다.
useReducer는 복잡한 상태 로직을 다루거나, 현재 상태에 기반한 다음 상태를 계산할 때 유용하다.
reducer 함수와 초기 상태를 받아, 상태와 dispatch 함수를 반환한다.
✅ const [ , set] = useReducer(( ) => { }, 초기값);
import React, { useReducer } from 'react';
const initialState = { count: 0 };
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
const Counter = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
};
export default Counter;
💡 dispatch에 기본적으로 메모이제이션 되어있어서
reducer를 사용하면 따로 훅을 통해 메모이제이션 하는 데 드는 비용을 낮출 수 있다.
3. Context API
Context API는 React 애플리케이션에서 전역 상태를 관리할 때 유용하다.
props drilling을 해결하기 위해 나온 개념으로,
데이터를 관리하고 있는 곳(Context)을 만들어서 그곳에서 다른 컴포넌트 안 거치고 바로 데이터를 전달해준다.
createContext를 통해 Context를 생성하고, Provider를 통해 값을 공급하며, useContext를 통해 값을 소비한다.
import React, { createContext, useContext, useState } from 'react';
const CountContext = createContext();
const CountProvider = ({ children }) => {
const [count, setCount] = useState(0);
return (
<CountContext.Provider value={{ count, setCount }}>
{children}
</CountContext.Provider>
);
};
const IncrementButton = () => {
const { setCount } = useContext(CountContext);
return <button onClick={() => setCount((count) => count + 1)}>Increment</button>;
};
const CountDisplay = () => {
const { count } = useContext(CountContext);
return <div>Count: {count}</div>;
};
const App = () => (
<CountProvider>
<CountDisplay />
<IncrementButton />
</CountProvider>
);
export default App;
실무에서는 별도의 context 폴더를 생성해서 context 코드를 옮기고 children 방식을 통해 메모이제이션 비용까지 줄인다.
how? ⇒ context라는 함수를 생성하여 고차 컴포넌트 방식을 취했기 때문.
열라 어렵다.. 머리가 팽팽 도는 기분~.~
프로젝트에서 직접 사용해볼 기회가 있으면 좋겠다!!
'🌐 Web > ⚛️ React' 카테고리의 다른 글
session에 대한 전역상태관리 useContext 흐름 이해하기(react, ts) (0) | 2024.10.05 |
---|---|
리액트에서 로그인 처리하는 방법 (1) | 2024.07.23 |
Link와 useNavigate의 활용도 차이 (Link태그 내 이벤트 존재 시 이벤트 작동 안 함 문제 해결을 위한 useNavigate 활용) (0) | 2024.04.18 |
CRA 생성 안됨 오류 Unknown command: "create-react-app" (0) | 2024.03.01 |
[react-redux] Actions must be plain objects. 오류 (0) | 2024.01.17 |