Facebook은 React를 왜 만들었을까?

84
Facebook은 React를 왜 만들었을까? NAVER Corp. Blog & Post Development Lab 김훈민

Transcript of Facebook은 React를 왜 만들었을까?

Page 1: Facebook은 React를 왜 만들었을까?

Facebook은 React를 왜 만들었을까?

NAVER Corp. Blog & Post Development Lab

김훈민

Page 2: Facebook은 React를 왜 만들었을까?

[명사] 1. 사물ㆍ현상이 놓여 있는 모양이나 형편.

#1 상태(state)

Page 3: Facebook은 React를 왜 만들었을까?

UI가 놓여있는 모습이나 형편

UI의 상태

Page 4: Facebook은 React를 왜 만들었을까?

상태 관리 -> 데이터 관리 상태가 복잡하다 -> 관리할 데이터가 많다

우리가 보는 UI는 특정 시점의 데이터를 표현

Page 5: Facebook은 React를 왜 만들었을까?

http://info.cern.ch/hypertext/WWW/TheProject.html

Page 6: Facebook은 React를 왜 만들었을까?

http://info.cern.ch/hypertext/WWW/TheProject.html

Page 7: Facebook은 React를 왜 만들었을까?

정적(static)인 상태User’s Action

Page

하이퍼 링크로 문서와 문서를 연결하는 아주 단순한 웹

Page 8: Facebook은 React를 왜 만들었을까?

http://www.infinitezest.com/articles/xmlhttprequest-and-ajax-on-google-suggest.aspx

Page 9: Facebook은 React를 왜 만들었을까?

JavaScript 비중 증가 심각한 브라우저 파편화 개발 난도(難度) 상승

동적(Dynamic)인 상태User’s Action

Page

Page 10: Facebook은 React를 왜 만들었을까?

크로스 브라우징, XHR 처리, DOM 셀렉팅, DOM 노드 탐색, 엘리먼트 상태/스타일 변경 등…

브라우저 호환성, 편리한 사용성, 간결한 인터페이스

Page 11: Facebook은 React를 왜 만들었을까?

