개발 블로깅/React 개념

[2019.08.11] Mobx-React 개념 및 사용법 1 (살짝 어려움 주의)

Hello이뇽 2019. 8. 11. 22:00

 

글로벌 상태 관리를 할 수 있는 MobX 개념 및 사용법에 대해 정리한다.

먼저 MobX는 리덕스와 같이 리액트에 종속되어 있는 라이브러리가 아니다.
React가 아닌 일반 javascript 내에서 또는 Vue.js와 같은 다른 웹 프레임워크에서도 사용할 수 있는 점을 참고하자.

 

MobX 개념 파악하기

- Observable (관찰받고 있는 state)

리액트 안에서 사용되는 글로벌 상태를 직접 변경하며 어떻게 상태가 변했는지 파악하는 요소.

- Computed Value (연산된 값)

이벤트 발생으로 인해 어떠한 연산에 필요로 하는 state에 변화가 생겼을 때, 그 state 변화에 따라서 새로운 연산 작업을 수행하게 된다. 해당 state에 변화가 없으면, 그냥 기존의 값을 사용한다.

- Actions (액션; 행동)

상태에 어떻게 변화를 일으킬 것인지 정의하는 부분.
Redux의 action과 같은 개념.

 

 

리액트에서 MobX 사용하기

Mobx 라이브러리 설치

yarn 패키지 관리자를 이용하여 아래와 같이 mobx, mobx-react 라이브러리를 설치한다.

$ yarn add mobx mobx-react

 

 

MobX 기본 사용 함수

import React, {Component} from 'react';
import {decorate, observable, action, computed} from 'mobx';
import {observer} from 'mobx-react';

 

주로 이용하게 될 함수들은 위와 같다.

 

Mobx를 이용한 React Component 구현하기

import React, { Component} from 'react';

class Counter extends Component {
  number = 0;
  
  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>
    )
  }
}

 

우선 위 코드는 두개의 버튼으로 숫자를 증가/감소시키는 간단한 카운터이다.
(number라는 변수를 this.state로 사용하지 않은 부분을 주의하자)

 

Decorate를 이용하여 카운트 기능에 MobX 적용하기

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

class Counter extends Component {
  number = 0;
  
  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>
    )
  }
}

// 데코레이터 설정 부분
decorate(Counter ,{
  number: 'observable',
  increase: 'action',
  decrease: 'action'
});

export default observer(Counter);

상단에 MobX 관련 기능을 선언한 후, 코드 하단 쪽에 decorate함수를 호출한 부분이 있다.

해당 decorate가 Counter 컴포넌트에게 상태변환을 할 수 있도록 도와주는 함수이다.
마치 리덕스에서 Connect함수의 인자로 mapStateToProps, mapDispatchToProps를 넣는 것과 유사한 형태이다. 

 

💡decorate 각 파라미터 설명
첫 번째 인자 : 상태와 연결할 컴포넌트
두 번째 인자 : Object 타입, Key는 상태로 적용할 변수명 및 함수명, Value 쪽은 각 Key에 맞는 요소에 적용할 액션(ex-observable:상태 관찰 대상, action: 상태 관찰 대상을 변화시킬 액션)

 

마지막 줄의 observer는 observable로 선언한 상태 값(위 코드에서는 number 값)이 변할 때, 컴포넌트 API인 forceUpdate()를 자동 호출하여 변경된 값이 화면에 반영된다.

 

Decorate기능 대신 Decorator로 mobx 사용하기

Decorator를 사용하면 훨씬 더 편하고 직관적으로 문법을 작성할 수 있다.
그러나 이 방법은 일반적인 자바스크립트 코드 작성 방식과 매우 다른 작성 법이므로 Babel 설정이 필요로 하다.

 

babel 설정에 필요한 플러그인 설치

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

 

create-react-app을 사용하는 경우, 기본스크립트 명령어로 들어가 있는 eject 실행으로, CRA에 종속되어 있는 설정을 모두 풀어내야 한다.

$ yarn eject

or

$ npm run eject

 

# eject로 풀어진 package.json 화면

 

위 사진과 같이 나타나면, package.json 내의 바벨 설정 부분으로 가서 아래와 같이 수정한다.

{
  {다른 설정들..}: "설정 값들",
  
  "babel": {
    "presets": [
      "react-app"
    ],
    "plugins": [
      [
        "@babel/plugin-proposal-decorators",
        {
          "legacy": true
        }
      ],
      [
        "@babel/plugin-proposal-class-properties",
        {
          "loose": true
        }
      ]
    ]
  }
}

 

