map, pluck, mapTo를 이용해 stream 변형하기
Observable(옵저버블)을 사용하다 보면, 어떤 타입의 stream(스트림)을 다른 타입의 스트림으로 변환해야 하는 상황이 자주 발생할 것입니다. 예를 들자면 클릭 이벤트 옵저버블을 clientX
와 clientY
좌표만 포함하는 객체 옵저버블로 변환시켜야 하는 상황이나, 계산 수행이나 요청 초기화를 위해 입력 이벤트 스트림에서 값을 추출해야 하는 상황이 있을 수 있죠. 또는 키 코드와 같은 객체에서 속성 하나를 추출하여 (파이프)라인에서 다른 작업을 수행해야 하는 상황일 수도 있습니다. 스트림을 변형해야 하는 상황들은 끊임없이 존재합니다.
이 문서에서는, 스트림을 변환하는 데 사용되는 가장 일반적인 연산자인 map
을 배워볼 것입니다. map
에 대해 이해하기 위해 먼저 Array.map
을 살펴보고, 이러한 접근 방식을 RxJS map
연산자를 통해 옵저버블에서 적용하는 방법을 알아보겠습니다. 마지막으로, 몇 가지 상황에서 map
대신 사용할 수 있는 연산자들과 해당하는 예시들을 확인해볼 것입니다. 그럼 시작해 볼까요?
map
소개
map
소개여러분이 자바스크립트 배열을 사용해본 적이 있다면 이미 Array.map
에 익숙할 것입니다. 배열을 다룰 때, map
메소드를 사용하면 주어진 함수를 배열 내의 각 요소에 적용하여 배열을 변환할 수 있습니다. 예를 들어, 1-5
의 숫자 배열이 있다고 해 봅시다.
이 배열을 각 숫자에 10을 곱한 배열로 변환하기 위해 map
메소드를 사용할 수 있습니다. 숫자 배열에서 map
을 호출하고, 배열의 각 값을 호출하는 함수를 전달하고, 10으로 곱한 숫자를 반환하면 됩니다.
map
메소드는 기존의 배열을 변경하지 않고 새 배열을 반환합니다. map
을 호출한 후 numbers
배열을 로깅해보면 numbers
배열이 바뀌지 않았다는 것을 알 수 있죠.
조금 더 자세히 이해하기 위해, Array.map
의 원리적인 구현을 살펴보겠습니다.
새 배열을 생성합니다.
소스 배열에 포함된 모든 요소에 대해 함수를 적용합니다.
함수의 결과를
resultArray
에 임시로 push 합니다.모든 요소에 이 작업을 완료하면, 새 배열을 반환합니다.
실제 Array.map
의 구현 은 index 추적 및 오류 관리 같은 기능들이 포함되어 있지만, 백그라운드에서 어떻게 작동하는지 간단히 알아보기 위해 축약했습니다.
그러면 map
메소드를 사용할 수 있는 다른 상황에는 무엇이 있을까요? Array.map
을 사용해서 객체를 변환할 수도 있습니다. 예를 들어, 성과 이름 속성이 있는 객체 배열이 있고 각 객체에 전체 이름 속성을 추가하려 한다고 가정해 봅시다. 이 문제는 각 객체의 모든 속성과 fullName
속성을 갖고 있는 새 객체에 매핑 하는 함수가 있다면 해결할 수 있을 것 같습니다. 이 예제에서는 전개 구문, 템플릿 리터럴 을 사용하고 있지만, 명시적으로 속성을 작성해도 괜찮습니다.
map
의 또 다른 사용 사례는 객체에서 하나의 속성만 추출하는 상황입니다. 위의 예시에서, 오직 성 속성만 보여주기로 했다고 가정해봅시다. 이번에는 함수에서 새 객체를 반환하는 대신에 기존 객체에서 필요한 속성만 반환하면 됩니다.
사람 객체 배열을 문자열 성 배열로 변환해보았습니다.
보셨다시피, map
메소드는 다양한 상황에서 유연하게 사용될 수 있습니다. 그렇다면 RxJS에서 map
을 활용하는 방법은 무엇이고, 언제 옵저버블과 함께 사용할 수 있을까요?
RxJS map
연산자
map
연산자RxJS의 map
연산자는 옵저버블에서 방출된 값을 제공된 함수를 기반으로 변환합니다. 배열 내에 포함된 값이 아니라, 옵저버블에서 방출된 값을 대상으로 한다는 것을 제외하면 Array.map
과 유사하죠.
Array.map
에서 활용했던 초기 예제로부터 시작해 보겠습니다. 대신에, 숫자 배열 대신 숫자 옵저버블을 변환해 봅시다. 숫자 배열을 옵저버블로 변환하기 위해 from
연산자를 사용하겠습니다.
from
연산자는 배열이 제공되었을 때 각 요소를 순서대로(동기적으로) 내보냅니다. 옵저버블을 구독하여 콘솔에 인쇄되는 값을 확인해 봅시다.
팁: 내부적으로 from
이 각 값 타입을 어떻게 처리하는지 알고 싶다면, subscribeTo
와 관련된 헬퍼 함수들을 확인해 보세요. 위의 예제에서는 subscribeToArray 가 사용되었습니다. 이러한 헬퍼 함수들은 mergeMap과 같은 병합 연산자의 옵저버블이 아닌 반환 값을 처리할 때에도 사용됩니다.
이 옵저버블을 방출된 값에 10을 곱한 옵저버블로 변환하기 위해 map
연산자를 이용해보겠습니다. Array.map
처럼, map
연산자는 소스의 각 요소를 변환하는 함수를 사용할 수 있습니다. 이 예제에서, 소스 옵저버블에서 방출된 값에 10을 곱한 값을 반환하는 함수를 사용합니다.
새 배열을 반환하기 전에 이 함수를 배열의 각 요소에 적용하는 대신, 옵저버블에 함수가 적용되고, 값이 스트림을 통과할 때 결과 값이 실시간으로 방출됩니다. RxJS 소스 코드에서 호출된 함수의 결과가 구독자(대상)에게 전달되는지 확인해 볼까요?
위에서 보았던 객체 배열 예제와 유사하게, map
연산자로 객체 옵저버블을 변환할 수 있습니다. 예를 들어, 클릭 이벤트 옵저버블을 clientX
와 clientY
좌표만 포함하는 객체 옵저버블로 변환시켜야 하는 상황이라고 가정해보면, 해당하는 속성만 있는 객체를 반환하는 함수를 이용해 map
연산자를 적용해볼 수 있겠습니다.
map
을 이용해 객체에서 단일 속성만 가져와야 하는 상황도 살펴보겠습니다. keyup
이벤트에서 code
속성만 존재하는 옵저버블이 있다면, 유저가 특정 문자나 키를 입력할 때 조치를 취할 수 있을 것입니다. 이 옵저버블을 만들어내기 위해, map
연산자를 필요한 속성만 반환하게끔 적용할 수 있겠죠?
map
은 여러 상황들에서 완벽하게 작동하지만, RxJS는 추가적으로 단일 속성에 매핑 하려는 경우, 모든 이벤트에서 항상 동일한 값에 매핑하려는 경우에 사용할 수 있는 헬퍼 연산자를 제공합니다. 먼저, 단일 속성에 매핑하려는 경우를 살펴볼까요?
pluck
으로 단일 속성 추출하기
pluck
으로 단일 속성 추출하기RxJS에는 단순히 다른 연산자를 단축하기만 한 연산자들이 많습니다. 예를 들어, 방출된 값에서 하나의 속성만을 가져오고 싶다면 언제든 map
대신 pluck
을 사용할 수 있죠. 위의 이벤트 코드 예제에서도 map
대신 pluck
을 사용해 이벤트
객체에서 code
속성을 추출할 수 있습니다.
pluck
에 여러 개의 값을 전달하여 객체 내부에 중첩된 속성을 가져올 수도 있습니다. 예를 들어, 클릭된 target
엘리먼트에서 nodeName
을 가져오고 싶다면 두 속성을 pluck
에 순서대로 전달하면 됩니다.
RxJS의 다른 헬퍼 연산자와 마찬가지로, pluck
은 내부에서 map
연산자를 재사용하여 적절한 속성을 가져오는 함수를 전달합니다.
예제에서 map
과 pluck
은 기능적으로 동일하게 작동하지만, 한 눈에 알아보기 쉬운 것을 선택하는 게 좋습니다.
마지막으로, 입력에 관계없이 항상 하나의 값에 매핑하려는 경우에 mapTo
연산자를 사용할 수 있습니다.
mapTo
로 상수 값에 매핑하기
mapTo
로 상수 값에 매핑하기단순히 map
을 사용한 후 입력을 무시하면, 매번 특정 값에 매핑하고 싶은 상황을 해결할 수 있습니다.
이 작업에서는 반환된 값을 무시하므로 매핑 함수가 필요하지 않습니다. 이러한 상황에서는 map
대신 mapTo
에 모든 방출에 대해 반환하려는 값을 제공하면 됩니다.
mapTo
는 pluck
과 마찬가지로 map
에 비해 기능적 이점을 제공하지는 않지만, 한 눈에 이해하기 조금 더 쉬울 수 있죠.
정리하기
정리해보자면, map
은 제공된 보조 함수를 사용해 스트림을 변환할 수 있는 다목적 연산자입니다. 키 코드 매핑, 입력 필드의 값 업데이트, 객체의 형태 변경 등 일상적인 상황에서 굉장히 많이 사용되죠. 단일 속성에 매핑하는 경우나 항상 상수 값에 매핑하는 경우 각각 pluck
과 mapTo
헬퍼 연산자를 사용할 수도 있습니다.
더 복잡한 값에 대한 매핑을 담당하는 변환 연산자와 그 예제 목록을 보려면, transformation operator(변환 연산자) 카테고리를 참고하세요. 다음 문서에서 이 주제들에 대해 더 자세히 알아보도록 하죠!
Last updated