개발새발 로그

React의 useCallBack, useMemo 과연 잘 쓰고 있는 걸까? 본문

React

React의 useCallBack, useMemo 과연 잘 쓰고 있는 걸까?

이즈흐 2024. 1. 22. 14:33

렌더링이 될 때 불필요한 렌더링을 줄이기 위해서useCallback과 useMemo를 사용하곤 했다.

그러다 과연 나는 이 최적화 도구를 적절하게 사용하고 있는 것일까에 대해 의문이 들었다.

 

본 내용은 useCallback과 useMemo와 같은 최적화 도구를 언제 사용하는 것이 적절한지에 대해 탐구하면서 작성했다.

 

 

useCallback과 useMemo

useCallback이란?

메모이제이션을 통해 함수 재생성을 방지하고, 의존성 배열을 사용하여 필요한 경우에만 함수를 업데이트할 수 있도록 한다.

이를 통해 애플리케이션의 성능을 향상시키고 사용자 경험을 개선할 수 있습니다.

useMemo란?

메모이제이션을 통해 연산 결과 재생성을 방지하고, 의존성 배열을 사용하여 필요한 경우에만 연산 결과를 업데이트 할 수 있도록 한다.

이를 통해 애플리케이션의 성능을 향상시키고 사용자 경험을 개선할 수 있습니다.

 

쉽게 말해서 렌더링 했을 때 값 또는 함수를 재사용하는 것입니다.

그러면 렌더링할 때 불필요한 초기화를 없애줍니다.

 

그래서 다들 이런 생각을 하셨을겁니다.

그럼 모든 함수나 값에 다 묶어주면 좋지않나?
성능저하가 체감이 안되는데 안써도 괜찮지 않을까?
남용해서 쓰면 더 안좋다던데…

 

저도 이러한 생각들을 해결하기위해 공부하게 되었고, 공부한 내용 해당 내용에 작성하게 됐습니다.

 

 

렌더링과 리-렌더링

그럼 먼저 이 메모이제이션을 잘 사용하기 위해 렌더링과 리-렌더링부터 차근차근 알아보도록하겠습니다

우리가 메모이제이션을 사용하는 이유가 이 렌더링을 줄이기 위해서잖아요?

그럼 렌더링을 일단 알아야겠죠?

아시는 분들이 많으니까 간단하게 설명하도록 하겠습니다.

 

렌더링

 

이미지를 왼쪽에서 부터 살펴보겠습니다.

Component라는 함수형 컴포넌트가 있습니다.

현재 App컴포넌트의 반환문에 Component라는 컴포넌트가 반환되고 있네요


함수형 컴포넌트는 말 그대로 함수!

그리고 함수형 컴포넌트가 렌더링 된다는 것그 함수가 호출된다는 에요

함수가 호출되고 나서 오른쪽과 같이 브라우저에 보이게 됩니다.

 

이때 사실은 중간에 하나의 과정이 있습니다.

바로  virtual DOM 객체입니다.

함수 컴포넌트가 호출되면 객체, Virtual DOM이 반환이 됩니다.

그리고 이 virtualDOMReal DOM에 반영이 됩니다.

함수가 호출되는 것을 Render라고 하고

그리고 반환된 객체 VirtualDOMReal DOM에 반영되는 것을 Commit이라고 합니다.

이 부분은 중요한 부분인데 뒤에서 다시 설명드리겠습니다.

 

 

리-렌더링

이제 리-렌더링에 대해서 설명드릴게요!

-렌더링은 보통 세 가지 조건으로 인해 일어납니다.

첫번째 state가 바뀌었을 때

두번째 props가 바뀌었을 때

세번째 부모 컴포넌트가 렌더링 되었을 때 입니다.


아주 간단하죠?

이 세 개의 경우들만 생각하시면 됩니다.

 

예제를 볼까요?

현재 App 컴포넌트에서는 button을 누르면 useStateset함수가 리-렌더링을 발생시킵니다.

그럼 set함수가 발생하면 App컴포넌트는 다시 호출하게 되는데요.

그럼 호출하고 있던 자식 컴포넌트들도 다시 호출하게 되겠죠?

