[JS] 자바스크립트에서의 함수형 프로그래밍

Seju·2023년 7월 14일
1

JavaScript

목록 보기
22/28
post-thumbnail

함수형 프로그래밍

🤔 함수형 프로그래밍이란?


📝 함수형 프로그래밍이란
성공적인 프로그래밍을 위하여 부수효과를 미워하고, 조합성을 강조하는 하나의 프로그래밍 패러다임이다.

  • 부수효과를 미워한다
    • 개발시 순수함수를 설계한다
  • 조합성을 강조한다
    • 모듈화의 수준을 높인다
  • 프로그래밍 시 순수함수가 가져다 주는 이점
    • 오류를 줄이고 안정성을 높인다
  • 모듈화 수준을 높일때 가져다 주는 이점
    • 생산성이 증가하고, 재사용성이 증가한 다.

🐧 순수함수란?


  • 들어온 인자가 같은 항상 같은 결과를 리턴한다.
  • 함수가 받은 인자 외 다른 외부의 상태에 영향을 끼치지 않는 함수
  • 리턴값 외에는 외부와 소통하는 것이 없는 함수

순수함수 예제

function add(a, b) {
      return a + b;
    }

    console.log(add(10, 6));
  • 위코드에서 add()순수함수이다
  • add() 함수가 순수함수인 이유
    • 항상 동일한 인수를 주면 동일한 결과를 return하기 때문이다.
    • 부수효과(sideEffect)가 없다.
      • 함수가 리턴값으로 결과를 만드는 것 외에 외부의 상태에 영향을 끼치지않는다.

❓ 순수함수가 아닌경우는?

  1. 동일한 인수를 주었을때 상황에 따른 다른 결과를 return하는 함수
let c = 10;
    function add2(a,b){
      return a + b + c
    }
  • 만약 위 코드에서 add2()에서 참조하는 전역변수로 할당한 c가 무언가에 의해 c라는 값이 변한다면 add2()순수함수가 아니다
  • 그러나 clet이 아닌 const, 즉 상수로서 동작한다면 add2()도 순수함수로 볼 수 있다.
console.log(add2(10, 2));
    console.log(add2(10, 3));
    console.log(add2(10, 5));
	// 함수실행시 c의 상태가 변경된다면?
    c = 20;
    console.log(add2(10, 2));
    console.log(add2(10, 3));
    console.log(add2(10, 5));

인자는 동일한 인자를 넣었지만 c가 변화함에 따라 add2()가 다른 결과를 낳고 있다

  1. 부수효과를 일으키는 함수
let c = 20;
    function add3(a, b) {
      c = b;
      return a + b;
    }
  • 위 코드는 부수효과가 있는 함수고, 순수함수가 아니다
    • c=b;와 같이 리턴값 외에도 함수가 다른 외부 상태(현재에선 전역변수로 설정한 c)에도 영향을 미치기 때문에
	console.log(c); //20
	//함수실행시 두번째 파라미터로 30을 전달했다 ➡️ 즉, 30에 b가 할당되었고 c에도 30이 할당되었다.
    console.log(add3(20, 30));
	//부수효과로 인해 c의 결괏값이 달라진다
    console.log(c); //30

비순수함수를 순수함수로 만들기

비순수함수

var obj1 = {val: 10};

    function objFunction(obj, b) {
      obj1.val += b;
    }

    console.log(obj1.val); //10
    objFunction(obj1, 20);
	// 함수로인해 obj.val가 변경되었음
    console.log(obj1.val); //30
  • 위코드는 objFunction()으로 객체의 key에 직접 참조해서 기존 obj객체의 value를 직접적으로 변경하고 있는 상태이다.

순수함수로 구현한다면?

var obj1 = {value: 10};

    function objFunction(obj, b) {
      return {val: obj.value + b};
    }
  • objFunction()의 리턴값으로 새로운 객체를 생성하며, 해당 val의 값은 obj.value + b이다.
  • 함수는 생성된 새로운 객체를 반환한다
  • 해당 함수는 새로운객체를 생성하기 때문에 전역 변수로 선언된 obj1을 훼손시키지않는다.

🚧 함수형으로의 리팩토링


일반적인 절차지향형 프로그래밍

users라는 객체로 이루어진 배열이 존재한다.

const users = [
      {id: 1, name: "ID", age: 36},
      {id: 2, name: "BJ", age: 23},
      {id: 3, name: "JM", age: 32},
      {id: 4, name: "PJ", age: 12},
      {id: 5, name: "HA", age: 36},
      {id: 6, name: "JE", age: 31},
      {id: 7, name: "JI", age: 25},
      {id: 8, name: "MP", age: 23},
    ];

🔏 여기서 4가지 조건이 있는데,

