본문으로 건너뛰기

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();
}

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

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

재사용하기 쉽다.​

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

유지보수하기 쉽다.​

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

테스트하기 쉽다.​

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

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

요점 정리​

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