아까 얘기했던 부모컴포넌트가 렌더링이 되었을 때의 경우입니다.

보이는 예제를 실행시켜 버튼을 누르면 오른쪽과 같이 버튼을 누를 때마다 콘솔로그가 뜨게됩니다.

지금 사용한 useEffect에 의존성배열을 명시하지않아 렌더링마다 실행이 되기 때문입니다.

즉 렌더링이 되었다는 뜻입니다.

아시다시피 useEffect를 사용하지 않고 console.log를 찍어도 똑같겠죠?

 

근데 자세히 보시면 사실 Button을 클릭한 행동이 Component라는 친구한테는 상관없는 행동이잖아요?

근데 렌더링을 한다는 건 아마 불필요하다고 생각하실 겁니다.

 

그럼 이 불필요한 리-렌더링을 막아야 할 것 같습니다.

보여준 예제에서는 이러한 리-렌더링이 성능 저하에 크게 관여하지 않지만

다른 상황에서 만약 렌더링이 많아지면 속도가 저하되고 메모리 사용량이 높아집니다.

그러니까 전혀 필요하지 않은 렌더링은 줄여주는게 좋겠죠?

관련된 예제를 보면서 진행해보겠습니다.

 

난 상관없는데.. 호출하지마..

아까와 같은 클릭 기능이 있는 예제입니다.

 

먼저 Parent 컴포넌트를 봅시다

아래에서 ChildA컴포넌트와 ChildB컴포넌트를 호출하고 있습니다.

ChildA컴포넌트는 count값을 props로 받고 있고,

ChildB컴포넌트는 handleClick 함수를 props로 받고 있습니다.

그리고 ParentButton을 클릭하면 state가 변하게 되고 리-렌더링이 발생할 것 같습니다.

그럼 우리 자식 컴포넌트들도 다시 호출되겠죠??

근데 여기서 문제가 발생합니다.

바뀐 state값과 무관한 ChildB도 리-렌더링하게 된다는 거죠.

 

이게 일반적인 상황에서 성능저하가 그렇게 티 나지는 않겠지만

만약 ChildB처럼 엄청 많은 컴포넌트를 호출하거나 무거운 작업을 하고있다면 성능저하가 일어나겠죠?

이때 저희는 아까와 같이 메모이제이션을 활용할 거에요. 그쵸?

메모이제이션을 활용하면 App컴포넌트가 리-렌더링이 되어도 상관없는 자식 컴포넌트들은 리-렌더링이 되지 않도록 할 수 있습니다.

어떤 메모이제이션을 사용해야 childB의 불필요한 렌더링을 줄일 수 있을까요?

 

 

useCallback을 써보자!

 

ChildA 컴포넌트와 ChildB 컴포넌트를 자세히 보겠습니다.

현재 두 컴포넌트들은 Parent컴포넌트에게 props를 전달받고 있습니다.

 

그럼 props를 살펴보겠습니다.

현재 childA는 변경된 state를 받기때문에 리-렌더링이 됩니다. 일반적인 상황이죠

childB에서는 onclick함수를 받고 있습니다.

onClick 함수는 눈으로 보기에 똑같은 함수입니다.

하지만 아까 말했듯이 불필요하게 리-렌더링이 되고있습니다.

 

왜 그럴까요? 추측해볼 수 있는 점이 하나 있습니다.

onClick은 함수입니다.

자바스크립트에서 함수는 객체.

원시값과 달리 객체는 참조값을 갖고있습니다.

그럼 아까 Parent에서 리렌더링이 일어나면 함수를 저장하고 있던 handleClick 변수는 초기화 되기 때문에 새로 만들어진 함수 객체를 다시 할당 받습니다.

그래서 눈으로 보기에 똑같은 함수라도 다른 참조 값을 props로 내려주고 있기 때문에 리-렌더링이 되는 것입니다.

결과적으로ChildB는 이 onclick함수가 다른 함수라고 판단한 것입니다.

그러면 우리가 아까 배운 3가지 경우가 있죠?

거기서 Props의 변경으로 인한 리-렌더링이라고 추측할 수 있을 것 같아요!

 

이때 여러분들이 사용하는 것이 useCallback일 것입니다.

