본문 바로가기

Web[웹]/ES6+ 함수형 언어

[ES6+: 응용] 사용자 정의 객체를 이터러블 프로그래밍으로 다루기

 

#1 사용자 정의 객체를 다루기

 

map, set 다루기

 

자바스크립트의 기본객체가 아닌 추가적으로 만들어인 사용자 정의 내장 객체 역시도 이터러블로 프로그래밍을 다룰 수 있다.

먼저 Map을 알아보자.

 

let m = new Map();
m.set('a', 1);
m.set('b', 2);
m.set('c', 3);

 

위와 같은 Map도 이터러블로 프로그래밍 하고 다시 새로운 Map으로 생성이 가능하다.

 

_.go(
   m,
   _.filter(([k, v]) => v % 2),
   entries => new Map(entries),
   console.log);
// Map(2) {"a" => 1, "c" => 3}

 

이어서 Set같은 경우도 마찬가지이다.

 

let s = new Set();
s.add(10);
s.add(20);
s.add(30);

console.log([...s]); // [10, 20, 30]

const add = (a, b) => a + b;
console.log(_.reduce(add, s)); // 60

 


 

Model, Collection 클래스 만들어서 이터러블 프로토콜 지원하기

 

Model, Colletion 클래스를 만들어서 사용자 정의 객체를 만들 것인데, 여기서 이터러블을 지원하도록 만들어 볼 것이다.

먼저, 다음과 같이 동작하는 Model과 Collection 클래스가 있다.

 

class Model{
    constructor(attrs = {}){
        this._attrs = attrs;
    }
    get(k){
        return this._attrs[k] = v;
    }
    set(k, v){
        this._attrs[k] = v;
        return this;
    }
}

class Collection{
    constructor(models = []){
        this._models = models;
    }
    at(idx){
        return this._models[idx];
    }
    add(model){
        this._models.push(model);
        return this;
    }
}

 

이 Model과 Collection을 일반적인 객체지향적 설계로 사용해보자.

일단 아래 코드처럼 coll을 통해 새로운 모델 3개를 생성했다.

 

const coll = new Collection();
coll.add(new Model({ id: 1, name: 'AA'}));
coll.add(new Model({ id: 3, name: 'BB'}));
coll.add(new Model({ id: 5, name: 'CC'}));

 

이를 일반적으로 불러올 경우는 아래처럼 한다.

 

console.log(coll.at(2).get('name')); // CC
console.log(coll.at(1).get('id')); // 3

 

하지만, 위 코드는 생성된 모델배열을 순회하는 코드를 짜려면 아래처럼 range처럼 범위를 직접 지정해주어야 가능하다. 

 

_.go(
   L.range(3),
   L.map(i => coll.at(i)),
   L.map(m => m.get('name')),
   _.each(console.log)); // AA BB CC

 

위와 같이 만들어 지는 것은, range라는 범위 숫자에 의존해야하며, 뒤떨어진 코드가 된다.

이를 이터러블 측면에서 순회되는 코드로 한번 바꿔보자.

 

Collection 클래스에 Symbol.iterator가 Model전체를 뽑아주는 역할을 해주게끔 하면 된다.

 

class Collection{
    constructor(models = []){
        this._models = models;
    }
    at(idx){
        return this._models[idx];
    }
    add(model){
        this._models.push(model);
        return this;
    }
    *[Symbol.iterator](){
        yield *this._models;
    }
    /*위와 같은 동작
    [Symbol.iterator](){
        return this._models[Symbol.iterator]();
    }
    */
}

 

위와 같이 짜준다면 coll 을통해 이터러블 순회하면서, 동작을 할 수 있다.

 

_.go(
    coll,
    L.map(m => m.get('name')),
    _.each(console.log)); // AA BB CC

/* 소문자로 id값 변화*/
_.go(
    coll,
    _.each(m => m.set('name', m.get('name').toLowerCase())));

_.go(
    coll,
    L.map(m => m.get('name')),
    _.each(console.log)); // aa bb cc

 


 

Product, Products 클래스로 메서드를 함수형으로 구현하기

 

이번엔 Model, Colletion 클래스를 상속받은 Product와 Products 클래스를 만들고 그에 어울리는 메소드를 만들며 어떻게 이터러블 프로토콜과 조화를 이룰 수 있는지 알아볼 것이다.

 

class Product extends Model{}
class Products extends Collection{}

 

각각 상속받은 Product와 Products가 있다. 이 코드에서 상품 몇개를 추가하고 값을 더하는 일반적인 코드를 만들어 보자.

 

class Product extends Model{}
class Products extends Collection{
    totalPrice() {
        let total = 0;
        this._models.forEach(product =>{
            total += product.get('price');
        });
        return total;
    }
}

const products = new Products();
products.add(new Product({ id: 1, price: 10000}));
products.add(new Product({ id: 3, price: 25000}));
products.add(new Product({ id: 5, price: 35000}));
console.log(products.totalPrice()); // 70000

 

이를 이터러블 프로그래밍에 더 어울리고 간결하게 해보자.

Products에서 this를 통해 이터러블이 순회할 수 있는지 확인해보면 다음과 같다.

 

class Products extends Collection{
    totalPrice() {
        console.log([...this]); // (3) [Product, Product, Product]

        let total = 0;
        this._models.forEach(product =>{
            total += product.get('price');
        });
        return total;
    }
}

 

위 Collection에서 Symbol.iterator를 구성했기 때문에 가능한 것인데, 이를 토대로 아래처럼 코드를 정리해줄 수 있다.

 

class Product extends Model{}
class Products extends Collection{
    totalPrice() {
        return _.go(
            this,
            L.map(p => p.get('price')),
            _.reduce((a, b) => a + b));
    }
}

const products = new Products();
products.add(new Product({ id: 1, price: 10000}));
products.add(new Product({ id: 3, price: 25000}));
products.add(new Product({ id: 5, price: 35000}));
console.log(products.totalPrice()); // 70000