728x90

🔀 Ramda 첫걸음: R.pipe – 순차적인 함수 조합의 시작

JavaScript에서 데이터를 다룰 때 가장 흔히 하는 일 중 하나는, 여러 함수를 차례대로 실행시키는 것입니다.
예를 들어, 문자열을 다듬고, 포맷을 바꾸고, 결과를 출력하는 과정을 생각해보세요.

Ramda의 R.pipe는 이런 작업을 순차적이고 선언적으로 처리할 수 있는 함수형 프로그래밍 도구입니다.
오늘은 pipe를 통해 “작은 함수들을 연결하여 큰 로직을 구성”하는 방식을 익혀봅니다.


🧠 Topic Summary

R.pipe란?

R.pipe는 여러 개의 함수를 왼쪽에서 오른쪽으로 순서대로 실행하여 하나의 새로운 함수를 만들어줍니다.
마치 파이프라인을 통해 데이터가 흐르듯, 함수들을 일렬로 연결하는 방식입니다.

const pipedFn = R.pipe(f1, f2, f3);
pipedFn(value); // f3(f2(f1(value)))

언제 쓰면 좋을까?

  • 코드 흐름을 명확하게 표현하고 싶을 때

  • 단일 책임 함수를 조합하여 큰 기능을 만들고 싶을 때

  • 복잡한 nested 호출을 피하고 싶을 때


🛠 Usage Examples

예제 1: 문자열 클린업 및 포맷팅

import * as R from 'ramda';

const cleanAndFormat = R.pipe(
  R.trim,                      // 공백 제거
  R.toLower,                   // 소문자로 변환
  R.replace(/\s+/g, '-')       // 띄어쓰기 → 하이픈
);

cleanAndFormat('   Hello Functional World   '); 
// 출력: 'hello-functional-world'

설명: R.trim → R.toLower → R.replace 순서로 실행되며, 데이터 흐름이 명확합니다.

여러 줄을 함수로 처리하는 것보다 선언적이고 유지보수에 용이합니다.


예제 2: 객체 필드를 연결하여 사용자 이름 생성

const user = { firstName: 'Alice', lastName: 'WONDERLAND' };

const getDisplayName = R.pipe(
  R.props(['firstName', 'lastName']), // ['Alice', 'WONDERLAND']
  R.map(R.toLower),                   // ['alice', 'wonderland']
  R.join(' ')                         // 'alice wonderland'
);

getDisplayName(user); 
// 출력: 'alice wonderland'

설명: 이처럼 pipe를 활용하면, 객체 → 배열 → 문자열로의 데이터 변환을 자연스럽게 이어갈 수 있습니다.


⚠️ Common Pitfalls

1. 모든 함수는 단일 인자여야 함

pipe는 각 함수가 하나의 인자만 받는 구조를 기대합니다.

두 개 이상의 인자가 필요한 함수는 R.curry 또는 R.partial을 사용해 처리해야 합니다.

const add = (a, b) => a + b;

R.pipe(
  add,      // ❌ Error
  R.square
)(2);

→ 해결 방법:

const curriedAdd = R.curry(add);

R.pipe(
  curriedAdd(3),  // 하나의 인자로 고정
  R.square
)(2); // (2 + 3)^2 = 25

2. pipe는 “즉시 실행되지 않음”

pipe는 함수를 리턴할 뿐, 실제 데이터가 주어지기 전까지 아무것도 실행되지 않습니다.

const transform = R.pipe(R.trim, R.toUpper);
// transform 자체는 함수일 뿐
transform('  hello  '); // 'HELLO'

3. 중간에 undefined를 반환하는 함수가 있다면 문제 발생

어느 한 함수가 undefined를 반환하면 다음 함수에서 에러가 날 수 있습니다.

R.pipe(
  () => undefined,
  R.toUpper // TypeError: Cannot read property ...
)

→ 항상 함수의 반환값을 신뢰할 수 있어야 안전한 조합이 됩니다.


💡 Advanced Tip: 디버깅을 돕는

R.tap

중간에 값이 잘 흘러가고 있는지 확인하고 싶다면 R.tap(console.log)를 넣어 디버깅할 수 있습니다.

R.pipe(
  R.trim,
  R.tap(console.log),
  R.toUpper
)('   hello   ');

✅ Call to Action

읽기 쉬운 코드는 협업에서도, 테스트에서도, 유지보수에서도 강력한 힘을 발휘합니다.

실습 아이디어:

  • 사용자 입력값 클렌징하기

  • 배열 데이터를 정렬 후 문자열로 만들기

  • 여러 단계의 숫자 가공 작업 만들기

728x90

+ Recent posts