패스트캠퍼스/자습

React MobX 초심으로 돌아가서 공부하기(MobX최신 버전반영/MobX6)

sunnykim91 2020. 12. 15. 12:58

<글을 쓰다가 중간에 발견 항 주의사항>

 

MobX 6버전 (2020년 9월 업데이트) 에서 부터 데코레이터 사용하지 않고,

makeObservable을 사용하는것으로 권장하는것으로 바뀌었다. 때문에 데코레이터를 아래에서 설정하는 부분은 건너 뛰고 makeObservable을 써서 하는것으로 중간부터 글을 작성할 예정입니다.





React를 어느정도 공부하다보면, 빼놓고 생각할 수 없는 한가지.

상태관리!

 

React의 상태관리를 하는 방법은 대다수가 사용하는 redux, mobX , context API 등 이있다.

 

이 중,  나는 현재 MobX를 사용하고 있다.

 

하지만, 프로젝트를 약 6개월간 만들었지만, 성능, 재사용성 등을 고려하지 않은채 개발을 하고 mobX의 대략적인 사용법만 안채 개발을 하다보니 원리에 대해서 전혀 알지 못한채 기계적인 개발만 하게 된 것 같았다. 그래서 이번 공부를 통해 다시 알아보고자 한다.

 

 

 

1. MobX 의 주요 개념들에 대해서 살펴보자.

 1) Observable State

   - 관찰 받고 있는 상태라는 뜻인데, 개발을 하다보면 상태는 항상 변할 수 있기 때문에 이 상태가 바뀐다면 MobX에서는 이를 캐치해낼 수 있습니다. 원시값들(string, number, boolean등등), 객체, 배열안에 객체 던 어떤 값들이 바뀌던지 MobX는 상태의 변화를 캐치할 수 있습니다. 

 

 2) Computed Value

   - 연산된 값이라는 뜻인데, 주로 성능 최적화를 위해서 많이 사용하긴 합니다. 연산에 기반 되는 값(상태값)이 변화할때만 새로 연산하고 바뀌지 않는다면 그냥 기존 값을 사용할 수 있도록 해줍니다.

 

 3) Reactions

   - 값이 바뀔때에 따라 해야 할 일을 정의하는 것 , 예를들어 1)의 Observable State가 바뀌었을때 이 리액션을 통해 어떤 로직을 실행시키는 것이죠.

 

 4) Actions

   - 상태에 변화를 일으키는 것, 1)의 Observable State를 변화 시키는 코드를 호출하면 액션

 

 

 

2. 이제 React에서 MobX를 사용해보자.

 

먼저 , CRA를 통해 프로젝트를 만들고, mobx와 mobx-react를 추가하고, 실행까지해보자, 명령어는 다음과 같다.

 

npx create-react-app mobx-with-react

cd mobx-with-react

yarn add mobx mobx-react

yarn start

 

실행이 잘되었다면, 파일을 하나 만들어보자. Counter.js라고.

 

 

코드는 다음과 같다.

import React, { Component } from "react";
import { makeObservable, observable, action } from "mobx";
import { observer } from "mobx-react";

class Counter extends Component {
  number = 0;

  constructor() {
    super();
    makeObservable(this, {  // 예전 버전의 mobx에서는 decorate 사용,  최신버전에서 makeObservable사용
      number: observable,  // observable이니 내가 관찰할 상태 
      increase: action,    // action 이니 상태의 변화를 일으킬 친구
      decrease: action,    // action 이니 상태의 변화를 일으킬 친구
    });
  }

  increase = () => {
    this.number++;
  };

  decrease = () => {
    this.number--;
  };

  render() {
    return (
      <div>
        <h1>{this.number}</h1>
        <button onClick={this.increase}>+1</button>
        <button onClick={this.decrease}>-1</button>
      </div>
    );
  }
}

export default observer(Counter);  
// observer로 감싸줌으로써 이 Counter 클래스는 mobx가 @observable로 지정된 state를 적절히 rerendering 시켜주는 역할을 하게된다.

 

설명은 주석을 참고하길 바랍니다 .^^

마지막의 export default observer(Counter) 를 통해서

observable값이 변할때 forceUpdate를 호출함으로서 자동으로 변화를 감지해 화면에 반영되는 것입니다.

(setState이런거 안해줘도 됩니다...신세계...)

 

 

App.js파일도 수정해준다.

import React, { Component } from "react";
import Counter from "./Counter";

class App extends Component {
  render() {
    return (
      <div>
        <Counter />
      </div>
    );
  }
}
export default App;

 

실행해보면,

잘 실행되는것을 볼 수 있다.

 

 

사실, 우리회사도 그렇고 블로그에서도 그렇고  Decorator(데코레이터) 문법을 많이 쓴다. (겁나 편함)

그렇다면 Decorator를 사용하는 방법도 알아보겠다.

 

아래와 같이 쓸 수 있다.

import React, { Component } from "react";
import { observable, action } from "mobx";
import { observer } from "mobx-react";

// 마지막에 있던 observer를 이렇게 앞에다가 써준다.
@observer
class Counter extends Component {
  @observable number = 0;

  @action
  increase = () => {
    this.number++;
  };

  @action
  decrease = () => {
    this.number--;
  };

  render() {
    return (
      <div>
        <h1>{this.number}</h1>
        <button onClick={this.increase}>+1</button>
        <button onClick={this.decrease}>-1</button>
      </div>
    );
  }
}

export default Counter;

 

 

하지만, 아마 에러가 나는 사람이 있을 것이다. 그 이유는 babel설정 때문이다.

(babel은 자바스크립트 컴파일러 로서, 최신문법 ES6, ES7이상의 문법들을 브라우저가 이해할 수 있는 문법으로 변환해주는 역할을 한다. )

 

