SUMMARY
- 액션에서 암묵적인 입력(전역변수)과 출력을 제거 → 계산으로 바꿀 수 있음
- 계산은 테스트 코드 적용과 코드 재사용성에 유리 (+ 부수효과 적음)
기능 요구 사항 및 기존 코드
// Action 전역변수
var shopping_cart = [];
var shopping_cart_total = 0;
// Action 장바구니에 상품 추가 및 장바구니 금액 업데이트
function add_item_to_cart(name, price) {
shopping_cart.push({
name: name,
price: price
});
calc_cart_total();
}
// Action 장바구니 합계 금액 계산 및 화면 업데이트
function calc_cart_total() {
shopping_cart_total = 0;
for(var i = 0; i < shopping_cart.length; i++) {
var item = shopping_cart[i];
shopping_cart_total += item.price;
}
set_cart_total_dom();
update_shipping_icons();
update_tax_dom();
}
TypeScript
복사
// Action 무료 배송 여부에 따른 아이콘 업데이트
function update_shipping_icons() {
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;
if(item.price + shopping_cart_total >= 20)
button.show_free_shipping_icon();
else
button.hide_free_shipping_icon();
}
}
// Action 세금 계산 및 화면 업데이트
function update_tax_dom() {
set_tax_dom(shopping_cart_total * 0.10);
}
TypeScript
복사
수정 요구 사항
테스트 부서 | 협업 부서 |
전역변수에 의존하지 않아야 함 | 전역 변수에 의존하지 않아야 함 |
DOM 업데이트와 비지니스 규칙이 분리 되야 함 | DOM 업데이트와 비지니스 규칙이 분리 되야 함 |
함수가 결과 값을 리턴 해야 함 |
테스트 부서의 어려움
function update_tax_dom() {
// BAD 테스트를 위해서 별도의 전역변수 설정이 필요
// BAD 결과값을 테스트하기 위해서는 DOM 접근이 필요
set_tax_dom(shopping_cart_total * 0.10);
}
TypeScript
복사
협업 부서의 어려움
function update_shipping_icons() {
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;
// BAD 전역변수에 대한 접근이 필요함 (타 부서는 비지니스 규칙만 필요, DOM 접근 불가)
if(item.price + shopping_cart_total >= 20)
// BAD DOM이 있는 곳에서만 사용 가능함 (결제팀은 영수증, 배송팀은 운송장 출력 목적)
button.show_free_shipping_icon();
else
button.hide_free_shipping_icon();
}
// BAD 명시적인 리턴값이 없음
}
TypeScript
복사
“부수 효과인 암묵적 입력과 출력을 없애야 한다”
(인자 외 입력값, 리턴값 외의 출력 값이 없어야 함)
단계별 계산 추출 방법
① 계산 코드를 추출 새 함수로 만듬
② 새 함수에서 암묵적 입력과 출력을 찾음
③ 암묵적 입력은 ‘인자’로 암묵적 출력은 ‘리턴값’으로 바꿈
액션에서 계산 빼내기 ① (calc_cart_total)
// Action
function calc_cart_total() {
// BAD 액션 내에 계산이 존재
// BAD 전역변수를 조작함 (암묵적 입력 & 출력)
shopping_cart_total = 0;
for(var i = 0; i < shopping_cart.length; i++) {
var item = shopping_cart[i];
shopping_cart_total += item.price;
}
set_cart_total_dom();
update_shipping_icons();
update_tax_dom();
}
TypeScript
복사
// Action
function calc_cart_total() {
// GOOD 액션에서 계산을 분리
shopping_cart_total = calc_total(shopping_cart);
set_cart_total_dom();
update_shipping_icons();
update_tax_dom();
}
// Calc
// GOOD 암묵적 입출력(전역 변수) 대신 인자와 리턴값을 사용
function calc_total(cart) {
var total = 0;
for(var i = 0; i < cart.length; i++) {
var item = cart[i];
total += item.price;
}
return total;
}
TypeScript
복사
액션에서 계산 빼내기 ② (add_item_to_cart)
// Action
function add_item_to_cart(name, price) {
// BAD 액션 내에 계산이 존재
// BAD 전역변수를 조작함 (암묵적 입력 & 출력)
shopping_cart.push({
name: name,
price: price
});
calc_cart_total();
}
TypeScript
복사
// Action
function add_item_to_cart(name, price) {
// GOOD 액션에서 계산을 분리
shopping_cart = add_item(shopping_cart, name, price);
calc_cart_total();
}
// Calc
function add_item(cart, name, price) {
// GOOD 암묵적 입력(전역 변수) 대신 인자로 받음
// GOOD 배열(cart)의 복사본을 만들어 변경 후 리턴
// (불변성을 구현하는 방법 - 카피-온-라이트)
var new_cart = cart.slice();
new_cart.push({
name: name,
price: price
});
return new_cart;
}
TypeScript
복사
액션에서 계산 빼내기 ③ (add_item_to_cart)
// Action
function update_tax_dom() {
// BAD 액션 (DOM 변경) 내에 계산이 존재
// BAD 전역변수를 조작함 (암묵적 입력 & 출력)
set_tax_dom(shopping_cart_total * 0.10);
}
TypeScript
복사
// Action
function update_tax_dom() {
// GOOD 액션에서 계산을 분리
set_tax_dom(calc_tax(shopping_cart_total));
}
// Calc
// GOOD 암묵적 입력을 명시적인 인자로 바꿈
function calc_tax(amount) {
return amount * 0.10;
}
TypeScript
복사
액션에서 계산 빼내기 ④ (update_shipping_icons)
// Action
function update_shipping_icons() {
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;
// BAD 액션 (DOM 변경) 내에 계산이 존재
// BAD 전역변수를 조작함 (암묵적 입력 & 출력)
if(item.price + shopping_cart_total >= 20)
button.show_free_shipping_icon();
else
button.hide_free_shipping_icon();
}
}
TypeScript
복사
// Action
function update_shipping_icons() {
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;
// GOOD 액션에서 계산을 분리
if(gets_free_shipping(shopping_cart_total, item.price))
button.show_free_shipping_icon();
else
button.hide_free_shipping_icon();
}
}
// Calc
// GOOD 암묵적 입력을 명시적인 인자로 바꿈
function gets_free_shipping(total, item_price) {
return item_price + total >= 20;
}
TypeScript
복사