[Front-end] 개발자 공부

[개발 공부 83일차] 함수형 코딩 | 액션과 계산, 데이터의 차이 알기

MOLLY_ 2024. 7. 21. 17:39
728x90

<목차>
0. TL;DR
1. 액션, 계산, 데이터를 나눠서 생각하기
2. 쿠폰 보내기 예제
3. 액션의 다양한 형태

 

 

 

 

0. TL;DR

  1. 함수형 프로그래머는 액션, 계산, 데이터를 구분
  2. 액션: 외부 세계에 영향을 주거나 받는 것 [함수]
  3. 계산: 입력값을 출력값으로 만듦. 어떤 걸 결정, 계획함 [함수]
  4. 데이터: 이벤트(일어난 일)에 대해 기록한 사실 [기본 데이터 타입]
  5. 액션 < 계산 < 데이터 순서로 선호하기

 

 

1. 액션, 계산, 데이터를 나눠서 생각하기

  1. 액션: 외부 세계에 영향을 주거나 받는 것 [함수]
    1. 실행 시점이나 횟수 혹은 둘 다에 의존
    2. 함수형 프로그래밍에서 가장 중요!
    3. 순서(언제 실행되는지), 반복(얼마나 실행되는지)
    4. 액션을 부르는 함수가 있다면 그 함수도 액션이 됨 ⇒ 액션이 코드 전체로 퍼져나감
    5. 순수하지 않은 함수, 부수 효과 함수라고도 부름
    6. 액션을 잘 사용하기 위한 방법
      1. 가능한 액션을 적게 사용 (액션 대신 계산을 쓸 수 있는지 생각)
      2. 액션을 가능한 작게 만들기 (액션에서 액션과 관련 없는 코드(결정 or 계획 관련 코드)는 모두 제거)
      3. 액션이 외부 세계와 상호작용하는 것 제한 (내부: 계산 & 데이터 / 가장 바깥쪽: 액션)
      4. 액션이 호출 시점에 의존하는 것 제한

  2. 계산: 입력값을 출력값으로 만듦. 어떤 걸 결정, 계획함 [함수]
    1. 실행 시점과 횟수에 관계없이 항상 같은 입력값에 대해 같은 출력값 돌려줌
    2. 계산에는 연산을 담을 수 있음
    3. 액션보다 계산이 좋은 이유
      1. 테스트 용이
      2. 기계적인 분석이 쉬움 (정적 분석에서 자동화된 분석 중요)
      3. 계산은 조합하기 쉬움 (계산을 조합해 더 큰 계산을 만들 수 있음)
    4. 계산을 쓰면서 걱정되지 않아도 되는 2가지
      1. 동시에 실행되는 것
      2. 과거에 실행됐던 것 or 미래에 실행할 것
      3. 실행 횟수
    5. 단점
      1. 실행해보기 전에 어떤 일이 발생할지 알 수 없음
      2. 소프트웨어 측면에서 함수는 블랙박스이며, 입력값으로 실행해야 결과를 알 수 있음
      3. 위 단점이 있으면 계산 or 액션 대신 데이터를 사용하자
    6. 순수함수, 수학함수라고도 부름

  3. 데이터: 이벤트(일어난 일)에 대해 기록한 사실 [기본 데이터 타입]
    • 변경 불가능함
    • 언제나 쉽게 해석할 수 있도록 표현해야 하는 것이 중요
    • 데이터 구조로 의미 담을 수 있음
    • 장점
      • 직렬화된 데이터는 전송하거나 디스크에 저장했다가 읽기 쉬움
      • 동일성을 비교하기 쉬움
      • 자유로운 해석이 가능하지만, 해석이 늘 반드시 필요하단 게 단점

 

 

2. 쿠폰 보내기 예제

사용자 늘리기 이벤트를 구현하라는 요청이 들어왔다고 해보자.

 

사용자가 친구 10명을 추천하면 더 좋은 쿠폰을 보내려고 한다. 큰 이메일 데이터베이스가 있으며 여기엔 이메일별로 각 사용자가 추천한 친구 수도 기록 중이다.

 

쿠폰에 대한 정보를 갖고 있는 DB도 있다.

쿠폰 DB는 각 쿠폰에 bad, good, best 같은 등급 정보가 있다. best는 추천을 많이 한 사용자를 위한 쿠폰, good은 모든 사용자에게 전달될 쿠폰, bad는 사용하지 않는 쿠폰이다.

 

 

 

1. 설계하기

 

액션, 계산, 데이터에 따라 단계를 나누면 위와 같다.

계산을 나누면 구현하기 더 쉬워지지만, 충분히 구현하기 쉽다고 생각되는 시점에 나누는 걸 멈춰야 한다.

 

 

 

2. 구현하기

1. Database에서 가져온 구독자 데이터 ⇒ ‘객체’로 표현

테이블 행은 자바스크립트 객체로 표현할 수 있다.

const subscriber = {
	email: 'sample@gmail.com',
	rec_count: 16
};

 

 

2. 쿠폰 등급 ⇒ ‘문자열’로 표현

const rank1 = 'best';
const rank2 = 'good';

 

 

3. 쿠폰 등급 결정 ⇒ ‘함수’

계산: ‘함수’로 구현 (입력값 → 출력값 만듦)

  • 입력값: 함수 인자
  • 출력값: 함수의 return 값
  • 계산 내용: 함수의 본문

 

// 어떤 구독자가 어떤 등급의 쿠폰을 받을지 결정하는 함수

