쏙쏙 들어오는 함수형 코딩 CH3

Yunes·2023년 9월 25일
0
post-thumbnail

액션, 계산, 데이터

액션과 계산, 데이터의 차이에 대해 알아보는 챕터

모든 개발 과정에서 액션과 계산, 데이터를 구분하는 기술을 적용 가능하다.

  • 문제에 대해 생각할때
    특별히 주의해야 하는 부분 액션
    데이터로 처리해야 할 부분 데이터
    결정을 내려야 할 부분계산
  • 코딩할 때
    액션, 계산, 데이터를 구분할 수 있고 되도록 액션에서 계산을, 계산에서 데이터를 분리할 수 있는지 생각한다.
  • 코드를 읽을 때
    액션, 계산, 데이터중 어디에 속한지 살펴보고 더 나은 코드를 위해 코드를 액션에서 계산, 데이터로 리펙터링을 하는 방법을 찾는다. 32p

의견
코드를 단순히 요구사항만 충족하는 돌아가는 코드를 짜는게 아니라 같은 코드라도 주의할 부분과 그렇지 않는 부분들을 나누어 코드를 짤 수 있다는 것으로 이해했다.

일상생활에서 액션, 계산, 데이터 적용해보기

이 책에서는 장보기에 액션, 계산, 데이터를 적용해 보는 식으로 함수형 프로그래밍을 어떻게 할 수 있는지에 대해 소개하고 있다.

어떻게 정리할까 하다가 이 부분은 책의 내용을 구체적으로 명시하는 편이 이해하는데 도움이 될 것 같다는 생각이 들었다.

앞에서 장보기 과정을 냉장고 확인하기 - 운전해서 상점으로 가기 - 필요한 것 구입하기 - 운전해서 집으로 오기 으로 타임라인 다이어그램을 제시하는데 여기서의 각 과정은 액션 에 해당한다.

이때 각 액션 에서 계산, 데이터 를 어떻게 이끌어 내는지에 대해 소개하고 있다.

초기 타임라인 다이어그램

계산, 데이터를 추출한 타임라인 다이어그램

이미지 출처 : https://www.yes24.com/Product/Goods/108748841

액션과 계산, 데이터는 어디서나 적용가능하며 액션 안에는 계산, 데이터, 또다른 액션이 있을 수 있다. 계산은 더 작은 데이터로 나누고 연결할 수 있고 데이터는 데이터만 조합 가능하다. 36p

의견
아직까지는 코드 없이 개념을 실생활에 적용해서 액션, 계산, 데이터를 어떻게 추출할 수 있는지에 대해서만 확인해봤다. 바로 이어서 코드와 함께 설명이 나오는데 이 부분에서 이해가 좀 잘 되었던 것 같다.

새로 만드는 코드에 함수형 사고 적용하기

이번엔 쿠폰에 관심있는 구독자들에게 이메일로 쿠폰을 보내주는 서비스에서 10명 이상 추천한 사용자에게 더 좋은 쿠폰을 발급할 수 있는 것처럼 쿠폰 추천 정책에 따라 사용자들에게 맞는 쿠폰을 보내는 프로그램을 만들어 달라는 요구사항을 받은 상황이다.

상황

  • 이메일 db 에 이메일별 추천한 친구 수를 기록하고 있다.
  • 쿠폰 db 에 여러가지 쿠폰과 각 쿠폰의 rank 를 best, good, bad 로 나누어 기록하고 있다.

직접 코드를 작성하기 위해 무엇을 알아야 하고 어떤 것을 결정해야 하고 무엇을 해야 하는지 적어봤다.

요구사항을 충족시키기 위해 해야 할 일들 적어보기

각각을 A - 액션 / C - 계산 / D - 데이터 로 구분할 수 있다.

예시

  • 이메일 보내기
  • db 에서 구독자 가져오기
  • 쿠폰에 등급 매기기

의견

  • db 에서 구독자별 추천수 가져오기 D
  • 추천수에 따라 best 쿠폰을 보낼 구독자 명단 계산하기 C
  • best 쿠폰을 보낼 구독자 명단, good 쿠폰을 보낼 구독자 명단 구하기 C
  • 쿠폰 db 에서 good 에 속하는 쿠폰 목록 구하기 C
  • 쿠폰 db 에서 best 에 속하는 쿠폰 목록 구하기 C
  • 각 구독자 명단 별로 쿠폰 이메일로 보내기 A

