ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • MobX @action @computed 간단 정리
    간단정리 2020. 7. 3. 18:43
    이미, 개념으로는 너무 좋은 글들이 많아 가볍게만 작성 하고, 저번 프로젝트 때 나의 mobx 사용이 왜 잘 못 됬는지. 실수한 방법만 기술하자!
    좋은 정리 글 (링크)

    MobX란?

     MobX는 상태 관리 라이브러리다. React, React-Native 등에서 사용 되며, 가장 많이 비교 되는 것으로는 Redux가 있다. MobX와 Redux의 가장 큰 차이점은 러닝커브라고 생각한다. MobX는 Observable 데코레이터만 잘 사용하면, 쉽게 상태를 변경할 수 있다. 그에 반하여 Redux는 여러 연관 된 라이브러리를 사용해야하고, 러닝커브가 많이 높다. 처음엔 '어려운 것이 좋은 것'이라는 생각이 있었는데. 아직 MobX에 부족한 점을 느끼지 못하여, Redux를 사용해보고 있지 않았다.

     

     최근에 프로젝트를 진행하면서, 간단하고 편한 MobX를 복잡하고 불편하게 쓰고 있다. 물론, MobX가 직접적으로 문제를 일으키지 않았지만 간단하고 편하게 쓸 마음이 없다면 Redux를 사용 해야되나 싶었다. 공동 작업자들이 있다보니, observable 변수는 private 하게, Component에서는 Store를 통해 observable를 직접 호출 하지 않고 public get 함수를 이용하여 사용하도록 했다. 그러다 보니 MobX가 지향했던 '편리함'과 멀어지고 있음을 느꼈다. (근데 사실 아직 잘 모르겠다..ㅎㅎ Redux를 알아야 이럴 때 Redux 쓰는건가 깨달을 수 있을텐데.)

     

     

    MobX 동작 방식

     

    내가 공유하고 싶은 것

     

    (1) @observable의 변화와 Render

     observable 변수가 수정 되면, MobX에 이벤트가 등록 된다 그렇게 차곡차곡 Stack에 쌓이고 하나하나 실행하며, 컴포넌트들을 Render 하는 방식이다. 놀랍게도 한 번의 이벤트 발생에 observable 변수 10개가 변화하면 10번의 랜더 이벤트가 call Stack에 추가 될 것이다.

    이전에 페이지 한 번 로딩에 최상단 컴포넌트 render()가 50번 호출 된 적이 있었는데. MobX를 모르고 사용한 탓이다.

    물론, DOM의 변경 사항이 없다면 VirtualDOM이 이를 지켜주지만 혹 Render()안에서 연산이 들어간다면 이 연산들은 실행 될 것 이고, 어찌 되었든 상태관리가 없는 것 보다는 심각한 기능 문제를 발생 시킬 수 있다. (기혁 DOM 정리 글)

     

    언젠가 한번 일어났었던, 에러

    요약: MobX observable 변수가 변경 하면 이벤트 실행 목록에 하나씩 쌓이고, Render를 기다리는 구나!

     


    (2) @action & @action.bound

     @action은 (1)번과 같은 문제를 해결 해준다.

      @observable private a = 0;
      @observable private b = 0;
      @observable private c = 0;
      @observable private d = 0;
      @observable private e = 0;
      @observable private f = 0;
    
      // @action
      set() {
        this.a = 1;
        this.b = 2;
        this.c = 3;
        this.d = 4;
        this.e = 5;
        this.f = 6;
      }

    위 예제에서 6개의 이벤트가 생성 된다. 하지만, @action을 사용하면, 이를 하나의 이벤트로 만들 수 있다.

    바인딩이 필요한 경우에는 @action.bound를 사용해야한다!

    이유는 아래의 예제를 통하여 차이를 살펴보자.

     

    #예제 1. 일반 @action 사용

      @observable private browserWidth = 0;
    
      constructor(props: HomePageProps) {
        super(props);
    
        window.addEventListener("resize", this.setBrowserWidth);
      }
    
      @action
      setBrowserWidth() {
        this.browserWidth = window.innerWidth;
      }

     

    결과 : 바인딩 되지 않아, 값 변하지 않음.

     

    #예제 2. 예제 1번의 `setBrowserWidth` Arrow Function으로 변경

      @action
      setBrowserWidth = () => {
        this.browserWidth = window.innerWidth;
      };

     

    결과 : Arrow Function이 값을 바인딩 해주어 값이 잘 변함. 하지만, @action.bound가 존재 하는 것으로 보아 해당 예제는 선호하지 않는 것 같다. (검색을 해보면, 해당 케이스는 볼 수 없다.) 개인적으로 아직 필요성?을 모르겠어서 Arrow Function을 잘 활용하지는 않는다.

     

     

    #예제3. 공식문서 사용 방법

      @action.bound
      setBrowserWidth() {
        this.browserWidth = window.innerWidth;
      }
    

     

    결과 : 정상적으로 바인딩이 되고, @action 처리도 진행

     


    (3) @computed

     이전에는 사실 사용조차 하지 않았다. 하지만, 이번 프로젝트에서는 @observable 변수의 직접 접근을 금지 했다. 이유는 혹시라도 실수로 인하여 값이 변경 될 것을 방지하기 위함 인데.. (사실 필요 없다.. 근데 그렇다면 TypeScript도 필요 없겠지 생각하며, 모든 상태를 get set으로 구분했다.) 이것을 구현하면서 getter를 구현 하게 되었고, 'getter를 observable로 만들기 위해서는 어떻게 해야할까?'를 찾다가 사용하게 되었다.

      @computed get BrowserWidth() {
        return this.browserWidth;
      }
    

     

    하지만, 단순한 Set, Get 구현 외에 computed는 `@observable 변수들 끼리의 연산` 에서 위 @action과 같이 하나의 함수 안에서 변수마다 발생하는 이벤트를 막아 줄수 있다고 한다. 그리고 computed 무조건 get 함수로 선언해야한다.

     

    나와 같은 @observable 변수에 직접 접근을 막기 위해 활용해도 좋을 것 같고, 공식 문서가 가이드 해주는 것 처럼 @observable 들의 연산이 필요할 때. 꼭 쓰자.

     

     

    사실 @action, @computed 또, React PureComponent 모두 사용하지 않아도 우리 눈에는 문제 없이 돌아간다. 하지만, 프로젝트가 커지다보면 엄청 큰 성능 차이로 돌아온다. 한 번 이해해 놓으면 설계단 부터 활용할 수 있어 좋을 것 같다.

    추후, 브라우저 동작 과정을 정확하게 파악 한 후 MobX의 이벤트 동작 과정도 내 눈으로 직접 보아야겠다.

     

     

     

     

     

     

     

     

     

     

    댓글

Developer RyuK