지금까지 학습한 함수들

const curry = (f) => (a, ..._) => (_.length ? f(a, ..._) : (..._) => f(a, ..._));

const map = curry((f, iter) => {
  let res = [];
  for (const a of iter) {
    res.push(f(a));
  }
  return res;
});

const filter = curry((f, iter) => {
  let res = [];
  for (const a of iter) {
    if (f(a)) res.push(a);
  }
  return res;
});

const reduce = curry((f, acc, iter) => {
  if (!iter) {
    iter = acc[Symbol.iterator]();
    acc = iter.next().value;
  }
  for (const a of iter) {
    acc = f(acc, a);
  }
  return acc;
});

const go = (...args) => reduce((a, f) => f(a), args);

const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);


총 수량 구하기

const products = [
  {name: '반팔티', price: 15000, quantity: 1},
  {name: '긴팔티', price: 20000, quantity: 2},
  {name: '핸드폰케이스', price: 15000, quantity: 3},
  {name: '후드티', price: 30000, quantity: 4},
  {name: '바지', price: 25000, quantity: 5}
];

go(
	products,
	map(p => p.quantity);    // [1,2,3,4,5]
	reduce((a,b) => a + b),  // 15
)

// 코드 정리
const add = (a, b) => a + b;

// 총 수량
const total_quantity = product => go(product,
	map(p => p.quantity),
	reduce(add);

// Pipe 적용
const total_quantity = pipe(
	map(p => p.quantity),
	reduce(add);

console.log(total_quantity(products));  // 15


총 금액 구하기

const products = [
  {name: '반팔티', price: 15000, quantity: 1},
  {name: '긴팔티', price: 20000, quantity: 2},
  {name: '핸드폰케이스', price: 15000, quantity: 3},
  {name: '후드티', price: 30000, quantity: 4},
  {name: '바지', price: 25000, quantity: 5}
];

const total_price = pipe(
	map(p => p.price * p.quantity),
	reduce(a

console.log(total_price(products));  // 345000


리팩토링 해보기

  • total_quantity, total_price 내부에 map 함수 인자에 전달하는 부분을 제외하고는 동일한 코드
  • 현재, map 함수 실행 후 reduce 실행하는 과정이 특정 도메인(products)에 의존하고 있음
  • 결국 total_quantity, total_price 은 특정 도메인(products)을 위한 코드 임
  • 추상화 레벨을 높여, 많은 곳에 사용할 수 있도록 리팩토링 진행
const products = [
  { name: '반팔티', price: 15000, quantity: 1 },
  { name: '긴팔티', price: 20000, quantity: 2 },
  { name: '핸드폰케이스', price: 15000, quantity: 3 },
  { name: '후드티', price: 30000, quantity: 4 },
  { name: '바지', price: 25000, quantity: 5 },
];

// 첫 번째 인자로 함수를 받아서 외부에서 위임하는 방식
// 즉, 파라미터에 전달할 때 결정할 수 있도록 리팩토링 가능
const sum = (f, iter) => go(iter, map(f), reduce(add));

// 총 수량
const total_quantity = (products) => sum((p) => p.quantity, products);

// 총 금액
const total_price = (products) => sum((p) => p.price * p.quantity, products);


커링을 이용해 더 리팩토링 해보기

const sum = curry((f, iter) => go(iter, map(f), reduce(add)));

// curry으로 sum함수를 감싸게 되면
// 밑에처럼 실행 가능
const total_quantity = (products) => sum((p) => p.quantity)(products);

// 즉, products를 받는 함수가
// 바로 sum이 리턴하는 함수에 products를 전달하고만 있기 때문에
// products를 받는 자리에 sum(p => p.quantity) 이 코드를 대신해서 평가해도
// 똑같이 동작 함
const total_quantity = sum((p) => p.quantity);
  • 다른 도메인에서도 사용
// sum은 다른 도메인에서도 사용 가능 하기때문에
// 추상화 레벨이 높음
sum((user) => user.age, [{ age: 30 }, { age: 20 }, { age: 10 }]);


함수형 프로그래밍으로 HTML로 출력하기

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>HTML 출력해보기 - 장바구니</title>
  </head>

  <body>
    <div id="cart"></div>
  </body>
</html>
  • JS를 통해서 HTML 랜더링
  • 템플릿 리터럴을 통해서 동적으로 표현
  • Sum이라는 함수를 통해 다양한 곳에서 사용 가능
  • Reduce를 통해 HTML을 하나의 문자열로 합침
const products = [
  { name: '반팔티', price: 15000, quantity: 1, is_selected: true },
  { name: '긴팔티', price: 20000, quantity: 2, is_selected: false },
  { name: '핸드폰케이스', price: 15000, quantity: 3, is_selected: true },
  { name: '후드티', price: 30000, quantity: 4, is_selected: false },
  { name: '바지', price: 25000, quantity: 5, is_selected: false },
];

const add = (a, b) => a + b;

const sum = curry((f, iter) => go(iter, map(f), reduce(add)));

const total_quantity = sum((p) => p.quantity);

const total_price = sum((p) => p.price * p.quantity);

document.querySelector('#cart').innerHTML = `
    <table>
      <tr>
        <th></th>
        <th>상품 이름</th>
        <th>가격</th>
        <th>수량</th>
        <th>총 가격</th>
      </tr>
      ${go(
        products,
        sum(
          (p) => `
          <tr>
            <td><input type="checkbox" ${p.is_selected ? 'checked' : ''}></td>
            <td>${p.name}</td>
            <td>${p.price}</td>
            <td><input type="number" value="${p.quantity}"></td>
            <td>${p.price * p.quantity}</td>
          </tr>
      `,
        ),
      )}
      <tr>
        <td colspan="3">합계</td>
        <td>${total_quantity(filter((p) => p.is_selected, products))}</td>
        <td>${total_price(filter((p) => p.is_selected, products))}</td>
      </tr>
    </table>
  `;



느낀점

이번 학습을 통해 함수형 프로그래밍으로 HTML을 출력하는 내용을 학습하면서 추상화가 높다는 의미가 어떤 것인지 다시 한번 알게 되었다. 결국 코드를 재 사용할 수 있는 범위가 높을 수록 효율적이고 생산적으로 일 할 수 있다는 것도 깨닫게 되었다. 더 제대로 학습해서 실무에서 적용할 수 있도록 해야 겠다.


참고

유인동님의 함수형 프로그래밍과 JS ES6+ 강의