일단 나는 이정도가 생각났다.

워우.. 다음 페이지에서 어떤 점들을 생각했는지 소개해주고 있다.

예시

  • 이메일 보내기 A
  • db 에서 구독자 가져오기 A
  • 쿠폰에 등급 매기기 C -> D
  • 데이터베이스에서 쿠폰 읽기 A
  • 이메일 제목 D
  • 이메일 주소 D
  • 추천 수 D
  • 어떤 이메일이 쿠폰을 받을지 결정하기 C
  • 구독자 db 레코드 D
  • 쿠폰 db 레코드 D
  • 쿠폰 목록 db 레코드 D
  • 구독자 목록 db 레코드 D
  • 이메일 본문 D

쿠폰에 등급 매기기가 쿠폰 이름을 읽고 등급을 구하는 거라고 생각해서 계산인줄 알았는데 저정도는 데이터인가 보다.

쿠폰 보내는 과정 그려보기

타임라인 다이어그램을 통해 어떤 순서로 어떤 액션, 계산, 데이터가 사용되는지 구해보자.

먼저 db 에서 구독자와 쿠폰 목록을 가져올 수 있다. A

이때의 구독자 목록, 쿠폰 목록은 데이터이다. D

그리고 구독자 목록과 쿠폰 목록을 토대로 이메일을 보낼 목록을 계산하여 부합하는 이메일 목록을 얻어낼 수 있다. C

이때의 해당하는 이메일 목록은 데이터이다. D

이 이메일 목록을 바탕으로 쿠폰을 해당 이메일로 보낸다. A

가능한 계산을 사용하려는 까닭은 테스트하기 쉽기 때문이다. 이메일을 실제로 보내고 결과를 주는 시스템은 테스트하기 어려우나 결과가 이메일 목록인 시스템은 테스트하기 쉽다. 44p

위의 표현이 참 기억에 남는것 같다. 위의 문장이 정확하게 액션에서 계산을 추출해야 하는 이유를 나타내고 있다. 유닛테스트를 하기에 액션에 속하는 코드들도 계산을 뽑아내면 충분히 테스트하기 쉬워질 것 같다는 생각이 들었다.

책에서는 마지막에 이메일 목록을 계산 하는 것과 얻은 이메일 목록 데이터 를 더 작은 계산과 데이터로 분리해 냈다.

쿠폰 목록에서 good / best 쿠폰을 계산 해서 good 쿠폰 / best 쿠폰 목록 데이터 를 얻었고

구독자 목록 데이터 를 받고 추천수에 따라 쿠폰 등급을 계산 해서 실제 보낼 쿠폰 등급 데이터 를 얻었다.

전체 과정이다.

우측의 계산과 데이터를 아래와 같이 잘게 분리한 것이다.


이미지 출처 : https://www.yes24.com/Product/Goods/108748841

의견
계산과 데이터, 액션은 분리하자면 정말 세밀하게 분리할 수 있는것 같은데 어느정도 수준까지 분리해야 하는지 결정하는게 어려울 것 같다는 생각이 든다.

쿠폰 보내는 과정 구현하기

이 단락에 대해 한번 쭉 읽어보고 다시 나만의 코드로 직접 적어보려 한다.

때마침 우아한스터디 23년도 여름시즌 발표회에서 쏙쏙 들어오는 함수형 코딩 스터디를 어떻게 진행하셨는지 유뷰브에 공유해주신 영상을 본 이후라 직접 코드에 적용해보는 것이 더 좋을거라는 생각이 들었다.

그전에 필요한 구현에 대해서 먼저 언급을 하려 한다.

앞선 단계의 쿠폰 보내는 과정 그려보기 에서 타임라인 다이어그램을 그렸지만 각 타임라인 다이어그램을 합치기 전에 어떤 그룹으로 되어있었는지만 명시하려 한다.

직접 구현해보기

각 구현은 데이터 > 계산 > 액션 순서로 구현을 진행한다. 먼저 쪼개진 타임라인 다이어그램을 보고 데이터, 계산, 액션 순서로 코드를 짜보고 최종적으로 책의 코드와 비교하는 식으로 학습을 진행해봤다.

하단의 모든 이미지는 쏙쏙 들어오는 함수형 코딩 책에서 참고했다.


