- 아듀 2018!
- 이전 글: “아듀 2018!” 만든 이야기
- 다음 글: Reduce - 이 연산의 끝을 잡고
우리는 함수를 통해 어떤 값 또는 객체를 다른 값 또는 객체로 변환할 수 있습니다. 하나의 Input은 하나의 Ouput과 연결됩니다. 여러 개의 Input을 여러 개의 Output에 연결하고 싶을 땐 어떻게 헤야 할까요?
JavaScript
로 map
을 이해하도록 합시다.
여기에 아주 간단한 함수가 있습니다. 이 함수는 입력 값을 두 배로 만들어서 돌려줍니다.
function double(x) {
return x * 2;
}
double(3) // => 6
double(13) // => 26
우리는 3
을 두 배로 만들 수 있습니다.
그리고 13
도 두 배로 만들 수 있습니다.
double
함수를 통해 우리가 원하는 모든 숫자를 두 배로 만드는 게 가능합니다.
하지만 이 함수는 한번에 하나 밖에 다루지 못합니다.
만약 3
과 13
을 배열로 만들어서 한꺼번에 처리하려면 어떻게 해야 할까요?
const xs = [3, 13];
const ys = [];
for (const x of xs) {
ys.push(double(x));
}
ys // => [6, 26]
일단 우리가 원하는 결과는 나왔습니다. 하지만 여기엔 몇 가지 오해할 수 있는 지점이 존재합니다.
먼저, 결과 ys
를 []
로 오해할 수 있습니다.
우리가 ys
를 상수로 만들었지만 여기에 push
가 가능하기 때문에
결과를 예측하기 어렵습니다.
분명히 빈 배열일 거라 생각했는데 두 개의 값이 들어갑니다.
그 다음으로, 이 작업은 “반복”이란 의미를 드러낼 필요가 없습니다.
그저 여러 개를 모두 double
이란 함수로 처리한다는
의미가 드러나는 게 중요합니다.
마지막으로, 우리가 실제로 수행할 작업이 명확하게 드러나지 않습니다.
우리가 원하는 건 push
가 아닙니다. double
입니다.
하지만 이 코드에선 이 둘이 미묘하게 섞여있습니다.
함수를 통해 ys
와 반복문, push
를 감춰봅시다.
function arrayDouble(xs) {
const ys = [];
for (const x of xs) {
const y = double(x);
ys.push(y);
}
return ys;
}
arrayDouble([3, 13]) // => [6. 26]
사용자 입장에선 arrayDouble
만 사용하면 됩니다.
위에서 논의했던 단점이 사용자에겐 노출되지 않죠.
자, 이번에는 반으로 나누는 함수를 적용합시다.
function half(x) {
return x / 2;
}
half(4) // => 2
half(12) // => 6
이것도 배열에 적용할 수 있게 해보죠.
function arrayHalf(xs) {
const ys = [];
for (const x of xs) {
const y = half(x);
ys.push(y);
}
return ys;
}
arrayHalf([4, 12]) // => [2, 6]
제가 이 코드를 어떻게 만들었을까요?
바로 위에 있던 코드를 복사해서 붙여 넣고 double
을 half
로 바꿨습니다.
얼마나 바보 같은 짓인가요?
우리는 JavaScript의 함수가 “일급 함수”인 걸 알고 있습니다. 그러면 여기서 한발 더 나아가죠. 우리는 일급 함수를 인자로 받거나 반환하는 함수를 만들 수도 있습니다. 이걸 고차 함수(high-order function)라고 부릅니다.
우리는 arrayDouble
과 arrayHalf
를 고차 함수로 바꿈으로써
중복을 제거할 수 있습니다.
function arrayMap(xs, f) {
const ys = [];
for (const x of xs) {
const y = f(x);
ys.push(y);
}
return ys;
}
arrayMap([3, 13], double) // => [6, 26]
arrayMap([2, 12], half) // => [1, 6]
만약 익명 함수를 바로 사용하면 어떻게 될까요?
arrayMap([3, 13], x => x * 3) // => [9, 39]
참 쉽죠?
좀 복잡한 데이터를 다뤄야 하는 상황을 생각해 봅니다.
const people = [
{ name: 'JOEKR', age: 13 },
{ name: 'Snoopy', age: 100 },
]
여기서 사람들(?)의 이름을 모아야 한다면 어떻게 헤야 할까요?
arrayMap(people, x => x.name)
만약 “이름 (나이)
” 같은 문자열을 만들려면 어떻게 해야 할까요?
arrayMap(people, x => `${x.name} (${x.age})`)
만약 JSX라면 어떻게 될까요?
{arrayMap(people, person =>
<div>
<p>{person.name} ({person.age})</p>
</div>
)}
이게 바로 React에서 여러 항목을 렌더링할 때 쓰는 방법입니다.
다행히 최신 JavaScript는 우리가 만든 arrayMap
을 기본으로 제공하고 있습니다.
배열에 map
이란 메서드로 제공하고 있죠.
[3, 13].map(double) // => [6, 26]
[3, 13].map(x => x * 3) // => [9, 39]
이게 전부입니다.
우리는 여러 개를 처리한다는 의도가 잘 드러나는 고차 함수를 만들었습니다. 그리고 그 고차 함수를 최신 JavaScript가 기본으로 제공한다는 것도 알게 되었습니다. 이 작업은 매우 보편적인 작업입니다. 그래서 기본으로 제공하고 있죠.
다음에는 또 다른 보편적인 작업인 reduce
를 알아보겠습니다.
- 아듀 2018!
- 이전 글: “아듀 2018!” 만든 이야기
- 다음 글: Reduce - 이 연산의 끝을 잡고