본문 바로가기
개발

[함수형 코딩] 액션, 계산, 데이터란?

by soyooooon 2025. 3. 12.
반응형

쏙쏙 들어오는 함수형 코딩

 

프론트엔드에서 클린 코드를 고민했을 때, 코드 개선에 실질적인 도움이 되었던 부분이 액션 함수와 순수 함수에 대한 개념이었다. 기존에 짠 코드들을 봤을 때, 순수 함수는 거의 존재하지 않았고, 대부분의 함수가 액션 함수였다. 물론 액션 함수가 있어야 웹에서 기능을 구현할 수 있지만, 순수 함수가 거의 없는 프로젝트는 각각의 함수가 여러 기능을 담당하게 되면서 가독성이 떨어졌고 이로 인해 전반적인 흐름을 이해하기도 쉽지 않았다. 이를 깨닫고 순수 함수를 최대한 분리하고 난 후, 이전보다 코드가 많이 개선되었던 기억이 있다. 그래서 함수형 프로그래밍에 대해 조금 더 딥하게 공부해 보고자 '쏙쏙 들어오는 함수형 코딩' 스터디를 시작했다.

이어지는 내용은 1~3 챕터까지를 읽으며 핵심이라고 생각한 액션, 계산, 데이터에 대해 정리해 보았다.

액션, 계산, 데이터 비교

  액션 계산 데이터
설명 실행 시점과 횟수에 의존 입력으로 출력을 계산 이벤트에 대한 사실
동의어 부수 효과, 부수 효과가 있는 함수, 순수하지 않은 함수 순수 함수, 수학 함수  
예시 이메일 보내기, 데이터베이스 읽기 최댓값 찾기, 이메일 주소가 올바른지 확인하기 사용자가 입력한 이메일 주소, 은행 API로 읽은 달러 수량

액션은 실행되는 시점에 따라 달라지거나 실행 횟수에 따라 결과가 달라지거나 둘 다에 의해 영향을 받는다. 이메일 보내기, 데이터베이스 조회하기 등 이벤트를 발생시키는 것들을 액션이라고 생각하면 된다.

표에 정리한 것처럼, 계산은 입력에 대한 출력을 계산한다. 그렇기 때문에 언제 어디서 얼만큼 실행하더라도 항상 동일한 결과를 반환한다. 가령 1+2의 결과값을 반환하는 함수가 있다고 했을 때, 1+2는 오늘 실행해도 3이고, 내일 실행해도 3이다. 또한 오늘 여러 번 실행해도 동일하게 3이 반환된다. 이처럼 계산은 입력에 대해 항상 동일한 출력값을 나타낸다.

데이터는 이벤트에 대한 사실이다. 처음에 나도 '뭔 말이야'라는 생각이 들었는데 예시를 보면 왜 이렇게 정의했는지에 대해 알 수 있다. 일어난 일의 결과를 기록한 것으로, 예를 들면 사용자가 create user와 같은 이벤트로부터 사용자를 데이터베이스에 저장한다. 또한 사용자 조회라는 이벤트로 API response에 담겨온 것도 데이터이다. (사실 이렇게까지 설명 안 해도 데이터는 평소에 익숙한 개념이라고 생각한다.)

액션, 계산, 데이터 중 액션은 조금 더 고려해야 할 부분이나 헷갈릴만한 부분이 있어 아래에 더 정리해 보았다.

자바스크립트에서의 액션

자바스크립트에서 '이것도 액션이야?'라고 생각할만한, 헷갈리는 것들을 이유와 함께 정리했다.

  • alert() : 팝업 창을 띄우기 때문에 액션
  • console.log() : 콘솔에 출력하기 때문에 액션
  • new Date() : 부르는 시점에 따라 값이 달라지기 때문에 액션
  • delete : 객체에서 속성을 지우는 것은 다른 코드에 영향을 주기 때문에 액션

액션을 사용할 때의 주의점

액션을 부르는 함수가 있다면 그 함수도 액션이 된다. 그렇기 때문에 액션 하나가 코드 전체로 퍼져 코드 전체가 액션이 될 수도 있다. 다음은 액션이 전체로 퍼지는 예시를 작성했다.