책에서는 데이터를 먼저, 이후에 계산의 코드를 구현해보라고 안내하고 있었다.

그럼 구독자 목록 데이터를 가져오는 코드, 구독자 목록으로부터 쿠폰 등급을 계산하는 코드, 해당 계산 코드에서 쿠폰 등급을 반환하는 코드가 필요할 것으로 보인다.

다시 타임라인 다이어그램을 보니 구독자 목록이 아니라 구독자 한명의 정보가 데이터이고 해당 1인 구독자에 대해 쿠폰 등급을 계산해서 쿠폰 등급을 반환하는 기능이 필요해 보인다.

책에서는 데이터는 프로그래밍 언어의 데이터 타입을 따르면 된다는데 여기서는 구독자 1인의 정보가 데이터이니 객체를 데이터 타입으로 정하면 될 것 같다.

구독자 데이터 - 데이터

const subscriber = {
	recommendCount: 10,
  	email: "test@gmail.com"
}

구독자의 정보로 해당 구독자에게 이메일을 보내야 하며 쿠폰 등급은 추천수에 기반하는 것이니 recommend_count 와 email 을 property 로 갖는 object 구조를 생각했다.

쿠폰등급 데이터 - 데이터

const rank = "good" // or "best"

쿠폰등급 데이터는 문자열로 표현했다.

쿠폰 등급 결정하기 - 계산

책에서는 계산과 액션은 함수로 구현한다고 소개되어 있다.

function calcCouponRank(subscriber) {
	if (subscriber.recommendCount >= 10) return "best"
    return "good"
}

다음은 쿠폰 목록으로부터 good 쿠폰 목록, best 쿠폰 목록을 계산하여 각각의 목록을 구하는 타임라인 다이어그램이다.

쿠폰 목록 데이터 - 데이터

쿠폰 목록은 배열인데 그 배열 내에 쿠폰 이름과 쿠폰 등급이 들어가는 객체가 들어가면 될 것 같다.

const couponList = [
  {
  	name: "FirstOrderCoupon", rank: "good"
  },
  {
  	name: "TenOrderCoupon", rank: "best"
  }
]

good 쿠폰, best 쿠폰 계산하기 - 계산

function defineCouponRank(couponList, rank) {
	let arr = [];
  	couponList.map((coupon) => {
    	if(coupon.rank === rank) arr.push(coupon);
    })
  	return arr;
}

이메일 - 데이터

이메일에는 보내는 주소, 받는 주소, 쿠폰 등급, 쿠폰 목록을 담고자 객체 형태를 생각했다.

const email = {
	from: "test@gmail.com",
  	to: "test2@gmail.com",
  	rank: "best",
  	coupons: "A, B, C ..."
}

구독자에게 보낼 이메일 구하기 - 계산

이번엔 구독자 1인, good 쿠폰 목록, best 쿠폰 목록의 인자를 받아서 해당 구독자의 쿠폰 등급에 따라 구독자에게 보낼 이메일을 계산하고 반환해야 한다.

function makeEmail(subscriber, goods, bests) {
	const rank = calcCouponRank(subscriber);
    if (rank === "best") {
    	return {
        	from: "from@gmail.com",
            to: "subscriber@gmail.com",
            rank,
            coupons: bests.join(", ")
        }
    } else {
    	return {
        	from: "from@gmail.com",
            to: "subscriber@gmail.com",
            rank,
            coupons: goods.join(", ")
        }
    }
}

보낼 이메일 목록 계산하기 - 계산

구독자 목록으로부터 전체 이메일 목록을 계산하는 코드를 짜야 한다.

function calcEmailList(subscribers, goods, bests) {
	let emails = [];
  
 	subscribers.map((subscriber) => {
    	let email = makeEmail(subscriber, goods, bests);
      	emails.push(email);
    })
  	return emails;
}


실제로 전체 구독자 목록으로부터 이메일을 전송하는 액션 - 액션

function sendEmail() {
	const subscribers = fetchSubscribersFromDB();
	const coupons = fetchCouponsFromDB();
  	const goods = defineCouponRank(coupons, "good");
	const bests = defineCouponRank(coupons, "best");
  	
  	const emails = calcEmailList(subscribers, goods, bests);
  
  	emails.map((email) => emailSystem.send(email));
    )
}

