쏙쏙 들어오는 함수형 코딩 - 5

binary·2022년 5월 28일
4
post-thumbnail

Chapter 5 더 좋은 액션 만들기

암묵적 입력과 암묵적 출력을 없애 액션을 계산으로 만들 수 있다. 그러나 모든 액션들을 없앨 수는 없다. 액션은 필요하니까! 액션을 만드는 것을 피할 수 없으니 액션을 더 좋게 만드는 방법을 정리해보려 한다.

📝 비즈니스 요구 사항과 설계를 맞추기

비즈니스 요구 사항은 장바구니에 담긴 제품을 주문할 때 무료 배송인지 확인하는 것이다.

function gets_free_shipping(total, item_price){
  return item_price + total >= 20;
}

코드를 보면 비즈니스 요구사항과 맞지 않고 맞는 것처럼 보이게 하는 함수인 것을 알 수 있다.

합계 금액과 제품 가격을 더해서 무료배송 금액과 비교하는 것이 아닌 주문 결과가 무료 배송인지 확인하는 코드를 작성해야 비즈니스 요구사항과 맞는 함수이다.

function calc_total(cart){
  var total = 0;
  for(var i = 0; i < cart.length; i++) {
    var item = cart[i];
    total = item.price + total; // 코드 중복
  }
  return total;
}

게다가 item.price + total , 장바구니 합계를 계산하는 코드가 중복이다.
중복이 항상 나쁘다고 할 수는 없지만, 코드에서 나는 냄새다. 코드의 냄새 (code smell) 는 나중에 문제가 될 수 있다.

진짜 말 너무 싹바가지없음 코드냄새라니 진짜 누가 저런 말 만들었을까

그래서 함수를 비즈니스 요구사항에 맞게 고쳐주려 한다.

function gets_free_shipping(cart) {
  return calc_total(cart) >= 20;
}

calc_total() 함수를 재사용하여 장바구니 안에서의 합계 금액을 구하여 무료 배송 금액과 비교한다.

그러면 비즈니스 요구사항에 맞추어 장바구니에 담긴 물건들의 금액으로 무료 배송인지 계산하면서, 또 코드의 중복도 없앤 함수가 되었다.

😶 암묵적 입력과 출력은 적을수록 좋다

인자가 아닌 모든 입력은 암묵적 입력이고, 리턴값이 아닌 모든 출력은 암묵적 출력이다.

어떤 함수에 암묵적 입력과 암묵적 출력이 있다면 다른 컴포넌트와 강하게 연결된 컴포넌트라고 할 수 있다.

또 아무때나 실행할 수 없기 때문에 테스트하기에도 어렵다.

function update_shipping_icons() {
  var buttons = get_buy_buttons_dom();
  for(var i = 0; i < buttons.length; i++){
    var button = buttons[i];
    var item = button.item;
    var new_cart = add_item(shopping_cart, item.name, item.price);
    if(gets_free_shipping(new_cart))
      button.show_free_shipping_icon();
    else
      button.hide_free_shipping_icon();
  }
}

코드에서 전역변수인 shopping_cart 를 사용하면서 암묵적 입력과 출력을 이용하고 있다.
shopping_cart 라는 전역변수 대신 인자를 넣어주어 암묵적 입력을 제거해보려 한다.

function update_shipping_icons(cart) {
  var buttons = get_buy_buttons_dom();
  for(var i = 0; i < buttons.length; i++){
    var button = buttons[i];
    var item = button.item;
    var new_cart = add_item(cart, item.name, item.price);
    if(gets_free_shipping(new_cart))
      button.show_free_shipping_icon();
    else
      button.hide_free_shipping_icon();
  }
}
update_shipping_icons(shopping_cart);

이제는 함수 안에서 전역 변수인 shopping_cart 를 사용하는 게 아니라 인자로 전달할 수 있다.

🪢 설계는 엉켜있는 코드를 푸는 것이다

함수를 사용하면 관심사를 자연스럽게 분리할 수 있다. 함수는 인자로 넘기는 값과 그 값을 사용하는 방법을 분리한다. 또 가끔은 분리된 것을 합치고 싶을 수도 있다. 하지만 분리한 것은 언제든 쉽게 조합할 수 있기 때문에 잘 분리하면 분리할 수록 좋다. 잘 분리가 된다면, 재사용하기 쉽고, 유지보수하기 쉽고, 테스트하기 쉬워진다.

카피-온-라이트 패턴을 빼내기

add_item() 이라는 함수가 있다. 장바구니에 제품을 추가하는 간단한 일을 하는 함수이다. 그런데 정말 간단한걸까?

function add_item(cart, name, price) {
  var new_cart = cart.slice(); // 1. 배열 복사
  new_cart.push({ // 2. 복사본에 item 추가
    name: name, // 3. item 객체 만들기
    price: price,
  });
  return new_cart; // 4. 복사본 리턴
}

어떤 일을 하는지 나누어보면 이렇게 나눌 수 있다.

🤔 음 또 분리할 수 있지 않을까?

1번, 3번, 4번은 값을 바꿀 때 복사하는 카피-온-라이트 를 구현한 부분이기 때문에 함께 두면서 분리하는 것이 좋다.

item 객체를 만드는 함수

function make_cart_item(name, price) {
  return {
    name : name,
    price : price,
  };
}

배열을 복사해서 복사본 반환하는 함수

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

함수 사용

add_item(shopping_cart, make_cart_item("shoes", 3.45))

❗️ 3줄 요약

  • 암묵적 입력과 암묵적 출력은 인자와 리턴값으로 바꿔 없애주기

  • 설계는 엉켜있는 것을 푸는 것이다. 풀려있는 것은 언제든 합칠 수 있다.

  • 엉켜있는 것을 풀어 각 함수가 하나의 일만 하도록 하면, 개념을 중심으로 쉽게 구성 가능!

0개의 댓글