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
- 일반적으로 암묵적 입력과 출력은 인자와 리턴값으로 바꿔 없애는 것이 좋다.
- 설계는 엉켜있는 것을 푸는 것이다. 언제든 다시 합칠 수 있다.
- 엉켜있는 것을 풀어 각 함수가 하나의 일만 하도록 하면, 개념을 중심으로 쉽게 구성할 수 있다.