만족

[React] MobX 개요 본문

[React] MobX 개요

FrontEnd/React Satisfaction 2020. 10. 4. 21:14

MobX란?

상태 관리 라이브러리다.

 

비슷한 종류의 대표적인 상태 관리 라이브러리로 redux가 있다.

 

개인적으로 redux의 지독한(...) 보일러플레이트에 깊은 빡침을 느껴 mobx를 사용해보려고 한다.

 

설명은 공식 문서를 기준으로 설명한다.

 

mobx.js.org/README.html

 

About MobX · MobX

logo

mobx.js.org

 

빠르게 알아보자

import React from "react"
import ReactDOM from "react-dom"
import { makeAutoObservable } from "mobx"
import { observer } from "mobx-react"

// Model the application state.
class Timer {
    secondsPassed = 0

    constructor() {
        makeAutoObservable(this)
    }

    increase() {
        this.secondsPassed += 1
    }

    reset() {
        this.secondsPassed = 0
    }
}

const myTimer = new Timer()

// Build a "user interface" that uses the observable state.
const TimerView = observer(({ timer }) => (
    <button onClick={() => timer.reset()}>Seconds passed: {timer.secondsPassed}</button>
))

ReactDOM.render(<TimerView timer={myTimer} />, document.body)

// Update the 'Seconds passed: X' text every second.
setInterval(() => {
    myTimer.increase()
}, 1000)

 

TimerView라는 컴포넌트는  자동으로 timer.secondsPassed라는 Observerble한 변수 값에 따라 렌더링 되는 것을 감지한다.

 

기존의 state변경이나 props의 변경이 일어나 리렌더링이 일어나는 상황을 생각해 보면

TimerView 내부에는 setState와 같이 상태값을 직접 바꿔주는 코드가 없는 것을 알 수 있다.

 

그럼에도 불구하고 Timer 클래스의 생성자에서 makeAutoObservable(this)로 해당 클래스가 옵저버블한(관찰 가능한)상태가 되어서

observer라는 함수로 래핑된 TimerView는 Timer를 관찰할 수 있는 상태가 된 것이다.

 

짧게 정리하면 TimerView는 관찰자이고, Timer는 관찰 대상이 된다.

 

그렇다면 관찰 대상의 상태를 변화시키는 코드는 어디에 있을까?

 

button컴포넌트의 onClick을 보면 timer.reset() 이 있고 (timer의 secondsPassed를 0으로 만드는 함수)

마지막 줄의 setInterval함수를 보면 1초에 한번씩 myTimer.increase()를 호출하고 있다 (myTimer의 secondsPassed를 1 증가시키는 함수)

 

이 두 함수가 상태값에 변화를 일으키는 액션(Action)이 된다.

 

이 액션이 발생하면 관찰자인 TimerView는 리렌더링이라는 사이드 이펙트를 발생시킨다.

 

이 과정을 도표로 나타내면 다음과 같다.

 

1. 이벤트가 발생되어 액션이 호출된다.

2. 액션은 옵저버블한 상태를 업데이트 시킨다.

3. 상태의 변화를 알린다.

4. 이전 상태와 현재 상태를 비교한다 (같을 경우 여기서 멈춘다)

5. 다를 경우 리렌더같은 사이드 이펙트를 발생시킨다

6. 이렇게 발생한 사이드 이펙트는 다시 액션을 호출시킬 수도 있다.

 

이것을 위의 TimerView에 적용시켜 보면

 

1. setInterval에 의해 myTimer.increase()라는 액션이 호출된다.

2. myTimer.increase()는 myTimer.secondsPassed를 업데이트 시킨다

3. 상태의 변화를 알린다

4. 이전 상태와 현재 상태를 비교한다 (초기값 기준 0과 1을 비교, 다르므로 계속 진행한다)

5. 다르므로 TimerView를 리렌더한다

 

와 같이 흘러간다.

 

timer.reset()역시 같은 레파토리로 진행한다.

 

음... 왜 쓸까?

