# Chapter04. 액션에서 계산 빼내기


# 1. 함수에는 입력과 출력이 있습니다

  • 어떤 함수 안에 액션이 하나만 있어도 그 함수 전체가 액션이 된다.
  • 함수에 암묵적 입력과 출력이 있으면 액션이 된다.
    • 여기서 암묵적 입력과 출력을 '부수 효과'라고 부른다.
var total = 0;
function add_to_total(amount) { // 인자는 명시적 입력
  console.log('Old total: ' + total); // 전역변수를 읽는 것은 암묵적 입력이고, 콘솔에 찍는 것은 암묵적 출력
  total += amount; // 전역변수를 바꾸는 것도 암묵적 출력
  return total; // 함수의 리턴값은 명시적 출력
}

# 2. 테스트와 재사용성 측면에서 코드 설계하기

# (1) DOM 업데이트와 비즈니스 규칙은 분리되어야 한다.

  • DOM을 업데이트하는 일은 함수에서 어떤 정보가 나오는 것이기 때문에 출력이다.
  • 하지만 리턴값이 아니므로 암묵적 출력이고 이러한 DOM 업데이트는 어디선가 해야 한다.

# (2) 전역변수가 없어야 하며 전역변수에 의존하지 않아야 한다.

  • 전역변수를 읽는 것은 암묵적 입력이고 바꾸는 것은 암묵적 출력이다.
  • 암묵적 인자는 함수의 인자로, 암묵적 출력은 함수의 리턴값으로 바꾸면 된다.

# (3) DOM을 사용할 수 있는 곳에서 실행된다고 가정하면 안 된다.

  • 특정 환경에서만 실행되는 함수를 만드는 것은 가급적 피해야 한다.
  • 항상 늘 DOM 요소에 접근할 수 있는 환경이 아닐 수 있다.
  • 그리고 DOM을 직접 쓰는 행위는 암묵적 출력인데 이는 함수의 리턴값으로 바꿀 수 있다.

# 3. 실제 예제

# (1) 예제 1

  • before
function calc_cart_total() {
  shopping_cart_total = 0; // 암묵적 출력
  for (var i = 0; i < shopping_cart.length; i += 1) { // 암묵적 입력
    var item = shopping_cart[i];
    shopping_cart_total += item.price; // 암묵적 출력
  }
    
  set_cart_total_dom();
  update_shipping_icons();
  update_tax_dom();
}
  • after
    • 전역변수 대신 지역변수를 사용하도록 바꾸고 지역변숫값을 리턴하도록 고친다.
    • 그리고 원래 함수는 새 함수의 리턴값을 받아 전역변수에 할당한다.
function calc_cart_total() {
  shopping_cart_total = calc_total(shopping_cart);
  set_cart_total_dom();
  update_shipping_icons();
  update_tax_dom();
}

function calc_total(cart) {
  var total = 0;
  for (var i = 0; i < cart.length; i += 1) {
    var item = cart[i];
    total += item.price;
  }
  return total;
}

# (2) 예제 2

  • before
function add_item_to_cart(name, price) {
  // 전역변수인 shopping_cart를 읽으면 함수가 안으로 데이터가 들어오기 때문에 암묵적 입력
  // push() 함수로 전역 배열을 바꿔 함수 밖으로 데이터가 나가기 때문에 암묵적 출력
  shopping_cart.push({
    name, price
  });
    
  calc_cart_total();
}
  • after
    • 호출하는 쪽은 전역변수를 인자로 넘기도록 고쳐 명시적인 입력으로 바꾼다.
    • shopping_cart 값을 바꾸는 대신 복사본을 만들고 복사본에 추가해 리턴해야 한다.
      • 이와 같이 어떤 값을 바꿀 때 그 값을 복사해서 바꾸는 방법을 '카피-온-라이트'라고 하며 불변성을 구현하는 방법 중 하나이다. (추후 6장에서 더 자세히...)
    • 그리고 자바스크립트에서는 배열을 직접 복사할 수 없어 이 예제에서는 Array.prototype.slice()와 같은 메서드를 사용했다.
