[JS] 명령형 -> 함수형 테스팅

yongkini ·2023년 8월 3일
1

JS

목록 보기
2/2

코드 예시

import "./styles.css";

const cart = [
  { name: "사과", price: 3000, quantity: 30 },
  { name: "수박", price: 10000, quantity: 20 },
  { name: "복숭아", price: 5000, quantity: 40 }
];

const list = () => {
  let html = "<ul>";
  let totalCount = 0;
  let totalPrice = 0;

  for (let i = 0; i < cart.length; i++) {
    html += "<li>";
    html += `<h2>${cart[i].name}</h2>`;
    html += `<div>가격 : ${cart[i].price}원</div>`;
    html += `<div>수량 : ${cart[i].quantity}상자</div>`;
    html += "</li>";

    totalCount += cart[i].quantity;
    totalPrice += cart[i].price * cart[i].quantity;
  }

  html += "</ul>";

  html += `<h2>전체 수량: ${totalCount}상자</h2>`;
  html += `<h2>전체 가격: ${totalPrice}원</h2>`;

  return `
    ${html}
  `;
};

document.getElementById("app").innerHTML = `
<h1>용기네 과일 가게</h1>
<div>
  ${list()}
</div>
`;

위의 코드는 for loop 안에 너무 많은 관심사가 겹쳐져 있다. 이를 심플하게 3개의 for문으로 먼저 분리해보자.

import "./styles.css";
import * as C from "./main";

const cart = [
  { name: "사과", price: 3000, quantity: 30 },
  { name: "수박", price: 10000, quantity: 20 },
  { name: "복숭아", price: 5000, quantity: 40 }
];

const list = () => {
  let html = "<ul>";

  for (let i = 0; i < cart.length; i++) {
    html += "<li>";
    html += `<h2>${cart[i].name}</h2>`;
    html += `<div>가격 : ${cart[i].price}원</div>`;
    html += `<div>수량 : ${cart[i].quantity}상자</div>`;
    html += "</li>";
  }
  html += "</ul>";

  let totalCount = 0;
  for (let i = 0; i < cart.length; i++) {
    totalCount += cart[i].quantity;
  }
  html += `<h2>전체 수량: ${totalCount}상자</h2>`;

  let totalPrice = 0;
  for (let i = 0; i < cart.length; i++) {
    totalPrice += cart[i].price * cart[i].quantity;
  }

  html += `<h2>전체 가격: ${totalPrice}원</h2>`;

  return `
    ${html}
  `;
};

document.getElementById("app").innerHTML = `
<h1>용기네 과일 가게</h1>
<div>
  ${list()}
</div>
`;

하지만 여전히 문제가 있다. 일단 문제는 제쳐두고 여기에 품절 처리를 하는 로직만 추가해보자

import "./styles.css";
import * as C from "./main";

const cart = [
  { name: "사과", price: 3000, quantity: 30, outOfStock: false },
  { name: "수박", price: 10000, quantity: 20, outOfStock: true },
  { name: "복숭아", price: 5000, quantity: 40, outOfStock: false }
];

const list = () => {
  let html = "<ul>";

  for (let i = 0; i < cart.length; i++) {
    if (cart[i].outOfStock === false) {
      html += "<li>";
      html += `<h2>${cart[i].name}</h2>`;
      html += `<div>가격 : ${cart[i].price}원</div>`;
      html += `<div>수량 : ${cart[i].quantity}상자</div>`;
      html += "</li>";
    } else {
      html += "<li class='gray'>";
      html += `<h2>${cart[i].name} (품절)</h2>`;
      html += `<div class='strike'>가격 : ${cart[i].price}원</div>`;
      html += `<div class='strike'>수량 : ${cart[i].quantity}상자</div>`;
      html += "</li>";
    }
  }
  html += "</ul>";

  let totalCount = 0;
  for (let i = 0; i < cart.length; i++) {
    if (cart[i].outOfStock === false) {
      totalCount += cart[i].quantity;
    }
  }
  html += `<h2>전체 수량: ${totalCount}상자</h2>`;

  let totalPrice = 0;
  for (let i = 0; i < cart.length; i++) {
    if (cart[i].outOfStock === false) {
      totalPrice += cart[i].price * cart[i].quantity;
    }
  }

  html += `<h2>전체 가격: ${totalPrice}원</h2>`;

  return `
    ${html}
  `;
};