useCallback으로 감싼다면 의존성 배열 안의 값이 바뀌지 않는 한 저장된 변수에는 같은 값을 갖게 됩니다.

참조 값이 같아지면 리-렌더링을 하지않겠죠??

useCallback으로 감싼 뒤 다시한번 실행해보겠습니다.

화면에는 React DevTools를 이용해 렌더링을 보여준 것입니다.

useCallback으로 감싸줬는데도 버튼을 클릭하게 되면 ChildB가 리-렌더링이 됩니다.

예상한 결과와 다릅니다.

왜 리-렌더링을 할까요?

아시는 분들도 계시겠지만 사실 이는 당연한 결과입니다.

 

 

React.createElement 넌 뭐니? 

리액트 17, 바벨 7.9.0 이후 버전에서는 다르게 트랜스파일 한다고 합니다.(children)

Parent 컴포넌트를 Babel로 컴파일한 코드입니다.

Parent 컴포넌트의 retur문안에는

ChildAChildB에 해당하는 Reat.createElement가 존재합니다.

여기서 React.createElement가 무엇인지 간단하게 설명드리겠습니다.

React.createElement새로운 리액트 엘리먼트를 생성해서 반환해 주는 것입니다.

 

먼저 오른쪽 코드는 App 컴포넌트를 React.createElement를 이용해서 리턴문을 작성한 것입니다.

App 컴포넌트가 렌더링이 되면 계층으로 구성된 React.createElement를 순차적으로 호출합니다.

왼쪽 코드는 react에서 기본적으로 제공하는 jsx 문법입니다.

Jsx문법을 활용해서 개발자가 보기 편하게 오른쪽 코드와 동일한 역할을 하는 코드를 작성해줄 수 있습니다.

React.createElement에 대해서 간단하게 설명해봤습니다.

다시 돌아와서 Parent 컴포넌트가 리-렌더링되면 내부의 로직들이 다 실행이 되면 ChildB에 해당하는 createElement도 실행됩니다.

그래서 아무리 useCallback을 사용해서 childB에 전달되는 props를 같은 값으로 만들어줬더라도

이에 상관없이 리렌더링 되는 것입니다.

 

 

 

그럼 부모-자식 관계에서의 props에 의한 리-렌더링을 피하기 위해 사용하는 useCallback은 효과가 없는 건가?

그러면 useCallback을 사용해서 렌더링 최적화에 아무런 효과를 주지 못한 것인지 궁금증이 생길 것입니다.

먼저 이 궁금증에 대한 답변을 먼저 말씀드리면 효과가 있습니다!

이 효과를 알기 위해서는 다시 리액트의 렌더링에 대해서 설명드려야하는데요

알기쉽게 설명드리겠습니다!

 

 

리엑트의 렌더링

우리가 리액트로 웹을 개발 할 때에는 리액트의 렌더링 브라우저의 렌더링, 두 가지로 나뉩니다.

지금 궁금한 것은 리액트에서의 렌더링이겠죠?

 

리액트 공식문서에 의하면 리액트의 렌더링은 2가지 렌더링과 2가지 단계가 있다고 합니다.

보시는 바와 같이 컴포넌트 렌더링엘리먼트 렌더링이 있고,

렌더 페이즈 커밋 페이즈 있죠.

 

컴포넌트 렌더링은 우리가 아는 컴포넌트를 실행해 리액트 엘리먼트를 리턴하는 것이고,

엘리먼트 렌더링엘리먼트를 DOM에 반영하는 거에요, fiber node를 구성, 업데이트, 재조정해서 변경된 부분만 DOM을 업데이트 하는 거죠.

 

참고로 Fiber node는 간단히 말해서 상태가 저장되는 공간입니다.

 

그리고 아래에 보면 2가지 단계 렌더페이즈 커밋페이즈 있습니다.

페이즈는 단계라고하죠

 

혹시 두 가지 렌더링과 2가지의 단계가 서로 일치하지 않고 있는 게 보이시나요?

렌더 페이즈 컴포넌트 렌더링부터 엘리먼트 렌더링의 재조정까지라고 합니다.

즉 이전 렌더링과 비교하여 변경된 부분을 파악까지만 합니다.

 