yarn add @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators

명령어를 통해 설치해주자.

 

package.json파일을 열어보자. babel을 커스터마이징 해야하는데 babel설정이 안보인다 .이를 위해서 아래와 같이 해보자.

 

yarn eject

위 명령어를 통해서 babel설정을 커스터마이징 할 수 있도록 해보자.

 

 

 

y/n선택에서는 y를 해주면, 아래와같이 에러가 뜨는 사람이 있을 것이다.

yarn eject를 통해서 했지만, This git repohis git repository has untracked files or uncommitted changes: 리스트가 뜨면서 아래와같이 뜰것이다.

 

 

그렇다면, 이렇게 해보자.

git add .

git commit -m '커밋메시지'를하고서 다시 yarn eject를 해보자.

 

 

 

이제  package.json파일을 열어보자. 맨 처음 열었을때보다 뭔가 엄청 많이 생겼다.

여기서 babel부분을 찾아보자.

"babel": {
    "presets": [
      "react-app"
    ],
    "plugins": [
        ["@babel/plugin-proposal-decorators", { "legacy": true}],
        ["@babel/plugin-proposal-class-properties", { "loose": true}]
    ]
  }

나는 맨 밑에 있었다. 이부분을 오른쪽처럼 바꿔보자.

 

그리고 다시 실행시켜보자.

 

이번엔 이런 오류를 맞이했다.

Cannot find module '@babel/plugin-syntax-jsx'

 

대부분 이런거는 그냥 깔아주면 된다.

 

yarn add @babel/plugin-syntax-jsx

 

설치 후 다시 실행해보자. 

 

 

 


글쓴이는 이 작업을 하던 도중 블로그와 똑같이 코드작성을 했는데도 작동을 하지 않았다.

그이유는 2020년 9월 mobx6버전으로 오면서 @문법을 호환성때문에 더이상 사용하지 않는 방향으로 바꾼것 같다....자세한건 

mobx.js.org/enabling-decorators.html#enabling-decorators- 

이 사이트를 참고 바란다.

 

Enabling decorators · MobX

mobx.js.org

 

 

여튼 위에까지 따라하신분들은 이렇게 쓰시면 됩니다...

 

@observer데코레이터가 가능한 이유는 우리가 데코레이터가 가능하게끔 사용 설정을 해놔서 그렇습니다..ㅎㅎ

import React, { Component } from "react";
import { observable, action, makeObservable, computed } from "mobx";
import { observer } from "mobx-react";

// **** 최하단에 잇던 observer 가 이렇게 위로 올라옵니다.
@observer
class Counter extends Component {
  number = 0;

  constructor(props) {
    super(props);
    makeObservable(this, {
      number: observable,
      increase: action,
      decrease: action,
    });
  }

  increase = () => {
    console.log("ince");
    this.number++;
  };

  decrease = () => {
    this.number--;
  };

  render() {
    return (
      <div>
        <h1>{this.number}</h1>
        <button onClick={this.increase}>+1</button>
        <button onClick={this.decrease}>-1</button>
      </div>
    );
  }
}

export default Counter;

 

 

 

 

3. 자 이제 Store로 분리시켜보자.

 

여기서 부터는 @(데코레이터) 없이 사용하도록 하겠습니다.

 

React에서  사용할때에는 공식문서에서는 

이 2가지 키워드를 중점적으로 사용하고 있다.

makeAutoObservable
observer

 

*App에서 CounterStore의 인스턴스를 만들어 그것을 props로 내려준다.

//App.js

import "./App.css";
import React from "react";
import Counter from "./Counter";
import CounterStore from "./store/CounterStore";

const myCounter = new CounterStore();

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <Counter counter={myCounter} />
      </header>
    </div>
  );
}

export default App;

 

*props로 받은 인스턴스를 이렇게 사용한다.

*함수형에서만 사용가능한듯(class에서 사용가능한지 모르겠다. (observer를 붙여줘야해서..)

*observer키워드를 함수 앞에 붙여준다.

//Counter.js

import React from "react";

import { observer } from "mobx-react";

const Counter = observer(({ counter }) => (
  <div>
    <h1>{counter.number}</h1>
    <button onClick={counter.increase}>+1</button>
    <button onClick={counter.decrease}>-1</button>
  </div>
));

export default Counter;

 

*Store에서는 이런식으로 클래스만 사용한다. console은 내가 함수가잘 작동하는지 테스트 하기위함.

//CounterSotre.js

import { makeAutoObservable } from "mobx";

class CounterStore {
  number = 0;

  constructor() {
    makeAutoObservable(this);
  }

  increase = () => {
    console.log("증가");
    this.number++;
  };

  decrease = () => {
    this.number--;
  };
}

export default CounterStore;

 

 

크게 어려운 코드는 없이 이렇게 사용할 수 있다. 아직 좀 더 공부가 필요한 것 같지만, 간단하게 이렇게 사용한다 정도 알고 있으면 좋을 것 같다.

 

 

 

*** 참고한, velopert님 블로그에는 makeObservable이 아니라, decorate로 되어있는데 

decorate는 mobx6버전에서 부터 삭제되었다고 한다....

그래서 위와 같이 쓴다!!


 

 

 

 

 

 

 

 

 

참고 블로그 : 

velog.io/@velopert/begin-mobx#autorun

 

MobX (1) 시작하기

MobX 는 또 다른, 하나의 인기있는 리액트 상태 관리 라이브러리입니다. 저는 MobX 는, 사실상 라이브러리 그 이상의 가치를 하는, 리액트의 개발 흐름 자체를 많이 바꿔주는 강력한 도구라고 생각

velog.io

 

반응형