본문 바로가기

FrontEnd

[번역] Mobx로 배우는 반응형 프로그래밍(reactive programming)의 원리

반응형

Mobx를 통해 반응형 프로그래밍(reactive programming)의 원리에 대해 알아봅니다.

MobX는 반응형 프로그래밍을 투명하게 적용하여 상태 관리를 간단하고 확장할 수 있는 실전에서 증명된 라이브러리입니다.
mobx 핵심 개념을 이해하려면 먼저 함수형 반응형 프로그래밍에 대해 알아보아야 합니다.

Functional Reactive Programming

함수형 프로그래밍은 데이터 구조를 변경하지 않고
부작용이 없는 순수 함수를 사용하여 응용 프로그램을 개발하는 패러다임입니다.
순수 함수는 주어진 동일한 입력에 대해 항상 동일한 출력을 반환하고
함수 범위 밖의 변수에 대해서는 작동하지 않습니다.
// Pure function
function result(a, b){
  return a + b
}
result(3,5); 
// Impure function
const c = 6;
function result(a,b){
   return a + b + c;
}
result(3,5)
함수형 프로그래밍은 예측 가능성을 제공하고 오류에 덜 취약한 이상적인 프로그래밍 솔루션입니다.
그러나 변경 가능한 데이터 구조와 부수 작용 없이 실제 프로그램을 개발하는 것은 어려울 것입니다.
작업이 발생하는 범위 외부에 영향을 미치는 경우 이를 부수 작용이라고 합니다.
일반적인 부수 작용은 렌더링, 네트워크 요청, 파일 작업, CLI/로그 출력 등이 될 수 있습니다.
함수형 반응형 프로그래밍을 사용하여 함수형 프로그래밍 원칙을 어느 정도 유지하면서 부수 작용을 효율적으로 제어할 수 있습니다.
반응형 프로그래밍은 부작용을 제어하는 ​​것입니다.
함수형 반응형 프로그래밍의 본질은
값의 선언 시점에 값의 동적 동작을 완전히 지정하는 것입니다.
(by 이벤트 구독)
스프레드시트 응용 프로그램을 고려해 보겠습니다.
스프레드시트에서 변수는 셀입니다. 스프레드시트의 셀이 변경되면 해당 셀을 참조하는 모든 셀도 변경됩니다.

Passive Programming Vs Reactive Programming

수동적 프로그래밍에서 두 구성 요소 간의 관계는 일반적으로 한 구성 요소가 다른 구성 요소를 제어한다는 것입니다.

Passive Programming. Ref: http://dontpanic.42.nl/2016/07/reactive-programming.html

Car.prototype.turnKey = function() {   
  this.engine.fireUp(); 
}
반응형 프로그래밍에서 관계는 반대입니다.
구성 요소 스스로 해당 값과 동작을 제어할 책임이 있습니다.

Reactive Programming, Ref: http://dontpanic.42.nl/2016/07/reactive-programming.html

Engine.listenToKey = function(car) {   
  car.onKeyTurned(() => {     
    this.fireUp();   
  }); 
}

The Observer Pattern

이 패턴엔, 구독한 리스너 목록을 유지하는 Producer가 있습니다.
생산자의 상태가 변경될 때마다 Listener에게 알림이 전송됩니다.

function Producer(){
 this.listeners = [];
}
Producer.prototype.add = function(listener){
 this.listeners.push(listener);
}
Producer.prototype.remove = function(listener){
 var index = this.listeners.indexOf(listener);
 this.listeners.splice(index,1);
}
Producer.prototype.notify = function(message){
 this.listeners.forEach(function(listener){
  listener.update(message);
})
}
var listener1 = {
 update: function(message){
  console.log('message')
 }
}
var notifier = new Producer();
notifier.add(listener1);
notifer.notify("Hello World");

Observable

옵저버블은 값을 순서대로 방출합니다.
즉, 소비자는 값을 요청하는 대신 값을 구독하며,
옵저버블은 값이 사용 가능해지면 컨슈머에게 값을 순서대로 푸시합니다.
(값을 순서대로 방출하여 리스너에게 푸시합니다.)
 