커밋페이즈 DOM 업데이트 하는 부분만 포함된다고 하네요

 

여기서  우리가 눈 여겨볼 부분은 컴포넌트 렌더링을 포함하고 있는 렌더 페이즈에 해당돼요!

그렇다면 이제 우리가 눈여겨볼 렌더페이즈와 커밋페이즈를 다시한번 살펴 보겠습니다.

 

 

 

렌더페이즈와 커밋페이즈

Reander Phase에서는 컴포넌트를 호출해서 리액트 엘리먼트를 반환합니다.

그리고 새로운 virtual DOM을 생성합니다.

만약에 이번이 첫 번째 렌더링이 아니라면 재조정 과정을 거칩니다.

재조정과정에서는 이전 virtualDOM과 현재 virtualDOM의 변경점을 체크해줍니다.

 

그리고 Commit Phase가 진행됩니다.

Commit phase에서는 렌더페이즈에서 체크해놓았던 변경점을 Real DOM에 반영해주는 단계입니다.

만약 변경이 필요한 부분이 없다면 Commit phase단계는 스킵이 됩니다.

 

그럼 아까 궁금해했던 문제점이 해결이 됩니다!

아까의 예제에서 렌더페이즈가 진행되어 렌더링이 진행됐지만

useCallback을 사용함으로써 props의 값을 같게 했으니 변경점이 없어 커밋페이즈를 스킵해 약간의 최적화가 적용되었습니다.

이유는 이전에 말씀드렸던 것처럼 렌더페이즈 내의 재조정과정에서 RealDOM의 변경이 필요하지 않다고 판단을 하기 때문입니다.

 

참고로 원래 재조정과정에서는 -렌더링 후 엘리먼트가 달라졌을 때 또는 동일한 엘리먼트들의 순서가 달라졌을 때와 같이 재조정과정에서 체크하는 부분들이 있는데 지금은 props 이외에는 체크할 부분들이 모두 같은 상황이라고 전제하겠습니다.

 

다시 돌아와서 하지만 우리가 원하는 ChildB의 렌더링을 막지 못했습니다.

ChildB렌더페이즈를 막으려면 어떻게해야할까요?

 

React.Memo가 등장!

바로 React에서 제공하는 React.memo를 사용하는 것입니다.

 

React.memo를 간단하게 설명하면

전달받은 props가 이전 props와 비교했을 때 값이 같으면-렌더링을 막고, 마지막에 렌더링된 결과를 재사용하는 고차 컴포넌트 입니다.

 

참고로 React.memoprops를 비교할 때 얕은 비교를 사용합니다.

 

 

React.Memo를 적용해볼까?

그럼 아까 예제에서 childB컴포넌트에 React.memo를 추가해보겠습니다.

이제 ChildB 컴포넌트는 렌더링을 진행하기 전에 props가 이전과 같은지 확인합니다.

서로 같은 값이라면 childB컴포넌트는 렌더링 과정을 생략하게 됩니다.

 

오른쪽 이미지는 React Devtools로 확인한 결과입니다.

이미지에서처럼 최초 렌더링에서는 ChildB를 렌더링하지만 이후 리-렌더링에서는 ChildB를 렌더링하지 않는 모습입니다.

 

헷갈리는 분들을 위해!

여기서 잠깐 헷갈리는 분들이 계실 겁니다.

아까 useCallback을 써서' props로 내려주는 함수의 참조 값을 같게 해줬는데'

React.memo로도 '이전의 Props값과 현재 Props 값이 같은 지 확인해야 한다는 게 뭐지?' 라고 생각하실 수 있는데요.

 

이건 다시 말씀해드리면

만약 useCallback사용하지않고 React.memo만 사용했다면

Parent 컴포넌트에서 리-렌더링이 될 때 props로 전해지는 함수를 갖고있는 변수가 초기화되기 때문에 새로운 참조 값을 갖게 됩니다.

그럼 ChildB 컴포넌트는 React.memo로 감싸줬다고 하더라도 이전 props값과 다르므로 렌더링을 하게 됩니다.

결과적으로 두 명령어 모두 사용하는게 좋겠죠?

 

 

useMemo는 생략합니다!