function subCouponRank(subsciber) { // 입력
	if (subscriber.rec_count >= 10) { // 계산
		return 'best'; // 출력
	} else {
		return 'good'; // 출력
	}
}

// 이 함수는 명확하고, 테스트하기 쉽고, 재사용할 수 있음

 

 

4. DB에서 가져온 쿠폰 데이터 ⇒ ‘객체’로 표현

이제 주어진 등급의 쿠폰 목록만 선택하는 부분을 구현해 보자.

const coupon = {
	code: '10PERCENT',
	rank: 'bad';
}

 

 

5. 특정 등급의 쿠폰 목록을 선택하는 계산 ⇒ ‘함수’

  • 입력값: 전체 쿠폰 목록, 선택할 등급
  • 출력값: 선택한 등급을 가진 쿠폰 목록
function selectCouponsByRank(coupons, rank) { // 입력
	let ret = []; // 빈 배열로 초기화
	
	for(let c = 0; c < coupons.length; c++) { // 모든 쿠폰에 대한 반복
		let coupon = coupons[c];
		
		if(coupon.rank === rank) { // 현재 쿠폰이 주어진 등급에 맞으면
			ret.push(coupon.code); // 쿠폰 코드를 배열에 넣음
		}
		
		return ret; // [출력] 배열을 return
	}
}

 

 

6. 이메일 ⇒ ‘데이터’

이제 구독자로 이메일을 만드는 부분을 구현해 보자. 다이어그램에서 가장 중요한 부분이다.

 

보내야 할 이메일을 데이터로 표현해 보자.

이메일 데이터는 보내는 주소, 받는 주소, 제목, 본문을 포함한다. 객체로 표현 가능하다.

 

// 이 객체는 이메일을 보내기 위한 모든 정보만 담고 있고,
// 어떠한 결정은 담고 있지 않음

const message = {
	from: 'newsletter@coupondog.co',
	to: 'sample@gmail.com',
	subject: 'your weekly coupons inside',
	body: 'Here are your coupons ...',
};

 

 

7. 구독자가 받을 이메일을 계획하는 계산

  • 인자: 구독자, good/best 쿠폰 목록
  • 결과값: 이메일 데이터
function emailForSubscriber(subcsriber, goods, bests) { // 입력
	let rank = subCouponRank(subscriber);
	
	if (rank === 'best') { // 등급 결정
		return { // 이메일 만들어 return
			from: 'newsletter@coupondog.co',
			to: subscriber.email,
			subject: 'Your best weekly coupons inside',
			body: 'Here are the best coupons: ' + bests.join(', ')
		} else { // rank === 'good'
			return { // 이메일 만들어 return
				from: 'newsletter@coupondog.co',
				to: subscriber.email,
				subject: 'Your good weekly coupons inside',
				body: 'Here are the good coupons: ' + goods.join(', ')
			};
		}
	}
}

 

 

8. 보낼 이메일 목록 준비하기 ⇒ 계산

필요한 코드가 모두 준비되었으니 모든 코드를 합쳐 이메일을 보내는 코드를 구현해 보자.

구독자 목록으로 전체 이메일 목록을 만들어야 한다. 반복문을 사용해서 구현해 보면 다음과 같다.

 

function emailForSubscribers(subscribers, goods, bests) {
	let emails = [];

	// 전체 이메일 목록을 만들려면 이메일 만드는 걸 반복하면 됨
	for (let s = 0; s < subscribers.length; s++) {
		let subscriber = subscribers[s];
		let email = emailForSubscriber(subscriber, goods, bests);
		emails.push(email);
	}

	return emails;
}

 

 

9. 이메일 보내기는 액션이다.

일반적으로 액션도 계산처럼 함수로 구현한다.

그래서 함수만 보고 계산인지 액션인지 알아보기가 쉽지 않다.

function sendIssue() {
	let coupons = fetchCouponsFromDB();
	let goodCoupons = selectCouponsByRank(coupons, 'good');
	let bestCoupons = selectCouponsByRank(coupons, 'best');
	let subscribers = fetchSubscribersFromDB();
	let emails = emailsForSubscibers(subscibers, goodCoupons, bestCoupons);
	
	for (let e = 0; e < emails.length; e++) {
		let email = emails[e];
		emailSystem.send(email);
	}
}

 

 

 

3. 액션의 다양한 형태

// 1. 함수 호출
alert('Hello world!'); // 팝업창이 뜨는 것 = 액션

// 2. 메서드 호출
console.log('Hello'); // 콘솔 출력

// 3. 생성자
new Date() // 부르는 시점에 현재 날짜와 시간을 초기화 하기 때문에 호출되는 시점에 따라 다른 값 가짐

// 4. 표현식
// (1) 변수 참조
// (2) y
// (3) 속성 참조
// (4) stack[0]
// (5) user.first_name
// (6) 배열 참조

// ⇒ 공유되고 변경 가능한 변수, 객체, 배열(첫 번째 항목만)이라면 읽는 시점에 따라 값이 다를 수 있음

// 5. 상태
// (1) 값 할당
// (2) z = 3; ⇒ 공유하기 위해 값을 할당했고 변경 가능한 변수라면 다른 코드에 영향을 주기 때문에 액션임
// (3) 속성 삭제
// (4) delete user.first_name; ⇒ 속성을 지우는 것은 다른 코드에 영향을 주기 때문에 액션임

 

 

 

728x90