MobX

MobX

  • API 참고서
  • 영어 문서
  • 후원자
  • GitHub

›Introduction

Introduction

  • MobX에 대하여
  • MobX 문서에 대하여
  • 설치 방법
  • MobX의 요지

MobX core

  • Observable state
  • Actions
  • Computeds
  • Reactions {🚀}

MobX and React

  • React 통합
  • React 최적화 {🚀}

Tips & Tricks

  • 데이터 스토어 정의
  • 반응성 이해하기
  • 서브클래싱
  • 반응 분석하기 {🚀}
  • 인수가 필요한 computed {🚀}
  • MobX-utils {🚀}
  • Custom observables {🚀}
  • Lazy observables {🚀}
  • Collection utilities {🚀}
  • Intercept & Observe {🚀}

Fine-tuning

  • 환경설정 {🚀}
  • 데코레이터 사용하기 {🚀}
  • MobX 4 또는 5에서 마이그레이션 하기 {🚀}
Edit

MobX의 요지

개념

MobX는 어플리케이션에서 다음 세 가지 개념을 구분합니다.

  1. 상태(state)
  2. 동작(action)
  3. 파생(derivation)

세 가지 개념에 대해 아래의 내용을 자세히 살펴보거나, 10분만에 알아보는 MobX와 React에서 단계별로 심층적으로 살펴보고 간단한 Todo list 앱을 만들어보세요.

1. 상태(state)를 정의하고 관찰 가능하게(observable) 만들기

State는 애플리케이션을 구동하는 데이터입니다. 일반적으로 todo 아이템과 같은 도메인 별 state가 있고 현재 선택된 요소와 같은 view state가 있습니다. State는 값을 보유하고 있는 스프레드시트 셀과 같습니다.

원하는 데이터 구조(일반 객체, 배열, 클래스, 순환 데이터 구조 또는 참조)에 state를 저장합니다. 어떤 데이터 구조를 사용하든 MobX의 작동에는 문제가 되지 않습니다. 단지 시간이 지남에 따라 변경하려는 모든 속성을 MobX가 추적할 수 있도록 observable로 표시하면 됩니다.

여기 간단한 예제가 있습니다.

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

class Todo {
    id = Math.random()
    title = ""
    finished = false

    constructor(title) {
        makeObservable(this, {
            title: observable,
            finished: observable,
            toggle: action
        })
        this.title = title
    }

    toggle() {
        this.finished = !this.finished
    }
}

Hint: makeAutoObservable을 사용하면 위 예제를 더 단순화 할 수 있지만, 이처럼 명시적으로 표시함으로써 다른 개념을 더 자세히 보여드리고 있습니다.

observable을 사용하는 것은 객체의 속성을 스프레드시트 셀로 바꾸는 것과 같습니다. 스프레드시트와 다른 점은 들어갈 수 있는 값이 원시 값(primitive values)뿐만 아니라 참조(reference), 객체 및 배열일 수도 있습니다.

action으로 표시된 toggle은 어떨까요?

2. action을 이용한 state 업데이트

action은 사용자 이벤트, 백엔드 데이터 푸시, 예약된 이벤트 등과 같이 state를 변경하는 코드 조각입니다. action은 스프레드시트 셀에 새 값을 입력하는 사용자와 같습니다.

위의 Todo 예제에서 finished 값을 바꾸는 toggle을 확인할 수 있으며, finished는 observable로 표시되어 있습니다. 위의 예시처럼 observable을 변경하는 코드는 action으로 표시하는 것이 좋습니다. 이렇게 하면 MobX가 트랜잭션을 자동으로 적용하여 성능을 쉽게 최적화할 수 있습니다.

action을 사용하면 코드를 구조화하는 데 도움을 줄 수 있으며 의도하지 않은 state 변경도 방지할 수 있습니다. state를 변경하는 메서드는 MobX 용어로 action이라고 합니다. 현재의 state를 기반으로 새로운 정보를 계산하는 view와는 대조적입니다. 모든 메서드는 이 두 가지 목표 중 하나에 기여해야 합니다.

3. 상태(state) 변화에 자동으로 응답하는 파생(derivation) 만들기

state에서 더 이상의 상호작용 없이 파생될 수 있는 모든 것이 derivation 입니다. derivation은 다음과 같이 다양한 형태로 존재할 수 있습니다.

  • 사용자 인터페이스
  • 남은 todos의 수와 같은 파생 데이터
  • 백엔드 통합 (예시: 서버에 변경사항 전송)

MobX는 다음과 같이 두 종류로 derivation을 구분합니다.

  • computed 값 : 현재의 observable state 에서 순수 함수를 사용하여 파생될 수 있는 값
  • reaction : state가 변경될 때 자동으로 발생해야 하는 부수효과 (명령형 프로그래밍과 반응형 프로그래밍 사이를 연결해주는 다리 역할)

MobX를 시작할 때 reaction을 과도하게 사용하는 경향이 있습니다. 가장 좋은 방식은 현재 state를 기반으로 값을 생성하려는 경우에 항상 computed를 사용하는 것입니다.

3.1. computed를 사용하여 파생된 값 모델링하기

computed 값을 생성하려면 JS getter 함수 get을 사용하여 속성을 정의하고 makeObservable을 사용하여 computed로 표시합니다.

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

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

MobX는 todo가 추가되거나 finished 속성 중 하나가 수정될 때 unfinishedTodoCount를 자동으로 업데이트합니다.

이러한 계산은 MS Excel과 같은 스프레드시트 프로그램의 공식과 유사합니다. 자동으로 업데이트되지만 필요할 때(무언가 결과에 영향을 미칠 수 있을 때)만 업데이트됩니다.

3.2. reaction을 사용하여 부수효과(side effect) 모델링하기

