facebook react (3)

react flux

저번까지는 리액트에 대해 알아 봤다.
저 정도만 이해해도 어느정도는 이해 했다고 봐도 될거 같다.
물론 이해정도만?
이번엔 flux에 대해 알아 볼려고 한다.
정확한거는 여길 참고

flux-simple-f8-diagram-with-client-action-1300w

flux는 위와 같은 아키텍처다.
실직적으로 프레임워크? 라이브러리? 라기보다는 아키텍쳐에 더 가깝다.
소스만 봐도 길지 않은 소스다.

위의 그림만 봐도 대충은 알것 같은 느낌이다.
액션이 생기면 Dispatcher가 받고 store로 전달한다음에 뷰로 전달된다.
일단 폴더 구조를 보자.
react-flux

일단 필자는 이렇게 해놨다.
actions 는 이벤트 발생 하는 시점을 말하는거 같다. 예로 버튼을 눌렀을때 액션을 호출해야된다.
실제 여기서 서버와 통신도 하는듯 하다.
components 는 뷰를 구현하는 폴더이다.
constants 는 상수를 정의하는 폴더이다.
core에는 Dispatcher가 포함되어있다.
stores에는 데이터들이 들어있다.
이제 flux을 설치하자

npm install flux

위와 같이 설치가 완료 되었다면 코딩으로 보자. 그게 더 이해하기 빠르니.

components의 Button이라는 컴포넌트다.

import React, { Component } from 'react';
import ButtonStore from '../stores/ButtonStore'
import ButtonCreator from '../actions/ButtonCreator'

export default class Button extends Component {

    constructor() {
        super();
        this._onChange = this._onChange.bind(this);
        this.state = {
            index : 0
        }
    }

    componentDidMount() {
        ButtonStore.addChangeListener(this._onChange);
    }
    componentWillUnmount() {
        ButtonStore.removeChangeListener(this._onChange);
    }
    addClick(index){
        ButtonCreator.addClick(index);
    }
    _onChange() {
        this.setState({index: ButtonStore.getIndex()})
    }
    render(){
        return(
            <div>
                <button onClick={this.addClick.bind(this,1)}>BUTTON</button>
                <input value={this.state.index}></input>
            </div>
        )
    }
}

버튼을 클릭하면 1을 더해서 출력해주는 컴포넌트다.
이벤트를 등록하고 클릭시 이벤트를 action에 전달한다.
액션의 ButtonCreator 파일을 보자

import Dispatcher from '../core/Dispatcher';
import ButtonConstants from '../constants/ButtonConstants';

export default {

    addClick:function(index){
        Dispatcher.handleViewAction({
            actionType: ButtonConstants.BUTTON_CONSTANT,
            index: index
        });
    }
}

액션은 실제 이벤트들이 모여 있는 곳이다.
액션타입과 데이터를 전달하면 된다.
다음으론 상수들을 정의해놓은 ButtonConstants다

export default  {
    BUTTON_CONSTANT: 'BUTTON_CONSTANT'
};

간단하게 상수 정의만 하면된다.
마지막으로 store를 보자

import Dispatcher from '../core/Dispatcher';
import ButtonConstants from '../constants/ButtonConstants';
import { EventEmitter } from 'events';

class ButtonStore extends EventEmitter {

    constructor() {
        super();
        this.subscribe(() => this._registerToActions.bind(this));
        this.index = 0;
    }
    subscribe(actionSubscribe) {
        this._dispatchToken = Dispatcher.register(actionSubscribe());
    }

    emitChange() {
        this.emit('CHANGE');
    }

    addChangeListener(cb) {
        this.on('CHANGE', cb)
    }

    removeChangeListener(cb) {
        this.removeListener('CHANGE', cb);
    }

    _registerToActions(payload) {
        let action = payload.action;
        switch (action.actionType) {
            case ButtonConstants.BUTTON_CONSTANT:
                this.index += action.index;
                break;
            default:
                return true;
        }
        this.emitChange();
        return true;
    }

    getIndex(){
        return this.index;
    }
}

export default new ButtonStore();

이벤트가 왔을 때 해당 액션타입을 보고서 이벤트를 결정한다.
액션에서 정의한 상수와 동일한 값이 전달된다.

