4장. 액션에서 계산 빼내기

온라인 쇼핑몰 예제 코드 개선하기

  • 장바구니에 담겨있는 제품의 금액 합계를 볼 수 있는 기능

  • 구매 합계가 20달러 이상이면 무료 배송 아이콘을 띄우는 기능

  • 세금 계산하기

개선 전 코드

// 장바구니 구현하기
var shopping_cart = [];
var shopping_cart_total = 0;
 
function add_item_to_cart(name, price) {
  shopping_cart.push({
    name: name,
    price: price,
  });
  calc_cart_total();
}
 
function calc_cart_total() {
  shopping_cart_total = 0;
  for (var i = 0; i < shopping_cart.length; i++) {
    var item = shopping_cart[i];
    shopping_cart_total += item.price;
  }
  set_cart_total_dom();
}
 
// 무료 배송비 계산하기
function update_shipping_icons() {
  var buy_buttons = get_buy_buttons_dom();
  for (var i = 0; i < buy_buttons.length; i++) {
    var button = buy_buttons[i];
    var item = button.item;
    if (item.price + shopping_cart_total >= 20)
      button.show_free_shipping_icon();
    else button.hide_free_shipping_icon();
  }
}
 
function calc_cart_total() {
  shopping_cart_total = 0;
  for (var i = 0; i < shopping_cart.length; i++) {
    var item = shopping_cart[i];
    shopping_cart_total += item.price;
  }
  set_cart_total_dom();
  update_shipping_icons();
}
 
// 세금 계산하기
function update_tax_dom() {
  set_tax_dom(shopping_cart_total * 0.1);
}
 
function calc_cart_total() {
  shopping_cart_total = 0;
  for (var i = 0; i < shopping_cart.length; i++) {
    var item = shopping_cart[i];
    shopping_cart_total += item.price;
  }
  set_cart_total_dom();
  update_shipping_icons();
  update_tax_dom();
}

코드 개선 목표

  1. 테스트하기 쉽게 만들기

    → DOM 업데이트와 비즈니스 규칙을 분리하기

    → 전역변수 제거하기

  2. 재사용하기 쉽게 만들기

    → 전역변수에 의존하지 않게

    → DOM을 사용할 수 있는 곳에서 실행한다고 가정하지 않기

    → 함수가 결과값을 리턴

계산 추출을 통해 코드 개선하기

  1. 계산 코드를 찾아 빼내기

    • 서브루틴 추출하기 리팩토링 : 동작을 유지하며 코드를 바꾸는 것(지역 변수를 입력으로 받고 지역변수를 리턴)
    • 함수 추출하기 리팩토링
  2. 새 함수에 암묵적 입력과 출력 찾기

  3. 암묵적 입력은 인자로 암묵적 출력은 리턴값으로 바꾸기

    ← 함수에 암묵적 입력과 출력이 있으면 액션이 되기 때문

function update_tax_dom() {
  set_tax_dom(shopping_cart_total * 0.1);
}
 
// 1. 코드를 선택하고 빼내기
// => 서브루틴 추출하기 (빼낸 코드를 새로운 함수로 만들고 이름 붙이기)
function update_tax_dom_ver1() {
  set_tax_dom(calc_tax());
}
function calc_tax_ver1() {
  return shopping_cart_total * 0.1;
}
 
// 2. 암묵적 입력과 출력 찾기
// (1) 암묵적 입력 : shopping_cart_total
// (2) 암묵적 출력 : X
 
// 3. 입력은 인자로 바꾸고 출력은 리턴값으로 바꾸기
function update_tax_dom_ver2() {
  set_tax_dom(calc_tax_ver2(shopping_cart_total));
}
 
function calc_tax_ver2(amount) {
  return amount * 0.1;
}
 
// -------------------------------------
function update_shipping_icons() {
  var buy_buttons = get_buy_buttons_dom();
  for (let i = 0; i < buy_buttons.length; i++) {
    var button = buy_buttons[i];
    var item = button.item;
    if (item.price + shopping_cart_total >= 20)
      button.show_free_shopping_icon();
    else button.hide_free_shipping_icon();
  }
}
 
// 1. 코드를 선택하고 빼내기
// => 서브루틴 추출하기 (빼낸 코드를 새로운 함수로 만들고 이름 붙이기)
function update_shipping_icons_ver1() {
  var buy_buttons = get_buy_buttons_dom();
  for (let i = 0; i < buy_buttons.length; i++) {
    var button = buy_buttons[i];
    var item = button.item;
    if (gets_free_shipping_ver1()) button.show_free_shopping_icon();
    else button.hide_free_shipping_icon();
  }
}
 
function gets_free_shipping_ver1() {
  return item.price + shopping_cart_total >= 20;
}
 
// 2. 암묵적 입력과 출력 찾기
// (1) 암묵적 입력 : shopping_cart_total
// (2) 암묵적 출력 :
 
// 3. 입력은 인자로 바꾸고 출력은 리턴값으로 바꾸기
function update_shipping_icons_ver2() {
  var buy_buttons = get_buy_buttons_dom();
  for (let i = 0; i < buy_buttons.length; i++) {
    var button = buy_buttons[i];
    var item = button.item;
    if (gets_free_shipping_ver2(item.price, shopping_cart_total))
      button.show_free_shopping_icon();
    else button.hide_free_shipping_icon();
  }
}
 
function gets_free_shipping_ver2(item_price, total) {
  return item_price + total >= 20;
}

keyPoint

  • 액션은 암묵적인 입력 또는 출력을 가지고 있다.

  • 계산의 정의에 따르면 계산은 암묵적인 입력이나 출력이 없어야 한다.

  • 공유 변수(전역변수 같은)는 일반적으로 암묵적 입력 또는 출력이 된다.

  • 암묵적 입력은 인자로 바꿀 수 있다.

  • 암묵적 출력은 리턴값으로 바꿀 수 있다.

  • 함수형 원칙을 적용하면 액션은 줄어들고 계산은 늘어난다.