액션에서 계산 빼내기
May 8, 2024
쏙쏙들어오는 함수형 코딩
# 함수의 입출력과 액션의 관계
용어 설명
- 명시적 입력 : 인자
- 암묵적 입력 : 인자 외 다른 입력
- 명시적 출력 : 리턴값
- 암묵적 출력 : 리턴값 외 다른 출력
let total = 0;function add_to_total(amount /* 명시적 출력 */) {console.log("Old total: "+ total /* 전역변수(total)를 읽는건 암묵적 입력 */);total += amount; /* 전역변수를 바꾸는 것도 암묵적 입력 */return total; /* 리턴값은 명시적 출력 */}
위의 코드와 같이 함수에 암묵적 입력과 출력이 있으면 액션이 됨.
암묵적 입력과 출력을 부수 효과라고 부른다. 부수효과는 함수가 하려고 하는 주요기능이 아님.
함수는 입력에따라서 리턴되는 값이 항상 같아야하는데, 전역변수를 사용하고 변경했으므로 함수의 역할에서 벗어났다고 할 수 있음.
# 코드의 재사용을 높이고, 코드의 테스트를 용이하게 하기
몇가지 중요한 규칙이 있다.
- 액션과 계산은 분리되어야 함.
- 전역변수에 의존하지 말아야함.
- 함수가 어디에서든 실행 가능하게 환경을 타면 안됨.
- 함수는 리턴값을 리턴해야 함.
- 전역변수가 없어야 함.
액션에서 계산 빼내기
원래 코드
function calc_cart_total() {shopping_cart_total = 0;for(let i=0; i<shopping_cart_length; i++) {let item = shopping_cart[i];shopping_cart_total += item.price;};set_cart_total_dom();update_shopping_icons();update_tax_dom();}
바꾼 코드(1)
function calc_cart_total() {calc_total();set_cart_total_dom();update_shopping_icons();update_tax_dom();}function calc_total() {shopping_cart_total = 0;for(let i=0; i<shopping_cart_length; i++) {let item = shopping_cart[i];shopping_cart_total += item.price;};}
하나의 액션에서 calc_total이라는 함수를 분리하여 사용하도록 수정했음. 하지만 분리한 함수는 암묵적 입력이 있으므로 액션임. 이 함수도 액션과 계산이 분리되어야함.
바꾼 코드(2)
function calc_cart_total() {shopping_cart_total = calc_total();set_cart_total_dom();update_shopping_icons();update_tax_dom();}function calc_total() {let total = 0; /* 지역변수 사용 */for(let i=0; i<shopping_cart_length;/* 암묵적 입력 사용 */ i++) {let item = shopping_cart[i];total += item.price;};return total; /* 지역변수 리턴 */}
전역변수를 사용하지 않고, 지역변수를 사용했으며, 지역변수값을 계산하여 출력한 모습임. 하지만 암묵적 입력이 아직 사용되고 있음. 이부분은 따로 인자로 받는게 좋을듯함.
바꾼 코드(3)
function calc_cart_total() {shopping_cart_total = calc_total(shopping_cart /* shopping_cart를 인자로 전달 */);set_cart_total_dom();update_shopping_icons();update_tax_dom();}function calc_total(cart) {let total = 0;for(let i=0; i<cart_length;/* 전역변수대신 인자 사용 */ i++) {let item = cart[i];total += item.price;};return total;}
암묵적 입력을 사용했던 함수에서 암묵적 코드를 인자로 대체한 함수로 리팩토링 함으로써 함수의 모든 입력은 인자이고, 모든출력값은 리턴값이 되었음.
이로써 위에서 적어놓은 중요한 규칙을 다 지킬수 있게 됨.
# 액션에서 또 다른 계산 빼내기
원래 코드
let shopping_cart = []function add_item_to_cart(name, price) {shopping_cart_push({name,price});calc_cart_total();}
바꾼 코드(1)
let shopping_cart = []function add_item_to_cart(name, price) {shopping_cart = add_item(shopping_cart, name, price);calc_cart_total();}function add_item(cart, name, price) {cart.push({name,price});}
전역변수에 값을 추가하는 계산을 따로 함수로 분리한 모습임. 하지만 전역변수에 직접 영향을 끼치는함수다보니(암묵적 입력) 수정이 필요해보임.
바꾼 코드(2)
let shopping_cart = []function add_item_to_cart(name, price) {shopping_cart = add_item(shopping_cart, name, price);calc_cart_total();}function add_item(cart, name, price) {const new_cart = cart.slice(); /* 복사본을 만들어 지역변수에 할당 */new_cart.push({name,price});return new_cart; /* 복사본 리턴 */}
복사본을 만들어 지역변수에 할당후 계산, 리턴한 모습임. 이로써 분리한 함수를 계산을 위한 함수로 만들었음.
# 요약
- 액션은 암묵적인 입력 또는 암묵적인 출력을 가지고 있음.
- 계산엔 암무적인 입력 또는 암묵적인 출력이 없어야 함.
- 전역변수는 일반적으로 암묵적 입력 또는 출력이 됨.
- 함수형 원칙을 사용하면 액션은 줄어들고, 계산은 늘어난다는것을 확인.