-
성능 최적화를 위한 React-hookReact 2022. 12. 21. 15:54
memo
- 정확히 말하면 hook은 아님
- 밑에서 정리할 useCallback이나 useMemo를 사용하기 위해 반드시 알아야하는 개념
- 컴포넌트에 불필요한 렌더링을 하지 않도록 해주는 함수 (화면에서 변경되는 부분이 없음에도 불구하고 화면이 다시 렌더링 되는 것)
화면이 리렌더링 되는 경우
- 부모 컴포넌트가 렌더링 된 경우
- 컴포넌트 스테이트가 변경 된 경우
- 부모로부터 전달받은 props 값이 변경되는 경우
불필요한 리렌더링을 줄이는 것만으로도 프로젝트의 부하를 줄이고 퍼포먼스 능력을 향상시킬 수 있기에
최적화를 잘 하면 굉장히 좋은 결과를 기대할 수 있음
리렌더링 과정
위 사진에서 보듯이, input의 value값에 한글자라도 변경이 일어나면 리렌더링 되는 것을 볼 수 있다.
input은 App.js에 있지만 리렌더링이 일어나지 않아도 될 button.jsx까지 리렌더링이 되고있다.
부모 컴포넌트인 App.js에서 리렌더링이 일어나고있기 때문이다.
memo 사용
// components/Button.jsx import React from 'react'; import {memo} from 'react' const Button = () => { console.log('리렌더링되고 있어요.'); return <button>버튼</button>; }; export default memo(Button);
Button.jsx에 memo를 사용하여 재시동 해봤다.
최초 한번만 렌더링 되고 그 뒤 input의 value값이 변경되어도 리렌더링이 일어나지 않는 것을 볼 수 있다.
근데 부모에서 Button.jsx를 부모에서 동작하게 하고자 어떤 함수를 props로 넘겨주면 Button.jsx는 다시 리렌더링이 시작된다.
위 memo를 사용하고 props로 함수를 넘겨주니 리렌더링 다시 시작됨
// App.jsx import React, {useState} from 'react'; import Button from './components/Button'; const App = () => { // App.js가 리렌더링 될때마다 재생성됨 const [value, setValue] = useState(''); // App.js가 리렌더링 될때마다 재생성됨 const onChangeHandler = (e) => { setValue(e.target.value); }; // App.js가 리렌더링 될때마다 재생성됨 const onClickHandler = () => { console.log('hello button!'); }; return ( <div> <input type='text' value={value} onChange={onChangeHandler} /> {/* 매번 재생성되는 함수를 props로 넘겨줌 -> Button.js 리렌더링 유발 */} <Button onClick={onClickHandler} /> </div> ); }; export default App;
App.js에서 Button.jsx까지 input에 값을 넣을 때마다 어떤 식으로 컴포넌트들이 동작하고 있을까?
- input의 값을 입력
- onChangeHandler 실행
- onChangeHandler 에 의해서 setValue 실행
- value state 변경
- value state 가 변경됨에 따라 App.js 리렌더링
- App.js가 리렌더링 되면 App.js 안에 있는 useState, 함수(onChangeHandler, onClickHandler)들이 다시 생성됨
- 위 useSate,함수 들이 재선언 되었기 때문에 Button.jsx 입장에서는 넘어온 onChangeHandler 함수가 새로운 것이라고 판단
- 새로운 props가 왔다 판단해서 계속 리렌더링이 된다.
Button.jsx의 리렌더링을 막으려면?
- App.js가 리렌더링을 할 때 함수를 계속 재생성 하지 않게 하면 된다.
- 이 재생성 하지 않게 해주는 hook이 밑에서 정리할 useCallback 이다.
useCallback
- 컴포넌트가 리렌더링 되더라도 생성된 함수를 새로 만들지 않고 재사용하고자 할 때 사용하는 하는 hook이다.
- useCallback은 useEffect 사용법과 굉장히 유사하다.
두번째 매개변수 자리에 의존성 배열을 받고, 그 안에 값이 변경될 때만 useCallback 안에 함수를 생성하는 로직을 가지고 있다.
// src/App.jsx import React, {useCallback, useState} from 'react'; import Button from './components/Button'; const App = () => { // App.js가 리렌더링 될때마다 재생성됨 const [value, setValue] = useState(''); // App.js가 리렌더링 될때마다 재생성됨 const onChangeHandler = (e) => { setValue(e.target.value); }; // App.js가 리렌더링 되어도 Button.jsx는 재생성되지 않음 const onClickHandler = useCallback(() => { console.log('hello button!'); }, []); return ( <div> <input type='text' value={value} onChange={onChangeHandler} /> {/* 매번 재생성되는 함수를 props로 넘겨줌 -> Button.js 리렌더링 유발 */} <Button onClick={onClickHandler} /> </div> ); }; export default App;
useCallback을 적용해주니 리렌더링이 되지 않는것을 볼 수 있다.
useMemo
- useCallback과 똑같은 기능을 하는 hook 이다. 사용방법도 똑같다.
- 다만, 그 대상이 함수가 아니라 배열이나 객체와 같은 값일 때 사용한다는 점의 차이가 있다.
// src/components/List.jsx import React, {memo} from 'react'; const List = ({data}) => { console.log('리렌더링되고 있어요.'); return ( <div> {data.map((todo) => ( <div key={todo.id}>{todo.title}</div> ))} </div> ); }; export default memo(List);
App.js에서 List.jsx로 data(배열형태)를 받아와 실행하였더니 memo를 사용하였음에도 리렌더링이 일어나고 있다.
마찬가지로 App.js가 리렌더링 될 때마다 재생성 되고있기 때문이다.
// src/App.jsx import React, { useState } from 'react'; import { useMemo } from 'react'; import List from './components/List'; const App = () => { const [value, setValue] = useState(''); const onChangeHandler = (e) => { setValue(e.target.value); }; // useMemo 사용 const data = useMemo(() => { return [ { id: 1, title: 'react', }, ] }, []); return ( <div> <input type='text' value={value} onChange={onChangeHandler} /> <List data={data} /> </div> ); }; export default App;
이렇게 useMemo를 적용 해주니,
리렌더링이 일어나지 않는 것을 볼 수 있다.
주의사항
- 무분별한 사용은 퍼포먼스 성능에 악영향이 될 수도 있다.
- 반드시 리렌더링이 필요한 컴퍼넌트나 값 에는 최적화 hook을 사용하는 것이 오히려 좋지 않다.
(의도치 않게 업데이트가 되지 않는 상황이 올 수 있기 때문) - 사용 시에는 React에게 전, 후 값을 비교하는 과정 하나가 추가되는 것 이기 때문에
굳이 비교하지 않아도 되는 것 들에게도 React가 비교하는 과정을 추가하기 때문에 퍼포먼스 성능에 악영향을 미칠 수 있다.
라는 것이다.
정리
useCallback의 사용 대상은 함수이고,
useMemo의 사용 대상은 객체나 배열과 같은 값이다.
그 외 사용 원리나 방법은 동일하다.
추가로 원시타입 데이터. 예를 들면 setState를 쓰지 않는 변수들은 useMemo를 사용하지 않아도 리렌더링 하지 않는다.
'React' 카테고리의 다른 글
Custom Hook (0) 2022.12.21 Redux 이용해서 TodoApp 만들기 (0) 2022.12.20 Redux 미들웨어(Thunk) (0) 2022.12.20 styled component porps해서 쓰기 (0) 2022.12.20 Redux(3) - react-router-dom, props children, Dynamic Route (0) 2022.12.20