document.getElementById("app").innerHTML = `
<h1>용기네 과일 가게</h1>
<div>
  ${list()}
</div>
`;

여기까지 명령형 프로그래밍 방식을 써서 구현을 해봤는데, 이걸 함수형으로 커스텀해보자. 그리고 이 때 사용할 키워드는 고차 함수이다.

고차함수(고계함수)

특징

  • 하나 이상의 함수를 인수로 취한다.
  • 함수를 결과로 반환한다.

map 함수 구현해보면서 고차함수 이해해보기(추상화된 함수)

export const map = <A, B>(array: Array<A>, f: (a:A) => B): Array<B> => {
	 const result: Array<B> = [];
     
	 for( const value of array) {
     	result.push(f(value)); 
     }
     
     return result;
}

TS Generic 이해하기

type MapType<A,B> = (xs: Array<A>, f: (x:A) => B) => Array<B>;

type MapType1 = MapType<number, string>;

const map: MapType1 = (array, f) => {
	 const result = [];
     
	 for( const value of array) {
     	result.push(f(value)); 
     }
     
     return result;
}

const arr = [1,2,3,4,5];
console.log(map(arr, (el) => el+ ""));

type Compose<A, B, C> = (g: (y:B) => C, f: (x:A) =>B) => (a:A) =>C;
type Compose1 = Compose<string, number, boolean>; 

다시 구현으로 돌아와서

구현 설계

1) 장바구니를 그려야 한다.

  • 장바구니를 순회하면서
  • 화면에 상품 이름, 가격, 수량 등을 표시한다.

2) 전체 가격과 전체 수량도 화면에 그려야 한다.

  • 2번의 동작을 수행할 때 totalPrice, totalCount에 값을 누적한다.

3) 재고 없는 상품의 처리

  • 2번과 6번의 동작을 수행할 때 재고 여부에 따라 다르게 동작시킨다.

순수함수의 조합

: 작은 단위의 순수 함수들을 레고 블록처럼 조립해서 큰 단위의 프로그램을 만들어보자.

기능 단위 구분

아이템 목록 화면

  • 재고가 있는 아이템
  • 재고가 없는 아이템

전체 수량 표시
전체 가격 표시

실제 구현

사실 리액트를 써봤다면 위의 코드 구조가 좀 더 익숙하게 다가올 것이라고 생각된다. 이렇게 본래 명령형 프로그래밍으로 만든 기능을 -> 함수형 프로그래밍으로 리팩토링 해봤다. 컴퓨터에 명령을 내리는 듯한 방식이 아니라 인간의 발상을 옮겨놓은 듯한 코딩이 포인트라고 하는데, 아직은 확 와닿지는 않는다. 좀더 살펴보면서 아이디어를 얻는 방향으로 공부해보자.

포인트 복습

: 사람이 사고하는 방식과 코드를 유사하게 만들자..(?)

forEach vs map

: forEach는 return 값이 void이다(보통). 따라서,

한계?

: 시간복잡도 측면의 효율성 vs 인간의 사고 방향과 유사한 코드 작성(직관적인 코드)

2중 for문

: for 문 vs map & flatMap (or flat() )

** 참고로 flat() 은 cloneDeep처럼 재귀함수 형태로 돼있기 때문에 모든 배열 depth를 flat하게 하고자 하면 flat(Infinity); 이렇게 써주면된다(파라미터가 depth이다 flat(depth)).

profile
완벽함 보다는 최선의 결과를 위해 끊임없이 노력하는 개발자

0개의 댓글