function add_item_to_cart(name, price) {
  shopping_cart = add_item(shopping_cart, name, price);
  calc_cart_total();
}

function add_item(cart, name, price) {
  var new_cart = cart.slice();
  new_cart.push({
    name, price
  });
  return new_cart;
}

# 4. 계산 추출을 단계별로 알아보기

# (1) 계산 코드를 찾아 빼낸다.

  • 빼낼 코드를 찾고 코드를 추출해 새로운 함수를 만들어 리팩토링 한다.
  • 새 함수에 인자가 필요하다면 추가한다.
  • 원래 코드에서 빼낸 부분에서 새 함수를 부르도록 바꾼다.

# (2) 새 함수에 임묵적 입력과 출력을 찾는다.

  • 암묵적 출력은 함수를 부르는 동안 결과에 영향을 줄 수 있는 것을 말하고 암묵적 출력은 함수 호출의 결과로 영향을 받는 것을 말한다.
  • 입력
    • 전역변수 읽는 행위
    • 데이터베이스에서 값을 가져오는 것
  • 출력
    • console.log로 데이터 찍는 행위
    • 전역변수의 배열에 원소를 추가하는 행위
    • 전역변수를 바꾸는 행위
    • 공유 객체를 바꾸는 행위
    • 웹 요청을 보내는 것

# (3) 암묵적 입력은 인자로 암묵적 출력은 리턴값으로 바꾼다.

  • 한 번에 하나씩 입력은 인자로 출력은 리턴값으로 바꾼다.
  • 새로운 리턴값이 생겼다면 호출하는 코드에서 함수의 결과를 변수에 할당해야 할 수도 있다.
  • 여기서 인자와 리턴값은 바뀌지 않는 불변값이라는 것이 중요하다.
  • 리턴값이 나중에 바뀐다면 암묵적 출력이다.
  • 또 인자로 받은 값이 바뀔 수 있다면 암묵적 출력이다.
// 암묵적 출력이 적용된 예시
function f1(arr, value) {
  arr.push(value);
  return arr;
}

const a = [1, 2, 3];
const b = f1(a, 4);
console.log(a); // [1, 2, 3, 4]
console.log(b); // [1, 2, 3, 4]

# 5. (p.79) 연습 문제 - 내가 풀어본 방식

var shopping_cart_total = 0;

function calc_cart_total() {
  shopping_cart_total = calc_total(shopping_cart);
  set_cart_total_dom();
  update_shipping_icons();
  update_tax_dom(shopping_cart_total);
}

function update_tax_dom(total) {
  const updated_shopping_cart_total = add_tax(total);
  set_tax_dom(updated_shopping_cart_total);
}

function add_tax(price) {
  return price * 0.10;
}

# 6. (p.82) 연습 문제 - 내가 풀어본 방식

function calc_cart_total() {
  shopping_cart_total = calc_total(shopping_cart); 
  set_cart_total_dom();
  update_shipping_icons(shopping_cart_total);    
  update_tax_dom(shopping_cart_total);
}

function updated_shipping_icons(total) {
  var buy_buttons = get_buy_buttons_dom();
    
  for (var i = 0; i < buy_buttons.length; i += 1) {
    var button = buy_buttons[i];
    var item = buttom.item;
      
    if (is_free_shipping_item(item.price, total)) {
      button.show_free_shipping_icon();
    } else {
      button.hide_free_shipping_icon();
    }
  }
}
    
function is_free_shipping_item(price, total) {
  return price + total >= 20;
}

# ❗️ 정리

  • 액션은 암묵적인 입력 또는 출력을 가지고 있다.
  • 계산의 정의에 따르면 계산은 암묵적인 입력이나 출력이 없어야 한다.
  • 공유 변수(전역변수 같은)는 일반적으로 암묵적 입력 또는 출력이 된다.
  • 암묵적 입력은 인자로 바꿀 수 있다.
  • 암묵적 출력은 리턴값으로 바꿀 수 있다.
  • 함수형 원칙을 적용하면 액션은 줄어들고 계산은 늘어난다.