원래는 useMemo설명드릴려고 했는데 아까의 설명과 동일한 부분이 많아서 useMemo 설명은 생략하겠습니다!

 

 

 

 

그럼 다시 생각해봅시다.

근데 메모이제이션은 남용하면 안된다며..?

 

그러면 모든 곳에 useCallback, useMemo, React.memo를 사용하는 것이 좋을까요?

이미 아시는 분들도 계시겠지만

메모이제이션은 내부적으로 특정한 동작을 실행시켜 줘야 하기 때문에 모두 비용이 듭니다.

그래서 useCallback, useMemo,React.memo어떻게 사용해주는 것이 적절한지는 항상 고민해야 되는 문제이기도 합니다.

그래서 저는 리-렌더링을 막을 다른 방법이 없을까? 에 대해 생각하게 되었습니다.

 

최적화 도구를 사용하기 이전에 근본적인 코드개선을 해야 된다!

그래서 찾게 된 것이 근본적인 코드 개선이었습니다.

만약 근본적으로 코드에 문제가 있다면 이를 해결하려고 노력한 이후에 최적화 도구를 사용하는 것입니다.

 

 

컴포넌트 트리와 렌더링 관계 트리

근본적인 코드를 개선하기 위해서

컴포넌트 트리와 렌더링 관계 트리에 대해 설명드리려고합니다.

이 내용은 부모 자식 관계의 컴포넌트가 리-렌더링 되는 조건을 알기 쉽게 파악하고,

이를 통해 최적화도구를 적절하게 사용할 수 있도록 도움을 줄 것입니다.

먼저 전제해야할 내용을 알려드리겠습니다.

1.부모 컴포넌트가 리-렌더링 되면 자식 컴포넌트도 리-렌더링 됩니다.
2.자식 컴포넌트에서 리-렌더링 되는 조건인 propsstate에 변경사항이 있었느냐는 무관합니다.

 

자 그럼 컴포넌트 트리와 렌더링 관계 트리를 알기 쉽게 이해하도록 예제를 통해 살펴볼까요?

 

 

 

컴포넌트 트리와 렌더링 관계트리, 예제를 보면서 쉽게 알아보자!

첫 번째 예제입니다.

예제에서 Child1, Child2, Child3 는 모두 Parent의 자식 컴포넌트들입니다.

 Parent 컴포넌트가 리렌더링되면 Child1, Child2, Child3 는 모두 리-렌더링 됩니다.

이 부모 자식관계를 이렇게 표현할 수 있을 것 같네요

이제 오른쪽 그림을 렌더링 관계 트리라고 부르겠습니다.

 

여기서 포인트는

Parent리턴하는 JSX안에 이런 형태로 등장하는 모든 컴포넌트들은 Parent의 자식이면서 서로는 형제입니다.

 

참고로 컴포넌트의 형제관계라는 용어는 확실하지 않은 용어인데요
하지만 아까 설명드렸던 React.createElement와 같이 React의 구현 디테일을 언급하지 않고 이해하실 수 있도록 형제관계라고 표현하겠습니다.

 

다시 돌아와서 형제 관계에서 중요한 점은 형제 컴포넌트들끼리는-렌더링에 서로 영향이 없다는 것입니다.

첫째인 Child1리렌더링된다고 둘째인 Child2리렌더링되지 않고,

Child2리렌더링 될 때 막내인 Child3리렌더링되지 않습니다.

Child1, Child2, Child3리렌더링되는지 여부는 오로지 부모인 Parent 컴포넌트의 리렌더링에만 의존적입니다.

자 그럼 렌더링 관계트리의 특징에 대해서 요약해보겠습니다.

 

부모컴포넌트가 리렌더링 되면 자식 컴포넌트 들도 리렌더링 됩니다.

자식 컴포넌트란 부모 컴포넌트의 JSX 안에서 사용된 모든 컴포넌트들을 말합니다.

형제 관계인 컴포넌트들끼리는 서로 리렌더링에 영향을 미치지 않습니다.

이 부분을 잘 기억해주세요!

 

 

두 번째 예제!

약간 더 어려운 예제에 이전의 원칙을 적용해보록 하겠습니다.

