주녁, DevNote
article thumbnail

개요

객체지향 프로그래밍은 필수적인 패러다임으로서 다루는 책과 글은 많다.

반면, 함수형 프로그래밍은 필수적이지도 않으며,

어떤 부분이 좋다고 콕 집어 말하기 어렵다.

 

이는 함수형 프로그래밍이 범용 패러다임이기 때문이다.

결국, 어디에서나 잘 어울릴 수 있다는 뜻이기도 하다

 

이 글은 아래 책을 읽고 난 후 작성되었습니다.

  • 쏙쏙 들어오는 함수형 코딩 / 에릭 노먼드

목표

함수형 프로그래밍의 주요 개념과 관점을 이해하고, 함수지향 설계와 아키텍쳐를 학습한다.


여정

계층형 설계

이번에는 분류하기(액션, 계산, 데이터)와는 약간 다른 방향에서 코드를 바라보자.

바로 계층형 설계이다. 

 

피라미드를 생각해보면 쉽다. 

각 층은 함수가 추상화된 레벨을 나타낸다.

이집트의 기자 피라미드

각 층별 동작은 이런식으로 이루어져 있다.

  • 맨 위층의 코드는 바로 아래층의 코드를 이용해서 다른 의미있는 행동을 한다.
  • 그 아래층의 코드는 바로 아래층의 코드를 이용해서 다른 의미있는 행동을 한다.
  • 바닥에 가까운 코드일수록 단순한 동작의 코드로 이루어져 있다.

 

다만, 이러한 규칙도 지켜야 한다.

  • 같은 계층에 있는 함수는 같은 목적을 가져야 한다.
  • 호출하는 화살표의 길이가 동일해야 같은 추상화 수준이다.

쉽게 예를 들자면, 저번 시간의 코드는 아래와 같이 표현할 수 있다.

6가지의 목적을 통해 추상화 레벨을 나누고,

코드 내에서 호출하는 함수를 그에 따라 분류하였다.

(추상화 레벨에서 몇가지 계층이 있는지는 사람마다 다를 수 있다.)

 

장바구니 기능의 계층형 구조

어떤가?

한 눈에 보기에도 이상하지 않은가?

같은 계층의 화살표의 길이가 모두 제각각이다.

 

다행인 점은 

화살표는 옆이나 위가 아닌 아래를 향한다는 점이다.

 

자, 이제 추상화 계층을 정리하면서 리팩토링을 진행해보자.


화살표 길이 줄이기

우선적으로 해야할 일은 추상화 계층의 화살표 길이를 동일하게 하는 일이다.

다만, 한번에 모두 동일하게 맞출 수는 없기 때문에, 순차적으로 진행해야 한다.

 

서로 다른 화살표 길이를 가진 메소드를 찾아보자.

장바구니 기본동작의 메소드들이 가장 눈에 띈다.

  • 길이 1 : calculateCartTotal()
function calculateCartTotal(cart) {
    let totalPrice = calculateTotal(cart);
    setCartTotalDom(totalPrice);
    return totalPrice;
}

길이가 1인 메소드는 크게 건드릴 부분이 없어보인다.

더 이상 줄일 동작이 없기 때문이다.

 

  • 길이 2 : addCartItem()
function addCartItem(cart, cartItem) {
    // 장바구니 케이스에 맞게 재사용
    return addElementLast(cart, cartItem);
}

길이가 2인 메소드도 카피 온 라이트 동작을 감싸서

그대로 사용하고 있기 때문에 건드릴 부분이 없어보인다.

 

  • 길이 3 : makeCartItem()
function makeCartItem(name, price) {
    return {
        name: name,
        price: price
    };
}

길이가 3인 메소드를 보면 몇 가지 눈에 띄는 점이 있다.

  • 매개변수에 의존한다 : 속성이 추가된다면 수정되어야 한다.
  • 범용성이 없다 : 장바구니가 아닌 찜 목록이라면 재사용할 수 없을지도 모른다.

이 부분을 중점적으로 수정해보자.


 

함수 빼내기

function makeCartItem(defaultProperties, cartItemProperties) {
    return makeItem({
        ...defaultProperties,
        ...cartItemProperties,
    });
}

function makeItem(properties) {
    return { ...properties };
}

makeItem을 통해 객체를 반환하는 자바스크립트 기본 동작을 작성했다.

이를 재사용해서 장바구니에 아이템을 추가하는 메소드를 작성했다.

 

defaultProperties라는 객체가 기본값을 가지고 있고,

외부에서 주입된 값을 덮어씌우는 형태로 작성되었다.

이렇게 하면 외부에서 Category라는 속성이 추가되었다고 해도

전혀 코드에 변함이 없어진다.

 

실제 사용하는 코드는 이렇게 바뀐다.

// 기존 코드
function addToCart(cart, name, price) {
    let cartItem = makeCartItem(name, price);
    cart = addCartItem(cart, cartItem);
    return calculateCartTotal(cart);
}


// 변경 코드
const defaultProperties = {
	// 이 부분은 아직 어색하나,
    // 추후 비즈니스 동작에서 수정하도록 하자
    name: '',
    price: 0,
};

// 전역변수(cart)를 읽는 것은 액션이다
function addToCart(cart, defaultProperties, cartItemProperties) {
    let cartItem = makeCartItem(defaultProperties, cartItemProperties);
    cart = addCartItem(cart, cartItem);
    return calculateCartTotal(cart);
}

이렇게 바뀐 코드를 토대로 다시 계층화 구조를 그려보면 아래와 같다.

변경된 장바구니 계층화 구조

단 한 계층만 추가되었을 뿐인데,

한결 깔끔해진 추상화 레벨을 볼 수 있다.

 

 

책의 저자는 거대한 추상화 레벨을 만들기까지 비즈니스가 기다려주지 않기 때문에
작업하는 코드가 편리하다고 느낀다면 설계는 잠시 멈춰도 된다고 조언했다!

현재 코드에서는 크게 추상화해도 이득을 볼 수 있는 부분이 없기 때문에

더 이상 수정하지 않도록 할 것이다.

 


Q & A

  • 제가 그린 그래프는 조금 다른데요?

그린 그래프와 계층의 모습은 서로 다를 수 있다.

정답은 없다. 아래 항목만 지킨다면 모두 올바른 그래프이다.

  • 모든 함수에 그래프가 있다.
  • 함수 안에서 다른 함수를 호출한다면 반드시 표시되어야 한다.
  • 화살표는 옆이나 위가 아닌 아래를 향해야 한다.

마무리

이번 시간에는 계층화 설계의 관점에서 코드를 바라보는 법을 배웠다.

 

다음 시간에는 좀 더 구현에 초점을 맞춘 케이스들을 살펴보도록 하자.


profile

주녁, DevNote

@junwork

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!