> 이 글은 [원문](https://blog.bitsrc.io/10-ways-to-optimize-your-react-apps-performance-e5e437c9abce)을 번역한 글 입니다. 최적화는 모든 소프트웨어, 특히 웹 앱을 개발할때 가장 신경을 써야 하는 것 중 하나이다. Angular, React 또는 다른 자바스크립트 프레임워크는 이와 관련해서 좋은 설정과 기능을 가지고 있다. 이 포스터에서는 앱의 성능을 최적화하는 데 도움이 되는 기능과 방법들에 대해서 알아보도록 하겠다. 코드를 최적화하기 위해 사용하는 특정 패턴과 방법에 상관 없이 **코드를 DRY하게 유지**하는 것은 매우 중요하다. DRY하게 유지하기 위해 최적화된 코드를 작성하는데 도움이 되는 구성 요소는 항상 재사용하도록 해야 한다. 만약 개발을 하면서 더 많은 시간을 훌룡한 코드를 개발하는데 쓰고 더 적은 시간을 평범한 코드를 개발하는데 (실수할 가능성이 훨씬 더 큰) 쓴다면 놀라운 일들이 일어날 것이다. 위와 같이 말하기는 했지만, 코드를 재사용하는 것은 큰 코드베이스나 다른 리포지토리를 다룰 때 어려운 일이 될 수 있는데, 크게 두가지 이유가 있다. - 재사용하기 유용한 코드 부분을 알지 못한다. - 리파지토리 간에 코드를 공유하는 전통적인 방식은 패키지를 통해 이루어지며, 이것은 복잡한 설정이 필요하다. 위와 같은 두가지 문제를 해결하기 위하여 **[Bit](https://bit.dev/)** ([Github](https://github.com/teambit/bit)) 과 같은 툴을 사용하는 것을 권장한다. Bit은 코드베이스에서 구성요소를 분리하여 bit.dev에 공유할 수 있도록 도와준다. bir.dev의 인상 깊은 검색 엔진으로 쉽게 관련 Component들을 찾을 수 있다. - 좋은 코드는 좋은 업무 습관에서 시작한다. ## 1. useMemo() 이 함수는 React Hook 중 하나로서 React에서 CPU 소모가 심한 함수들을 캐싱하기 위해 사용된다. 예시를 보도록 하자: ``` jsx function App() { const [count, setCount] = useState(0) const expFunc = (count)=> { waitSync(3000); return count * 90; } const resCount = expFunc(count) return ( <> Count: {resCount} setCount(e.target.value)} placeholder="Set Count" /> ) } ``` 위 코드에 `expFunc`은 3분후 실행되는 비싼 함수이다. 이 함수는 `count`를 입력받아 3분을 기다린 후 90을 곱하여 리턴한다. 또한 `useState` hook에서 `count` 변수를 받아 `expFunc`을 실행하는 `resCount` 또한 확인할 수 있다. 여기서 `count`는 입력할 때마다 값이 변경되어야 한다. 아무거나 입력할 때마다 앱 컴포넌트가 다시 렌더링 되어 `expFunc` 함수가 호출 된다. 계속 입력을 하면 대규모 성능 병목 현상을 유발하는 기능이 실행되고 있다는 것을 알게 될 것이다. 각각의 입력마다 렌더링하는 데 최소 3분이 소요된다. 만약 `3`을 입력하게 되면 expFunc은 3분동안 실행되고 다시 `3`을 입력하면 또 3분간 실행이 된다. 하지만 이전 입력과 같이 때문에 두번째 입력에서는 다시 실행되어서는 안되고 결과를 어딘가 저장한 후 `expFunc`이 실행되지 않고 값을 리턴해야 한다. 위와 같은 문제는 `useMemo`를 통해 expFunc을 최적화 함으로써 해결할 수 있다. useMemo는 아래와 같은 구조를 가진다. ``` jsx useMemo(()=> func, [input_dependency]) ``` `func`은 캐시하고 싶은 함수이고, `input_dependency`는 useMemo가 캐시할 func에 대한 입력의 배열로서 해당 값들이 변경되면 func이 호출된다. 이제, useMemo를 이용하여 위의 코드를 최적화 해보도록 하자: ```jsx function App() { const [count, setCount] = useState(0) const expFunc = (count)=> { waitSync(3000); return count * 90; } const resCount = useMemo(()=> { return expFunc(count) }, [count]) return ( <> Count: {resCount} setCount(e.target.value)} placeholder="Set Count" /> ) } ``` 이제 expFunc은 입력에 대해 캐싱되며 동일한 입력이 다시 발생할 때 useMemo는 expFunc을 호출하지 않고 입력에 대해 캐시된 결과값을 리턴한다. 이것은 앱 컴포넌트를 최적화 시키는 방법 중 하나이다. useMemo 캐싱 기술을 이용해 성능을 향상시키고, 함수형 컴포넌트에서는 prop 값들 또한 캐싱하는 것 또한 도움이 된다. ## 2. 가상화된 List 만약 거대한 list data를 렌더링 한다면 브라우저의 viewport에 보여지는 부분만 렌더링하고 나머지는 스크롤 할때 보여지도록 하는 것을 권장한다. 이것은 "windowing" 이라 부르며, 많은 React 라이브러리들이 존재한다. 이 중 Brian Vaughn이 개발한 [react-window](https://github.com/bvaughn/react-window)와 [react-virtualized](https://github.com/bvaughn/react-virtualized)가 있다. ## 3. React.PureComponent Class Component에서 ShouldComponentUpdate의 역활과 비슷한 React.PureComponent가 있다. React.PureComponent는 기본 component 바탕에서 state와 prop값을 체크하여 component가 업데이트 되어야 하는지 확인한다. 예제에서 shouldComponent 부분을 변환해 보도록 하겠다. ``` jsx class ReactComponent extends Component { constructor(props, context) { super(props, context) this.state = { data: null } this.inputValue = null } handleClick = () => { this.setState({data: this.inputValue}) } onChange = (evt) => { this.inputValue = evt.target.value } shouldComponentUpdate( nextProps,nextState) { if(nextState.data === this.state.data) return false return true } render() { l("rendering App") return (
{this.state.data}
) } } ``` React.PureComponent를 사용한다면: ```jsx class ReactComponent extends React.PureComponent { constructor(props, context) { super(props, context) this.state = { data: null } this.inputValue = null } handleClick = () => { this.setState({data: this.inputValue}) } onChange = (evt) => { this.inputValue = evt.target.value } render() { l("rendering App") return (
{this.state.data}
) } } ``` 위와 같이 shouldComponentUpdate를 지우고 React.PureComponent를 상속받도록 하였다. text 창에 2를 입력하고 `Click Me`를 연속으로 누르면 ReactComponent는 한번 렌더링 된 후 다시는 렌더링 되지 않는 것을 볼 수 있다. 이것은 단순히 객체 참조만 비교하는 것이 아니라 이전 props와 state 객체들과 변경할 state와 props를 얇게 비교한다. React.PureComponent는 component가 렌더링 되는 횟수를 줄임으로서 최적화를 하게 해준다. ## 4. Caching functions 함수는 JSX 컴포넌트 render 메소드 안에서 호출 될 수 있다. ``` jsx function expensiveFunc(input) { ... return output } class ReactCompo extends Component { render() { return (
{expensiveFunc}
) } } ``` 함수 실행이 오래 걸릴 경우, 렌더링 하는 나머지 부분이 대기하게 되어 사용자들의 경험에 방해가 된다. ReactCompo를 보면, expensiveFunc은 JSX 안에서 렌더링 되어 리렌더링 될때마다 실행되어 값을 리턴하여 DOM에 렌더링 시킨다. 함수는 CPU 집약적으로 리렌더링될때마다 실행되며 React는 해당 실행이 끝날때 까지 남은 리렌더링 알고리즘을 실행하기 위해 기다려야 한다. 위 상황에서 최고의 선택은 입력되는 값이 같다면 캐시 처리하여 같은 값을 리턴하도록 하는 것이다. 따라서 같은 입력이 다시 발생할 경우 함수의 연속 실행이 더 빨라지게 된다. ``` jsx function expensiveFunc(input) { ... return output } const memoizedExpensiveFunc = memoize(expensiveFunc) class ReactCompo extends Component { render() { return (
{memoizedExpensiveFunc}
) } } ``` ## 5. Reselect selectors 사용하기 [reselect](https://github.com/reduxjs/reselect)를 사용하면 Redux 상태 관리가 최적화 된다. 리덕스 이뷰터블하게 동작한다는 것은 action이 dispatch 할때마다 새로운 객체 참조가 생성 된다는 뜻이다. 이것은 컴포넌트에서는 변경 되지 않았지만 오브젝트 참조가 변경될 경우 리렌더링이 되므로 성능에 방해가 되는 요소가 될 수도 있다. Reselect 라이브러리는 Redux state를 캡슐화하여 component를 확인하고 렌더링 할지 안할지 여부를 알려준다. 따라서 reselect는 메모리 참조가 서로 다르지만 변경되었는지 확인하기 위해 이전 및 현재 Redux 상태를 얕게 체크함으로써 시간을 절약하게 한다. 필드가 변경된 경우 React에게 리렌더링 해야 한다고 알려주고 필드가 변경되지 않을 경우 새로운 상태 객체가 생성되었음에도 불구하고 리렌더를 취소한다. ## 6. Web worker 자바스크립트 코드는 싱글 쓰레드에서 동작한다. 동일한 쓰레드에서 오래걸리는 프로세스를 실행하면 UI 렌더링 코드에도 심각한 영향을 미치므로 최선의 방책은 프로세스를 다른 쓰레드로 옮기는 것이다. 이것은 Web worker들이 하는 역활이다. 이것들은 UI 흐름을 방해하지 않고 메인 쓰레드와 동시에 실행할 수 있는 게이트웨이 이다. React에서 공식적으로 지원하지는 않지만 Web worker를 다양한 방식으로 사용할 수 있다. 그 중 한가지는 아래와 같다: ``` jsx // webWorker.js const worker = (self) => { function generateBigArray() { let arr = [] arr.length = 1000000 for (let i = 0; i < arr.length; i++) arr[i] = i return arr } function sum(arr) { return arr.reduce((e, prev) => e + prev, 0) } function factorial(num) { if (num == 1) return 1 return num * factorial(num - 1) } self.addEventListener("message", (evt) => { const num = evt.data const arr = generateBigArray() postMessage(sum(arr)) }) } export default worker // App.js import worker from "./webWorker" import React, { Component } from 'react'; import './index.css'; class App extends Component { constructor() { super() this.state = { result: null } } calc = () => { this.webWorker.postMessage(null) } componentDidMount() { let code = worker.toString() code = code.substring(code.indexOf("{") + 1, code.lastIndexOf("}")) const bb = new Blob([code], { type: "application/javascript" }); this.webWorker = new Worker(URL.createObjectURL(bb)) this.webWorker.addEventListener("message", (evt) => { const data = evt.data this.setState({ result: data }) }) } render() { return (

