티스토리 뷰

공부

[JS] React?

승가비 2018. 8. 12. 16:20
728x90

[React를 왜 사용하는가?]

우리는 왜 리액트를 사용해야만 할까?

웹사이트를 보면 전체적인 틀은 대부분 동일하고 내부에 콘텐츠만 변경되는 경우가 많다.

이런 경우에 전체를 다시 그리지 않고 내부에 콘텐츠 영역만 다른 것으로 바꿔주면 

다시 그려줘야 할 영역이 감소하므로 성능이 향상된다. 


이렇게 나오게 된 녀석이 SPA(Single Page Application)이다.

서버에 페이지를 새롭게 요청하지 않고 화면을 그리기 때문에 빠르다.

(하지만 새롭게 요청이 필요하지 않은 정적인 페이지에서는 SPA로 구성하지 않는 편이 비교적 빠르다.)


여기에 추가적으로,

SPA에서는 관리해야할 DOM이 많아지고, 각 DOM마다 상태를 각각 컨트롤 해줘야 한다.

많은 DOM 요소들과 상태를 각각 관리하는 것은 귀찮은 일이다.


그래서 여러 개의 DOM과 상태를 묶어서 표현하는 프레임워크와 라이브러리가 만들어졌다.

AngularJS, ReactJS, VueJS 가 있는데, 그중에서 React를 살펴보려고 한다.


jQuery를 먼저 살펴보면, 실제 DOM을 가지고 조작을 한다.

DOM 조작이 이루어지면 브라우저는 아래 과정인 reflow(layout)와 repaint가 발생한다.

이 과정이 빈번하게 발생하여 리소스를 많이 차지 한다.

DOM -> DOM Tree

CSS -> CSSOM Tree

DOM Tree + CSSOM Tree = Render Tree

Render Tree <- Reflow / Repaint


jQuery에서 여러 번의 DOM 조작은 여러번의 reflow(layout)와 repaint를 발생시키므로

SPA 환경에서 동적인 변화가 많아지면 성능저하가 발생한다.

한줄 한줄 마다 reflow와 repaint가 일어나진 않는다. 스타일 변화가 없고 다른 연산이 들어갈 때 발생한다.(Batch DOM Update)


그렇다면 우리는 SPA에서 reflow와 repaint를 줄여야 한다. 규모가 점점 커지는 SPA에서 잦은 Batch DOM Update는 성능을 급격하게 저하시키기 때문이다.
이런 이유에서 우리는 React를 사용해야하는 이유가 생겼다. (jQuery의 단점을 극복하고자 React가 등장했다.)


React는 Virtual DOM 을 도입하여, 비용이 많이드는 reflow와 repaint를 최소화 해준다.

Real DOM Tree / In-memory Virtual DOM Tree 두가지를 가지고 성능을 향상시켰다.

또한 React는 상태를 Immutable 하게 만들어서 변화에 대한 사이드 이팩트를 막는데(thread-safe), 그래서 렌더링 마다 컴포넌트 자체를 새롭게 그린다.

이 과정은 많은 비용이 들기 때문에 VirtualDOM과 lifecycle을 이용해서 성능을 향상시키려 하였다.


위 이미지에서 나온 것처럼 맨처음에 Model 을 가지고 Real DOM과 In-memory DOM을 그린다.

Model에 변경이 일어나면 Virtual DOM을 그린 후, Real DOM과 비교하는 과정을 거친다.(Reconciliation: Smart Diffing Algorithm)

이 과정에서 변경이 된 녀석들을 구분하고(Dirty)하위 컴포넌트까지 다시 그려야 한다고 생각한다(Re-rendered)

이때 변경요소들을 충분히 하나로 묶어서 작업한다. (더블 버퍼링을 통한 최소화)



다시 그려야될 요소들을 모두 다시 그리는 것은 아니다. 이때 한 번 더 각 컴포넌트의 lifecycle 함수인 shouldComponentUpdate를 통해서 최적화 시킬 수 있다.

shouldComponentUpdate가 true인 경우만 다시 그리게 된다.

