킹의 개발일지
쏙쏙 들어오는 함수형 코딩 45일차 본문
45일차 <반응형 아키텍처>
이전 파트에서 반응형 아키텍처에 대해서 저자는 ' 반응형 아키텍처에서는 하려고 하는것만 처리한다. 그리고 모든 것은 어떤 일이 일어나면 그에 대한 응답으로 처리한다.' 라고 설명 했었다.
이를 지금까지 봐왔던 '장바구니 예제'를 가지고 이해해 보자. 장바구니 예제에서는 전역 상태는 장바구니 하나뿐이다. 때문에 필요한것은 장바구니가 변경될 때 Y(특정 행동) 하는것이다.
우선 장바구니를 몇 가지 동작과 함께 객체로 만들어보자. 다음 함수는 변경 가능한 값(장바구니)을 일급 함수로 만드는 코드이다.
#ValueCell
function ValueCell(initialValue) {
var currentValue = initialValue;
return {
val: function() {
return currentValue;
},
update: function(f) {
var oldValue = currentValue;
var newValue = f(oldValue);
currentValue = newValue;
}
};
}
이 함수는 값을 변경하기 위해 값을 직접 사용하지 않고 메서드(update)를 호출한다. 아직은 상태가 변경될 때 특정 동작을 하진 않는다. 이를 위해 감시자(watcher) 개념을 추가해보자.
감시자는 상태가 바뀔 때마다 실행되는 핸들러 함수이다.
# 감시자를 적용한 코드
function ValueCell(initialValue) {
var currentValue = initialValue;
var watchers = [];
return {
val: function() {
return currentValue;
},
update: function(f) {
var oldValue = currentValue;
var newValue = f(oldValue);
if (oldValue !== newValue) {
currentValue = newValue;
forEach(watchers, function(watcher){
watcher(newValue);
})
}
},
addWatcher: function(f) {
watchers.push(f);
}
};
}
이제 update 메서드를 호출 할 때마다 watchers 리스트를 순회하면서 새로운 값에 특정 행동을 취할 수 있게 됐다.
참고로 감시자는 observer, listener, event handler, callback과 같은 이름으로 불린다. (옵저버 패턴 이런것을 한 번쯤 들어봤었는데...)
이제 위에서 만든 ValueCell을 이용해서 장바구니가 변경 될 때마다 배송 아이콘을 갱신하도록 기존 add_item_to_cart를 리팩터링해보자.
# 고친코드
var shopping_cart = ValueCell({});
function add_item_to_cart(name, price) {
var item = make_cart_item(name, price);
shopping_cart.update(function(cart) { // 장바구니 변경
return add_item(cart, item);
});
var total = calc_total(shopping_cart.val()); // 현재 장바구니 조회
set_cart_total_dom(total);
}
shopping_cart.addWatcher(update_shipping_icons); // 상태 변경이 일어날 때 호출될 메서드
이제 장바구니를 바꾸는 모든 핸들러에서 update_shipping_icons() 를 부르지 않아도 된다.
다음으로 구현할 FormulaCell은 이미 있는 셀에서 파생한 셀을 만들 수 있다. 이는 다른 셀의 변화가 감지되면 값을 다시 계산한다.
# FormulaCell
function FormulaCell(upstreamCell, f) {
var myCell = ValueCell(f(upstreamCell.val())); // ValueCell을 재사용, total 값을 담는다.
upstreamCell.addWatcher(function(newUpstreamValue) { // 셀 값을 다시 계산하기 위해서 감시자 추가
myCell.update(function(currentValue) { // 장바구니가 변경되면 calc_total을 적용한 값을 업데이트한다
return f(newUpstreamValue);
});
});
return {
val: myCell.val, // val()과 addWatcher()를 myCell에 위임
addWatcher: myCell.addWatcher
};
}
FormulaCell은 값을 직접 바꿀 수는 없다. 감시하던 상위 셀 값이 바뀌면 FormulaCell 값이 바뀐다. 그리고 상위 셀이 바뀌면 상위 값을 가지고 셀 값을 다시 계산한다. 때문에 FormulaCell이 total 값이라면 total 값이 바뀔 때 실행 할 액션을 추가할 수 있다.
# 고친 코드
var shopping_cart = ValueCell({});
var cart_total = FormulaCell(shopping_cart, calc_total); // 이제 shopping_cart가 바뀔 때
// cart_total도 같이 바뀐다.
function add_item_to_cart(name, price) {
var item = make_cart_item(name, price);
shopping_cart.update(function(cart) { // 장바구니 변경
return add_item(cart, item);
});
}
shopping_cart.addWatcher(update_shipping_icons); // 상태 변경이 일어날 때 호출될 메서드
cart_total.addWatcher(set_cart_total_dom);
cart_total.addWatcher(update_tax_dom);
고친 함수는 클릭 핸들러가 더 간결해 졌음을 볼 수 있다. 이제 장바구니가 바뀔 때 항상 DOM 3개가 갱신된다. 일반적인 아키텍처와 반응형 아키텍쳐를 비교해보자.

일반적인 아키텍처였다면 제품 추가 이외에 삭제, 장바구니 비우기 등등 장바구니를 변경하는 모든 UI 이벤트 핸드럴에 배송아이콘 업데이트, total DOM 업데이트... 와 같은 코드를 일일히 적어줬어야 했을 것이다. 만약 이벤트가 수만가지였다면 반복적인 작업이 그만큼 늘어날 것이다... 얼마나 좋은 변경인가!
오늘은 반응형 아키텍처를 코드와 함께 살펴보았다. 반응형 아키텍처를 공부하다보니 ValueCell이 하는 일이 내가 개발할 때 사용했던 상태관리 툴, redux와 판박이었다. 저자 또한 후반에 ValueCell과 비슷한 것들을 소개해주면서 redux의 store를 예로 들었다.
무심코 사용했던 툴이 이런 의도에서 파생된 것이라고 생각하니 이해가 더 깊어짐을 느꼈다. 다음장 부터는 어니언 아키텍터에 대해 설명한다는데 또 기대가 된다.
'독서 > 책너두 챌린지' 카테고리의 다른 글
| 쏙쏙 들어오는 함수형 코딩 44일차 (0) | 2023.09.27 |
|---|---|
| 쏙쏙 들어오는 함수형 코딩 43일차 (0) | 2023.09.27 |
| 쏙쏙 들어오는 함수형 코딩 42일차 (0) | 2023.09.23 |
| 쏙쏙 들어오는 함수형 코딩 41일차 (0) | 2023.09.23 |
| 쏙쏙 들어오는 함수형 코딩 40일차 (0) | 2023.09.23 |