
react developer tool이라는 크롬 확장 프로그램을 아시나요?
해당 확장자는 리액트로 만들어진 페이지의 렌더링을 직접 눈에 보이게 표시해 주는 프로그램입니다.
해당 프로그램을 다운 받고 작업하던 페이지의 렌더링 과정을 살펴보니 불필요한 렌더링이 너무 많이 일어나는 것을 확인할 수 있었습니다.
변화가 없는 컴포넌트가 렌더링 된다면 이는 분명 낭비가 발생하게 될 것입니다.
따라서 위와 같은 낭비를 최소화하기 위해 렌더링 최적화와 관련된 주제로 정해보았습니다.
리액트의 렌더링이란 함수를 실행하는 것이라고 할 수 있습니다.
(app 컴포넌트의 내부 로직을 실행)
렌더링을 실행하는 경우는 아래와 같습니다.
props가 변경되었을 때state가 변경되었을 때또한, 부모 컴포넌트가 렌더링이 되었을 경우 자식 컴포넌트도 렌더링이 됩니다.
parent 컴포넌트childA 컴포넌트childB 컴포넌트위와 같은 구조로 구성이 되어있는 컴포넌트라 가정해 봅시다.
parent 컴포넌트 **(state 정의, handle 함수 정의)**childA컴포넌트 **(props로 state 전달)**childB컴포넌트 **(props로 handle 함수 전달)**위와 같은 기준으로 state와 함수를 정의해 보았습니다.
parent에서 state의 값이 변경되었을 때, 렌더링이 되는 기준으로 생각해 보면
state 전달이 된 childA의 컴포넌트는 리렌더링이 일어나는 것이 맞습니다.
그러나, 새로 그려질 필요가 없는 childB의 경우에도 리렌더링이 일어나게 됩니다.(불필요 리렌더링 발생)
childB가 리렌더링이 일어나게 될 경우 childB 하위에 있는 자식 컴포넌트들 또한 리렌더링이 일어나게 되어 메모리 및 자원 낭비가 일어날 것입니다.
그렇다면 어떻게 childB에서 리렌더링을 막을 수 있을까요?
새로 그려질 필요가 없는 childB는 왜 리렌더링이 되는 것일까요?
간단하게 설명을 하자면, parent 컴포넌트에 정의되어 있는 state가 변경이 되며 새로 렌더링이 되는 parent 컴포넌트에서 hadle 함수가 재정의 되며 이전의 함수와 다른 참조값을 가지고 있기에 다른 함수로 인식하여 새롭게 childB 또한 리렌더링 되는 것입니다. (props의 값이 변경되면 재렌더링 된다는 개념에 의거)
그렇다면 함수의 참조값이 바뀌지 않게 한다면 리렌더링이 일어나지 않게 할 수 있지 않을까요?
⇒ 이는 react에서 사용하는 useCallback 훅을 사용하여 구현할 수 있습니다.
const handleSample = useCallback( ( ) ⇒ { }, [ ] );그렇다면 위의 useCallback을 통해서 childB의 불필요한 렌더링은 일어나지 않을까요?
아쉽게도 그렇지는 않습니다.
이는 parent 컴포넌트가 컴파일 되는 과정에서의 이해가 조금 필요합니다.
state가 변경된 parent 컴포넌트는 리렌더링이 일어납니다.
이 경우 js 컴파일러인 babel이 실행되며 parent 컴포넌트의 자식들인 childA, childB 컴포넌트를 React.creatElement를 통해 새로운 요소를 생성하게 됩니다.
이러한 이유로 useCallback을 사용하여 childB에 전달되는 prop를 이전과 동일하게 해주어도 불필요한 리렌더링이 일어나는 이유입니다.
+그렇다면 useCallback을 사용하는 것은 기능적으로 이득이 없나요?
그렇다면 더욱 확실하게 childB의 리렌더링을 막기 위해 (render phase)를 막기 위해서는 어떻게 할 수 있을까요?
(객체를 porp로 넘겨준다면)
const memoizedTest = useMemo( ( ) ⇒ test, [ ] );위처럼 useMemo를 사용하게 된다면 test는 동일한 참조값을 가지고 있을 것이며 이는 React.memo를 의도한 대로 사용할 수 있을 것입니다.
useMemo, useCallback, React.memo 이는 렌더링을 줄이는 기능을 가지고 있지만 이 또한도 하나의 코드이며 동작입니다. 최적화를 위해 사용했다 오히려 부담이 더 가는 코드를 만들 수도 있습니다.
따라서 무작정 사용하는 것이 아닌 기획의도와 어떻게 사용하여야 좋을지 고민하여 적절한 사용이 가장 옳은 최적화라고 할 수 있겠습니다.
또한, 최적화 도구들을 사용하기 이전, 기초적이고 근본적으로 코드를 수정하고 잘 만드는 것에 초점을 맞추는 것이 매우 중요하겠습니다.
부모 컴포넌트 안의 자식 컴포넌트로 존재하고 있는 구조를 children을 통해 주입시킨다면 이는 부모 컴포넌트가 재실행 되어도 리렌더링 되지 않을 것입니다.
많은 블로그 영상들에서 이른 최적화 만큼이나 불필요한 것이 없다고 이야기합니다.
그러나 모르고 쓰는 것과 알고 쓰는 것은 다르다는 말에 매우 공감되었습니다.
최적화 도구들과 근본적 코드 수정법 등을 접해보고 이후 필요할 때에 적절한 방법을 통해 렌더링을 최적화할 수 있는 능력을 기르는 것은 꽤나 중요하다고 느꼈습니다. 이 외에도 다양한 기법을 익혀보면 좋을 것 같습니다.