1. 30세 이상인 users를 거른다

let temp_users = [];
    for (let i = 0; i < users.length; i++) {
      if (users[i].age >= 30) {
        temp_users.push(users[i]);
      }
    }

2. 30세 이상인 users의 names를 수집한다

const names = [];
for (let i = 0; i < temp_users.length; i++) {
  names.push(temp_users[i].name);
}

3. 30세 미만인 users를 거른다

let temp_users2 = [];
for (let i = 0; i < users.length; i++) {
  if (users[i].age < 30) {
    temp_users2.push(users[i]);
  }
}

4. 30세 미만인 users의 age를 수집한다

let ages = [];
//기존 수집했던 30세미만의 users의 배열(temp_users2)을 참조한다
for (let i = 0; i < temp_users.length; i++) {
  ages.push(temp_users2[i].age);
}
  • 해당 4가지의 코드 모두 평범한 for문을 이용해 새로 선언한 배열에 push하는 로직이다
  • 해당 코드들을 전부 함수형으로 리팩토링하면?

함수형 프로그래밍

먼저 30세이상/30세미만의 users들을 걸러내는 코드로직이 중복되는 부분이 많다.
조건부 일부만 다르고 나머지 코드로직은 전부 동일한형태로 돌아가고있다.

🎇 _filter() 함수 만들기


const _filter = (list, cut) => {
  let new_list = [];
  for (let i = 0; i < list.length; i++) {
    if (cut(list[i])) {
      new_list.push(list[i]);
    }
  }
  return new_list;
};

const filteredOver30 = _filter(users, (user) => {
  return user.age >= 30;
});

const filteredUnder30 = _filter(users, (user) => {
  return user.age < 30;
});
  • _filter함수의 매개변수로 listcut을 전달받고있는데 해당 list는 내가 전달할 객체 또는 배열이다, cut()은 전달인자로 전달할 함수로 cut() 내에서 조건처리를 하는 함수를 전달받는다.
  • new_list라는 빈 배열을 선언 후 할당하고,
  • 해당 listfor문으로 반복하게되는데, cut()함수를 if문 안쪽에서 실행하게 하면서 user들을 조건처리하게된다. 해당 조건이 true로 평가되면, new_List에 조건에 통과한 list[i](배열로 받는 경우list의 i번째 요소, 객체로 받을땐 list의 i번째 속성의 value)을 push한다
  • 최종적으로 새로이 담은 new_list배열을 return한다

_filter()함수로 객체의 value들이 필터링 된 모습

두번째로, 30세 이상/30세 미만인 usersnames를 수집하는 코드로직을 모든 값을 받을 수 있는 유연한 _map()으로 리팩토링해보자.

🎇 _map() 함수 만들기

const _map = (list, mapping) => {
  const new_list = [];
  for (let i = 0; i < list.length; i++) {
    new_list.push(mapping(list[i]));
  }
  return new_list
};

const newMappingUserValue = _map(users, (user) => {
  return user.name;
});
  • _filter함수와 마찬가지로 listmapping()이란 함수를 파라미터로 전달받는다,
  • mapping()으로 객체의 value를 반복문을 돌면서 새로운값으로 return한다.

_map()함수의 인수로 list와 mapping함수(users의 name만을 return하는)를 전달하고 있다

🤔 그러면 해당 함수를 이용해서 30세 이상인 새로운배열로 이루어진 users의 name만을 얻고싶다면?

const over30Users = _filter(users, (user) => user.age >= 30);
const over30UsersName = _map(over30Users, (user) => user.name);
  • over30Users라는 변수를 선언에 _filter()userage가 30세이상인 객체들을 배열로 만들어 할당하고
  • _map()에 첫번째 인수로 over30Users로 새로 필터링된 배열을 전달후,
    over30Users 배열에서 name값만을 반환하는 배열을 새로 over30UsersName 변수에 최종적으로 전달함

users 객체에서 30세 이상인 user의 name들을 받아 배열로 반환한 모습

🫣 마치며..

함수형 프로그래밍을 찍먹해봤는데, 이게 함수형프로그래밍에서의 코드중에서도 기초중의 기초 인데도 벌써부터 로직이 이해하기 어려워지는 느낌을 많이 느꼈다. 클로저에 대한 정확한 이해도 필요하고, 함수형프로그래밍의 특성상 함수는 무조건 중첩이 되는데, 함수가 중첩될때, 어떤함수가 파라미터로 전달받고, 또 어떤함수가 인수로 함수에 전달하는지에 대한 연습도 필요할것같다. 따라서 먼저 기본 javascript를 더 숙달한후, 함수형프로그래밍은 조금씩 천천히 연습해보는걸로해야겠다.. 👀

profile
Talk is cheap. Show me the code.

0개의 댓글