update: function( el ) { if( !el.offsetWidth ) { return; }

var $el = $( el ),

height = $el.outerHeight(), initialOffset = $el.data( S.keys.offset ), startTop = $el.data( S.keys.startTop ), scroll = S.getScrollTop(), isAlreadyOn = $el.is( '.' + S.classes.active ), toggle = function( turnOn ) {

$el[ turnOn ? 'addClass' : 'removeClass' ]( S.classes.active ) [ !turnOn ? 'addClass' : 'removeClass' ]( S.classes.inactive );

}, viewportHeight = $( window ).height(), position = $el.data( S.keys.position ), skipSettingToFixed, elTop, elBottom, $parent = $el.parent(), parentOffset = $parent.offset().top, parentHeight = $parent.outerHeight();

if( initialOffset === undefined ) {

initialOffset = $el.offset().top + startTop; $el.data( S.keys.offset, initialOffset ); $el.after( $( '<div>' ).addClass( S.classes.clone ).height( height ) );

}

if( !position ) { // Some browsers require fixed/absolute to report accurate top/left values. skipSettingToFixed = $el.css( 'top' ) !== 'auto' || $el.css( 'bottom' ) !== 'auto';

if( !skipSettingToFixed ) {

$el.css( 'position', 'fixed' ); }

position = {

top: $el.css( 'top' ) !== 'auto', bottom: $el.css( 'bottom' ) !== 'auto'

};

if( !skipSettingToFixed ) { $el.css( 'position', '' );

}

$el.data( S.keys.position, position ); }

function isFixedToTop() {

var offsetTop = scroll + elTop;

Page 12: Facebook은 React를 왜 만들었을까?

관심사 분리나 OOP 지원 같은 설계 관점 부재 - Smart UI 양산 - 상태 관리 전략 모호 - 잦은 DOM 셀렉팅으로 인한 성능 저하

HTML + JavaScript + CSS가 강하게 결합 - 코드 유연성 저하 - 테스트 곤란

복잡도 증가

Page 13: Facebook은 React를 왜 만들었을까?

웹 프론트엔드에 불기 시작한 새로운 바람

이렇게 된 이상

MVC로 간다!

Page 14: Facebook은 React를 왜 만들었을까?

대표적인 SPA Framework 웹 프론트엔드 환경에 맞게 MVC를 재해석 도메인 모델 + REST API

Backbone.js

ServerClient

Page 15: Facebook은 React를 왜 만들었을까?

대표적인 SPA Framework 웹 프론트엔드 환경에 맞게 MVC를 재해석 도메인 모델 + REST API

Backbone.js

View Model Server

Page 16: Facebook은 React를 왜 만들었을까?

initialize : function(){ this.listenTo(this.model, "add", this._onAddFriend, this); this.listenTo(this.model, "change:display", this._onChangeDisplay, this); this.model.fetch();

}, addFriend : function(friend){

this.model.add(friend); }, hideFriend : function(){

this.model.set("display", false); }, _onAddFriend : function(friend){

var friendView = new FriendView({ model : friend, oGroupModel : this.model

});

this.$el.append(friendView.render()); }, _onChangeDisplay : function(isDisplay){

if(isDisplay){ this.$elFriendList.show();

} else { this.$elFriendList.hide();

} }

Page 17: Facebook은 React를 왜 만들었을까?

표현 로직과 업무 로직 분리에 초점 데이터 동기화, UI 상태 관리는 여전히 개발자의 몫

동기화는 “님”들이 알아서

View Model

동기화는 누가?동기화는 누가?

View의 상태는?

Page 18: Facebook은 React를 왜 만들었을까?

동기화를 프레임워크가 책임진다면 정말 좋겠네

Page 19: Facebook은 React를 왜 만들었을까?

View의 상태와 Model의 데이터를 바인딩하여 한 쪽의 상태 변경을 다른 쪽에 실시간으로 반영하는 프로세스

데이터 바인딩(Data Binding)

Page 20: Facebook은 React를 왜 만들었을까?

Ember.js KVO(Key-Value Observing) 프레임워크에 종속적인 코드 스타일을 강제 엔터프라이즈에 강점

Model과 View를 알려주면

내가 동기화 해줄게!

Page 21: Facebook은 React를 왜 만들었을까?

App.GravatarImageComponent = Ember.Component.extend({ size: 200, email: '', gravatarUrl: Ember.computed('email', 'size', function() { var email = this.get('email').toLowerCase(), size = this.get('size'); return 'http://www.gravatar.com/avatar/' + md5(email) + '?s=' + size; }) });

<img src={{gravatarUrl}}> <div class="email-input"> {{input type="email" value=email placeholder="Enter your Gravatar e-mail"}}</div>

Page 22: Facebook은 React를 왜 만들었을까?

user = Ember.Object.create({ fullName: 'Kara Gates'}); UserComponent = Ember.Component.extend({ userName: Ember.computed.oneWay('user.fullName') }); userComponent = UserComponent.create({ user: user}); // Changing the name of the user object changes// the value on the view.user.set(‘fullName', 'Krang Gates'); // userComponent.userName will become "Krang Gates"// ...but changes to the view don't make it back to// the object.userComponent.set('userName', 'Truckasaurus Gates'); user.get(‘fullName'); // "Krang Gates"

Page 23: Facebook은 React를 왜 만들었을까?

Angular.js 양방향(Two-Way) 데이터 바인딩 POJO(Plain Old JavaScript Object) + Dirty Checking

그냥, 다 확인하지 뭐

Template View Model

Page 24: Facebook은 React를 왜 만들었을까?

<div ng-controller="Controller"> Date format: <input ng-model="format"> <hr/> Current time is: <span my-current-time="format"></span> </div>

angular.module('docsTimeDirective', []) .controller('Controller', ['$scope', function($scope) { $scope.format = 'M/d/yy h:mm:ss a'; }]) .directive('myCurrentTime', ['$interval', 'dateFilter', function($interval, dateFilter) { function link(scope, element, attrs) { var format, timeoutId; function updateTime() { element.text(dateFilter(new Date(), format)); } scope.$watch(attrs.myCurrentTime, function(value) { format = value; updateTime(); }); element.on('$destroy', function() { $interval.cancel(timeoutId); }); // start the UI update process; save the timeoutId for canceling timeoutId = $interval(function() { updateTime(); // update DOM }, 1000); } return { link: link }; }]);

Page 25: Facebook은 React를 왜 만들었을까?

#2 React Begins못 살겠다, 바꿔보자

Page 26: Facebook은 React를 왜 만들었을까?

디버깅의 어려움 높은 학습비용 고질적인 성능 이슈

아직도 배가 고프다

Page 27: Facebook은 React를 왜 만들었을까?

No magical data binding No model dirty checking No more explicit DOM operations

조금만 더

단순할 수는 없을까?User’s Action

Page

Page 28: Facebook은 React를 왜 만들었을까?

상태 변경 -> 렌더링 DOM은 그저 특정 시점의 상태를 표현할 뿐

Make it Reactive!

Page 29: Facebook은 React를 왜 만들었을까?

var Timer = React.createClass({ displayName: "Timer", getInitialState: function getInitialState() { return { secondsElapsed: 0 }; }, tick: function tick() { this.setState({ secondsElapsed: this.state.secondsElapsed + 1 }); }, componentDidMount: function componentDidMount() { this.interval = setInterval(this.tick, 1000); }, componentWillUnmount: function componentWillUnmount() { clearInterval(this.interval); }, render: function render() { return React.createElement( "div", null, "Seconds Elapsed: ", this.state.secondsElapsed ); } }); ReactDOM.render(React.createElement(Timer, null), mountNode);

Page 30: Facebook은 React를 왜 만들었을까?

JavaScript로 DOM 엘리먼트 생성하는

코드 넘 구린 듯!

차라리 템플릿이 나은 듯!

Page 31: Facebook은 React를 왜 만들었을까?

JavaScript로 DOM 엘리먼트 생성하는

코드 넘 구린 듯!

차라리 템플릿이 나은 듯!

인정!

JSX 쓰면 어떰?

Page 32: Facebook은 React를 왜 만들었을까?

var Timer = React.createClass({ getInitialState: function() { return {secondsElapsed: 0}; }, tick: function() { this.setState({secondsElapsed: this.state.secondsElapsed + 1}); }, componentDidMount: function() { this.interval = setInterval(this.tick, 1000); }, componentWillUnmount: function() { clearInterval(this.interval); }, render: function() { return ( <div>Seconds Elapsed: {this.state.secondsElapsed}</div> ); } }); ReactDOM.render(<Timer />, mountNode);

Page 33: Facebook은 React를 왜 만들었을까?

XML 스타일의 코드를 작성할 수 있게 JavaScript를 확장한 문법 ECMAScript 표준 스펙과는 별개 트랜스파일하여 ECMAScript로 변환

JSXvar dropdown = <Dropdown> A dropdown list <Menu> <MenuItem>Do Something</MenuItem> <MenuItem>Do Something Fun!</MenuItem> <MenuItem>Do Something Else</MenuItem> </Menu> </Dropdown>; render(dropdown);

Page 34: Facebook은 React를 왜 만들었을까?

JavaScript로 DOM 엘리먼트 생성하는

코드 넘 구린 듯!

차라리 템플릿이 나은 듯!

인정!

JSX 쓰면 어떰?

으악! 저게 머임!

님 관심사 분리 모름?

누가 요즘 JavaScript 코드 안에 HTML 씀?

Page 35: Facebook은 React를 왜 만들었을까?

JavaScript로 DOM 엘리먼트 생성하는

코드 넘 구린 듯!

차라리 템플릿이 나은 듯!

인정!

JSX 쓰면 어떰?

으악! 저게 머임!

님 관심사 분리 모름?

누가 요즘 JavaScript 코드 안에 HTML 씀?

HTML이랑 JavaScript를 분리한다고

관심사 분리가 됨?

Page 36: Facebook은 React를 왜 만들었을까?

복잡성을 덜기 위해 관심사를 분리 HTML과 JavaScript의 분리는 관심사가 아닌 기술의 분리

관심사란

객체가 맡고 있는 책임

Page 37: Facebook은 React를 왜 만들었을까?

어떤 입력을 처리하고, 무엇을 보여줄 것인가?

재사용할 수 있는 대상은 주로 컴포넌트

진짜 관심사는, UI 인터랙션

Page 38: Facebook은 React를 왜 만들었을까?

export default class NaverMain extends React.Component { constructor(){ super(); } render() { return ( <div> <SearchBar/> <TabMenu /> <NewsHeadLineList /> <RealTimeSearchPanel /> <HotNewsSlide /> </div> ); } }

Page 39: Facebook은 React를 왜 만들었을까?

JavaScript는 HTML을 객체화 한 DOM을 조작 HTML과 JavaScript는 논리적으로 강하게 결합

기능(JavaScript)과

구조(HTML)를 분리하면 의존성을 줄일 수 있을까?

Page 40: Facebook은 React를 왜 만들었을까?

<!DOCTYPE html> <html> <head> <script> function myFunction() { document.getElementById("demo").innerHTML = "Paragraph changed."; } </script> </head> <body> <h1>My Web Page</h1> <p id="demo">A Paragraph</p> <button type="button" onclick="myFunction()">Try it</button> </body> </html>

Page 41: Facebook은 React를 왜 만들었을까?

function addElement () { var newDiv = document.createElement("div"); var newContent = document.createTextNode("Hi there and greetings!"); newDiv.appendChild(newContent); var currentDiv = document.getElementById("div1"); document.body.insertBefore(newDiv, currentDiv); }

Page 42: Facebook은 React를 왜 만들었을까?

var html=""; html += "<div class=\"content\" data-jiis=\"cc\" id=\"main\"><span class=\"ctr-p\">"; html += " <div style=\"height:233px;margin-top:89px\" id=\"lga\">"; html += " <div style=\"padding-top:109px\">"; html += " <div title=\"Google\" align=\"left\" id=\"hplogo\">"; html += " <div nowrap=\"\">한국<\/div>"; html += " <\/div>"; html += " <\/div>"; html += " <\/div>"; html += " <div style=\"height:118px\"><\/div>"; html += " <div data-jibp=\"h\" data-jiis=\"uc\" id=\"prm-pt\"><\/div>"; html += " <div class=\"ctr-p\" id=\"footer\">"; html += " <div data-jibp=\"h\" data-jiis=\"uc\" id=\"fbarcnt\" ><\/div>"; html += " <\/div>"; html += " <div data-jibp=\"h\" data-jiis=\"uc\" id=\"footc\">"; html += " <div id=\"xfoot\">"; html += " <div id=\"xjsd\">"; html += " <\/div>"; html += " <div id=\"xjsi\">"; html += " <\/div>"; html += " <\/div>"; html += " <\/div>"; html += "<\/div>"; document.body.innerHTML = html;

Page 43: Facebook은 React를 왜 만들었을까?

<div class="post"> {{> userMessage tagName="h1" }} <h1>Comments</h1> {{#each comments}} {{> userMessage tagName="h2" }} {{/each}} </div>

Page 44: Facebook은 React를 왜 만들었을까?

DSL(특정 도메인 전용 논리 언어) 추가 학습비용 DSL의 낮은 표현력

템플릿(Template)도 조금 부족해

<ul> {{#each items}} <li>{{agree_button}}</li> {{/each}} </ul>

var context = { items: [ {name: "Handlebars", emotion: "love"}, {name: "Mustache", emotion: "enjoy"}, {name: "Ember", emotion: "want to learn"} ] }; Handlebars.registerHelper('agree_button', function() { var emotion = Handlebars.escapeExpression(this.emotion), name = Handlebars.escapeExpression(this.name); return new Handlebars.SafeString( "<button>I agree. I " + emotion + " " + name + "</button>" ); });

<ul> <li><button>I agree. I love Handlebars</button></li> <li><button>I agree. I enjoy Mustache</button></li> <li><button>I agree. I want to learn Ember</button></li> </ul>

Page 45: Facebook은 React를 왜 만들었을까?

매번 다시 그리는 건 낭비 아님?

성능에 문제 없음?

Page 46: Facebook은 React를 왜 만들었을까?

바뀐 부분만 다시 그릴 거임!

매번 다시 그리는 건 낭비 아님?

성능에 문제 없음?

Page 47: Facebook은 React를 왜 만들었을까?

님, 장난함?

DOM 상태를 비교한다고?

DOM API 호출하는 비용 무시함?

매번 다시 그리는 건 낭비 아님?

성능에 문제 없음?

바뀐 부분만 다시 그릴 거임!

Page 48: Facebook은 React를 왜 만들었을까?

님, ㄴㄴㄴ

가상의 DOM 객체를 만들어서

비교한 후에 실제 바뀐 부분만 다시 그려주면 됨!

매번 다시 그리는 건 낭비 아님?

성능에 문제 없음?

님, 장난함?

DOM 상태를 비교한다고?

DOM API 호출하는 비용 무시함?

바뀐 부분만 다시 그릴 거임!

Page 49: Facebook은 React를 왜 만들었을까?

UI 상태에 대한 정보를 가지고 있는 가상의 표현 객체 ReactElement ReactDOMTextComponent(ReactText)

Virtual DOMvar HelloMessage = React.createClass({ render: function() { return <div>Hello {this.props.name}</div>; } }); ReactDOM.render(<HelloMessage name="John" />, mountNode);

Page 50: Facebook은 React를 왜 만들었을까?

var HelloMessage = React.createClass({ render: function() { return <div>Hello {this.props.name}</div>; } }); ReactDOM.render(<HelloMessage name="John" />, mountNode);

<div> ReactElement

<span> ReactText

“Hello”

<span> ReactText

“John”

Page 51: Facebook은 React를 왜 만들었을까?
Page 52: Facebook은 React를 왜 만들었을까?

https://github.com/facebook/react/blob/master/src/isomorphic/classic/element/ReactElement.js

Page 53: Facebook은 React를 왜 만들었을까?

https://github.com/facebook/react/blob/master/src/renderers/dom/shared/ReactDOMTextComponent.js

Page 54: Facebook은 React를 왜 만들었을까?

#3 React Rises이제는 조금 더 빨라져야 할 시간

Page 55: Facebook은 React를 왜 만들었을까?

하나의 트리를 다른 트리로 변환하는가장 빠른 알고리즘

시간 복잡도는 O(n^3)

Page 56: Facebook은 React를 왜 만들었을까?

불필요한 비교 단계를 생략하는 백트레킹(Backtracking)과 유사한 전략

휴리스틱하게

O(n)으로 만들면 되지

Page 57: Facebook은 React를 왜 만들었을까?

비교 범위를

동일한 레벨의 노드로 한정

Tree A Tree B

Page 58: Facebook은 React를 왜 만들었을까?

자식 컴포넌트 리스트 비교

List A List B

Page 59: Facebook은 React를 왜 만들었을까?

리스트의 중간에

새로운 컴포넌트를 하나 추가하면?

List A List B

Page 60: Facebook은 React를 왜 만들었을까?

컴포넌트에 Key를 할당하여 맵핑함으로써 동일한 노드를 추적

Key를 이용한 리스트 비교

List A List B

A

B

C

D

A

B

C

D

E

Page 61: Facebook은 React를 왜 만들었을까?

List A List B

A

B

C

D

A

B

C

D

E

컴포넌트에 Key를 할당하여 맵핑함으로써 동일한 노드를 추적

Key를 이용한 리스트 비교

Page 62: Facebook은 React를 왜 만들었을까?

Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `CardThumbnailSlide`. See https://

fb.me/react-warning-keys for more information.

Page 63: Facebook은 React를 왜 만들었을까?

동일한 컴포넌트 클래스만 세부 비교 진행

Tree A Tree B

클래스가 다르다면 비교할 이유가 없지!

Page 64: Facebook은 React를 왜 만들었을까?

Tree A Tree B

동일한 컴포넌트 클래스만 세부 비교 진행클래스가 다르다면 비교할 이유가 없지!

Page 65: Facebook은 React를 왜 만들었을까?

Tree A Tree B

동일한 컴포넌트 클래스만 세부 비교 진행클래스가 다르다면 비교할 이유가 없지!

Page 66: Facebook은 React를 왜 만들었을까?

Tree A Tree B

동일한 컴포넌트 클래스만 세부 비교 진행클래스가 다르다면 비교할 이유가 없지!

Page 67: Facebook은 React를 왜 만들었을까?

배치 처리 선택적 서브 트리 렌더링

렌더링 최적화로 좀 더 빠르게

Page 68: Facebook은 React를 왜 만들었을까?

setState를 호출하면 이벤트 루프 동안에 상태를 변경해야 하는 컴포넌트를 찾아서 더티 체크(dirty check)

더티 체크

setState

Page 69: Facebook은 React를 왜 만들었을까?

이벤트 루프가 끝나면 더티 체크한 컴포넌트의 render 함수를 일괄 호출

배치 처리

setState render

Page 70: Facebook은 React를 왜 만들었을까?

setState를 호출하면 모든 자식 컴포넌트의 render를 호출

서브 트리 렌더링

setState render

Page 71: Facebook은 React를 왜 만들었을까?

What…?

Page 72: Facebook은 React를 왜 만들었을까?

최상위 노드에서 setState를 호출할 일은 많지 않다

대부분의 변경은 일부 영역에서 발생

render

Page 73: Facebook은 React를 왜 만들었을까?

shouldComponentUpdate를 오버라이딩

선택적 서브 트리 렌더링으로 방어 처리 가능

setState shouldComponentUpdate: function(nextProps, nextState) { return false; }

Page 74: Facebook은 React를 왜 만들었을까?

setState

shouldComponentUpdate를 오버라이딩

선택적 서브 트리 렌더링으로 방어 처리 가능

Page 75: Facebook은 React를 왜 만들었을까?

CPU를 많이 점유하는 코드 작성은 지양! 컴포넌트 트리의 깊이를 적당(?)한 수준으로 유지할 것!

shouldComponentUpdate는

아주 자주 불리는 메서드

Page 76: Facebook은 React를 왜 만들었을까?

자동 이벤트 위임 처리 크로스 브라우징 이벤트 객체 풀링(Pooling) 메모리 소비 감소

Event Delegation

Page 77: Facebook은 React를 왜 만들었을까?

import React from 'react'; import { Button, Glyphicon } from 'react-bootstrap'; import CardActions from '../actions/CardActions.js'; export default class CardInsertButton extends React.Component { constructor(){ super(); } render() { return ( <div className="card-thumbnail-slide__card-insert-btn"> <Button onClick={ this._handleClick }> <Glyphicon glyph="plus" /> </Button> </div> ); } _handleClick(){ CardActions.add({ id : Date.now(), src : "https://unsplash.it/400/400?random&bs=" + Date.now() }); } }

Page 78: Facebook은 React를 왜 만들었을까?
Page 79: Facebook은 React를 왜 만들었을까?
Page 80: Facebook은 React를 왜 만들었을까?
Page 81: Facebook은 React를 왜 만들었을까?

Virtual DOM이나 컴포넌트 단위 사고 방식은 좀 더 쉽게 문제를 풀려다가 곁가지로 나온 해결책

React의 본질은 단순함

Page 82: Facebook은 React를 왜 만들었을까?

결국 도구일 뿐이고 장점과 단점은 동전의 앞면과 뒷면 문제를 먼저 살피고 도구를 선택하는 것이 현명

그래서,

React 써야 하나요?

Page 83: Facebook은 React를 왜 만들었을까?

• World Wide Web - http://info.cern.ch/hypertext/WWW/TheProject.html

• XMLHttpRequest and AJAX on Google Suggest - http://www.infinitezest.com/articles/xmlhttprequest-and-ajax-on-

google-suggest.aspx

• Be Predictable, Not Correct. - https://www.youtube.com/watch?v=h3KksH8gfcQ

• React:Rethinking Best Practices - http://www.slideshare.net/floydophone/react-preso-v2

• React’s diff algorithm - http://calendar.perfplanet.com/2013/diff/

• Model-View-Controller(MVC) Its Past and Present - http://heim.ifi.uio.no/~trygver/2003/javazone-jaoo/MVC_pattern.pdf

참고자료

Page 84: Facebook은 React를 왜 만들었을까?

김코딩 email : [email protected] blog : http://huns.me facebook : https://www.facebook.com/jeokrang

Facebook Developer Group https://www.facebook.com/groups/reactist/

감사합니다.