완료가 되었다면 다시 컴파일을 해서 확인을 해보자.
버튼이한개 있고 버튼을 클릭하면 1씩증가한다.

근데 만약 저 버튼컴포넌트에서 클릭하면 다른 컴포넌트에 이벤트가 발생했다고 알려줘야되는데 그건 어떻게 하는지 보자.
TextBox라는 클래스를 만들자


import React, { Component } from 'react'; export default class TextBox extends Component { render(){ return( <div> <input value={this.state.text}></input> </div> ) } }

이런 간단한 컴포넌트다. 버튼컴포넌트에서 버튼을 누르면 버튼을 클릭하셨습니다 라고 띄어 줄 것이다.
그럼 간단한 작업만으로 가능하다.

import ButtonStore from '../stores/ButtonStore'
import ButtonCreator from '../actions/ButtonCreator'

...

constructor() {
    super();
    this._onChange = this._onChange.bind(this);
    this.state = {
        text : ""
    }
}

componentDidMount() {
    ButtonStore.addChangeListener(this._onChange);
}
componentWillUnmount() {
    ButtonStore.removeChangeListener(this._onChange);
}

_onChange() {
    this.setState({text: "버튼을 클릭 했습니다."})
}

위와 같이 추가 하자.
ButtonStore에 이벤트를 바인딩 시키자.
그런후에 버튼컴포넌트에 가서 아래와 같이 TextBox를 추가 시키자

import TextBox from '../components/TextBox'

...

<div>
    <button onClick={this.addClick.bind(this,1)}>BUTTON</button>
    <input value={this.state.index}></input>
    <TextBox />
</div>

그런 후에 다시 확인해보자.
그럼 버튼을 클릭하면 input text에 버튼을 클릭 했다고 나올 것이다.
이벤트를 받고 싶다면 해당 컴포넌트에 이벤트를 바인딩 시키면 된다.

이것으로 react와 flux에 대해 조금 알아봤다.
하지만 flux는 좋은 아키텍쳐 이지만 너무 복잡하다.

그래서 flux와 같지만 조금은 덜 복잡하게 만들어진 라이브러리도 존재한다.
그 중에 유명한게 fluxxor 이다.
flux 말고 redux도 좋아 보인다!
관심이 있다면 살펴보는 것도 나쁘지 않다!
이것으로 react는 마무리를 지어야 겠다!

소스는 github 에 올려놨다.

facebook react (2)

react

이번 시간엔 webpack과 바벨컴파일러를 써서 개발을해보자!
일단 npm(node.js)이 있어야된다.
nodejs 사이트가서 다운로드 받자.
설치후 터미널을 열어서 해당 프로젝트에서 다음과 같이 인스톨을 하자

npm install babel-loader babel-core babel-preset-es2015 babel-preset-react react react-dom --save-dev

바벨과 리액트를 설치 했다.
그럼 node_moduls라는 파일이 생겼을 것이다. 파일이 많다..
그럼 다음으로 webpack을 설치 하자

npm install webpack -g

다음은 루트 폴더에 webpack.config.js 파일을 추가 하자

module.exports = {
    entry: './app.js',
    output: {
        filename: 'bundle.js',
        path: './build'
    },
    module: {
        loaders: [
            {
                test: /\.(js|jsx)?$/,
                exclude: /node_modules/,
                loaders: [ 'babel' ]
            },
        ]
    }
};

실제 컴파일된 파일은 bundle.js에 담겨 있다.
바벨 로더를 갖고 컴파일을 할거다. 컴파일러는 바벨 말고 딴것도 있던거 같은데..
필자가 할 수 있는건 바벨 밖에 없어서…

다음은 .babelrc파일을 루트에 다음과 같이 넣어야된다.

{
   "presets": ["es2015", "react"]
}

