프론트엔드에서 클린 코드를 고민했을 때, 코드 개선에 실질적인 도움이 되었던 부분이 액션 함수와 순수 함수에 대한 개념이었다. 기존에 짠 코드들을 봤을 때, 순수 함수는 거의 존재하지 않았고, 대부분의 함수가 액션 함수였다. 물론 액션 함수가 있어야 웹에서 기능을 구현할 수 있지만, 순수 함수가 거의 없는 프로젝트는 각각의 함수가 여러 기능을 담당하게 되면서 가독성이 떨어졌고 이로 인해 전반적인 흐름을 이해하기도 쉽지 않았다. 이를 깨닫고 순수 함수를 최대한 분리하고 난 후, 이전보다 코드가 많이 개선되었던 기억이 있다. 그래서 함수형 프로그래밍에 대해 조금 더 딥하게 공부해 보고자 '쏙쏙 들어오는 함수형 코딩' 스터디를 시작했다.
이어지는 내용은 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 테스트의 순으로 테스트 설정이 복잡해진다.)
이번 챕터를 통해 함수형 프로그래밍을 했을 때 대표적으로 이야기되는 액션, 계산, 데이터에 대해 정리할 수 있었다. 다음 챕터부터는 이 개념들을 바탕으로 적절하게 함수형 프로그래밍을 하는 방법들에 대해 다룰 예정이다.
'개발' 카테고리의 다른 글
[함수형 코딩] 더 나은 액션 만들기 (2) | 2025.03.19 |
---|---|
[시나브로 자바스크립트] Monorepo란? (5) | 2025.03.14 |
[시나브로 자바스크립트] Date, Intl.DateTimeFormat으로 날짜 다루기 (4) | 2025.03.07 |
좋은 프론트엔드 폴더 구조란? 🤔 (6) | 2025.02.22 |
[시나브로 자바스크립트] 명령형 프로그래밍 vs 선언형 프로그래밍 (7) | 2025.02.21 |