앞선 강의 내용
L.entries = function *(obj) {
for (const k in obj) yield [k, obj[k]];
};
const join = curry((sep = ',', iter) =>
reduce((a, b) => `${a}${sep}${b}`, iter));
const queryStr = pipe(
L.entries,
L.map(([k, v]) => `${k}=${v}`),
join('&')
);
console.log(queryStr({ limit: 10, offset: 10, type: 'notice' }));
앞서 배웠던 join 함수는 reduce 계열의 함수라고 볼 수 있다. 즉, reduce로 만들 수 있는 함수인 것이다.
entries의 경우는 map을 통해서 만들어지는 함수라고 볼 수 있다.
함수형 프로그래밍에서는 유사한 계열을 가지도록 함수를 만들 수 있는데, 앞으로 만들어볼 find라는 함수는 take를 통해서 만들 수 있다.
take, find 함수
find 함수를 만들기에 앞서 아래와 같은 데이터가 있다고 가정해보자
const users = [
{ age: 32 },
{ age: 31 },
{ age: 37 },
{ age: 28 },
{ age: 25 },
{ age: 32 },
{ age: 31 },
{ age: 37 },
];
이 users 객체 배열에서 원하는 값만 추출하고자 할 때 사용할 수 있는 것이 find 함수이다.
const find = (f, iter) => go(
iter,
filter(f)
);
console.log(find(u => u.age < 30, users));
// (2) [{...}, {...}]
// 0: {age: 28}
// 1: {age: 25}
// length: 2
// __proto__: Array(0)
지금은 조건을 일치하는 모든 값을 뽑아내고 있다. 하지만, find 함수는 하나의 값만 뽑아내는 함수이기 때문에 아래와 같이 하나만 추출하도록 한다.
const find = (f, iter) => go(
iter,
filter(f),
take(1),
([a]) => a
);
console.log(find(u => u.age < 30, users));
// {age: 28}
이때, 함수가 내부적으로 어떻게 동작하는지 확인해보면 아래와 같다.
const find = (f, iter) => go(
iter,
filter(a => (console.log(a), f(a))),
a => (console.log(a), f(a)),
take(1),
([a]) => a
);
console.log(find(u => u.age < 30, users));
// {age: 32}
// {age: 31}
// {age: 37}
// {age: 28}
// {age: 25}
// {age: 32}
// {age: 31}
// {age: 37}
// (2) [{...}, {...}]
// {age: 28}
여기서 인자로 이터러블한 값을 받기 때문에 지연평가를 수행할 수 있다.
const find = (f, iter) => go(
iter,
L.filter(f),
take(1),
([a]) => a
);
console.log(find(u => u.age < 30, users));
함수형 프로그래밍의 장점을 다시 확인할 수 있는데, 언젠가 다시 이 함수를 보게 됐을 때, 함수명을 통해 어떻게 흐름이 진행될지를 파악할 수 있기 때문에 가독성이 좋다.
const find = curry((f, iter) => go(
iter,
L.filter(f),
take(1),
([a]) => a
));
console.log(find(u => u.age < 30)(users));
여기에 currying하여 위처럼 표현할 수도 있다. 함수를 정의하고 나중에 users를 인자로 받아 실행할 수 있다. 즉, 위를 만족하기 때문에 go 함수 내에서 아래와 같이 사용할 수도 있다.
go(users,
L.map(u => u.age),
find(n => n < 30),
console.log);
go랑 curry가 너무 좋다. 일단 가독성이 매우 향상된다는 점이 큰 메리트인 것 같다. 매번 백엔드 코드를 다시 읽을 때면, 절차를 모두 이해해야 했는데, 이렇게 함수와 함수가 체이닝되어 사용되다보니 그 함수명을 통해서 어떤 흐름인지 확실하게 파악할 수 있어서 좋았다. 물론 아직 함수형 프로그래밍의 시작 단계이지만, 왜 함수형 프로그래밍이 각광받는지 알 수 있을 것 같다. 다만, 같은 스터디원분께서 한 가지 제언한 부분이 있는데, 협업의 과정에서 바로 실무에 적용해도 괜찮냐는 것이었다. 이는 처음에 go, curry를 이해하지 못한 내 모습을 되돌이켜보면서 탄식이 나오는 순간이었다. 아무리 효율적인 코드더라도 팀원들과 align을 맞추지 않은 상태이면 과연 그것 또한 좋은 방법론일지는 미지수다. 매번 좋은 방법이 등장했을 때 한 번 더 의심해보고 받아들이는 습관을 기르자.
출처: 인프런 함수형 프로그래밍과 JavaScript ES6+
'프론트엔드 > JS' 카테고리의 다른 글
[JavaScript] L.flatten, flatten, L.deepFlat 함수 만들기 (0) | 2023.05.09 |
---|---|
[JavaScript] L.map, L.filter로 map과 filter 만들기 (0) | 2023.05.09 |
[JavaScript] Array.prototype.join 보다 다형성 높은 join 함수 (0) | 2023.05.08 |
[JavaScript] map, filter & reduce로 결과를 만드는 함수 생성 (0) | 2023.05.07 |
[JavaScript] map, filter 계열 함수들이 가지는 결합 법칙, 지연평가의 장점 (0) | 2023.05.05 |