Observable은 시간이 지남에 따라 아이템을 사용할 수 있게 되는 시퀀스입니다.
옵저버가 옵저버블을 구독하면, 필요할 때 값을 요청하는게 아니라,
값이 사용할 수 있을 때 (시퀀스 형태의) 값을 받습니다.
다음은 RxJS 라이브러리를 사용한 Observable 예제입니다.
var source = Rx.Observable.fromEvent(document.body, 'mousemove');
source.subscribe(val => console.log(val),
 e => console.log(e),
 () => console.log('completed')
);
source.subscribe(val => {
  console.log(`x: ${val.offsetX} & y: ${val.offsetY}`)
  },
  e => console.log(e),
 () => console.log('completed')
);​

 

우리는 일련의 mousemove 이벤트의 옵저버블을 생성할 수 있으며
해당 옵저버의 변경 사항을 수신하는 옵저버는 이벤트에 반응할 수 있습니다.

Mobx

애플리케이션 상태에서 파생될 수 있는 모든 것은 파생되어야 합니다. 자동으로.

MobX Flow Diagram
MobX Flow Ref: https://www.packtpub.com/sites/default/files/downloads/MobXQuickStartGuide_ColorImages.pdf

const { observable, autorun } = require("mobx");
const todoStore = observable({
/* some observable state */
  todos: [],
  /* a derived value */
  get computedCount() {
    return this.todos.filter((todo) => todo.completed).length;
  },
});
/* a function that observes the state */
autorun(function () {
  console.log(
    `Completed ${todoStore.computedCount} of  ${todoStore.todos.length}`);
});
/* ..and some actions that modify the state */
todoStore.todos[0] = {
  title: "Take walk",
  completed: false
};
// synchronously prints 'Completed 0 of 1 items'
todoStore.todos[0].completed = true;
// synchronously prints 'Completed 1 of 1 items'

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

observable 메서드를 사용하여 상태를 관찰 가능한 변수로 바꿀 수 있습니다.
중첩 수준이 있을 수 있는 상태 개체에 대한 프록시 개체를 만듭니다.
읽기 및 쓰기 모두에서 프록시를 통해 객체에 액세스를 추적합니다.
관찰 가능한 todos는 다음과 같습니다.
plain todos array

todos observable with proxy object

참고:

Proxy 개체는 다른 개체를 래핑하고 속성 및 기타 읽기/쓰기와 같은 작업을 가로채고
선택적으로 자체적으로 처리하거나 개체가 처리할 수 있도록 투명하게 허용합니다.
이 기사에서 프록시 객체 작성의 실용적인 사용 사례를 배울 수 있습니다.
https://czaplinski.io/blog/make-your-own-mobx/
 

Build your own MobX-like state management library in 40 lines of code

Build your own MobX-like state management library in 40 lines of code.

czaplinski.io

2. 파생(derivation) 처리

추가 상호 작용 없이 상태에서 파생될 수 있는 모든 것이 파생입니다.

위의 예에서 계산된 값은 영향을 주는 값이 변경되면 상태에서 자동으로 파생됩니다.

3. action을 이용해 상태 변경

액션은 상태를 변경하는 코드 조각입니다.
(리덕스 액션이랑 다르게 그냥 수정하면 됨)

4. 부수 효과 다루기

예제에서 스토어 업데이트의 부수 작용은 autorun 메서드 내에서 수행됩니다.

autorun 메서드는 스토어 값이 업데이트될 때마다 자동으로 실행됩니다.

Redux와의 차이점

Redux는
  • 불변 저장소를 구현합니다.
  • redux의 액션은 저장소를 변경할 책임이 있습니다.
  • 리듀서는 모든 상태가 변경될 때마다 항상 새로운 상태를 반환하는 순수 함수입니다.
반대로 mobx는
  • 데이터 변경 전파를 이용해 변경 가능한 저장소를 구현합니다.
  • 액션은 스토어를 변경할 책임이 있습니다.
  • 그러나 저장소는 작업 없이 직접 변형될 수도 있습니다.
  • MobX는 관찰 가능한 변경 가능한 데이터 구조를 따라 상태 생명 주기를 구현하고 변경 사항을 추적합니다.

참고

https://levelup.gitconnected.com/the-principles-behind-mobx-and-reactive-programming-4c43e73742e7#:~:text=The%20typical%20side%20effects%20can,programming%20principles%20to%20some%20extent.

 

The Principles Behind MobX and Reactive Programming

MobX is a battle tested library that makes state management simple and scalable by transparently applying functional reactive programming.

levelup.gitconnected.com

 

반응형