5장. 더 좋은 액션 만들기
- 비즈니스 요구 사항과 설계 맞추기 : 요구 사항에 맞춰 더 나은 추상화 단계 선택하기
 - 비즈니스 요구 사항과 함수 맞추기
 - 암묵적 입력과 출력 줄이기 → 암묵적 입력과 출력이 있는 함수는 아무 때나 실행할 수 없기 때문에 테스트하기 어렵다. ← 모든 입력값을 설정하고 테스트를 돌린 후 모든 출력값을 확인해야 하기 때문
 
add_item 함수 개선하기
1. 계산을 분리해 더 좋은 설계 만들기
cart에 대한 동작 / item에 대한 동작 / 비즈니스 규칙에 대한 함수인지 표시해보기
C  I
function add_item(cart, name, price) {
    var new_cart = cart.slice(); // 배열을 복사한다.
    new_cart.push( // 복사본에 item을 추가한다.
		{ // item 객체를 만든다.
        name: name,
        price: price
    });
    return new_cart; // 복사본을 리턴한다.
}📍 설계는 엉켜있는 코드를 푸는 것이다.
- 재사용하기 쉽다
 - 유지보수하기 쉽다
 - 테스트하기 쉽다 (관심사가 분리된 함수이기 때문)
 
⇒ cart와 item으로 이루어진 함수에서, item에 관한 코드를 별도의 함수로 분리
function add_item(cart, item) {
  var new_cart = cart.slice();
  new_cart.push(item);
  return new_cart;
}
 
function make_cart_item(name, price) {
  return {
    name: name,
    price: price,
  };
}
 
add_item(shopping_cart, make_cart_item("shoe", 3.45));2. 카피-온-라이트 패턴을 빼내기
function add_item(cart, item) {
  const new_cart = cart.slice();
  new_cart.push(item);
  return new_cart;
}기존 계산 코드는 장바구니를 넘기는 상황에서만 쓸 수 있는 것처럼 보임.
⇒ 함수 이름과 인자 이름을 더 일반적인 이름으로 바꾸기
function add_item(cart, item) {
  return add_element_last(cart, item);
}
 
function add_element_last(array, elem) {
  var new_array = array.slice();
  new_array.push(elem);
  return new_array;
}이렇듯 어떤 배열이나 항목에도 쓸 수 있는, 재사용할 수 있는 함수를 유틸리티 함수라고 부름
update_shipping_icons 함수 개선하기
function update_shipping_icons(cart) {
  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;
    const new_cart = add_item(cart, make_cart_item("shoes", 3.45));
    if (gets_free_shipping(new_cart)) button.show_free_shipping_icon();
    else button.hide_free_shipping_icon();
  }
}1. 함수가 하는 일 작성 및 분류하기
- 모든 버튼 가져오기 ← 
구매하기 버튼 - 버튼을 가져오고 반복하기 ← 
구매하기 버튼 - 버튼에 관련된 제품 가져오기 ← 
구매하기 버튼 - 가져온 제품을 가지고 새 장바구니 만들기 ← 
cart와 item - 장바구니가 무료 배송이 필요한지 확인하기 ← 
cart와 item - 아이콘 표시하거나 감추기 ← 
DOM 관련 동작 
2. 관심사에 따라 함수 분리하기
function update_shipping_icons(cart) {
  // 구매하기 버튼 관련 동작
  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;
 
    var hasFreeShipping = gets_free_shipping_with_item(cart, item);
 
    set_free_shipping_icon(button, hasFreeShipping);
  }
}
function gets_free_shipping_with_item(cart, item) {
  // cart와 item 관련 동작
  var new_cart = add_item(cart, item);
  return gets_free_shipping(new_cart);
}
function set_free_shipping_icon(button, isShown) {
  // DOM 관련 동작
  if (isShown) button.show_free_shipping_icon();
  else button.hide_free_shipping_icon();
}- 개선된 전체 코드
/** 액션 */ // 장바구니 구현하기 var shopping_cart = []; var shopping_cart_total = 0; function add_item_to_cart(name, price) { var item = make_cart_item(name, price); shopping_cart = add_item(shopping_cart, item); var total = calc_total(shopping_cart); set_cart_total_dom(total); update_shipping_icons(shopping_cart); update_tax_dom(total); } function set_cart_total_dom(total) { // ... total; // ... } // 무료 배송비 계산하기 function update_shipping_icons(cart) { // 구매하기 버튼 관련 동작 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; var hasFreeShipping = gets_free_shipping_with_item(cart, item); set_free_shipping_icon(button, hasFreeShipping); } } function gets_free_shipping_with_item(cart, item) { // cart와 item 관련 동작 var new_cart = add_item(cart, item); return gets_free_shipping(new_cart); } function set_free_shipping_icon(button, isShown) { // DOM 관련 동작 if (isShown) button.show_free_shipping_icon(); else button.hide_free_shipping_icon(); } // 세금 계산하기 function update_tax_dom(total) { set_tax_dom(calc_tax(total)); } /** 계산 */ function add_item(cart, item) { return add_element_last(cart, item); } function add_element_last(array, elem) { var new_array = array.slice(); new_array.push(elem); return new_array; } function make_cart_item(name, price) { return { name: name, price: price, }; } function calc_total(cart) { var total = 0; for (var i = 0; i < cart.length; i++) { var item = cart[i]; total += item.price; } return total; } function gets_free_shipping(cart) { return calc_total(cart) >= 20; } function calc_tax(amount) { return amount * 0.1; } 
keyPoint
- 일반적으로 암묵적 입력과 출력은 인자와 리턴값으로 바꿔 없애는 것이 좋다.
 - 설계는 엉켜있는 것을 푸는 것이다. 언제든 다시 합칠 수 있다.
 - 엉켜있는 것을 풀어 각 함수가 하나의 일만 하도록 하면, 개념을 중심으로 쉽게 구성할 수 있다.