안녕하세요 볼드나인 서버 개발자 이준규 입니다
저는 요즘 공부하고 있는 함수형 프로그래밍에 대해 이야기해 보려고 합니다.
함수형 프로그래밍과 객체지향에 대해서는 익히 들어서 알고는 있었지만 정확히 어떤 패러다임인지 알지는 못했어요.
일하며 여러 레퍼런스를 보던 중 책 쏙쏙 들어오는 함수형 프로그래밍 이란 책을 접하게 되었고 책의 후기를 보니 개념적으로 설명만 하는 책이 아닌 현실적인(?) 함수형 프로그래밍을 알려주는 책이라고 해서 더 관심이 가서 읽어보게 되었습니다.
간략한 후기를 남기면 다 읽어보진 못 했지만 수많은 글과 코딩 예시들을 가독성 좋고 부담스럽지 않은 그림과 글로 설명하여 이해하기 쉬웠으며 챕터를 넘어갈 때마다 스스로 생각해 볼 수 있는 페이지도 마련되어 있어 더 몰입 되더라고요. 이걸 볼드나인 시스템 중 어디에 먼저 적용할 수 있을까 생각해 봤어요.
볼드나인 시스템에는 market-api라는 OMS 서버가 있습니다.
OMS 서버에는 여러 마켓들이 있어요. 쿠팡, 스마트 스토어 등등 서로 비슷하거나 같은 기능들을 사용하기에 함수형 프로그래밍을 적용한다면 코드를 서로 공유하여 코드가 매우 간결해지고 새로운 마켓을 연동할 때도 시간 절약에서도 큰 도움이 될 것 같아요. 어서 적용해 보고 싶네요!
우선 함수형 프로그래밍이 무엇인지 실제 어떻게 코드가 달라지는지에 대해 알아보죠!
함수형 프로그래밍 개념
함수형 프로그래밍(Functional Programming)은 프로그래밍 패러다임 중 하나로, 데이터 처리를 수학적 함수의 계산으로 취급하는 것을 중심으로 한 프로그래밍 방식이에요. 이에 따라 함수형 프로그래밍에서는 프로그램을 수학적 함수의 조합으로 생각하고, 상태 변경이나 가변 데이터보다는 불변 데이터와 함수 조합으로 문제를 해결해요.
이에 따라 함수형 프로그래밍에서는 다음과 같은 기본 개념이 있습니다.
순수 함수(Pure Function)
순수 함수는 함수형 프로그래밍에서 가장 중요한 개념 중 하나입니다. 순수 함수는 입력값이 같으면 항상 같은 결과를 반환하며, 부작용(side effect)이 없는 함수를 말합니다. 부작용이란 함수 외부의 상태(state)를 변경하거나, 입출력(I/O)을 수행하거나, 예외를 던지는 등의 동작을 말합니다. 이러한 부작용이 없는 순수 함수는 입력값만으로 결과를 예측할 수 있어 예측 가능하고, 동시성 처리에도 용이합니다.
불변 데이터(Immutable Data)
함수형 프로그래밍에서는 불변 데이터를 사용합니다. 불변 데이터란, 한 번 생성된 이후에는 변경할 수 없는 데이터를 말합니다. 이러한 불변 데이터는 안정성과 예측 가능성을 보장하며, 동시성 처리에도 용이합니다.
또한 불변 데이터는 함수 인자로 전달받은 데이터를 복사하여 원본 데이터를 조작하지 않도록 설계해야 해요. 그 인자 데이터는 어느 함수에서도 원 상태 그대로 받을 수 있어야 해요.
불변성 유지(Immutability)
함수형 프로그래밍에서는 상태를 변경하는 것이 아니라, 새로운 상태를 반환하는 방식으로 작업을 처리합니다. 이러한 방식을 통해 불변성을 유지할 수 있어, 프로그램의 안정성과 예측 가능성을 높일 수 있습니다.
고차 함수(Higher-Order Function)
고차 함수는 함수를 인자로 받거나, 함수를 반환하는 함수를 말합니다. 이러한 고차 함수를 사용하면, 함수를 인자로 전달하여 다양한 동작을 수행하거나, 함수를 반환하여 코드의 재사용성을 높일 수 있어요.
추상적인 내용들이라 저는 이해가 잘 안되더라구요!
제가 읽고 있는 책에서는 함수형 프로그래밍을 어떤 기준으로 분류하여 말하는데요.
중요하게 봐야 하는 3가지가 있었어요. 코드는 액션, 계산, 데이터로 구분된다는 점이에요 .
액션
실행 시점이나 횟수 또는 둘 다에 의존
예) 주문 수집, API 요청, 전역 변수 변경, DOM 변경
계산
받은 인자를 단순히 계산하여 output을 반환하며 실행을 해도 다른 곳에 영향을 주지 않는 것
예) 정산 금액 계산
데이터
db에서 가져온 데이터, 이벤트에 대한 사실
예) 주문 정보, 반품, 송장 정보 등
지금은 액션, 계산, 데이터 중 책에서 나온 쇼핑몰 예제로 액션에 대해 더 자세히 살펴볼게요.
쇼핑몰의 기능 중 장바구니에 담겨있는 제품들 금액의 합을 볼 수 있는 기능과 구매 합계가 20달러 이상이면 무료배송 아이콘을 띄워주는 기능입니다.
const shopping_cart = [] // 장바구니 제품과 금액 합계를 담고 있는 전역변수
let shopping_cart_total = 0
function add_item_to_cart(name, price){
// 장바구니에 제품을 담기 위해 배열에 주문 데이터 추가
shopping_cart.push({
name,
price
})
// 장바구니 제품이 업데이트 되었기에 금액 합계도 업데이트
calc_cart_total()
}
function calc_cart_total(){
shopping_cart_total = 0;
for(let i = 0; i < shopping_cart.length; i++){
const item = shopping_cart[i]
shopping_cart_total += item.price // 모든 제품의 금액 더하기
}
set_cart_total_dom() // 금액의 합계를 변경하기 위해 DOM 업데이트
update_shipping_icons() // 무료 배송인지 확인
}
function update_shipping_icons(){
const buy_buttons = get_buy_buttons_dom() // 페이지에 있는 모든 구매 버튼을 가져옵니다
for(let i = 0; i < buy_buttons.length; i++){
const button = buy_buttons[i]
const item = button.item
if(item.price + shopping_cart_total >= 20) // 무료배송이 가능한지 확인
button.show_free_shipping_icon() // 무료배송 아이콘을 보여줍니다
else
button.hide_free_shipping_icon() // 무료배송 아이콘을 보여주지 않습니다
}
}
JavaScript
복사
여기서 액션은 무엇인지 살펴보겠습니다.
•
shopping_cart (전역 변수)
•
shopping_cart_total (전역 변수)
•
add_item_to_cart() (전역 변수를 변경하는 함수)
•
calc_cart_total() (전역 변수를 변경하는 함수)
•
update_shipping_icons() (DOM을 바꾸는 것도 액션)
위에서 작성한 모든 코드가 액션입니다..! 전역변수를 변경하는 함수와 DOM을 변경하는 함수는 호출된 시점에 따라 전역변수의 값과 DOM의 결과가 다르기 때문입니다. 서로에게 영향을 주는 코드이기에 재사용성이 매우 떨어집니다.
어떤 함수 안에 액션이 하나만 있어도 그 함수 전체가 액션이 됩니다.
책에서는 아래 조건들로 코드를 수정합니다.
•
DOM 업데이트와 비즈니스 규칙은 분리되어야 한다.
•
전역변수에 의존적이지 않아야 한다.
•
함수가 결과값을 리턴해야 한다.
액션은 계산과 데이터로 분리할 수 있습니다.
const shopping_cart = [] // 장바구니 제품과 금액 합계를 담고 있는 전역변수
let shopping_cart_total = 0
function calc_cart_total(){
shopping_cart_total = 0;
for(let i = 0; i < shopping_cart.length; i++){
const item = shopping_cart[i]
shopping_cart_total += item.price // 모든 제품의 금액 더하기
}
set_cart_total_dom() // 금액의 합계를 변경하기 위해 DOM 업데이트
update_shipping_icons() // 무료 배송인지 확인
}
JavaScript
복사
// 1번
// 위 코드에서 계산을 분리합니다
// calc_total을 따로 분리하여 호출
function calc_cart_total(){
calc_total()
set_cart_total_dom() // 금액의 합계를 변경하기 위해 DOM 업데이트
update_shipping_icons() // 무료 배송인지 확인
}
// 분리했지만 아직 완벽하진 않습니다 shopping_cart_total 전역변수를 사용하고 있네요!
function calc_total(){
shopping_cart_total = 0;
for(let i = 0; i < shopping_cart.length; i++){
const item = shopping_cart[i]
shopping_cart_total += item.price // 모든 제품의 금액 더하기
}
}
JavaScript
복사
// 2번
// 전역 변수(shopping_cart_total)를 사용하지 않고 결과값을 return하겠습니다.
function calc_total(){
let total = 0;
for(let i = 0; i < shopping_cart.length; i++){
const item = shopping_cart[i]
total += item.price // 모든 제품의 금액 더하기
}
return total
}
// 아래 함수에서 calc_total의 결과값을 전역변수에 할당하도록 수정합니다.
function calc_cart_total(){
shopping_cart_total = calc_total()
set_cart_total_dom() // 금액의 합계를 변경하기 위해 DOM 업데이트
update_shipping_icons() // 무료 배송인지 확인
}
JavaScript
복사
// 3번
// 아직 2번의 calc_total()는 아직 전역 변수인 shopping_cart를 사용합니다.
// 인자로 받아 사용하도록 수정하겠습니다
function calc_total(cart){
let total = 0;
for(let i = 0; i < cart.length; i++){
const item = cart[i]
total += item.price // 모든 제품의 금액 더하기
}
return total
}
JavaScript
복사
마지막으로 코드 수정의 기준들이 부합했는지 확인해 보겠습니다.
1. DOM 업데이트와 비즈니스 규칙은 분리되어야 한다. (장바구니 합계를 계산하는 것이 비즈니스 규칙입니다.) ⭕️
2.
전역변수에 의존적이지 않아야 한다. ⭕️
3.
함수가 결과값을 리턴해야 한다. ⭕️
모든 조건에 부합하게 코드를 수정하였습니다. calc_total 함수는 더 이상 전역 변수를 사용하지 않고 비즈니스 규칙과 액션(calc_cart_total 함수)을 분리하였으며 결과값을 리턴합니다!
위 예시들이 액션이란 개념을 이해하시는 데 도움이 되셨으면 좋겠습니다!
아직 살펴볼 내용은 많지만 간단히 액션은 무엇이고 어떻게 분리해 내는지 알아보았습니다.
액션을 분리할 때 리팩토링처럼 기존 동작과 기능은 유지하며 코드를 유연하게 수정해야 합니다.
함수형 프로그래밍을 더 공부하여 다음에는 OMS 시스템 리팩토링 후기를 써보겠습니다.
읽어주셔서 감사합니다