본문 바로가기

Backend/함수형 자바스크립트

더 많은 함수형 자바스크립트 사용.. (1)

- _.each

 

일반적으로 코드 작업을 할 때, 우리는 분기 처리를 하기 위해 if 문을 굉장히 자주 사용합니다.

간단한 예시를 보겠습니다.

 

const test1 = (list) => {
  if (Array.isArray(list)) {
    list.forEach(item => {
      console.info(item);
    });
  }
};

test1([1, 2, 3]);
// 1
// 2
// 3
test1('123');

 

배열 전용 메서드인 forEach 를 타입 에러 없이 사용하기 위해 if 문을 넣은 test1 함수입니다.

만약 위와 같은 if 문이 없었다면 test1('123'); 실행은 TypeError: list.forEach is not a function 와 같은 에러를 냈을 것 입니다.

이런 함수를 작성할 때 _.each 를 사용하면, if 문 없이도 에러가 나지 않고 실행이 가능합니다.

 

const _ = require('partial-js');

const test1 = (list) => {
  _.each(list, item => {
    console.info(item);
  });
};

test1([1, 2, 3]);
// 1
// 2
// 3
test1('123');
// 1
// 2
// 3
test1(undefined);

 

if 문이 없어도 string type 인 '123' 에서 정상 작동하며, undefined 에선 출력되는 결과물은 없지만 에러는 발생하지 않습니다.

이는 _.each 가 어느정도 다형성을 지원하면서, 지원하지 않는 타입의 변수가 들어왔을 때 에러를 던지지 않도록 내부에 적절한 처리가 되어 있기 때문입니다. (이전에 partial-js 의 여러 함수들을 소개하면서 이미 한 번 얘기한 부분이기도 합니다)

 

JS 로 개발시엔 생각보다 위와 같은 상황이 빈번하게 나오는데, 간단히 _.each, _.map, _.filter 정도를 사용하는 것 만으로도 분기 처리를 생략할 수 있습니다. 사실 typescript 를 사용한다면, 실용성은 조금 떨어지는 얘기긴 합니다.

 

- _.map, _.filter, _.reduce..

 

partial-js 의 다양한 함수들을 폭넓게 쓰고 있진 않지만, _.map 과 _.filter 및 특히 _.reduce 는 꽤 자주 활용을 하고 다른 팀원의 코드리뷰를 할 때도 단골 메뉴처럼 언급하고 있습니다.

 

const itemList = [
  { id: 1, name: 'a', group_id: 1 },
  { id: 2, name: 'b', group_id: 1 },
  { id: 3, name: 'c', group_id: 2 },
  { id: 4, name: 'd', group_id: 3 }
];
const items = {};

for (let i = 0 ; i < itemList.length ; i++) {
  const { name, group_id } = itemList[i];
  
  if (!items[group_id]) {
    items[group_id] = [];
  }
  
  items[group_id].push(name);
}

console.info(items); // { '1': [ 'a', 'b' ], '2': [ 'c' ], '3': [ 'd' ] }

 

group_id 를 key 로 해서 같은 group_id 끼리 묶은 name 배열을 value 로 같는 object 를 하나 만든다고 할 때,

위의 방식은 일반적으로 접근할 수 있는 방법이고 실제로 실무에서도 이렇게 코드를 작업하는 사람이 많습니다.

 

const _ = require('partial-js');
const itemList = [
  { id: 1, name: 'a', group_id: 1 },
  { id: 2, name: 'b', group_id: 1 },
  { id: 3, name: 'c', group_id: 2 },
  { id: 4, name: 'd', group_id: 3 }
];
const items = _.reduce(itemList, (acc, { name, group_id }) => {
  if (!acc[group_id]) {
    acc[group_id] = [];
  }

  acc[group_id].push(name);

  return acc;
}, {});

console.info(items); // { '1': [ 'a', 'b' ], '2': [ 'c' ], '3': [ 'd' ] }

 

동일한 로직을 _.reduce 로 바꾼 코드입니다. 처음엔 오히려 생소하고 일반적인 for-loop 를 사용하는게 더 직관적이라고 느껴질 수 있습니다. 사실 위와 같은 예시는 너무 간단하기 때문에 굳이 reduce 를? 라는 생각이 들기도 합니다. 하지만 값을 점점 누적시키는 reduce 메서드의 특성을 이해하면, 두번째 인자로 들어가는 함수만 보더라도 어떤 값이 나올지 쉽게 파악이 가능합니다.

 

또한 지금은 따로 분리를 하지 않았지만, 두번째 인자에 들어간 부분을 별도의 함수로 분리해 다른 문제 해결에 재사용할 수도 있고, 지난 장에서 계속해 봤던 것 처럼 함수를 더 추상화시켜 다른 유용한 함수를 만들어낼 수 도 있습니다.

 

만약 어떤 값을 뽑아내고, 분기처리를 해서 새로운 객체에 넣는 작업이 단순 for-loop 로 만들어져있다면 해당 for 문 블락을 시작부터 끝까지 다 봐야지만 어떤 일이 벌어지는지 알 수 있을 겁니다. 하지만 동일한 코드가 _.map -> _.filter -> _.reduce 로 구성되어 있다면 훨씬 파악이 쉽고, 생산성은 높아지겠죠.

 

- 기본 객체

 

함수형 프로그래밍과 객체 지향 프로그래밍이 서로 대척점에 있지는 않지만, 지금까지 예시를 봐왔던 것 처럼 함수형 프로그래밍에서 사용하는 함수의 조합으로 로직을 만드는 방법은 기본 객체일수록 편하며 실용성이 높습니다.

 

비록 함수형 프로그래밍이 자바스크립트에서만 가능한건 아니지만, 많은 자바스크립트 개발자들이 기본 객체를 다루는 다양한 함수들을 만들어 공유하면 훨씬 더 많은 개발자들이 이를 활용해 더 쉽고 생산성 높은 프로그래밍을 할 것입니다.

이렇게 된다면, 언어가 달라도 문제는 같을 시 자바스크립트에서 공유된 함수의 조합이 다른 언어에서도 동일하게 적용해 동일한 문제를 풀어낼 수 있게 될 것입니다.

 

개인적으로는 저도 아직 익숙해지진 않았지만, _.reduce 는 정말 다양하게 사용하면서 기능 개발시 문제 해결에서도 함수의 조합을 계속해서 활용하려고 노력하는 편입니다. 최근에도 맡고 있는 서버 일부분을 리팩토링하면서 함수형을 적용해봤는데, 팀에서는 크게 관심이 없긴 했네요(...)

 

위에서 이미 얘기한 것 처럼 함수형 프로그래밍은 분기 처리가 적고 함수의 나열을 통해 로직을 단순하게 나타내고자 합니다.

개념적인 부분은 이미 많이 얘기했으니, 다음 장에선 좀 더 다양한 예시들을 살펴보면서 함수형 프로그래밍 얘기를 끝내보겠습니다.

'Backend > 함수형 자바스크립트' 카테고리의 다른 글

더 많은 함수형 자바스크립트 사용.. (2)  (0) 2021.12.04
기본 객체 다루기  (0) 2021.11.13
값 다루기  (0) 2021.11.06
지연 평가 (3)  (0) 2021.10.30
지연 평가 (2)  (0) 2021.10.23