여기까지 바벨 설정이 완료되었으면, 현재 프로젝트가 실행 중일 시 다시 재 실행을 꼭 해준다!!

Decorator를 사용할 바벨 설정이 완료되었으니 이제 이어서 코드 작업을 해보자.

 

Decorator 코드로 카운트 컴포넌트 리펙토링

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

@observer // Decorator 적용
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>
    )
  }
}

// decorate(Counter, {
//   number: observable,
//   increase: action,
//   decrease: action
// })
// 더 이상 사용되지 않음

// export default observer(Counter);  
// observer가 class명 상단으로 올라감

export default Counter;

일반 자바스크립트 작성법과는 많이 다른 @로 시작하는 Decorator코드를 볼 수 있다.

위에서 Decorate로 MobX를 적용했던 방식들을, 컴포넌트명 위, 그리고 각 함수에 @를 이용해서 선언하여 사용하는 방식으로 변경했다.

 

컴포넌트에서 action, store 등 분리시키기

위에서 만든 카운터는, action과 number라는 상태가 Counter라는 클래스 컴포넌트 내에서 선언되어 있다. 

이러한 mobx 개념들이 적용된 요소들을 컴포넌트에서 떼어내, 리덕스와 같이 글로벌한 로직으로 사용할 수 있도록 변경해보자.

 

우선 action과 store를 담을 클래스를 새로 하나 생성한다.

// stores/counter.js

import { observable, action } from 'mobx';

export default class CounterStore {
  @observable number = 0;
  
  @action
  increase = () => {
    this.number++;
  }
  
  @action
  decrease = () => {
    this.number--;
  }
}

 

이렇게만 하면 끝! 매우 간단하다..!

리덕스에선 리듀서, 액션, 스토어를 모두 분리하여 구현해야 했는데, MobX에서는 하나의 클래스를 생성하여 observable 값과 그 값을 변경시킬 action 함수만 생성하면 된다.

 

Provider로 프로젝트에 Store 적용

Mobx는 리덕스와 같이 프로젝트에 스토어를 적용할 때, 프로젝트 엔트리 파일인 index에 Provider를 적용한다.

// src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

import { Provider } from 'mobx-react'; // MobX 에서 사용하는 Provider
import CounterStore from './stores/counter'; // 방금 만든 스토어 불러와줍니다.

const counter = new  CounterStore();

ReactDOM.render(
  <Provider counter={counter}>
    <App />
  </Provider>,
  document.getElementById('root')
);

registerServiceWorker();

 

 

inject를 이용하여 컴포넌트에서 Store 불러오기

글로벌하게 사용하기 위해 src/stores/counter.js 파일로 빼냈던 actionobservable 값을 컴포넌트로 가져와서 사용해보자.

// src/Counter.js

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

@inject('counter')
@observer
class Counter extends Component {
  render() {
    const { counter } = this.props;
    return (
      <div>
        <h1>{counter.number}</h1>
        <button onClick={counter.increase}>+1</button>
        <button onClick={counter.decrease}>-1</button>
      </div>
    );
  }
}

export default Counter;

상단 부분에 @inject('스토어명')을 이용해서, 해당 스토어의 observable 값과 action 함수를 가져올 수 있다.

가져온 스토어들은 해당 컴포넌트의 props로 내려받아서 사용한다.

 

대신 위와 같이 사용하면, counter에 있는 모든 스토어를 가져오게 되는데, 만약 특정 스토어만 가져올 시 아래와 같이 쓸 수 있다.

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

@inject(stores => ({
  number: stores.counter.number,
  increase: stores.counter.increase,
  decrease: stores.counter.decrease
}))

@observer
class Counter extends Component {
  render() {
    const { number, increase, decrease } = this.props;
    return (
      <div>
        <h1>{number}</h1>
        <button onClick={increase}>+1</button>
        <button onClick={decrease}>-1</button>
      </div>
    );
  }
}

export default Counter;

 

 

여기까지..! 지금까지 MobX의 기본적인 개념과 사용법에 대해 알아보았다.

 


 

오랜만에 기술 블로그를 작성해봤다. 요즘 새롭게 접하는 내용이 없어서 구직생활 내용만 적다가 기술적인 블로깅을 하니까 설명이 잘 되었는지 모르겠다.

다시 차츰차츰 기술 블로그도 잘 써보도록 노력해야겠다!

 

반응형