본문으로 건너뛰기

05 더 좋은 액션 만들기

이번 장에서 살펴볼 내용
  • 암묵적 입력과 출력을 제거해서 재사용하기 좋은 코드를 만드는 방법을 알아봅니다.
  • 복잡하게 엉킨 코드를 풀어 더 좋은 구조로 만드는 법을 배웁니다.

이 장에서는 액션에서 암묵적 입력과 출력을 줄여 설계를 개선하는 방법에 대해 알아보겠습니다.

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

요구 사항에 맞춰 더 나은 추상화 단계 선택하기

gets_free_shipping() 함수는 비즈니스 요구 사항으로 봤을 때 맞지 않는 부분이 있습니다. 요구 사항은 장바구니에 담긴 제품을 주문할 때 무료 배송인지 확인하는 것 입니다. 하지만 함수를 보면 장바구니로 무료 배송을 확인하지 않고 제품의 합계와 가격으로 확인하고 있습니다. 이것은 비즈니스 요구 사항과 맞지 않는 인자라고 할 수 있습니다.

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;
}
return total;
}

다음 함수를

gets_free_shipping(total, item_price)

아래와 같이 바꿉니다.

gets_free_shipping(cart)

그리고 calc_total() 함수를 재사용하여 중복을 없애 봅시다.

비즈니스 요구 사항과 함수를 맞추기

함수의 동작을 바꿨기 떄문에 엄밀히 말하면 리팩터링이라고 할 수 없습니다.

gets_free_shipping() 함수는 장바구니 값을 인자로 받아 합계가 20보다 크거나 같은지 알려줍니다.

원래 코드
function gets_free_shipping(total, item_price) {
return item_price + total >= 20;
}
새 시그니처를 적용한 코드
function gets_free_shipping(cart) {
return calc_total(cart) >= 20;
}

바꾼 함수는 합계와 제품 가격 대신 장바구니 데이터를 사용합니다. 장바구니는 전자상거래에서 많이 사용하는 엔티티 타입이기 때문에 비즈니스 요구 사항과 잘 맞습니다.

함수 시그니처가 바뀌었기 때문에 사용하는 부분도 고쳐야 합니다.

원래 코드
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;

if (gets_free_shipping(shopping_cart_total, item.price)) {
button.show_free_shipping_icon();
} else {
button.hide_free_shipping_icon();
}
}
}
새 시그니처를 적용한 코드
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();
}
}
}

gets_free_shipping() 함수가 잘 동작하도록 고쳤습니다. 이제 gets_free_shipping() 함수는 장바구니가 무료 배송인지 아닌지 알려줍니다.

원칙: 암묵적 입력과 출력은 적을수록 좋습니다.

인자가 아닌 모든 입력은 암묵적 입력이고 리턴값이 아닌 모든 출력은 암묵적 출력입니다. 앞에서 암묵적 입력과 출력이 없는 함수를 작성했고 이 함수는 계산이라고 불렀습니다.

계산을 만들기 위해 암묵적 입력과 출력을 없애는 원칙은 액션에도 적용할 수 있습니다.

어떤 함수에 암묵적 입력과 출력이 있다면 다른 컴포넌트와 강하게 연결된 컴포넌트라고 할 수 있습니다. 다른 곳에서 사용할 수 없기 때문에 모듈이 아닙니다. 이런 함수의 동작은 연결된 부분의 동작에 의존합니다. 암묵적 입력과 출력을 명시적으로 바꿔 모듈화된 컴포넌트로 만들 수 있습니다.

암묵적 입력과 출력이 있는 함수는 아무 떄나 실행할 수 없기 때문에 테스트하기 어렵습니다. 모든 입력값을 설정하고 테스트를 돌린 후에 모든 출력값을 확인해야 합니다. 입력과 출력이 ㅁ낳을수록 테스트는 더 어려워집니다.

계산은 암묵적 입력과 출력이 없기 때문에 테스트하기 쉽습니다. 모든 암묵적 입력과 출력을 없애지 못해 액션을 계산으로 바꾸지 못해도 암묵적 입력과 출력을 줄이면 테스트하기 쉽고 재사용하기 좋습니다.

암묵적 입력과 출력 줄이기

update_shipping_icons() 함수에 이 원칙을 적용해 암묵적 입력과 출력을 줄여봅시다. 먼저 암묵적 입력을 명시적 입력인 인자로 바꿔 봅시다.

원래 코드
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();
}
}
}
암묵적 입력을 명시적 입력으로 바꾼 코드
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() 함수를 호출하는 부분입니다.

원래 코드
function calc_cart_total() {
shopping_cart_total = calc_total(shopping_cart);
set_cart_total_dom();
update_shipping_icons();
update_tax_dom();
}
인자 전달하기
function calc_cart_total() {
shopping_cart_total = calc_total(shopping_cart);
set_cart_total_dom();
update_shipping_icons(shopping_cart);
update_tax_dom();
}

원칙: 설계는 엉켜있는 코드를 푸는 것이다.

함수를 사용하면 관심사를 자연스럽게 분리할 수 있습니다. 함수는 인자로 넘기는 값과 그 값을 사용하는 방법을 분리합니다. 가끔 어떤 것을 합치고 싶을 수도 있습니다. 크고 복잡한 것이 더 잘 만들어진 것 같다고 느끼기 때문입니다. 하지만 분리된 것은 언제든 쉽게 조합할 수 있습니다. 오히려 잘 분리하는 방법을 찾기가 더 어렵습니다.

재사용하기 쉽다.

함수는 작으면 작을수록 재사용하기 쉽습니다. 하는 일도 적고 쓸 때 가정을 많이 하지 않아도 됩니다.

유지보수하기 쉽다.

작은 함수는 쉽게 이해할 수 있고 유지보수하기 쉽습니다. 코드가 작기 때문에 올바른지 아닌지 명확하게 알 수 있습니다.

테스트하기 쉽다.

작은 함수는 테스트하기 좋습니다. 한 가지 일만 하기 때문에 한 가지만 테스트하면 됩니다.

함수에 특별한 문제가 없어도 꺼낼 것이 있다면 분리하는 것이 좋습니다. 그렇게 하면 더 좋은 설계가 됩니다.

요점 정리

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