이제 리액트 앱을 어떻게 발전해나갈 수 있을까요?
서로 다른 컴포넌트들이 어떻게 연관되어 있고
어떻게 실행되는지 배웠던 것을 활용해 봅시다
제가 준비해둔 이 데모 앱에는
한 가지 문제라면 문제가 있는데
고쳐야 하는 use case가 하나 있습니다
컴포넌트들이 어떻게 실행되는지와 연관되었는지를
잘 이해했다면 고칠 수 있습니다
그래서 이 콘솔창을 다시 열어보겠습니다
여기서 어떤 컴포넌트들이 렌더링되고
연관되어 있는지가 보입니다
이걸 지워보겠습니다
그리고 여기서 새로운 숫자를 입력창에 넣으면
'설정' 을 클릭해도 아직 아무 일도 일어나지 않지만
새로운 숫자를 입력하면
그러니까 여기 문자 하나를 입력해보면
여러 컴포넌트 한수들이
다시 실행된 게 보입니다
이 앱을 구성하는 모든 컴포넌트들입니다
왜 그런 일이 발생하는 걸까요?
왜냐하면 이 입력 구간이
앱 컴포넌트에 직속해 있기 때문입니다
여기 보이시는 이 입력 구간입니다
그리고 해당 입력 필드에는 onChange 핸들러(handler)가 있습니다
그러므로 문자를 입력할 때마다 상태가 업데이트됩니다
그리고 물론, 이전에 배웠듯이, 상태가 바뀌면
이 컴포넌트 함수가 재실행됩니다
그리고 지난 강의들을 통해 알다시피
이게 무슨 뜻이냐면
모든 자식 컴포넌트 함수들이 재실행된다는 겁니다
그래서 문자를 입력할 때마다
이 모든 컴포넌트 함수들이 재실행됩니다
자, 그렇다고 이게 꼭
렌더링되는 DOM에 영향을 주진 않으므로 나쁘진 않습니다
이 섹션에서 추후에 배우게 되겠지만
이게 최선은 아닙니다
코드들이 너무 많이 실행되기 때문입니다
그리고 이제 왜 이런 일이 발생하는지 이해할 수 있습니다
그럼 어떻게 이 문제를 고칠 수 있을까요?
일단 이 문제를 고치려면
크게 두 가지 해결책이 있습니다
첫 번째 해결책을 보여드릴게요
Counter 컴포넌트의 경우입니다
리액트가 내장 함수를 지원하기 때문에
컴포넌트 함수들을 감쌀 수 있고
불필요한 컴포넌트 함수 실행을 방지합니다
그 함수는 memo 함수이며
꼭 리액트로부터 불러와야 합니다
이 함수를 불러왔다면
이제 여러분의 컴포넌트 함수를 감쌀 수 있습니다
그냥 실행하기만 하고 컴포넌트 함수를 memo의 인수 값으로
전달하면 됩니다
그러면 여기 경고가 뜨는데요
몇 개발 모드 기능들이 제대로 작동하지 않습니다
이런 식으로 한다면 말이죠
그래서 사실 추천드리는 방식은
결과를 변수나 상수에
따로 저장해두고
이름은 컴포넌트 함수 이름과 동일하게 지어줍니다
그리고 이 변수 혹은 상수를 내보내기만 하면 됩니다
그러나 이건 그저
좋운 개발자로써의 경험을 쌓기 위함입니다
memo도 같은 방식으로 동작합니다
memo가 하는 것은
컴포넌트 함수의 속성을 살펴보고
컴포넌트 함수가
정상적으로 다시 실행될 때, 예를 들어
앱 컴포넌트 함수가 실행되면
memo가 이전 속성 값과
새로 받을 속성 값을 살펴봅니다
만약 컴포넌트 함수가 실행됐는데
속성 값들이 완전히 동일하다면
그 말은 배열과 객체가
메모리 안에 있는 배열과 객체와 동일하다는 뜻입니다
하지만 만약 그게 동일하다면
이 컴포넌트 함수 실행을
memo가 저지합니다
그래서 이 컴포넌트 함수가
이 Counter나 컴포넌트 함수가 딱 한 번만 실행됩니다
initialCount가 바뀌거나
내부적 상태가 바뀌면
그건 memo에 아무런 영향을 받지 않습니다
memo는 오직 부모 컴포넌트, 그러니까 앱 컴포넌트에
의해 함수가 실행되었을 때만 이를 저지합니다
그렇기 때문에 memo가
속성을 살펴보는 건 당연한 겁니다
만약 여기 있는 initialCount 속성의 값이
바뀌지 않았다면, Counter 컴포넌트가
재실행될 이유가 없습니다
그저 부모 컴포넌트가 재실행됐다는 이유만으로는 그렇습ㄴ다
물론, 그 내부적 상태가 바뀌면
당연히 이 컴포넌트 함수를 작동시키게 됩니다
그리고 말했듯이, memo는 이를 저지하지 않습니다
memo는 내부적 변화에는 신경 쓰지 않습니다
하지만 외부적 변화도 마찬가지로
이 속성 값이 바뀌어야만
컴포넌트 함수가 실행됩니다
그렇지 않고는 굳이 이 함수를 재실행하는 건
이전과 같은 결과를 낼 것이기 때문에
불필요하다고 볼 수 있습니다
그리고 당연하게도, 이 Counter 컴포넌트 함수가
재실행되는 것이 방지되면
안에 있는 중첩된 컴포넌트 함수 또한
재실행되지 않습니다
이 코드는 애초에 실행되지 않기 때문에
그리고 이 모든 Counter 함수 코드가
실행되지 않기 때문입니다
그리고 결과적으로, 만약 memo를 감싸기로 했다면
이 Counter 함수를 둘러서 감싸고 재로딩 한 후
그 다음 이 값을 바꾸면, 볼 수 있습니다
오직 Header 컴포넌트만이
컴포넌트에 추가로 불러와집니다, 왜냐하면 제가 한 건 앱
그저 여기 입력된 값을 바꾼 것이기 때문입니다
'설정'이나 다른 버튼은 클릭하지 않았습니다
그러므로 당연히 앱 컴포넌트 안에서
상태가 업데이트되었지만, 이 enteredNumber 상태는
Counter 컴포넌트와는 상관이 없고
initialCount도 바꾸지 않습니다
그러므로 이 컴포넌트가 작동되어 실행되지 않아야 합니다
그리고 memo 덕분에 그러지 않을 겁니다
이쯤되면 memo가 유용하기 때문에
모든 컴포넌트를 감싸는 건 안 되는지 궁금해 할 겁니다
그래도 됩니다, 하지만 모든 컴포넌트를
감싸서는 안 됩니다
대신, 이런 예시에서는
최대한 상위 트리에 속한 컴포넌트를
감싸는 건 가능합니다
이 Counter 컴포넌트의 경우처럼 말입니다
왜냐하면 그 컴포넌트가 재실행되는 게
저지된다면, 모든 중첩 컴포넌트들 또한
재실행되지 않기 때문입니다
그리고 여러분이 memo로 모든 컴포넌트를 감싼다면
즉 말하자면
리액트는 컴포넌트 함수를 실행하기 전 항상
속성들을 확인해야 한다는 겁니다
그리고 물론 속성 값을 확인하는 건
그만큼 성능에 부담을 주게 됩니다
그건 함수를 감쌀 때 특히 문제가 됩니다
항상 속성을 바꿔야 하기 때문입니다
그러므로 이 비교는 항상 모든 컴포넌트 함수가
실행되어야 한다는 결과를 도출해냅니다
왜냐하면 memo를 사용하지 않았을 때와 동일한 결과를 얻기 위해
이 확인 과정을 수행함으로써
성능에 효율적이기 때문입니다
그러니까 신경써서 사용해야 합니다
이것처럼 간단한 앱에서는 솔직히
아예 쓸 필요가 없습니다
성능에 큰 영향을 주지 않기 때문입니다
하지만 여러분이 쓰고 싶고, 영향을 줄 수 있다면
신경써서 조심히 사용하고
꼭 재-렌더링을 방지할 수 있는 컴포넌트와
최대한 상위 컴포넌트 트리에 있는 컴포넌트를 사용하시기 바랍니다