함수형 프로그래밍과 추상화

이효범·2022년 5월 4일
1
post-thumbnail

지금까지 작성했었던 함수들을 가지고 간단한 예제들을 통해 함수형 프로그래밍을 연습해보도록 하자.

추상화 레벨을 높인 함수

앞서 사용했었던 데이터를 우리가 작성했던 함수들을 가지고 HTML에 출력하는 예제이다.

우선 우리의 목표는 다음과 같다.

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 }
];

// 총 수량을 구해보자.
let total_quantity;

// 총 수량에 따른 가격의 총합을 구해보자.
let total_price;

이에 대해서 우선적으로는 다음과 같이 작성할 수 있겠다.

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_quantity = products => go(products,
		map(p => p.quantity),
		reduce((a, b) => a + b));

console.log(total_quantity(products)); // 15
// 위 코드는 pipe 함수를 이용하면 더욱 간결하게 표현할 수 있으니 곧바로 리팩토링 하도록 한다.

const total_quantity = pipe(
		map(p => p.quantity),
		reduce((a, b) => a + b));

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


// 총 수량에 따른 가격의 총합을 구해보자.
const total_price = pipe(
		map(p => p.price * p.quantity),
		reduce((a, b) => a + b));

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

이에 대해서 중복을 피하고 좀 더 간결하게 코드를 리팩토링 해보자.

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

const total_quantity = pipe(
		map(p => p.quantity),
		reduce(add);

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


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

console.log(total_price(products));

위와 같이 리팩토링을 하였지만 위와 같은 코드는 특정 도메인에서만 사용할 수 있는 재사용성이 낮은 코드라고 할 수 있다.

따라서 더욱 추상화를 높여보도록 하자.

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

const sum = (f, iter) => go(
  		iter,
		map(f),  // 이 부분을 통해 완전한 위임을 준다.
		reduce(add));

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

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


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

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

잘게 나누어가는 방식을 통해 훨씬 간결하고 재사용성이 높은 함수를 만들 수 있다.
이와 같은 함수는 인자로 원하는 동작 방식을 평가한 함수를 전달하면 되기 때문에,
특정 도메인에서만 사용해야하는 것이 아닌, 어디서든 유용하게 사용이 가능하다.

하지만 여기서 코드를 더욱 더 간결하게 만들 수 있는 방법이 있는데, 이전에 작성한 curry 함수를 다음과 같이 활용하여 코드를 작성할 수 있다.

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

const sum = curry((f, iter) => go(  // curry 함수로 감싸준다.
  		iter,
		map(f), 
		reduce(add)));

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

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

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

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

이제 위의 sum 함수는 위의 특정한 데이터의 재가공을 위해서만을 사용해야하는 것이 아니라
다음과 같이 다양한 데이터에 대해서도 수용이 가능하다.
즉 sum 함수는 추상화 레벨이 뛰어난 함수이다.

console.log(sum(user => user.age, [
  { age: 30 },
  { age: 20 },
  { age: 10 }
])));  // 60 

HTML에 출력하기

이제 위의 작성한 코드를 이용하여 실제 HTML 화면에 동적으로 데이터를 출력해보도록 하자.

<!DOCTYPE html>
<html>
<head>
	<title>산술 연산</title>
</head>
<body>
	<div id="cart"></div>
	<hr>
	<script>
		// ... 생략(위의 코드)
      
        document.querySelector("#cart").innerHTML = `
      		<table>
              <tr> 
                <th>상품 이름</th>
                <th>가격</th>
                <th>수량</th>
                <th>총 가격</th>
              </tr>
              ${go(products,
                  map(p => `
					<tr>
					  <td>${p.name}</td>
					  <td>${p.price}</td>
					  <td><input type="number" value="${p.quantity}"></td>
					  <td>${p.price * p.quantity}</td>
					</tr>
				`),
                reduce(add)
              )}
              <tr>
                <td colspan="2">합계</td>
                <td>${total_quantity(products)}</td>
                <td>${total_price(products)}</td>
              </tr>
      		</table>
      	`
	</script>
</body>
</html>

이제 이러한 코드를 작성했을 시 실제 화면에 나타난 것은 다음과 같이 표현된다.

그런데 우리가 앞서 작성한 sum 함수의 구조와

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

// 출력하는 코드
${go(products,
	 map(p => `
		<tr>
			<td>${p.name}</td>
			<td>${p.price}</td>
			<td><input type="number" value="${p.quantity}"></td>
			<td>${p.price * p.quantity}</td>
		</tr>
	`),
	reduce(add)
)}

위 템플릿 리터럴로 표현한 코드와 동일한 구조임을 확인할 수 있다.
따라서 이 코드 또한 sum 함수를 이용해 더욱 간결하게 코드를 작성할 수 있다.

<!DOCTYPE html>
<html>
<head>
	<title>산술 연산</title>
</head>
<body>
	<div id="cart"></div>
	<hr>
	<script>
		// ... 생략(위의 코드)
      
        document.querySelector("#cart").innerHTML = `
      		<table>
              <tr> 
                <th>상품 이름</th>
                <th>가격</th>
                <th>수량</th>
                <th>총 가격</th>
              </tr>
              ${go(products, sum(p => `
					<tr>
					  <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="2">합계</td>
                <td>${total_quantity(products)}</td>
                <td>${total_price(products)}</td>
              </tr>
      		</table>
      	`
	</script>
</body>
</html>

sum을 이용해 작성해도 코드가 잘 동작하는 것을 확인할 수 있다.
이렇게 함수형 프로그래밍은 다형성이 굉장히 높다.
어떠한 함수 하나가 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 }
];

따라서 선택되었는지에 대한 여부도 표현을 해보도록 하자.

<!DOCTYPE html>
<html>
<head>
	<title>산술 연산</title>
</head>
<body>
	<div id="cart"></div>
	<hr>
	<script>
		// ... 생략(위의 코드)
      
        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>
      	`
	</script>
</body>
</html>

선택되었는지 여부를 표시해 주었고, 선택된 상품의 수량과 가격만을 총 함계에 넣었다.
이에 대한 화면은 다음과 같다.

선택된 상품 수량 4개와 선택된 상품에 대한 총 가격을 표시하고 있는 것을 함수형 프로그래밍을 통해 잘 표현한 것을 확인할 수 있다.

profile
I'm on Wave, I'm on the Vibe.

1개의 댓글

comment-user-thumbnail
2024년 1월 26일

추상화 개념에 대해 궁금했는데 잘 읽었습니다!

답글 달기