사실 위의 코드만 봐선 쓸데없이 왜 이짓을 하는지 모르겠다고 생각할 수도 있다.

 


const TimerView = ({}) => {
  const [secondsPassed, setSecondsPassed] = useState(0);
  useEffect(() => {
    setTimeout(() => {
      setSecondsPassed(secondsPassed + 1);
    }, 1000);
  }, [secondsPassed]);

  return <button onClick={() => setSecondsPassed(0)}>{secondsPassed}</button>;
};

가령 위의 코드처럼 작성하는게 더 효율적이라고 생각할 수도 있고, 

실제로 단순히 하나의 컴포넌트에서만 사용하는 상태를 저렇게 분리시키는 것은 비효율적이다.

 

mobx나 redux같은 상태 관리 라이브러리는 서로 다른 컴포넌트 간,

특히 props로 전달해주기 어려운 구조의 컴포넌트 간에 상태를 공유하기 위해 사용한다는 점을 명심하자.

 

이 코드에선 상태와 액션과 컴포넌트가 하나로 뭉쳐져 있지만

import React from "react"
import ReactDOM from "react-dom"
import { makeAutoObservable } from "mobx"
import { observer } from "mobx-react"

// Model the application state.
class Timer {
    secondsPassed = 0

    constructor() {
        makeAutoObservable(this)
    }

    increase() {
        this.secondsPassed += 1
    }

    reset() {
        this.secondsPassed = 0
    }
}

const myTimer = new Timer()

// Build a "user interface" that uses the observable state.
const TimerView = observer(({ timer }) => (
    <button onClick={() => timer.reset()}>Seconds passed: {timer.secondsPassed}</button>
))

ReactDOM.render(<TimerView timer={myTimer} />, document.body)

// Update the 'Seconds passed: X' text every second.
setInterval(() => {
    myTimer.increase()
}, 1000)

여기에서는 [상태와 액션]/[컴포넌트]로 나누어져 있으므로

다른 컴포넌트와 [상태와 액션]을 공유해서 사용하기 편리하다.

 

그래서 쓰는 것이다.

 

설치

리액트를 위해 출시된 mobx의 두 가지 타입이 있다.

 

mobx-react와 mobx-react-lite인데,

mobx-react-lite는 함수형 컴포넌트만을 지원하고 mobx-react는 모두 지원한다.

 

클래스형 컴포넌트를 아직 사용중이라면 mobx-react를 설치하고

그게 아니라면 mobx-react-lite를 설치한다.

yarn add mobx mobx-react

 

데코레이터는?

 

mobx에 대해 다른 사이트를 뒤져보았다면 

mobx를 더 편하게 사용할 수 있도록 해주는 데코레이터(어노테이션)을 본 적이 있을 것이다.

 

If you have used MobX before, or if you followed online tutorials, you probably saw MobX with decorators like @observable. In MobX 6, we have chosen to move away from decorators by default, for maximum compatibility with standard JavaScript. They can still be used if you enable them though.

그러나 공식 문서에 쓰여 있듯이

함수형 컴포넌트가 대세가 됨에 따라(뇌피셜) 굳이 데코레이터가 필요 없게 되었고

MobX6부턴 기본적으로 데코레이터를 지원하지 않는다고 한다.

 

굳이 꼭 써야겠다면 따로 찾아보는 걸 추천한다만,

내가 써보니까 없어도 함수형 컴포넌트를 사용하는데 전혀 지장 없다.

(클래스형 컴포넌트는 안써서 잘 모른다)

 

활용 방안

로그인 정보(엑세스 토큰 값)과 같은 정보는 특정 페이지를 벗어난다고 해서 초기화되면 불편한 것이므로

해당 정보를 mobx에 옵저버블한 상태로 만들고 관찰하는 방식으로 사용할 수 있다.

 

그 외에도 활용방안은 다양하겠으나 

일단은 여기까지만 작성하는 걸로...

 

다음 포스트에서는 실제 용례를 알아본다.



Comments