이렇게 코드를 짰다. 책을 읽고서 코드를 짜서 구조가 비슷한 형태가 된 것 같다. 그래도 직접 코드를 짜보니 그저 책만 읽을때와는 확실히 느껴지는게 다르다.

기존 코드에 함수형 사고 적용하기

책에서는 수수료를 보내기위한 코드를 예시로 보여주는데 여기서 수수료를 보내는 sendPayout 뿐 아니라 해당 액션을 갖고 있는 모든 함수가 액션이 되어서 액션이 퍼져나가는 과정을 보여준다.

해당 예시코드

function figurePayout(affiliate) {
	var owed = affiliate.sales * affiliate.commission;
    if (owed > 100) sendPayout(affiliate.bank_code, owed);
}

function affiliatePayout(affiliates) {
	for(var a = 0; a < affiliates.length; a++) figurePayout(affiliates[a]);
}

function main(affiliates){
	affiliatePayout(affiliates);
}

액션을 부르는 함수가 있다면 그 함수도 액션이 된다. 액션을 사용하는 순간 코드 전체로 퍼져나가기 때문에 사용시 주의해야 한다. 56p

확실히 사용하는 시점, 횟수에 영향을 받는 액션은 다른 코드들에도 이곳저곳 영향을 끼치니 액션을 사용할때 굉장히 주의해야겠다는 생각이 든다.

액션의 다양한 형태

지금까지 액션을 계산과 같이 함수의 형태로만 생각했는데 생각보다 여러 형태의 액션이 존재했다.

함수형 프로그래머는 액션을 관리하는 법을 배우고 액션을 관리하기 위해 액션이 코드에서 어떤 형태로 나타나는지 알아야 한다. 57p

액션에 속하는 경우

  • 함수 호출
  • 메서드 호출
  • 생성자
  • 표현식
    • 변수 참조
    • 속성 참조
    • 배열 참조
  • 상태
    • 값 할당
    • 속성 삭제

여기서 표현식과 상태에 속하는 내용이 액션에 속하려면 해당 대상이 공유되고 변경가능해야 한다. 이런 점에서 함수형 프로그래머는 var, let 보다 const 를 되도록 사용하고 mutable 보다는 immutable 한 update 를 지향하지 않을까 라는 생각이 들었다.

마지막에 액션을 잘 사용하기 위한 방법이 너무너무 잘 설명되어 있어서 그대로 내용을 전달하고 싶다.

액션을 잘 사용하기 위한 방법

가능한 액션을 적게 사용하기

  • 액션을 전혀 사용할수는 없으니 액션대신 계산을 사용할수 없는지 생각하기

액션을 가능한 작게 만들기

  • 액션에서 결정, 계획과 같은 부분은 계산으로 빼내기
  • 액션과 관련 없는 코드를 모두 제거하기

액션이 외부 세계와 상호작용하는 것을 제한하기

  • 액션은 외부 세계에 영향을 주거나 받을 수 있어 내부엔 계산과 데이터만 두고 가장 바깥쪽에 액션을 두자.
  • 어니언 아키텍처 참고

액션이 호출 시점에 의존하는 것을 제한하기

  • 함수형 프로그래머는 액션이 호출 시점과 횟수에 덜 의존하도록 만드는 기술을 알고 있다.

정리

이번 챕터에서는 데이터, 계산, 액션이 무엇인지, 실제 실생활에 세가지 개념을 적용해보는 것과 기존 코드에 세가지 개념을 적용해보는 것을 토대로 각각에 대해 알아가는 내용이 주를 이루고 있다.

코드를 짤 때도 데이터에서 계산, 액션 순서로 코드를 짜고 아직 어니언 아키텍처 가 무엇인지 잘 모르겠지만 주변에 영향을 막대히 끼치는 액션으로부터 계산과 관련된 코드를 모두 제거하여 내부엔 계산과 데이터만, 액션은 되도록 가장 바깥쪽에 위치하도록 코드를 구상해보자는 내용을 소개하고 있다.

아직 실제 코드에 어떻게 적용할지 감이 잘 잡히는 것은 아니지만 우아한 스터디 사례처럼 코드에 하나씩 실제로 적용해보는 것으로 보다 더 체화가 잘 될 수 있도록 해보자.

profile
미래의 나를 만들어나가는 한 개발자의 블로그입니다.

0개의 댓글