본문 바로가기

공부일지/React

비전공자의 공부일지 - Mobx

728x90
반응형
반응형
728x90

Mobx란??

 

Mobx는 Redux이후 React 개발자들이 많이 사용하는 상태관리 라이브러리라고 한다.

Mobx는 간단하고 확장 가능한 상태 관리, 쉽고 확장성 있게 만들어 주는 검증된 라이브러리라고 소개되어있다.

 

https://ko.mobx.js.org/README.html

 

MobX에 대하여 · MobX

<img src="https://mobx.js.org/assets/mobx.png" alt="logo" height="120" align="right" />

ko.mobx.js.org

 

Mobx 5 버전에서는 데코레이터가 권장되었다고 한다.

 

class TodoList{
  @observable todos = []
  @computed get unfinishedTodoCount () {
    return this.todos.filter((todo) => !todo.finished).length
  }
}

 

나는 지금 6 버전으로 공부하고 있지만 5 버전도 한번 알아두면 좋을 것 같다.

 

Mobx의 작동 원리

 

event가 발생이 되면 action이 실행되고 observable state에 state가 업데이트되고 computed 쪽에 실행돼야 될 이벤트가 있으면 실행이 되고 렌더링이 되는 구조이다.

 

Mobx의 구조

 

Mobx도 큰 프로젝트로 공부하는 게 아니라서 CodeSandBox를 이용하였다.

https://codesandbox.io/s/zealous-jones-j7oofq?file=/src/index.tsx

 

zealous-jones-j7oofq - CodeSandbox

zealous-jones-j7oofq by piwe2004 using mobx, mobx-react-lite, react, react-dom, react-scripts

codesandbox.io

 

카운트 앱을 만들려고 한다.

 

countStore.js

import { action, computed, makeObservable, observable } from "mobx";

export default class counterStore {
  count = 0;
  constructor() {
    makeObservable(this, {
      count: observable,
      isNegative: computed,
      increase: action,
      decrease: action
    });
  }
  get isNegative() {
    return this.count < 0 ? "Yes" : "No";
  }

  increase() {
    this.count += 1;
  }
  decrease() {
    this.count -= 1;
  }
}


index.js

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import counterStore from "./countStore";

const store = new counterStore();

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <App myCounter={store} />
  </React.StrictMode>
);

 

App.js

function App({ myCounter }) {
  console.log(myCounter);

  return (
    <div style={{ textAlign: "center", padding: 16 }}>
      카운트 : {myCounter.count}
      <br />
      <br />
      마이너스?: {myCounter.isNegative}
      <br />
      <br />
      <button onClick={() => myCounter.increase()}>+</button>
      <button onClick={() => myCounter.decrease()}>-</button>
    </div>
  );
}

export default App;

 

이렇게 작성하면 기본 틀은 완성이다.

 

하지만 버튼을 클릭해도 값은 변하지 않는다. 

이유는 observable을 사용해서 구독을 해야 된다고 한다. 

observable을 그냥 사용하는 게 아니라 mobx-react 나 lite 한 mobx-react-lite을 설치해주어야 한다.

그런데 mobx-react-lite이걸 설치해서 observer을 불러오면 값이 변하지 않아 mobx-react를 설치해 주었더니 잘 작동하였다.

 

App.js

import { observer } from "mobx-react";

function App({ myCounter }) {
  console.log(myCounter);

  return (
    <div style={{ textAlign: "center", padding: 16 }}>
      카운트 : {myCounter.count}
      <br />
      <br />
      마이너스?: {myCounter.isNegative}
      <br />
      <br />
      <button onClick={() => myCounter.increase()}>+</button>
      <button onClick={() => myCounter.decrease()}>-</button>
    </div>
  );
}

export default observer(App);

이렇게 하니 카운트와 마이너스 여부도 잘 변경되었다.

 

데코레이터 사용하기

