- 일반적으로 jax(javascript as XML)을 사용하지 않고 react를 만드는 경우는 거의 없고 홈페이지에서도 jax 를 사용하는 것을 권장함
- 리액트가 내부적으로 어떤 식으로 동작하는 파악하기 위해서 순수 리액트에 대해서 알아봄
- 리액트를 다루기 위해서는 기본적으로 React, ReactDOM 라이브러리를 불러와야 함
- React : 뷰를 만들기 위한 라이브러리
- ReactDOM: UI를 DOM에 실제 렌더링할 때 사용하는 라이브러리
- 기존에는 합쳐져 있었으나 0.14 때 분리
-
HTML은 브라우저가 문서 객체 모델(DOM)을 구성하기 위해 따라야 하는 절차
-
HTML element --> 브라우저 --> DOM element --> 사용자가 볼 수 있다.
-
이런 부분을 다 신경써서 프로그램을 해야 성능 문제를 해결...
-
가상 DOM을 사용할 경우 가상 DOM을 변경할 경우 최적화 되는 방법을 찾아서 React에서 자동으로 DOM을 변경해준다.(자세한 건 뒤에 다시..)
- DOM 엘리먼트와 비슷해보지만 생긴건 완전히 다름.
- 리액트 엘리먼트는 그 에 대응하는 실제 DOM 엘리먼트가 어떻게 생겨야 하는지를 기술
React.createElement('h1', null, '구운 연어'); //첫 번째 인자 엘리먼트의 타입, 두 번째 인자는 프로퍼티, 세 번째 인자는 자식 노드
// 결과는 <h1>구운 연어</h1>
React.createElement('h1', {id: 'test', 'data-type': 'title'}, '구운 연어');
// 결과는 <h1 data-reactroot id="test" data-type:"title">구운 연어</h1>
// data-reactroot는 이 element가 root elemenet 라는 것을 나타낸다.
// 생성된 element
{
$$typeof: Symbol(React.element),
"type": "h1",
"key": null,
"ref": null,
"props": {"children": "구운 연어"},
"_owner": null,
"_store": {}
}- 리액트 엘리먼트를 브라우저에 렌더링하는 데 필요한 모든 도구가 들어 있다.
- 이번 스터디를 하면서는 redner 만 거의 사용할 듯.
const header = React.createElement('h1', null, '구운 연어');
ReactDOM.render(header, document.getElementById('react1'));- ReactDOM은 항상 한 엘리먼트만 DOM으로 렌더링할 수 있다.
- 리엑트 props.children 을 사용해 자식 엘리먼트를 렌더링한다. 이럴 경우 해당 작식 엘리먼트들은 부모 엘리먼트와 트리 구조로 구성된다. ==> 컴포넌트 트리
<ul class="ingredients">
<li>연어 500그램</li>
<li>잣 1컵</li>
<li>버터 상추 2컵</li>
<li>옐로 스쿼시 1개</li>
<li>올리브 오일 1/2컵</li>
<li>마늘 3쪽</li>
</ul>
React.createElement(
'ul',
{className: 'ingredients'},
React.createElement('li', null, '연어 500그램'),
React.createElement('li', null, '잣 1컵'),
React.createElement('li', null, '버터 상추 2컵'),
React.createElement('li', null, '옐로 스쿼시 1개'),
React.createElement('li', null, '올리브 오일 1/2컵'),
React.createElement('li', null, '마늘 3쪽')
);
{
$$typeof: Symbol(React.element),
"type": "ul",
"key": null,
"ref": null,
"props": {"children": [
{tpye: 'li', props: {'children': '연어 500그램'} ... },
{tpye: 'li', props: {'children': '잣 1컵'} ... },
{tpye: 'li', props: {'children': '버터 상추 2컵'} ... },
{tpye: 'li', props: {'children': '옐로 스쿼시 1개'} ... },
{tpye: 'li', props: {'children': '올리브 오일 1/2컵'} ... },
{tpye: 'li', props: {'children': '마늘 3쪽'} ... },
]},
"_owner": null,
"_store": {}
}- 리액트는 단순한 자바스크립트이기 떄문에 자바스크립트에서 사용할 수 있는 모든 함수를 사용 가능
- 데이터와 구현 부분을 분리해서 표시가 가능
- 배열을 이터레이션해서 자식 엘리먼트의 리스트를 만드는 경우 key 값을 생성하도록 권장함
var items = [
'연어 500그램',
'연어 500그램',
'잣 1컵',
'버터 상추 2컵',
'옐로 스쿼시 1개',
'올리브 오일 1/2컵',
마늘 3쪽
];
React.createElement(
'ul',
{className: 'ingredients'},
items.map( (item, i) => React.createElement('li', {key: i}, item))
);-
모든 UI는 여러 가지 부분으로 이루어진다.
-
props 를 이용해서 UI를 만들 때 사용하는 로직과 데이터를 분리할 수 있음
-
컴포넌트를 구성할 수 있는 방법은 총 3가지가 있다.
-
React.createClass
- 가장 오래전부터 사용한 방법
- 조만간 없어 질 것이라고 함.
const IngredientsList = React.createClass({ displayName: 'IngredientsList', render() { return React.createElement( 'ul', {className: 'ingredients'}, React.createElement('li', null, '연어 500그램'), React.createElement('li', null, '잣 1컵'), React.createElement('li', null, '버터 상추 2컵'), React.createElement('li', null, '옐로 스쿼시 1개'), React.createElement('li', null, '올리브 오일 1/2컵'), React.createElement('li', null, '마늘 3쪽') ); } }); const list = React.createElement(IngredientsList, null, null); ReactDOM.render(list, ...);
-
React.Component
- ES2015+ 에서 추가된 class를 이용하는 방법
- React.Component 는 추상화 클래스로 해당 클래스를 상속받아서 render() 를 구현하는 방식으로 사용할 수 있다.
const IngredientsList = class extends React.Component{ render() { return React.createElement( 'ul', {className: 'ingredients'}, React.createElement('li', null, '연어 500그램'), React.createElement('li', null, '잣 1컵'), React.createElement('li', null, '버터 상추 2컵'), React.createElement('li', null, '옐로 스쿼시 1개'), React.createElement('li', null, '올리브 오일 1/2컵'), React.createElement('li', null, '마늘 3쪽') ); } }; const list = React.createElement(IngredientsList, null, null); ReactDOM.render(list, ...);
-
상태가 없는 함수형 컴포넌트
- 객체가 아니고 함수로 표현
- 될 수 있으면 해당 방식을 사용하도록 추천
const IngredientsList = props => React.createElement( 'ul', {className: 'ingredients'}, React.createElement('li', null, '연어 500그램'), React.createElement('li', null, '잣 1컵'), React.createElement('li', null, '버터 상추 2컵'), React.createElement('li', null, '옐로 스쿼시 1개'), React.createElement('li', null, '올리브 오일 1/2컵'), React.createElement('li', null, '마늘 3쪽') );
-
- react에서 render() 함수를 호출할 경우 각각 새로운 React elements Tree가 생성이 된다.
- 바로 이전에 생성된 Tree와 비교를 해서 가장 효율적인 방법을 사용해서 UI를 업데이트한다.
- 기본적인 tree 비교 로직은 O(n^3)의 시간 복잡도를 가지고 있기 때문에 비효율적이다.
- 그래서 아래와 같이 2가지의 가정을 가진 휴리스틱 알고리즘을 사용해서 O(n)에 근사하도록 구현을 했다.
- 다른 타입의 두 엘리먼트는 다른 트리를 만들 것이다.
- 각 렌더링에서 유지되는 엘리먼트에
key프로퍼티를 통해 같은 엘리먼트라는 것을 알린다.
2개의 트리를 비교하는 알고리즘
-
타입이 다른 Elements
- 지우고 다시 새로 element를 추가
- 부모가 틀리고 자식은 같더라도 그냥 다 지우고 다시 추가한다.
-
동일한 타입의 DOM Elements
- 속성이 틀린 경우에 속성만 변경한다.
-
동일한 타입의 React Elements
- 인스턴스 자체는 변하지 않는다.
- 기존 인스턴스의 프로퍼티를 새 인스턴스로 변경을 하고, 기존 인스턴스의 componentWillReceiveProps(), componentWillUpdate() 함수 실행
- render() 함수를 호출하고 이전 element와 다음 elements에 해당 알고리즘을 제귀로 돌린다.
-
자식 재귀
- 기본적으로 DOM 노드의 자식을 재귀할 때는 동시에 전/후 리스트를 확인해서 차이점을 비교한다.
- 마지막노드를 추가할 경우에는 그냥 마지막 노드만 추가된 것으로 인식한다.
- 하지만 첫 번째 노드가 추가될 경우 각 리스트의 값들이 틀리기 때문에 기존에 element를 수정하게 된다.
- 이럴 경우 효율성에 문제가 생긴다.
-
키
- 위 효율의 문제를 개선하기 위해서 key 값을 추가되었다.
- 자식이 키를 가지고 있다면, react는 key 값을 비교해서 전/후 리스트를 확인하고 차이점을 비교한다.
- h1 element 같은 것은 ReactDOMFactories.h1 을 이용해서 쉽게 만들 수 있다.
- 하지만 jax 를 사용하면 어짜피 의미 없는 것이라.... 그냥 이런게 있다고만 알고 있으면 될 듯
- 러닝 리액트
- https://reactjs.org/docs/react-without-jsx.html
- https://reactjs.org/docs/refs-and-the-dom.html
- http://d2.naver.com/helloworld/59361
- https://github.com/nhnent/fe.javascript/wiki/Reflow%EC%99%80-Repaint
- http://taligarsiel.com/Projects/howbrowserswork1.htm
- http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/
- https://reactjs.org/docs/reconciliation.html
- http://meetup.toast.com/posts/110