❌  1. 잘못된 코드

sendEmail은 이메일을 발송하는 액션이다. sendEmail을 processOrder에서 호출 > saveOrder에서 호출함에 따라 결국 전체 코드가 액션이 되어버렸다.

// 1. 이메일 발송 기능 : 액션
const sendEmail = (recipient, message) => {
  console.log(`📧 Sending email to ${recipient}: ${message}`);
  // 실제 이메일 전송 로직이 있다고 가정
};

// 2. 사용자의 주문을 처리하는 함수 : 액션인 sendEmail을 호출하기 때문에 액션
const processOrder = (user, order) => {
  if (!order.isValid) {
    sendEmail(user.email, "Your order is invalid.");
    return "Order is invalid";
  }

  sendEmail(user.email, "Your order is confirmed!");
  return "Order processed";
};

// 3. 주문 내역을 저장하는 함수 : 액션인 processOrder를 호출하기 때문에 액션
const saveOrder = (user, order) => {
  processOrder(user, order);
};

const user = { name: "Alice", email: "alice@example.com" };
const order = { user, items: [], isValid: false };

saveOrder(user, order);

 

✅ 2. 수정된 코드

수정된 코드는 handleOrder에서만 sendEmail을 호출하고, 나머지는 입력에 따라 값을 return 하는 순수 함수로 변경하였다.

// 1. 이메일 발송 기능 : 액션
const sendEmail = (recipient, message) => {
  console.log(`📧 Sending email to ${recipient}: ${message}`);
  // 실제 이메일 전송 로직이 있다고 가정
};

// 2. 사용자의 주문을 처리하는 함수 : 순수 함수
const processOrder = (order) => {
  if (!order.isValid) {
    return { success: false, message: "Order is invalid" };
  }
  return { success: true, message: "Order processed successfully" };
};

// 3. 주문 내역을 저장하는 함수 : 순수 함수
const saveOrder = (user, order) => {
  return processOrder(order);
};

// 4. 최종적으로 액션을 수행하는 곳 : 액션
const handleOrder = (user, order) => {
  const result = saveOrder(user, order);

  // 이전 코드와 달리 한 곳에서만 실행됨
  sendEmail(user.email, result.message);
};

const user = { name: "Alice", email: "alice@example.com" };
const order = { user, items: [], isValid: false };

handleOrder(user, order);

 

물론 위 코드는 예시로 들기 위함이고, 일부 함수는 생략하는 게 더 간단하기도 하다. 때문에 지금은 액션이 전체 코드로 어떻게 퍼져나가는지를 간단하게 나타내기 위함으로 생각하면 좋을 것 같다. '굳이 이렇게까지 해야 하나?'라는 생각이 들 수도 있지만, 실무에서 사용하는 코드처럼 훨씬 기능이 복잡한 프로젝트를 다룬다고 했을 때를 가정해 보자. 1번처럼 전체 코드가 액션으로만 이루어져 있다면 각 함수가 어떤 역할을 하는지, 어떤 결과를 나타낼 지에 대해 파악하기가 어려워질 수 있다. 또한 테스트 코드를 짠다고 했을 때, 1번과 같은 경우 테스트가 어려워진다. 액션을 테스트하기 위해, 단위 테스트가 아닌 통합이나 e2e 테스트를 진행해야만 검증을 할 수 있을 것이다. 하지만 2번은 순수 함수가 적절하게 분리되어 있기 때문에, 기대되는 입출력에 따라 단위 테스트로 간단하게 테스트할 수 있다. (단위 테스트 < 통합 테스트 < E2E 테스트의 순으로 테스트 설정이 복잡해진다.)

 

이번 챕터를 통해 함수형 프로그래밍을 했을 때 대표적으로 이야기되는 액션, 계산, 데이터에 대해 정리할 수 있었다. 다음 챕터부터는 이 개념들을 바탕으로 적절하게 함수형 프로그래밍을 하는 방법들에 대해 다룰 예정이다.

반응형