그리고 루트 폴더에 app.js를 만들자.

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class AppMain extends Component {
    constructor(props) {
        super(props);
        console.log("constructor");
    }

    render() {
        console.log("render")
        return(
            <div>
                <button onClick={this.unmount.bind(this)}>unmount</button>
            </div>
        );
    }

    componentWillMount(){
        console.log("componentWillMount")
    }

    componentDidMount(){
        console.log("componentDidMount")
    }

    componentWillUnmount(){
        console.log("componentWillUnmount");
    }
    unmount(){
        React.unmountComponentAtNode(document.getElementById('app'))
    }

}
ReactDOM.render(<AppMain/>, document.getElementById('app'));

저번 시간에 만들었던 코드를 갖다 놨다.
나머지코드는 동일하지만
맨 위에 import하는 부분이 있다.
모듈을 import하는 것으로 require(“react”) 와 동일 하다고 생각하면 된다.

마지막으로 html 파일을 생성하자 기존에 있다면 수정하자.
나머지는 필요 없고

<div id="app"></div>
<script src="../build/bundle.js"></script>

위와 같이 컴파일된 js와 실제 그릴 태그만 있으면 된다.
다 완료 되었다.
이제 webpack으로 컴파일을하자.
그냥 webpack만 터미널에 치면 된다.

webpack

에러가 나면 에러 표시가 나올 것이다.
에러가 나오지 않았다면 브라우저를 열어 확인해보자.
이상없이 잘 된다면 다음으로 넘어가자.

이번엔 import export에 대해 알아보자.
내가 만든 클래스(컴포넌트)를 모듈화 시킬 수 있다.
하는 방법은 간단하다.
Button.js 파일을 만들자 거기에 이렇게 넣어 보자!

import React, { Component } from 'react';

class Button extends Component {
    render(){
        return(
            <button>BUTTON</button>
        )
    }
}

그냥 위에서 배운 간단한 button 컴포넌트다.
그래도 이전에 만들었던 것 중에서 지금은 필요 없는 것들은 제외 했다.

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Button from './js/Button';

class AppMain extends Component {
    constructor(props) {
        super(props);
    }

    render() {
        return(
            <div>
                <Button />
            </div>
        );
    }
}
ReactDOM.render(<AppMain/>, document.getElementById('app'));

그리고 컴파일을 해보자.

webpack

그리고 브라우저를 띄우면 에러가 난다.
export를 안했다.
다시 button으로 가자!
맨 아래에 이렇게 추가 하자.

export default Button;

그리고 다시 컴파일을 해보자.
그리고 브라우저를 띄우면 버튼 한개가 있을 것 이다.
혹은 클래스앞에 키워드로 써도 된다.

export default class Button extends Component {
    render(){
        return(
            <button>BUTTON</button>
        )
    }
}

이렇게 써줘도 가능하다.
필자는 저렇게 후자 같이 썼다.

이렇게 react에 대해 알아봤다.
앵귤러도 배우고는 싶지만 지금당장은 필요 없을 것같다.
프론트는 리액트 하나로 족하다ㅜㅜ
이것으로 리액트에 대해 알아봤다.

참고 : webpack을 계속 치기 귀찮으니까 –watch 옵션으로 실시간으로 컴파일 하자!

webpack --watch

facebook react (1)

react

필자가 프론트엔드 개발자는 아니지만 관심깊게 봐온 react에 대해 알아보겠다.
앵귤러와 비교 대상은 아니지만 리액트가 더 맘에 든다.
앵귤러는 공부를 안해봐서 그런지도.
처음에 선택한 이유는 일단 페이스북이 직접 사용하고 있기 때문에 조금 안심(?) 이다.
크롬에서 페이스북으로 들어간 다음에 개발자 도구에서 다음과 같이 입력해보자

require("React").version
#"15.0.0-rc.1"

아무튼 리액트를 다운받자
react download 다운로드 받으면 된다.

그럼 프로젝트를 한개 만들자. 아무 빈 프로젝트면 된다.
필자는 인텔리j로 했다.

<script src="../js/react.min.js"></script>
<script src="../js/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>

다운받은 리액트를 원하는 경로에 넣고 위와 같이 선언하자
원래 예전에 봤을때는 jsx컴파일러를 썼는데 지금 홈페이지가보니까 babel을 사용하라 한다.
그래서 바벨을 사용하겠다 또한 es6문법을 사용하겠다(아직 초보)

그럼 다음에 body안에 아래와 같이 입력하자

