쏙쏙 들어오는 함수형 코딩 Chapter 5
🖍 쏙쏙 들어오는 함수형 코딩 - 심플한 코드로 복잡한 소프트웨어 길들이기
이 글은 쏙쏙 들어오는 함수형 코딩 - 심플한 코드로 복잡한 소프트웨어 길들이기를 읽고 작성한 글입니다. 함수형도 함수형이지만, 이 책을 통해 좀더 깔끔하고 직관적인 코드를 작성할 수 있을 것이란 팀원들의 의견을 토대로 이번 스터디 서적으로 선정하였습니다.
Chap 5. 더 좋은 액션 만들기
- 암묵적 입력과 출력을 제거해서 재사용하기 좋은 코드 만들기
- 복잡하게 엉킨 코드를 풀고 더 좋은 코드 만들기
모든 액션을 없앨 수는 없다. 액션은 필요하다. 하지만 암묵적 입력과 출력을 줄임으로써 액션을 최소화할 수 있다.
1) 비즈니스 요구사항과 설계 고려하기
- 기계적인 리팩토링만이 항상 최선의 구조를 만들어주진 않는다.
- 비즈니스 요구사항을 고려해야 한다.
비즈니스 요구 사항: 장바구니에 담긴 제품을 주문할 때 무료 배송인지 확인하는 기능
아래 함수는 비즈니스 요구사항인 장바구니에 담긴 제품
에 대한 내용 없이 총액
과 무료배송
에 대한 내용만을 가지고 있다.
function getFreeShipping(total, itemPrice) { return total + itemPrice >= 20; }
이를 좀더 비즈니스 로직에 맞게 개선하면 아래와 같다.
function getFreeShipping(cart) { return getTotalPriceFromCart(cart) >= 20; } function getTotalPriceFromCart(cart) { return cart.reduce((result, item) => result + item.price, 0); }
사실 위의 경우, 함수의 동작
을 변경했기 때문에 엄밀히 말하면 리팩토링
이라고 할 수 없다.
Tip1) 리팩토링(함수 분리)을 하다보면 코드가 더 길어지기도 하는데, 괜찮은건가?<br/>
- 함수를 분리함으로써 코드의 가독성이 높아지고, 재사용성이 높아지기 때문에 코드의 길이가 늘어나더라도 좋은 코드라고 할 수 있다.<br/> Tip2) 불변성 유지를 위해 객체를 복사하는 게 오히려 비용을 많이 쓰지 않나?<br/>
- 굳이 비교하자면 기존 객체를 변경하는 방식보다는 비용이 더 든다. 하지만 현대의 언어 런타임과 가비지 컬렉터는 이를 최적화하고 있기 때문에 큰 문제가 되지 않는다. 또한, 이에 반해 불변성을 유지했을 때 얻는 이점이 너무 크다.
2) 압묵적 입력과 출력은 적으면 적을수록 좋다!
- 암묵적 입력: 전역변수를 읽는 등
인자 외의 다른 모든 입력값
- 암묵적 출력: console.log(), DOM 변경, 전역변수 변경 등
반환값 외의 다른 모든 출력값
어떤 함수(컴포넌트)에 암묵적 입력과 출력이 있다면, 다른 함수(컴포넌트)와 강하게 연결된 컴포넌트가 된다. 이는 재사용성을 떨어뜨리고, 테스트하기 어렵게 만든다.
또한 다른 곳에서 사용할 수 없기 때문에 모듈
이라고 할 수 없다. 암묵적인 것들을 명시적으로 변경함으로써 모듈화할 수 있다.
계산: 암묵적 입력과 출력이 없는 함수 => 모듈화 가능 및 테스트하기 좋다.
3) 설계는 엉켜있는 코드를 푸는 것이다.
- 함수를 사용하면 자연스럽게 관심사를 분리할 수 있다.
- 함수는 인자와 반환값을을 사용하는 방법을 분리한다.(너무도 자연스럽게 input과 output을 나누게 되니까)
- 때로는 함수를 합치고 싶을 수 있다. 그러나 조합하는 것은 언제든 쉽게 할 수 있지만, 분리하는 것은 어렵다. 즉, 가능하면 함수를 분리해두는 것이 더 좋다.
함수를 작게 분리해두었을 때의 이점
- 재사용하기 쉽다.
- 유지보수하기 쉽다.
- 테스트하기 쉽다.
4) 패턴 빼내기(유틸 함수 만들기)
만약 아래와 같은 함수가 존재한다고 해보자.
function addItem(cart, item) { let newCart = [...cart]; newCart.push(item); return newCart; }
잘 생각해보자. 위의 함수는 과연 cart(장바구니)
에만 한정된 함수인가??? 아니다. 새로운 배열(cart)에 새로운 값(item)을 추가하는 유틸 함수
이다. 따라서 아래와 같이 변경해주는 것이 좋다.
function addItem(array, item) { let newArray = [...array]; newArray.push(item); return newArray; }
이로써 의미적으로 좀더 다양한 곳에서 활용할 수 있는 유틸 함수
를 만들었다.
Tip1) 코드에서 비즈니스 로직은 어떻게 구분할 수 있는가?<br/>
- 예를 들어
장바구니 기능을 담당하는 코드
는 대부분의 전자상거래 서비스에서 공통적으로 사용된다. 즉, 그 서비스만의 비즈니스 로직이라고 볼 수 없다.<br/>- 반면
총 가격이 50,000원이 넘어갈 때는 배송비가 무료
라는 기능은 그 서비스만의 로직이다. 즉, 그 서비스만의 비즈니스 로직이라고 볼 수 있다.<br/>- 일반적으로
비즈니스 로직
을 담당하는 코드는 더 자주 바뀐다.(기획에 따라서 언제든 변경되니까) 반면에 유틸 함수는 그렇지 않다.
5) 정리
- 일반적으로 암묵적 입력과 출력은 명시적 입력과 출력인
인자와 리턴값로 바꿔주는 게 좋다.
- 코드를 설계하는 것은 엉켜있는 것을 푸는 것이다. 최대한 많이 풀어두면 언제든 쉽게 조합할 수 있다.
- 엉켜있는 것을 풀어 각 함수가 각자 하나의 책임, 역할을 갖게 하면 개념을 기준으로 쉽게 코드를 구성할 수 있다.(ex. 장바구니 기능, 배송비 기능, 할인 기능, 유틸 함수 등)
undefined