MobX observable state 생성

Kyungs·2022년 4월 29일
1

JavaScript

목록 보기
9/11
post-thumbnail
post-custom-banner

진행중인 프로젝트에서 사용하는 MobX의 observable 동작 원리에 대해 간단하게나마 기록해두고자 해서 쓰게 된 글이다. (MobX v6 기준)

MobX 상태 저장소를 생성하는 방법은 아래와 같이 세 가지가 있다.

  • makeObservable
  • makeAutoObservable
  • observable

위 세 가지는 모두 인자를 최소 한 개, 최대 세 개 까지 받으며, 반드시 입력해야하는 한 개의 인자는 저장소 객체이다. MobX v4, v5에서는 decorate 또는 decorator 의 사용이 필수적이었으나, 자바스크립트의 decorator가 더 이상 ES 표준이 아니게 되면서 MobX v6에서부터는 annotations 사용이 권장되고있다.

이 글에서는 Class로 주로 활용하는 makeObservable, makeAutoObservable에 대해 정리해보려한다.

decorators, annotations

MobX가 관리하는 객체 내에 decorator를 사용하여 MobX 함수를 적용할 수 있다. 대표적인 decorator로는 observable, action, computed 등이 있다.

@observable: 관찰할 상태 값
@action: 관찰중인 상태를 변경하는 작업
@computed: 이미 존재하는 상태를 이용하여 실행하는 작업

makeObservable 함수는 annotations를 두 번째 인자로 받을 수 있다. 이러한 경우 decorator 사용이 강제되지 않으며, 각 사용 예시는 아래 makeObservable에 있다.

makeObservable

makeObservable 함수는 일반적으로 class 안 constructor 내부에서 사용하며, class는 객체 자신을 가리키는 this를 첫 번째 인자로 넘긴다. decorator를 사용하는 경우 두 번째 인자는 비워둬도 되며, decorator를 사용하지 않는 경우 annotations를 두 번째 인자로 반드시 넘겨야 한다.

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

// decorators를 사용하는 경우
export class StoreWithDecorators {
  @observable numbers = [];
  
  constructor() {
    makeObservable(this);
  }
  
  @action
  addNumber() {
    this.numbers.push(this.numbers.length + 1);
  }
  
  @computed
  get maxNumber() {
    return Math.max.apply(null, this.numbers);
  }
}

// annotations를 사용하는 경우
export class StoreWithAnnotations {
  numbers = [];
  
  constructor() {
    makeObservable(this, {
      numbers: observable,
      addNumber: action,
      maxNumber: computed
    })
  }
  
  addNumber() {
    this.numbers.push(this.numbers.length + 1);
  }
  
  get maxNumber() {
    return Math.max.apply(null, this.numbers);
  }
}

MobX 라이브러리 내 makeObservable 함수를 보게 되면 아래와 같이 세 가지 인자를 받는다.

options의 경우는 autobind, proxy 등의 부가 기능에 대한 선택 여부 등을 받는다.
이후, annotations 입력 여부 확인하여 입력하지 않은 경우 collectStoredAnnotations 함수를 통해 decorators를 바인딩해준다.

collectStoredAnnotations 함수 내부에서 annotations, decorators가 모두 없음을 확인하게 되면 아래와 같이 에러를 발생시킨다.

makeAutoObservable

makeAutoObservable 함수는 개발자가 입력하지 않아도 자동으로 annotations를 유추한다. 따라서 아래와 같이 코드가 간결해질 수 있다. 다만, 개발자가 annotations를 제공하는 것이 아니기 때문에 superclass에서는 사용이 불가(subbest class에서만 사용이 가능)하며, 객체당 한 번만 호출할 수 있도록 제한되어있다.

  • subbest class에서만 사용이 가능한 이유는 makeAutoObservable을 사용한 클래스가 다른 자식 클래스에 이를 상속한 경우, MobX는 makeAutoObservable이 자식 클래스에서 정의된 것인지, 부모에게서 상속받은 것인지 알 수 없기 때문이다. 같은 값이나 메소드에 중복해서 annotations를 지정해야하는 비효율성도 생길 수 있다. makeObservable에서는 개발자가 annotations를 모두 정의해주기 때문에 위와 같은 이슈가 발생하지 않는다.
import { makeAutoObservable } from 'mobx';

// annotations를 사용하는 경우
export class StoreWithAnnotations {
  numbers = [];
  
  constructor() {
    makeAutoObservable(this);
  }
  
  addNumber() {
    this.numbers.push(this.numbers.length + 1);
  }
  
  get maxNumber() {
    return Math.max.apply(null, this.numbers);
  }
}

MobX 라이브러리 안 makeAutoObservable 함수는 두 번째 인자로 annotations 대신 overrides를 받고 있으며, 유추되는 annotation이 아닌 특정 annotation을 지정하려는 경우에 사용할 수 있다.

함수 상단에서는 superclass가 있는 객체가 아닌지, 이미 observable로 생성되지는 않았는지 검사한다.

make_ 메소드

makeObservable과 makeAutoObservable 함수는 (서로 조금 다른 방식이긴 하지만) 마지막에 observableObject 클래스의 make_ 함수를 호출한다. 확실하진 않지만... make_ 메소드에서 각 annotation을 target 객체에 담아준 후 반환하는 것으로 보인다.

참고 자료

[MobX 공식 문서] Creating observable state
[MobX 공식 문서] Enabling decorators
[MobX 공식 문서] Subclassing
Why makeAutoObservable does not support subclassing #2850

post-custom-banner

0개의 댓글