현재 예제에서 Parent 컴포넌트가 리렌더링되면 ChildA, ChildB, ChildC 중 어떤 컴포넌트가 리렌더링될까요?

 

ChildA는 확실히 Parent의 자식인데, props로 받은 children과 lastChild가 햇갈리게 합니다.

App 컴포넌트에서 ChildB는 Parent 컴포넌트에 감싸져있으므로 왠지 ChildB도 Parent 컴포넌트와 함께 리렌더링될 것 같습니다.

즉, 오른쪽과 같은 렌더링 관계 트리를 떠올리시는 분들도 계실 것 같습니다

어떤가요? 이 트리가 맞는 것 같나요?

 

하지만 정답은 ChildA 컴포넌트만 Parent 컴포넌트와 함께 리렌더링 됩니다.

아까 정리했던 3가지 내용에 대해서 기억하시나요?

3가지 내용 중에"자식 컴포넌트란 부모 컴포넌트의 JSX 안에 사용된 모든 컴포넌트들"이라고 정의했으므로

ChildB와 ChildC는 App의 자식 컴포넌트입니다.

 

App 컴포넌트가 리턴하는 JSX에는 Parent, ChildB, ChildC가 있습니다.

 즉, Parent, ChildB, ChildC는 App 컴포넌트의 자식 컴포넌트들입니다.

ChildC는 App 컴포넌트가 렌더링하여 Parent의 lastChild props으로 전달해주는 것이고,

 ChildB도 App 컴포넌트가 렌더링하여 Parent의 children props으로 전달해줍니다.

 

참고로 props의 children은 제가 말하는 자식컴포넌트와 다른 말이라는 점 유의해주세요!

 

이제 트리를 오른쪽과 같이 그릴 수 있겠습니다.

이 렌더링 관계트리를 통해 Parent 컴포넌트가 리렌더링될 때마다 함께 리렌더링되는 컴포넌트는 어떤 것인지 명확하게 알 수 있습니다.

 

 

컴포넌트 트리와 렌더링관계트리를 ReactDevtools로 확인해보자

아까 첫 번째 예제의 코드를 다시 가져와서

 React DevTools를 이용해 컴포넌트 트리를 확인해보겠습니다.

 

여기 컴포넌트 트리를 보면 Child2가 Child1의 자식이어서 Child1이 리렌더링되면 Child2와 Child3도 리렌더링될 것 같습니다.

하지만 이 컴포넌트 트리는 렌더링 관계가 아닌 중첩된 DOM 트리 구조를 보여주는 것입니다.

그렇다면, 리렌더링 관계는 정말 컴포넌트 트리와는 다른 것일까요?

위  코드에서 React DevTools 탭의 컴포넌트 트리에서 Child2를 선택해보겠습니다.

그러면 오른쪽에 rendered by에 Child2를 리렌더링할 수 있는 컴포넌트들을 보여줍니다.

App 또는 Parent가 리렌더링되면 Child2가 리렌더링된다는 의미입니다.

rendered by에 Child1은 존재하지 않습니다.

따라서, DOM 트리나 컴포넌트 트리를 보고 리렌더링 관계를 판단하면 틀릴 수도 있음을 유의해야합니다.

 

두번째 예제도 확인해 볼까요?

ChildA, ChildB, ChildC가 모두 Parent의 자식인것처럼 보이지만,

하나씩 선택하여 rendered by를 확인해보면 ChildB와 ChildC는 App컴포넌트에 의해서만 리렌더링 되는 것을 확인할 수 있습니다.

컴포넌트 트리와 렌더링 관계 트리가 동일한 경우도 있을 수 있으나, 두번째 예제에서도 컴포넌트 트리와 렌더링 관계 트리가 다르다는 것을 알 수 있습니다.

 

분명 컴포넌트를 불러왔는데 왜 렌더링을 안하는거지?

이 내용은 아까 생략했던 React.createElement의 추가 설명입니다!

두 번째 예제를 Babel로 컴파일하면 오른쪽과 같이 나타나게 됩니다.

Parent 컴포넌트의 return문에는 ChildB와 ChildC 컴포넌트에 대한 react.createElement가 존재하지 않기 때문에