사용자가 화면에서 state의 변화나 computed 값의 변화를 볼 수 있으려면 GUI의 일부를 다시 그리는 reaction이 필요합니다.

reaction은 computed 값과 유사하지만, 정보를 생성하는 대신 콘솔 출력, 네트워크 요청, DOM 패치 적용을 위해 React 컴포넌트 트리를 점진적으로 업데이트하는 등의 부수효과를 생성합니다.

간단히 말해, reaction은 반응형 프로그래밍과 명령형 프로그래밍의 세계를 연결합니다.

지금까지 가장 많이 사용되는 reaction 형태는 UI 컴포넌트입니다. action과 reaction 모두 부수효과를 일으킬 수 있습니다. form을 제출할 때 네트워크 요청을 하는 것처럼, 트리거 될 수 있는 명확하고 명시적인 출처가 있는 부수효과는 관련 이벤트 핸들러에서 명시적으로 트리거 되어야 합니다.

3.3. 반응형 리액트 컴포넌트

React를 사용하는 경우 설치 중에 선택한 바인딩 패키지에서 observer 함수를 이용하여 컴포넌트를 감싸서 반응형으로 만들 수 있습니다. 해당 예제에서는 더 가벼운 mobx-react-lite 패키지를 사용합니다.

import * as React from "react"
import { render } from "react-dom"
import { observer } from "mobx-react-lite"

const TodoListView = observer(({ todoList }) => (
    <div>
        <ul>
            {todoList.todos.map(todo => (
                <TodoView todo={todo} key={todo.id} />
            ))}
        </ul>
        Tasks left: {todoList.unfinishedTodoCount}
    </div>
))

const TodoView = observer(({ todo }) => (
    <li>
        <input type="checkbox" checked={todo.finished} onClick={() => todo.toggle()} />
        {todo.title}
    </li>
))

const store = new TodoList([new Todo("Get Coffee"), new Todo("Write simpler code")])
render(<TodoListView todoList={store} />, document.getElementById("root"))

observer는 리액트 컴포넌트를 렌더링하는 데이터의 derivation으로 변환합니다. MobX를 사용하면 smart 컴포넌트나 dumb 컴포넌트의 구분이 없습니다. 모든 컴포넌트는 smart하게 렌더링 되지만, dumb하게 정의됩니다. MobX에서는 필요할 때마다 컴포넌트가 다시 렌더링 되며, 그 이상은 렌더링 되지 않습니다.

따라서 위 예제의 onClick 핸들러는 toggle action을 사용할 때 적절한 TodoView 컴포넌트를 강제로 다시 렌더링하지만, TodoListView 컴포넌트는 완료되지 않은 작업의 수(unfinishedTodoCount)가 변경된 경우에만 다시 렌더링 됩니다. Tasks left줄을 지우거나 별도의 컴포넌트에 넣으면 Todo를 체크할 때 TodoListView 컴포넌트는 더 이상 다시 렌더링 되지 않습니다.

React가 MobX와 어떻게 작동하는지 자세히 알아보려면 React 통합 섹션을 확인해보세요.

3.4. 커스텀 reaction

거의 필요하지는 않지만 autorun, reaction, when 함수를 사용하여 상황에 맞게 만들 수 있습니다. 예를 들어, autorun은 unfinishedTodoCount의 수가 변경될 때마다 로그 메시지를 출력합니다.

// state를 자동으로 관찰하는 함수입니다.
autorun(() => {
    console.log("Tasks left: " + todos.unfinishedTodoCount)
})

unfinishedTodoCount가 변경될 때마다 새 메시지를 출력하는 이유는 무엇일까요? 답은 아래의 원칙에 있습니다.

MobX는 추적된 함수를 실행하는 동안 읽은 기존의 observable 속성에 반응합니다.

MobX가 반응해야 하는 observable 항목을 결정하는 방법에 대해 자세히 알아보려면 반응성 이해하기 섹션을 확인해보세요.

원칙

MobX는 action이 state를 변경하는 단방향 데이터 흐름을 사용하여 영향을 받는 모든 view를 업데이트합니다.

Action, State, View

  1. state가 변경되면 모든 derivation이 자동으로 그리고 원자 단위로 업데이트됩니다. 결과적으로 중간 값을 관찰할 수는 없습니다.

  2. 기본적으로 모든 derivation은 동기식으로 업데이트 됩니다. 이는 action이 state를 변경한 직후에 computed 값을 안전하게 검사할 수 있음을 의미합니다.

  3. computed 값은 느리게 업데이트됩니다. 활발히 사용되지 않는 계산된 값은 부수효과(I/O)에 필요할 때까지 업데이트되지 않습니다. 만약 view가 계산된 값을 사용하지 않으면 가비지가 자동으로 수집됩니다.

  4. 모든 computed 값은 순수해야 하며 state를 바꾸면 안 됩니다.

이러한 배경이 생기게 된 맥락에 대해 더 알고 싶다면 MobX의 기본 원칙을 확인하세요.

직접 시도하기

CodeSandbox에서 위의 예제를 직접 해볼 수 있습니다.

Linting

MobX의 멘탈 모델을 채택하기 어렵다면 매우 엄격하게 구성하고 이러한 패턴을 벗어날 때 런타임에 경고하세요. linting MobX 섹션을 확인해보세요.

← 설치 방법Observable state →
  • 개념
    • 1. 상태(state)를 정의하고 관찰 가능하게(observable) 만들기
    • 2. action을 이용한 state 업데이트
    • 3. 상태(state) 변화에 자동으로 응답하는 파생(derivation) 만들기
  • 원칙
  • 직접 시도하기
  • Linting
MobX
Docs
About MobXThe gist of MobX
Community
GitHub discussions (NEW)Stack Overflow
More
Star