최적화(Optimization)
적용
1. 시기 : 하나의 프로젝트를 거의 완성한 상태에서 시행
- 기능 구현 → 최적화
- 최적화를 먼저 하면 기능 수정할 때 최적화가 풀리거나 꼬여서 작동을 안할 수도 있음
2. 적용 대상 : 꼭 최적화가 필요한 것에만 시행
- 최적화도 마찬가지로 연산이 필요하기 때문에, 단순한 연산일 경우 그냥 리렌더링 되도록 두는게 더 빠를 수도 있음
- TodoItem을 생성하는 것과 같이 유저의 행동에 따라 개수가 많아질 수 있는 컴포넌트
- 혹은 함수들을 많이 가지고 있어 무거운 코드의 경우 적용하는 게 좋음
useMemo
- "메모이제이션" 기법을 기반으로 불필요한 연산을 최적화하는 리액트 훅
- 반복되는 내용을 저장해뒀다가 필요할 때 꺼내 쓰는 느낌
- useMemo를 사용하면 연산 자체를 메모이제이션 할 수 있고,
- 특정 조건을 만족했을 때에만 결과 값을 다시 계산하도록 설정도 가능함
사용해보기
todoAnalyzedData 생성
- 실습을 위해 List에 todoAnalyzedData 생성
import { useState, useMemo } from 'react';
(...)
const List = ({ todo, onUpdate, onDelete }) => {
// 검색어 저장 변수
const [search, setSearch] = useState('');
const onChangeSearch = (e) => {...};
// 검색 결과 찾는 함수
const getSearchResult = () => {...};
// todoAnalyzedData
const { totalCount, doneCount, notDoneCount } = useMemo(() => {
console.log('getAnalyzedData 호출');
const totalCount = todo.length;
const doneCount = todo.filter((item) => item.isDone).length;
const notDoneCount = totalCount - doneCount;
return {
totalCount,
doneCount,
notDoneCount,
};
}, [todo]);
return (
<div className="List">
<h3>Todo List🌱</h3>
<div>
<div>total : {totalCount}</div>
<div>done :{doneCount}</div>
<div>notDone :{notDoneCount}</div>
</div>
(...)
</div>
);
};
export default List;
- 현재는 useMemo가 적용된 코드이지만, useMemo를 적용하지 않으면 분석과 관계없는 동작을 수행할 때에도 컴포넌트 전체가 리렌더링되는 것을 확인할 수 있음
- useMemo를 사용하면 컴포넌트의 비효율적인 리렌더링을 없애 최적화된 코드 작성이 가능해짐
useMemo 사용
1. import 해오기
import { useMemo } from 'react';
2. useMemo 호출
useMemo(()=> {}, [])
- 첫 번째 인수로는 콜백 함수, 두 번째 인수로는 배열을 넣음
- 이 배열은 의존성 배열(deps)로, deps가 수정될 때 useMemo의 콜백 함수가 실행됨
- 라이프사이클에서 배웠던 useEffect의 훅과 비슷함
- useEffect : deps 값이 바뀌면 콜백 함수를 다시 실행
const a = useMemo (()=> {return 1}, [])
- 콜백 함수가 반환하는 값을 useMemo가 그대로 반환하기 떄문에 변수에 담아서 사용 가능
3. 구조분해 할당으로 필요한 값 가져오기
// todoAnalyzedData
const { totalCount, doneCount, notDoneCount } = useMemo(() => {
console.log('getAnalyzedData 호출');
const totalCount = todo.length;
const doneCount = todo.filter((item) => item.isDone).length;
const notDoneCount = totalCount - doneCount;
return {
totalCount,
doneCount,
notDoneCount,
};
}, [todo]);
- totalCount, doneCount, notDoneCount 값만 사용할 것이므로 구조분해할당으로 값 담을 수 있음
memo
- 컴포넌트를 인수로 받아 최적화된 컴포넌트로 만들어 반환
- 부모 컴포넌트가 리렌더링 되더라도 자신이 받는 props가 변경되지 않으면 리렌더링이 일어나지 않음
const MemoizedComponent = memo(Component)
사용해보기
1. import memo
import { memo } from 'react';
2. export memo
export default memo(Header);
예시
import './Header.css';
import { memo } from 'react';
const Header = () => {
return (
<div className="Header">
<h3>오늘은🗓️</h3>
<h1>{new Date().toDateString()}</h1>
</div>
);
};
export default memo(Header);
객체타입에서의 memo
- 객체타입은 주소값을 가지고 있음.
- 주소값은 리렌더링 될 때마다 바뀜
- 그러므로 얕은 비교를 통해 값을 비교하는 memo는 이걸 걸러낼 수 없어 최적화가 안됨
- 즉, 위와 같이 memo를 붙인다고 해서 memo되지 않음
해결방법 1
export default memo(TodoItem, (prevProps, nextProps) => {
if (prevProps.id !== nextProps.id) return false;
if (prevProps.isDone !== nextProps.isDone) return false;
if (prevProps.content !== nextProps.content) return false;
if (prevProps.date !== nextProps.date) return false;
return true;
});
- memo 함수의 두번째 인수로 콜백함수를 전달해서
- 리렌더링 될 때마다 prevProps(과거의 props)와 nextProps(현재의 props)를 전달해서 함수의 반환값에 따라 변동 여부를 확인하는 방법
- data가 수정되거나 개수가 많을 때 비효율적
해결방법2
useCallback 사용하기
useCallback
1. import useCallback
import { useCallback } from 'react';
2. useCallback 호출하기
const func = useCallback(()=> {},[])
- 첫 번째 인수로는 최적화하고 싶은(불필요한 재생성을 막고 싶은) 함수를 넣기
- 두 번째 인수로는 deps 배열을 넣어주기
- 첫 번째 인수로 전달한 콜백 함수를 그대로 생성해서 반환하기 때문에 변수에 담을 수 있음
- 두 번째 인수인 deps가 변경될 때만 콜백 함수 실행
- deps를 빈 배열로 두면 처음 마운트 될 때 딱 한번만 실행 됨
3. export 하기
export default memo(TodoItem);
- 이전처럼 props를 받을 컴포넌트에서 memo메서드만 작성해서 export 하면 끝
Todolist app에 적용해보기
import { useState, useRef, useReducer, useCallback } from 'react';
(...)
const mockTodo = [...];
function reducer(state, action) {...}
function App() {
const [state, dispatch] = useReducer(reducer, mockTodo);
const idRef = useRef(3);
const onCreate = useCallback((content) => {
dispatch({
type: 'CREATE',
data: {
id: idRef.current++,
content: content,
isDone: false,
createdDate: new Date().getTime(),
},
});
}, []);
const onUpdate = useCallback((targetId) => {
dispatch({
type: 'UPDATE',
targetId: targetId,
});
}, []);
const onDelete = useCallback((targetId) => {
dispatch({
type: 'DELETE',
targetId: targetId,
});
}, []);
return (
<div className="App">
(...)
</div>
);
}
export default App;
'개발새발개발 > React' 카테고리의 다른 글
[React] useReducer 사용하기 (0) | 2025.02.20 |
---|---|
[React] Context 사용해서 props 없이 편하게 데이터 전달하기 (0) | 2025.02.18 |
[React + TypeScript + Tailwind] 가독성 높이는 UI 컴포넌트 만들기 (1) | 2025.02.14 |
[React] VSCode Prettier 적용 안됨 5가지 해결 방법 (2) | 2025.02.13 |
[React+Typescript] Todos 앱 만들기 (0) | 2025.02.11 |
댓글