본문 바로가기
Develop/MobX

MobX에서 비동기를 시도해보자

by 지오는 짱짱걸 입니다 2023. 5. 11.
반응형

안녕하세요 오늘은 MobX에서 비동기 처리하는 방법에 대해 알아보도록 할게요. 그럼 시작해볼까요?

Q. MobX는 action 함수 내부에서 promise 작업을 한 이후 다시 action 함수를 호출해야 정상적으로 observable 값을 바꿀 수 있으나 runInAction을 사용하면, 함수 내부에서 observable값을 바꿀 수 있습니다. 라는데, 너무 말이 장황합니다. 이 문장을 한 번 다시 알아볼까요?

MobX는 자바스크립트 상태 관리 라이브러리에요. 애플리케이션 상태를 간편하게 관리할 수 있게 도와주는 것이에요. 'runInAction'은 MobX에서 제공하는 유틸리티 함수로, action 내부에서 비동기 작업을 수행한 후에도 observable값을 바꿀 수 있게 해줘요.

먼저 async/await 에서 'runInAction'을 사용하지 않는 기본적인 MobX 사용 예제를 살펴봐볼까요?

import { observable, action } from 'mobx';

class Store {
  @observable value = 0;

  @action
  async increase() {
    const newValue = await someAsyncFunction();
    this.value = newValue;  // 이 부분에서 에러가 발생할 수 있습니다.
  }
}

async function someAsyncFunction() {
  // ...some code here...
  return new Promise(resolve => setTimeout(() => resolve(1), 1000));
}

위 코드에서 'increase' action은 비동기 함수에요. 이 함수는 'someAsyncFunction'이라는 비동기 함수를 호출하고, 그 결과를 'this.value'에 할당해요. 그러나 MobX는 action 함수 내에서 비동기 작업을 통해 얻은 결과를 직접 observable에 할당하는 것을 허용하지 않아요. 왜냐하면 MobX의 동작 원리는 동기적인 데이터 흐름에 의존하기 때문이에요. 즉, observable이 변경되면 MobX는 자동으로 관련된 모든 reaction(observable state를 변경하면 그에 따른 computed가 계산돼요. 만약에 이 값들을 사용하고 있는 곳이 있다면 거기도 자동으로 변경되어야하는데, 그걸 Reaction이라고 불러요)을 업데이트해요. 그러나 이 모든 과정은 동기적으로 이루어져야해요. 이러한 동기성은 MobX가 언제 어떻게 상태 변경이 발생했는지 명확하게 파악하고, 반응을 효과적으로 관리할 수 있게 해줘요.
그러나 비동기 작업은 이 동기성을 깨뜨려요. 비동기 작업이 완료될 때까지 MobX는 그 결과를 알 수 없고, 이로 인해 상태 업데이트가 언제 이루어질지 예측할 수 없게 돼요. 이러한 이유로 MobX는 비동기 작업을 통해 얻은 결과를 직접 observable에 할당하는 것을 허용하지 않아요. 따라서 위 코드는 에러를 발생시킬 수 있어요.

이럴 때 'runInAction'을 사용하면 문제를 해결할 수 있어요.

import { observable, runInAction } from 'mobx';

class Store {
  @observable value = 0;

  async increase() {
    const newValue = await someAsyncFunction();
    runInAction(() => {
      this.value = newValue;  // runInAction 내부에서 observable 값을 변경합니다.
    });
  }
}

async function someAsyncFunction() {
  // ...some code here...
  return new Promise(resolve => setTimeout(() => resolve(1), 1000));
}

위 코드에서는 'increase' 함수 내에서 'runInAction'을 사용했어요. 'runInAction'은 즉시 호출되는 임시 action을 생성할 수 있어요. 그러므로 'runInAction' 함수를 인자로 받아, 그 함수 내에서 observable 값을 안전하게 변경할 수 있게 해줘요. 이렇게 하면 비동기 작업 후에도 observable 값을 정상적으로 변경할 수 있게돼요.

그러면 다음으로 Promise를 사용한 예제를 살펴볼까요?

import { makeAutoObservable, runInAction } from "mobx";

class UserStore {
  users = [];

  constructor() {
    makeAutoObservable(this);
  }

  fetchUsers() {
    fetch('https://api.example.com/users')
      .then(response => response.json())
      .then(data => {
        runInAction(() => {
          this.users = data;
        });
      });
  }
}

위 코드에서 'fetchUsers' 메서드는 비동기 작업을 수행하고, 그 결과를 처리하는 부분에서 'runInAction'을 사용했어요. 'runInAction'은 상태 변경을 action 안에서 수행하도록 보장하므로, 비동기 작업의 결과를 처리할 때 상태를 안전하게 변경할 수 있어요. 이렇게 하면 MobX는 상태 변경을 올바르게 추적할 수 있어요. 여기서 중요한 점은 상태를 변경하는 부분에서 'runInAction'을 사용했다는 점이에요.

이제 'runInAction'대신 'action'을 사용하는 방법에 대해서 알아볼까요?

import { makeAutoObservable } from "mobx"

class Store {
    githubProjects = []
    state = "pending" // "pending", "done" or "error"

    constructor() {
        makeAutoObservable(this)
    }

    fetchProjects() {
        this.githubProjects = []
        this.state = "pending"
        fetchGithubProjectsSomehow().then(this.projectsFetchSuccess, this.projectsFetchFailure)
    }

    projectsFetchSuccess = projects => {
        const filteredProjects = somePreprocessing(projects)
        this.githubProjects = filteredProjects
        this.state = "done"
    }

    projectsFetchFailure = error => {
        this.state = "error"
    }
}

위 코드는 'runInAction' 대신 ''action'을 사용했어요. 참고하자면 Promise 핸들러가 클래스 필드라면 makeAutoObservable에 의해 'action'이 자동으로 래핑 돼요. 그래서 위 코드에서 상태를 변경하는 함수인 projectsFetchSuccess 함수와 projectsFetchFailure 함수는 'action'함수가 되는 것이에요.

'runInAction' 대신 'action' 함수만을 사용해서 비동기를 처리해 보았는데요. 사실 이렇게하면 콜백 전체를 위한 action을 만들기 위해 여러 'action' 함수를 많이 만들어야해요. 그래서 실 사용에서는 상태변화를 일으키는 콜백만 따로 action을 적용할 수 있기 때문에 코드를 복잡하지 않게 하는 'runInAction' 함수를 많이 사용해요.

이렇게 MobX에서 비동기처리에 대해 알아보았어요. 이제 MobX에 한층 더 친숙해진 것 같아요. 그럼 다음에 만나요! MobX를 잘 사용하시길 바래요.

참고

https://ko.mobx.js.org/actions.html#examples

반응형