Mobx 6 이전에는 위에서 만들었던 observable, computed, action을 표시하기 위해 ES.next 데코레이터를 사용하도록 권장했다고 한다. 그런데 데코레이터는 ES의 표준이 아니며 표준화 과정에도 오랜 시간이 소요된다고 한다. 그리고 표준화되는 데코레이터는 기존의 시행되었던 방식과 다를 것으로 보인다.

 

https://ko.mobx.js.org/enabling-decorators.html

 

데코레이터 사용하기 · MobX

<script async type="text/javascript" src="//cdn.carbonads.com/carbon.js?serve=CEBD4KQ7&placement=mobxjsorg" id="_carbonads_js"></script>

ko.mobx.js.org

 

countStore.js

import { action, computed, makeObservable, observable } from "mobx";

export default class CounterStore {
    @observable count = 0;

    constructor() {
        makeObservable(this)
    }

    @computed get isNegative() {
        return this.count < 0 ? 'Yes': 'No';
    }

    @action increase() {
        this.count++;
    }

    @action decrease() {
        this.count--;
    }
}

6 버전과 다르게 하나하나 import 해주어야 한다.

import { action, computed, makeObservable, observable } from "mobx";

 

 

App.js

import { observer } from 'mobx-react'
import React, { Component } from 'react'

@observer
export class App extends Component {
    render() {
        const myCounter = this.props.counter;
        return (
            <div style={{ textAlign: 'center', padding: 16 }}>
                카운트: {myCounter.count}
                <br /><br />
                마이너스?: {myCounter.isNegative}
                <br /><br />
                <button onClick={() => myCounter.increase()}>+</button>
                <button onClick={() => myCounter.decrease()}>-</button>
            </div>
        )
    }
}

export default App

이렇게 사용하면 된다

 

App.js를 보면 6 버전과 다른 게 @observer라고 선언을 해주어야 한다고 한다. 그리고 클래스라서 const가 아니라 class를 사용해서 Component를 확장받아야 된다.

 

 

React Context 를 이용한 Observable 공유하기

간단하게 카운터 앱을 만들기 위해 <App myCounter={store} /> 이렇게 작업을 하였는데 App.js 말고 다른 컴포넌트에도 state를 받을 수도 있는데 그때 사용 한다고 한다.

 

reactContext에서 작업했던 방식으로 provider를 생성해서 value를 넣어주는 방식인데

 

일단 context폴더에 countContext.js를 하나 만들어서

import { createContext, useContext } from "react";

// Context 생성
export const CounterContext = createContext();

// Provider 생성 
export const CounterProvider = CounterContext.Provider;

// Store에 있는 value를 사용하기 위한 Hooks
export const useCounterStore = () => useContext(CounterContext);

context를 만들어 준다.

 

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { CounterProvider } from './context/counterContext';
import CounterStore from './CountStore';

const store = new CounterStore();

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <CounterProvider value={store}>
      <App />
    </CounterProvider>
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

기준에 <App myCounter={store} />에서

 

App.js

import './App.css';
import { observer } from 'mobx-react';
import { useCounterStore } from './context/counterContext';


function App() {

  const myCounter = useCounterStore();

  console.log('myCounter', myCounter);
  return (
    <div style={{ textAlign: 'center', padding: 16 }}>
      카운트: {myCounter.count}
      <br />
      <br />
      마이너스?:{myCounter.isNegative}
      <br />
      <br />
      <button onClick={() => myCounter.increase()}>+</button>
      <button onClick={() => myCounter.decrease()}>-</button>
    </div>
  );
}

export default observer(App);

countContext에서 만들었던 useCounterStore를 불러와서 myCounter에 넣어서 사용해 주면 된다.

 

아마 프론트쪽 으로 내가 이직하게 되면 꽤 많이 사용할 거 같다.

데이터 관리가 중요하니깐 상태 관리 쪽은 많이 공부해야 될 거 같다. 지금은 클론코딩이라 괜찮지만 혼자 막상 프로젝트를 작업하다 보면 멘붕이 올 거 같다...

728x90
반응형