<div id="app"></div>
<script type="text/babel">
    class AppMain extends React.Component {
        render() {
            return (
                    <h2>hello world!!</h2>
            );
        }
    }
    ReactDOM.render(<AppMain/>, document.getElementById('app'));
</script>

그리고 파일을 열어보자 그럼 hello world!!가 보일 것이다.
일단 한개씩보자.
리액트는 컴포넌트를 통해 뷰만 담당한다.
그래서 위에 말했듯이 앵귤러랑은 비교 대상이 아니다.
여기서 컴포넌트란 재사용을 가능하게 하고 컴포넌트끼리의 결합등을 이야기 한다.
또한 리액트는 VIRTUAL DOM을 갖고 있다. 구조의 전후 상태를 비교하여 변경에 필요한 부분만 반영한다.
그래서 처음에는 조금 느려도 시간이 지날수록 빠르다.
저기 render 함수를 보면 html코드가 그대로 있다.
저문법은 jsx문법으로 xml과 같은 문법이기 때문에 몇몇개의 속성이 다르다.
예를들어 class는 쓸수 없다. class는 className으로 바꿔서 써야된다.

아래와 같이 해보자

class AppMain extends React.Component {
    render() {
        return (
                <h2>hello {this.props.name}</h2>
        );
    }
}
ReactDOM.render(<AppMain name="wonwoo"/>, document.getElementById('app'));

props라는 키워드가 보인다.
props란 부모 컴포넌트에서 전달 받은 객체다. readOnly라 정하기 했지만 사실상 변경 가능은하다.

다음을 보자. 이번엔 state다.
이아이는 현재 컴포넌트의 상태 값을 저장하고 있는 아이다. 변경이 가능하며 실제 외부에선 접근하지 않는다.(private)
만약 state가 변경이 되면 render를 다시 호출한다.
일단 소스를 보자.

class AppMain extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            index: 0,
        };
    }
    increase(i){
        this.setState({index: this.state.index += i});
    }
    render() {
        return(
                <div>
                    <button onClick={this.increase.bind(this,1)}>increase</button>
                    <h2>hello {this.state.index}</h2>
                </div>
        );
    }
}
ReactDOM.render(<AppMain/>, document.getElementById('app'));

일단 생성자에 state를 초기화 할수 있다.(getInitialState 와 라이프사이클이 비슷한걸로 보인다. 라이프사이클은 밑에 알아보겠다.)
버튼을 클릭했을때 setState을 호출하여 index를 증가 시켰다. 어느정도 감이 왔을 것으로 보인다.

다음은 라이프사이클을 알아보겠다.
일단 자주 쓰는거에 대해서만 알아보겠다.
나머지는 문서 참조!

class AppMain extends React.Component {
    constructor(props) {
        super(props);
        console.log("1");
    }

    render() {
        console.log("2")
        return(
            <div>
                <button onClick={this.unmount.bind(this)}>unmount</button>
            </div>
        );
    }

    componentWillMount(){
        console.log("3")
    }

    componentDidMount(){
        console.log("4")
    }

    componentWillUnmount(){
        console.log("5");
    }
    unmount(){
        React.unmountComponentAtNode(document.getElementById('app'))
    }

}
ReactDOM.render(<AppMain/>, document.getElementById('app'));

어디보자
constructor는 생성자가 있다. 위에 언급했듯이 getInitialState와 비슷한 라이프사이클은 갖는듯 하다. 제일 처음에 호출 된다.
다음은 render가 호출된다. 실제 뷰를 그리는 역할을 하고 있다.

componentWillMount 마운트 직전에 한번 호출된다. 업데이트된 state를 확인할 수 있고 state가 변함에도 불구하고 render()가 한번만 실행 된다. 또한 DOM 엘리먼트를 조작 할 수 없다.

다음에 호출 되는 componentDidMount 는 이시점 부턴 DOM을 조작 가능하고 또한 서버와 통신을 여기서 한다.

componentWillUnmount 는 언마운트 될때 호출 된다.

리액트를 잠깐 알아봤다.
다음엔 webpack과 바벨을 통해서 컴파일하면서 개발 하는법을 알아보겠다.