Chapter 4

액션에서 계산 빼내기

  • 4.1 MegaMart 쇼핑몰 예제
  • 4.2 암묵적 입력과 출력
  • 4.3 명시적 입력과 출력으로 바꾸기
  • 4.4 액션에서 계산 빼내기 리팩터링
  • 4.5 테스트와 재사용성이 좋아지는 이유

암묵적 입력은 인자로, 암묵적 출력은 리턴값으로 — 이 한 줄이 액션을 계산으로 바꾸는 리팩터링의 전부야.

MegaMart라는 온라인 쇼핑몰 예제가 이 책에서 계속 쓰이는 핵심 예제인데, 여기서 처음 등장해. 장바구니에 물건을 넣고, 합계를 보여주고, 무료 배송인지 아닌지 표시하는 기능이 있어. 처음 코드는 이런 식이야 — 전역 변수 shopping_cart에 아이템을 넣고, 전역 변수 shopping_cart_total에 합계를 저장하고, DOM을 직접 업데이트하는 함수들이 있지.

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++) {
    shopping_cart_total += shopping_cart[i].price;
  }
  set_cart_total_dom();
  update_shipping_icons();
  update_tax_dom();
}

이 코드의 문제점? 전부 액션이야. 전역 변수를 읽고 쓰고, DOM을 업데이트하고 — 어디까지가 계산이고 어디까지가 액션인지 구분이 안 돼. 전부 엉켜있거든.

저자가 여기서 핵심 개념 하나를 꺼내는데 — **암묵적 입력(implicit input)**과 **암묵적 출력(implicit output)**이야. 명시적 입력은 함수의 인자로 들어오는 값이고, 암묵적 입력은 함수 인자가 아닌데 함수 안에서 읽는 값(전역 변수, DB, 설정 파일 등)이야. 명시적 출력은 함수의 리턴값이고, 암묵적 출력은 리턴값이 아닌데 함수가 바꾸는 것(전역 변수 수정, DOM 변경, 콘솔 출력 등)이지. 암묵적 입력이나 암묵적 출력이 있으면 그 함수는 액션이야. 위의 calc_cart_total() 함수를 보면 암묵적 입력으로 shopping_cart(전역 변수를 읽음)가 있고, 암묵적 출력으로 shopping_cart_total(전역 변수를 씀)과 DOM 업데이트가 있어. 암묵적인 것들이 잔뜩 있으니 당연히 액션이 될 수밖에 없는 거야.

리팩터링의 핵심 원리는 단순해. 암묵적 입력은 함수 인자로 바꾸고, 암묵적 출력은 리턴값으로 바꾸면 돼. 이렇게 하면 액션이 계산으로 바뀌거든.

// Before (액션)
function calc_cart_total() {
  shopping_cart_total = 0;
  for (var i = 0; i < shopping_cart.length; i++) {
    shopping_cart_total += shopping_cart[i].price;
  }
}

// After (계산)
function calc_total(cart) {
  var total = 0;
  for (var i = 0; i < cart.length; i++) {
    total += cart[i].price;
  }
  return total;
}

shopping_cart(전역 변수)를 cart(인자)로, shopping_cart_total(전역 변수 수정)을 return total(리턴값)로 바꿨어. 이제 이 함수는 계산이야. 같은 cart를 넣으면 항상 같은 total이 나와.

저자가 제시하는 리팩터링 단계는 이래. 함수에서 암묵적 입력과 출력을 찾고, 암묵적 입력은 인자로 바꾸고, 암묵적 출력은 리턴값으로 바꾸고, 바뀐 함수의 호출부를 수정하는 거야. 이 과정을 MegaMart의 여러 함수에 반복 적용하지. 중요한 점 — 액션이 완전히 사라지는 게 아니야. DOM 업데이트, 전역 상태 변경 같은 건 결국 어디선가 해야 해. 목표는 액션을 없애는 게 아니라, 계산을 최대한 빼내서 액션의 크기를 줄이는 것이야. 얇은 액션 껍질 안에 두꺼운 계산 알맹이를 넣는 구조.

리팩터링 전과 후를 비교해보면 차이가 확실해. 리팩터링 전에는 calc_cart_total()을 테스트하려면 전역 변수 shopping_cart를 세팅하고 DOM이 있어야 하고 결과도 전역 변수에서 읽어야 했어. 리팩터링 후에는 calc_total(cart)를 테스트하려면 cart 배열 하나 만들어서 넘기고 리턴값 비교하면 끝이야. 어디서든 cart 배열만 있으면 가져다 쓸 수 있어. 이게 계산의 힘이야. 컨텍스트에 의존하지 않으니 테스트도 쉽고 재사용도 쉬워.


정리

4장 읽고 기억할 거 세 가지:

  1. 암묵적 입력/출력이 함수를 액션으로 만든다. 전역 변수 읽기, DOM 수정, 콘솔 출력 — 전부 암묵적 입출력
  2. 리팩터링 공식: 암묵적 입력 → 인자, 암묵적 출력 → 리턴값. 이거 하나만 기억하면 됨
  3. 목표는 액션 제거가 아니라 액션 최소화. 얇은 액션 껍질, 두꺼운 계산 알맹이