Parent 컴포넌트가 호출되어 렌더링 되어도 ChildB와 ChildC 컴포넌트는 호출되지 않습니다.

 

이유는 아까 말씀드렸던 JSX안에서 사용된 '<Component/>' 형태의 컴포넌트가 아니기 때문이죠!

 

그럼 내용을 다시 한번 정리해볼까요?

부모 컴포넌트가 리-렌더링 되면 자식 컴포넌트들도 리-렌더링 된다.
자식 컴포넌트는 부모 컴포넌트의 JSX 안에서 사용된 모든 컴포넌트들이다.
"JSX 안에서 사용된 컴포넌트" 화면에 나와있는 형태로 사용된 컴포넌트이다.
props, children으로 받은 컴포넌트는 자식 컴포넌트가 아니다.
자식 컴포넌트들끼리는 형제이다.
형제 관계인 컴포넌트들끼리는 서로 리-렌더링에 영향을 미치지 않는다.
 

이제 자식컴포넌트가 리렌더링이 되는 조건들에 대해서 이해하는 것이 쉬워지셨나요??

자 그럼 이제 처음에 설명드렸던 문제를 다시 갖고 와보겠습니다.

 

일단 아까 있었던 ChildA 컴포넌트는 실제로 props의 변경을 줘야 하는 컴포넌트이기에 제거했습니다.

그리고 ChildB가 받아야 할 함수 객체는 App에서 전달하도록했어요.

 Parent에서는 리-렌더링이 일어나도록 set함수를 동작할 수 있게 구현했습니다.

 

아까와의 다른 점은

Parent 컴포넌트에서 children을 사용하고, App 컴포넌트에서는 Parent 컴포넌트가 ChildB를 감싸고 있습니다.

일반적인 상황이 아닐 수 있지만 이대로 진행해보겠습니다.

 

혹시 지금 코드에서 렌더링 관계 트리가 한 눈에 보이시나요?

그럼 저는 정말 뿌듯할 것 같습니다.

결과를 확인해보면

최초렌더링에서는 예상대로 ChildB가 렌더링됩니다.

하지만 Parent 컴포넌트에서 set함수로 리-렌더링을 발생시키면 Parent 컴포넌트만 리-렌더링됩니다.

childB를 React Devtools로 확인해보면 App컴포넌트에 의해서만 렌더링이 된다고 알려주고 있습니다.

 

이를 통해 최적화도구를 사용하지 않고 최적화를 만들어냈습니다.

어떤신가요?

너무 재미있지 않나요?

 

부모 컴포넌트가 렌더링되면 자식 컴포넌트도 렌더링되는 개념이

컴포포넌트 트리렌더링 관계 트리에서 일치하지 않을 수 있습니다.

 

그래서 컴포넌트 트리와 별도로  "렌더링 관계 트리" 라는 것을 정의하여

렌더링에 영향을 주는 부모 / 자식관계를 더 잘 이해할 수 있도록 해보았습니다.

 

렌더링 관계 트리를 이해함으로써 여러분들이 조금이나마 최적화를 더 효율적으로 하셨으면 좋겠습니다.

 

https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/#memoize-everything

마지막으로 이런 post가 있어서 갖고와봤는데요!

외국의 한 블로그에서 작성된 post인데요!

차라리 모든 함수 컴포넌트를 React.memo()로 감싸면 안되느냐는 의견이 있었는데

어..아마도 이런 이야기가 꽤 많았던 모양입니다

여기에 대해서 댄 애브라모는 "모든 JS 함수에 대하여 Lodash의 memoize()를 사용하면 성능이 나아지겠는가?"라고 트윗했었다고 합니다.

좋은 답변이죠?

 

 

출처

https://velog.io/@mogulist/understanding-react-rerender-easily

 

리액트의 리렌더링 조건을 더 쉽게 이해해보기

부모 컴포넌트가 리렌더링되면 자식 컴포넌트도 리렌더링 되는데요. 여기서 부모/자식 관계는 DOM 트리에서 부모/자식과는 다를 수 있습니다. 렌더링 관계 트리를 통해 리렌더링되는 조건을 확

velog.io

 

728x90
반응형
LIST