따라서 React는 VirtualDOM과 lifecycle 함수인 shouldComponentUpdate로 render 통해서, reflow / repaint 작업을 최소화 하여 jQuery보다 성능을 높여 준다.


[reflow / repaint 성능 튜닝]

1. 인라인 스타일 쓰지말기(reflow 비용, 가독성), table-layout인 경우 table-layout:fixed 사용하기

2. 숫자 css 줄이기

3. DOM Tree depth 줄이기

4. flow 애니메이션 줄이기

5. DOM Tree 클래스 변경 최소화하기

6. 제거하지 말고 display:none 사용하기

7. Batch DOM Update 사용하기

8. 영향받는 엘리먼트 갯수 줄이기



[Lifecycle]

React는 DOM을 컴포넌트로 추상화할 수 있으며 각 컴포넌트는 고유 저장소인 state를 갖는다.

또한 lifecycle을 선언하여 각 단계별로 추가 작업을 하도록 할 수 있다.


Mount: constructor -> getDerivedStateFromProps -> render -> componentDidMount

Update: getDerivedStateFromProps -> shouldComponentUpdate -> render -> getSnapshotBeforeUpdate -> componentDidUpdate

Unmount: componentWillUnmount

Error: componentDidCatch


constructor(props)

static getDerivedStateFromProps(nextProps, prevState): 변경되는 state를 nextPorps, prevState를 가지고 조합해서 만듬

shouldComponentUpdate(nextProps, nextState): 렌더링 최적화에 사용

getSnapshotBeforeUpdate(prevProps, prevState)

componentDidCatch(error, info)



[React Component 종류]

- functional component

  함수로 선언된 컴포넌트이다.

  props 만 전달하여 생성하며 state를 사용하지 않고

  lifecycle을 따로 선언할 수 없어서, 최적화가 불가능하다.

  정적으로 props 변경 없이 렌더링만 하는 컴포넌트를 만들 때 주로 사용한다.

- pure component 

  shouldComponentUpdate가 이미 구현되어 있으며, 

  props와 state를 shallow로 변화를 판단하는 로직이 추가된 컴포넌트다.

  성능에 관련된 shouldComponentUpdate를 신경쓰지 않아도 된다.

  하지만 얕은 비교이기 때문에 props, state가 복잡한 경우 기대하지 않은 결과가 발생할 수 있다.

- component

  state, lifecycle이 존재하며, shouldComponentUpdate를 따로 선언하지 않으면 true인 경우.


[setState 가 비동기인 이유?]

setState는 비동기로 수행된다. 여러번의 setState는 순서대로 큐에 담기며 하나로 합쳐진다음 (_pendingStateQueue)

reconcilication 과정을 거친 후 렌더링된다.

잦은 상태변화를 하나로 처리하여 성능을 높였다.


[기타]

- ref: 실제 DOM 요소를 직접 핸들링해야 할때 사용한다. (크기, 이벤트 전달)

- HOC(High Order Component): 컴포넌트를 인자로 받아 컴포넌트를 리턴하는 함수



[출처] 

[reflow / repaint / VirtualDOM]: http://blog.drakejin.me/React-VirtualDOM-And-Repaint-Reflow/

[Dirty / Re-render]: https://medium.com/@coolram2104/digging-deeper-inside-the-reconciliation-algorithm-of-react-f0d428ba4ae9

[PureComponent]: https://wonism.github.io/react-pure-component/

[setState]: https://www.vobour.com/%ED%95%A8%EC%88%98%ED%98%95-setstate%EA%B0%80-%EB%A6%AC%EC%95%A1%ED%8A%B8-react-%EC%9D%98-%EB%AF%B8%EB%9E%98%EC%9D%B4%EB%8B%A4-functiona


728x90

'공부' 카테고리의 다른 글

[JS] ES5 vs ES6  (0) 2018.08.12
[JS] Redux?  (0) 2018.08.12
[Redis] 명령어 정리  (0) 2018.07.05
[CSS] padding-top: *%  (0) 2018.06.25
[DB] Mongo 필수 SQL 메모  (0) 2018.06.13
댓글