Result: { this.state.result }

) } } ``` 이 앱은 10만개의 요소가 들어있는 배열의 합을 계산하는데, 만약 메인 쓰레드에서 작업을 했다면 메인쓰레드는 10만개의 요소들을 통과하고 그 합계를 계산할 때까지 다른 작업은 할 수 없을 것이다. 자, 이제 Web worker로 옮겼다. 메인 쓰레드는 web worker와 원할하게 병렬로 실행될 것이고, 10만개의 요소가 담긴 배열의 합은 계산될 것이다. 결과는 완료되었을 때 전달되며 메인 쓰레드는 결과만 제공하면 된다. 빠르고, 간단하며 성능 또한 뛰어나다. ## 7. Lazy Loading Lazy loading은 부하를 단축하기 위해 자주 사용되는 최적화 기법 중 하나이다. **Lazy Loading**은 일부 웹 앱 성능 문제의 위험을 최소화 하는데 도움이 된다. React에서 component를 lazy load를 이용하기 위해 React.lazy() API를 사용한다. React.lazy는 React v16.6에 새로 추가된 기능이다. 이것은 React Component를 쉽고 직관적으로 lazy-loading과 코드 스플리팅을 사용하기 위해 제안된 방법이다. > React.lazy 함수는 동적 import를 사용하며 일반 component처럼 렌더링할 수 있게 해준다. - [React blog](https://gist.github.com/philipszdavido/0d5d92a688ca9e1bbca65ba72de90f53) React.lazy는 동적 import를 사용하여 component를 생성하고 렌더링 하는 걸 쉽게 만들어준다. React.lazy는 파라미터로 함수를 받는다. ``` jsx React.lazy(()=>{}) // or function cb () {} React.lazy(cb) ``` 이 콜백 기능은 반드시 동적 import 구문을 이용하여 컴포넌트 파일을 불러와야 한다. ``` jsx // MyComponent.js class MyComponent extends Component{ render() { return
MyComponent
} } const MyComponent = React.lazy(()=>{import('./MyComponent.js')}) function AppComponent() { return
} // or function cb () { return import('./MyComponent.js') } const MyComponent = React.lazy(cb) function AppComponent() { return
} ``` React.lazy의 콜백 기능은 `import()` 호출을 통해 Promise를 반환한다. Promise는 모듈이 성공적으로 불러왔는지 여부를 확인하고 네트워크 오류, 잘못된 경로 확인, 파일 없음 등으로 인해 모듈을 로드하는 동안 오류가 발생했는지를 확인한다. webpack이 코드를 컴파일하고 번들링할 때 `React.lazy()`와 `import()`를 히트할때 별도의 번들을 만든다. 앱은 다음과 같이 될 것이다. ```jsx react-app dist/ - index.html - main.b1234.js (contains Appcomponent and bootstrap code) - mycomponent.bc4567.js (contains MyComponent) /** index.html **/
``` 이제 앱은 멀티 번들로 분리된다. AppComponent가 렌더링 될 때 mycomponent.bc4567.js 파일은 로드되어 MyComponent가 DOM에 보여진다. ## 8.React.memo() useMemo와 React.PureComponent와 같이 React.memo()는 함수 컴포넌트를 캐시하는데 사용된다. ```jsx function My(props) { return (
{props.data}
) } function App() { const [state, setState] = useState(0) return ( <> ) } ``` App 컴포넌트는 state를 `data`라는 props로 My 컴포넌트에 넘겨준다. 버튼 엘리먼트의 onClick을 보면 클릭할 때 마다 state 값을 0으로 변환해주는 작업을 한다. 만약 버튼을 계속 누른다면, state 값이 동일함에도 불구하고 My 컴포넌트는 계속 리렌더링이 된다. App 과 My 컴포넌트 하위로 수천개의 컴포넌트들이 존재한다면 엄청난 성능 이슈가 될 것이다. 리렌더링 되는 것을 줄이기 위해서, My 컴포넌트를 memoized 버전으로 리턴하는 React.memo를 이용하여 한번 감싼 후 App에 포함 시켜 줄 것이다. ``` jsx function My(props) { return (
{props.data}
) } const MemoedMy = React.memo(My) function App() { const [state, setState] = useState(0) return ( <> ) } ``` 위와 같이 변경하면 버튼을 연속적으로 클릭해도 My 컴포넌트는 오직 한번만 렌더링 된 후 다시는 리렌더링 되지 않는다. 이것은 React.memo가 props 값을 memoize 한 후 캐싱된 결과를 리턴하기 때문에 동일한 입력에 대해서는 My 컴포넌트를 실행하지 않기 때문이다. React.PureComponent가 class component를 위한 거라면 React.memo는 함수형 component를 위한 캐싱 방법이다. ## 9. useCallback() 이전 포스팅에서 useCallback을 아래와 같이 설명했다 : [`useMemo`, `useCallback`을 이용하여 함수형 컴포넌트에서 성능 향상시키기](https://gist.github.com/philipszdavido/0d5d92a688ca9e1bbca65ba72de90f53) 이 작업은 useMemo와 비슷하지만 차이점은 함수 선언을 memoize 하는데 사용된다는 것이다. 아래와 같은 컴포넌트가 있다고 하자. ``` jsx function TestComp(props) { l('rendering TestComp') return ( <> TestComp ) } TestComp = React.memo(TestComp) function App() { const [count, setCount] = useState(0) return ( <> setCount(count + 1)} /> ) } ``` App 컴포넌트는 useState를 이용하여 count 값을 관리하고 있다. 언제든 setCount를 실행시키면 App 컴포넌트는 리렌더링 된다. App 컴포넌트는 하위로 button 과 TestComp 컴포넌트를 가지고 있다. 만약 Set Count 버튼을 클릭하면 App 컴포넌트는 자식 트리를 포함하여 리렌더링 된다. 여기서 TestComp는 불필요한 렌더링을 막기 위해 memo를 이용하여 memoize 되어 있다. React.memo는 현재와 다음 props를 비교하여 이전 props와 같다면 컴포넌트를 리렌더링 하지 않는다. TestComp는 func props를 함수로 받고 있는데 언제든 App이 리렌더링 될 때 TestComp에게 전달되는 func props가 동일한지 체크한 후 동일하다면 리렌더링 되지 않아야 한다. 하지만 문제는 TestComp는 새로운 인스턴스의 함수를 전달받는다. 어떻게 이런 결과가 나온 것일까? 아래 JSX를 보자. ``` jsx ... return ( <> ... setCount(count + 1)} /> ) ... ``` 화살표 함수 선언이 전달되므로 App 컴포넌트가 리랜더링 할때마다 항상 새 참조로 새로운 함수 선언이 전달된다(메모리 주소 포인터). 따라서 얕은 비교를 하는 React.memo는 다른 결과가 들어왔다고 이해하고 리렌더링을 하도록 실행한다. 여기서 이 문제를 어떻게 해결할 수 있을까? 함수 선언을 컴포넌트 밖에서 해야 할까? 이렇게 된다면 좋겠지만 그럴 경우 setCount 함수를 참조할 수 없게 된다. 여기서 useCallback이 필요한 것이다. useCallback으로 함수와 변경될 기준 값을 같이 전달하면 useCallback은 memoize된 함수를 리턴하고 이 함수 값을 TestComp에 전달하면 된다. ```jsx function App() { const check = 90 const [count, setCount] = useState(0) const clickHndlr = useCallback(()=> { setCount(check) }, [check]); return ( <> ) } ``` 여기서 `clickHndlr`는 dependency 값인 `check`가 변경되지 않는 한 App 컴포넌트가 리 렌더링 되어도 새로 생성되지 않으므로 `Set Count` 버튼을 반복해서 클릭해도 TestComp는 다시 리렌더링 되지 않는다. `useCallback`이 `check` 변수값을 확인하여 이전 값과 변경되었다면 새로운 함수를 리턴하고 `TestComp`와 `React.memo`는 새로운 참조가 되었으므로 리렌더링 된다. 만약 동일하다면 `useCallback`은 아무것도 리턴하지 않고 `React.memo`는 함수 참조가 이전과 같다고 판단하여 `TestComp`를 리렌더링 하지 않도록 할 것이다. ## 10. shouldComponentUpdate() React 에서는 보통 App.js 파일 내에 있는 App 컴포넌트를 루트 컴포넌트로 하여 컴포넌트들을 구성한다. ```jsx class ReactComponent extends Component { render() { return (
) } } ``` 가장 기본적인 React Component이다. 이 컴포넌트 트리는 부모-자식 관계를 만들어 컴포넌트 내의 state 값들이 업데이트 될 경우 해당 컴포넌트와 컴포넌트의 자식들 까지, 서브 컴포넌트 트리를 따라 전파되어 리렌더링 된다. 리렌더링 되기 위해서는 React는 이전 데이터(props와 context) 와 현재 데이터(props와 context)를 비교한다. 만약 값들이 동일하다면 리렌더링 되지 않지만 다르다면 해당 컴포넌트와 자식 컴포넌트까지 모두 리렌더링 된다. React는 props와 context가 오브젝트일 경우 일치 연산자 `===`를 이용하여 객체 참조를 비교한다. 따라서 React는 참조를 통해 이전 props와 state와 현재 props와 state를 확인한다. ```jsx class ReactComponent extends Component { constructor(props, context) { super(props, context) this.state = { data: null } this.inputValue = null } handleClick = () => { this.setState({data: this.inputValue}) } onChange = (evt) => { this.inputValue = evt.target.value } render() { l("rendering App") return (
{this.state.data}
) } } ``` 위 component를 보면 state 객체 안에 data 값이 있다. 만약 input에 값을 입력한 후 Click Me 버튼을 누른다면 input에 있는 값이 렌더링 될 것이다. 언제 ReactComponent가 렌더링 되는지 확인해 보기 위해 render 메소드안에 `l("rendering App")를 넣었다. 이제, 2를 입력하고 버튼을 클릭하면 컴포넌트는 렌더링 된다. 이것은 반드시 렌더링 되어야 하는데 왜냐하면 이전 state는 ```js state = { data: null } ``` 이었고 다음 state 객체는 ```js state = { data: 2 } ``` 이기 때문이다. 이는 setState가 실행될 때마다 새로운 state object를 생성하고, 일치 연산자는 이전과 현재의 state object의 메모리 참조가 다르므로 컴포넌트가 리렌더링 되도록 한다. 만약 여기서 버튼을 한번 더 누른다면 또다시 리렌더링 될 것이다. `data`값이 동일하므로 실제로는 렌더링이 되지 않아햐 하지만 setState가 새로운 state object를 생성하고 React는 state object 참조가 다르므로 두개를 다르다고 보고 리렌더링을 시킨다. 만약 하위로 수천개의 컴포넌트들이 있다면 이 리렌더링의 비용은 무시하지 못할 것이다. 위와 같은 이유로 React는 React 내부의 로직 대신 리렌더링을 제어할 수 있는 권한을 주고 있는데, 이것이 바로 shouldComponentUpdate 메소드 이다. 이 shouldComponentUpdate는 매번 컴포넌트가 리렌더링 될때마다 호출되며, 만약 리턴되는 값이 `true` 라면 컴포넌트는 리렌더링 되고 `false` 라면 렌더링은 취소된다. ReactComponent에 shouldComponentUpdate를 추가해 보자. 이 메소드는 다음 state와 props를 객체로 받는다. 이를 이용해 React에게 우리가 원할때 렌더링 될 수 있도록 알려주는 것이다. ```jsx class ReactComponent extends Component { constructor(props, context) { super(props, context) this.state = { data: null } this.inputValue = null } handleClick = () => { this.setState({data: this.inputValue}) } onChange = (evt) => { this.inputValue = evt.target.value } shouldComponentUpdate( nextProps,nextState) { if(nextState.data === this.state.data) return false return true } render() { l("rendering App") return (
{this.state.data}
) } } ``` shouldComponent 내부를 보면 data 값이 다음 state object인 nextState와 현재 state object와 같은지 비교한다. 만약 같지 않다면 `true`를 리턴하고 리렌더링 된다. 동일하다면 `false`를 리턴하고 렌더링은 취소한다. 다시 실행을 시켜 2를 입력하고 `Click Me` 버튼을 연속해서 클릭해도 렌더링은 한번만 발생 하고 다시는 발생하지 않는다는 것을 확인할 수 있을 것이다 :) 위에서 확인했듯이 shouldComponentUpdate 메서드를 이용해서 리렌더링이 효율적으로 일어나 component의 성능을 향상시켜주는 것을 확인했다. ## 결론 React는 대단하다!! 여기에 있는 트릭들이 전부 구현될 필요는 없다. 명심할 것은 미리 최적화를 수행하지 말로 먼저 프로젝트를 개발한 후 필요한 곳에 최적화를 하길 바란다. ## 변역 후기 여기서 저자는 리액트를 최적화 하는 방법 10가지를 나열했다. 단 한문장으로 압축하자면 **"Component가 렌더링 되는 것을 최소한으로 하라"** 정도로 될 것이다. 그만큼 리액트를 포함한 SPA 에서 실제 DOM을 그리는데 드는 비용이 가장 많이 드는 만큼 렌더링을 하는데 가장 많은 신경을 써야한다. 무심코 지나칠 수 있는 함수를 inline으로 넘겨 리렌더링 된다는 부분이라던지 state 값들이 변경되어 하위 child 컴포넌트들이 전부 리렌더링 된더단지의 문제로 인해 React가 빠르다고 하여 개발에 들어갔지만 실제로는 어마무시하게 느려지는 경우가 발생할 수 있다. 그만큼 렌더링을 최소한으로 하는 것은 중요하고 또 중요하다. 저자가 제시한 10가지 방법 중 `PureComponent` 와 `shouldComponentUpdate`는 class component를 사용하지 않는다면 고려하지 않아도 되는 부분이다. 요즘은 또 함수형 컴포넌트로 개발이 넘어가고 있기 때문에 이것 외에 다른 방법이 더 들어갔으면 싶은 생각도 든다. 위에서 제시한 방법 외에도 자식 컴포넌트로 넘기는것을 다르게 하여 성능을 향상시키는 방법도 있다. ```jsx function Parent() { return
} function App() { return } ``` 위와 같이 inline으로 자식을 불러오는 방식에서 만약 Child 컴포넌트에서 Parent 컴포넌트의 state 값을 넘겨받을 필요가 없다면 아래와 같은 방법을 이용할 수도 있다. ``` jsx function Parent(Child) { return
{Child}
} function App() { return } /> } ``` 이렇게 하게 될 경우 Parent state 값이 변경되어 리렌더링 되더라도 Child 컴포넌트는 렌더링 되지 않는다. 여기서 또한번 `Redux`의 중요성이 강조된다. Redux를 통해 값들을 외부 store로 보관할 경우 부모-자식 간의 참조되는 관계가 느슨해지고 따라서 위와 같은 형태로 Component들을 전달하여 보여줄 수 있게 된다. 물론 두개를 모두 포함한 App z컴포넌트의 state 값이 변경될 경우 모두 리렌더링 되겠지만, App 컴포넌트의 역활은 그저 Wrapper의 역활로 지정한다면 각 컴포넌트별로 정말로 필요에 의해 값이 변경될 경우에만 렌더링 하도록 할 수 있다. 위와 같은 방법은 React를 좀 더 View에 초점을 두게 하고 실제 Component의 포함 관계는 데이터를 통해 하위로 전달되는 것이 아니라, DOM 구조에 의해 설계한 후 실제 데이터의 관리와 핸들링은 Redux와 Redux-Saga에서 함으로써 코드 설계를 좀 더 단단하게 할 수 있을 것이다.