2016년 12월 29일 목요일

RxJS - Purity(순수성)

Reactive extension JS (RxJS)


Purity(순수성)

RxJS는 순수함수로 이루어져있다는 뜻이다. 순수함수란 함수형 프로그래밍에서 쓰이는 단어이다. 사람들마다 그리고 언어들마다 순수함수에 대한 정의가 약간씩 다른 것 같긴 하지만.. 내가 아는 순수함수의 정의는 아래와 같다.

  • 같은 입력에 항상 같은 출력을 반환한다.
  • 사이드 이펙트가 없다.
  • 외부 상태와 무관하다.
순수함수는 해당 함수보다 상위의 스콥에서 정의된 함수를 참조하지 않는다. 오로지 인자로 인해 제어된다. 같은 값을 출력하기때문에 랜덤(Math.random)이나 날짜(new Date)등을 사용하지 않는다. 따라서 오류가 발생할 가능성이 적다. RxJS는 이 부분을 장점으로 내세우고 있다.

아래 예제를 보면...

const observable = Rx.Observable.from([1,2,3,4])
 .map(x => x*x)
 .scan((a,b) => a+b, 0);
observable.subscribe(x => console.log(x)); //1, 5, 14, 50

RxJS는 위처럼 체이닝으로 method들을 이어 나간다. 그리고 다음 method들의 함수 내부는 이전함수로 부터 return받은 인자로만 이루어 진다. 물론 global 영역에 변수를 지정하고 가져오는게 불가능 한건 아니지만, 되도록 순수하게 함수를 사용하는 것을 권장한다. 아무래도 순수함수가 가진 안정성과 속도의 장점을 가져가려는 것이 아닐까 생각한다.

Flow(흐름)

RxJS에는 관찰자를 통해 이벤트가 흐르는 방식을 제어하는 데 도움이되는 다양한 연산자(operator)가 있다. 위에서 등장한 map, scan 등이 그것이다. 하나 하나의 흐름을 operator들로 전달하는 방식으로 동작하는 것이다.

Values(값)

RxJS의 흐름이 진행되는 과정에서 인자로 값들을 넘긴다. RxJS는 operator들을 통해 이 값들을 원하는 값을 도출한다.

2016년 12월 23일 금요일

Reactive extension JS (RxJS) 시작!

Reactive extension JS (RxJS)

RxJS에 대해 학습한 내용을 정리해서 포스팅 해보고자 한다.
여기 글을 RxJS공식 사이트(http://reactivex.io/rxjs)를 근간으로 하고 있다.

RxJS 공식사이트에서는 RxJS를 한문장으로 정의하고 있다..

Think of RxJS as Lodash for events.

이벤트의 로다시 같다는 것이다.. 로다시가 무엇인가. 함수형 프로그래밍을 위한 라이브러리가 아닌가...? 대신 로다시가 절차지향적 방식이라면 RxJS는 이벤트 기반의 비동시 방식이라는 차이가 있는 것으로 보인다.

RxJS는 다음과 같은 키워드로 정의되고 있다.
  • Observable: 미래의 값(value)나 이벤트의 호출 가능한 수집의 개념을 나타냄.
  • Observer: Observable에 의해 전달 된 값을 처리하는 콜백 콜렉션.
  • Subscription: Observable의 실행을 나타냄. 주로 실행 취소에 유용함.
  • Operators: map, filter, concat, flatMap 등과 같은 method를 사용하여 컬렉션을 다루는 함수 프로그래밍 스타일을 가능하게하는 순수(pure) 함수.
  • Subject: EventEmitter와 동일하며 여러 Observers에 값 또는 이벤트를 멀티 캐스팅함.
  • Schedulers: 동시 처리를 제어하는 중앙 집중식 디스패처로서 계산이 언제 발생하는지 조정할 수 있다. setTimeout or requestAnimationFrame or others.

RxJS가 힘든 이유는 2가지 인것 같다. 
  • 이벤트 기반의 비동기 처리 프로그래밍 방식이 개념적으로 익숙하지 않다.
  • 익혀야할 용어나 method들이 많다. 
앞으로 하나씩 하나씩 익혀보자.

초반 개념을 잡는데 네이버 김훈민 님의 강의가 많은 도움이 됐다. 아래는 관련 블로그나 강의 영상의 링크다. 내용이 많이 겹치지만 일부 다른 부분들도 있어서 되도록 알고있는 모든 링크를 첨부한다.

블로그: http://huns.me/development/2051
나프타 컨퍼런스 영상: https://www.youtube.com/watch?v=3FKlYO4okts
웹 프론트엔드 개발자의 얕고 넓은 Rx 이야기: http://www.slideshare.net/jeokrang/rx-70197043

2016년 12월 16일 금요일

React Life Cycle

React Life Cycle..

자꾸 잊어버려서 기록해 둔다...



컴포넌트를 생성 할 때는 constructor -> componentWillMount -> render -> componentDidMount.

컴포넌트를 제거 할 때는 componentWillUnmount 메소드만 실행.

컴포넌트의 prop이 변경될 때엔 componentWillReceiveProps -> shouldComponentUpdate -> componentWillUpdate-> render -> componentDidUpdate.

컴포넌트의 state가 변경될 떄엔 props 를 받았을 때 와 비슷하지만 shouldComponentUpdate 부터 시작.

2016년 12월 15일 목요일

함수형프로그래밍

함수형 프로그래밍에 익숙해지기고 functional한 사고를 키우기위해 연습한 내용들을 기록한 포스트입니다.

두개의 배열을 하나의 배열로 합쳐보자.

  1. 각 배열의 요소는 객체이다.
  2. 각 배열의 객체의 "id" 프로퍼티가 일치하는 객체끼리 합친다.
  3. 남자이고 나이가 30이상이면 target을 true로, 아니면 false로 

Trial 1. underscore의 map, extend, find 함수 활용

const arr1 = [
  {name: 'owen', gender: 'male', age: 36, id: 11},
  {name: 'bbo', gender: 'female', age: 34, id: 12},
  {name: 'woo', gender: 'male', age: 20, id: 13}
];

const arr2 = [
  {id: 10, phone: '010-1111-1111'},
  {id: 11, phone: '010-2222-2222'},
  {id: 12, phone: '010-3333-3333'},
  {id: 13, phone: '010-4444-4444'}
];

const arr3 = _.map(arr1, function(item){
  return _.extend(item, _.find(arr2, function(i){
    return item.id === i.id;
  }), {target: item.age >= 30 && item.gender === 'male'})
});

console.log(arr3);
/*[
{'name':'owen','gender':'male','age':36,'id':11,'phone':'010-2222-2222','target':true},
{'name':'bbo','gender':'female','age':34,'id':12,'phone':'010-3333-3333','target':false},
{'name':'woo','gender':'male','age':20,'id':13,'phone':'010-4444-4444','target':false}
]*/

[Check]

  • 위에서 find는 findWhere로 대체할 수 있다. 퍼포먼스 차이는 크게 없어 보인다.

2016년 9월 1일 목요일

ECMA5 javascript method : map

오늘은 ECMA5 javascript 메소드 중에 하나인 map에 대해서 포스팅 하고자 한다.

map메소드는 Array객체의 프로토타입 프로퍼티에 존재하는 메소드다. (즉 모든 배열에서 사용 가능하다.) 이 메소드는 배열의 각 요소들에 대해서 인자로 넘기는 함수를 실행시키고, 해당 배열을 리턴으로 반환한다.

array.map(function(currentValue,index,arr), thisValue)

위에서 볼 수 있듯이 콜백함수에 첫번째 값으로 요소의 값을, 두번째로 인덱스, 세번째로 배열 자체를 넘긴다. 아래는 제곱근을 구하는 예제이다.

var numbers = [4, 9, 16, 25];
var sq = numbers.map(function(val, idx){
  return Math.sqrt(val);
});
console.log(sq);//[2, 3, 4, 5]

map은 원래의 배열에 영향을 미치지는 않는다. 따라서 다시 numbers를 콘솔로 찍으면 원래 값([4, 9, 16, 25])을 반환한다.

ECMA3에서 map메소드는 없지만, 다음과 같이 스크립트를 활용해 만들 수 있다.

if(!Array.prototype.map){
  Array.prototype.map = function(a, f){
    var results = [];
    for (var i = 0, len = a.length; i < len; i++) {
      if(i in a) results[i] = f.call(null, a[i], i, a);
    }
    return results;
  };
}


2016년 8월 28일 일요일

ECMA5 javascript method : reduce

자바스크립트를 쓰다보면 ECMA5에서 표준화된 메소드들을 접하게 된다. 이를 사용하면 ECMA3보다 훨신 효율적인 소스를 제작 할 수 있음에도 불구하고, 아직 우리나라는 익스8버젼을 고려해야하기 때문에 포기하는 경우가 많았다.

하지만 가까운 미래에 우리나라도 ECMA5기반으로 만들어진 사이트 들이 늘어날 것이고, 또 node.js의 경우에는 서버단의 언어이고 V8엔진 기반으로 해석되기 때문에 익스에 대한 고민없이 ECMA5 또는 6기반으로 소스를 설계할 수 있다.

그래서 ECMA5기반의 메소드들을 익숙하게 사용할 수 있도록 익혀 보고자 한다.
그리고 첫번째로 reduce 메소드를 꼽았다.

reduce는 배열의 메소드다. 배열의 각 요소들을 순회하는데, 순회할 때 마다 리턴값을 만들어서 다음 순회에 넘겨준다. 다음은 w3c의 reduce 메소드에 대한 설명이다.
http://www.w3schools.com/jsref/jsref_reduce.asp

reduce 매소드의 기본형태는 아래와 같다.

array.reduce(function(리턴값,현재요소,현재요소의 index,배열),초기값)

여기서 리턴값이란 1번째 순회에서는 초기값을, 이후 순회에서는 그전 순회에서 받은 리턴값을 나타낸다. 아래는 reduce의 가장기본적인 예제이다.

var arr = [1, 3, 2, 5, 4];
var sum = arr.reduce(function(total, val){return total + val;}, 0);
console.log(sum);//15

위 예제에서는 리턴값에 현재요소를 더한값을 다음 순회로 리턴시킨다. 결국 모든 요소를 더한값을 반환한다.

다음은 배열안의 수 중 가장 큰 수를 추출하는 스크립트이다.

var arr = [1, 6, 4, 100, 5, 2];
var max = arr.reduce(function(a, b){return a > b ? a : b;});
console.log(max);//100

위 예제는 리턴된 값과 현재요소를 비교해서 큰 값을 리턴시킨다. 물론 배열에서 가장 큰 값을 구하는 방법은 apply함수를 이용하는 방법이 더 효율적이긴 하다.
> Math.max.apply(Math, arr);

이제 좀 더 복잡한 예제를 보자. 아래는 배열중에 연속된 수들이 있으면 해당 수들을 배열로 묶어서 반환시키는 함수이다.

var arr = [1, 6, 4, 100, 5, 2, 101, 103];
arr.sort(function(a, b) {return a - b}); //sort 메소드로 오름차순 정렬.
var cnt = 0,tArr = [],len;
var max = arr.reduce(function(a, b) {
    if (a + 1 === b) { //현재요소가 리턴값과 이어지는 정수면...
        if (!cnt) { //연속된 수의 첫 시작이면
            var tArr2 = [];
            tArr2.push(a, b);
            len = tArr.push(tArr2);
            cnt = 1;
        } else { //연속된 수가 계속 이어지고 있는 상태면..
            tArr[(len - 1)].push(b);
        }
    } else { //수가 이어지지 않으면..
        cnt = 0;
    }
    return b; //현재 요소를 반훤
});
console.log(tArr); //[[1, 2], [4, 5, 6], [100, 101]]

결론적으로 reduce는 어떤값을 리턴시키느냐에 따라 다양하게 확장해서 사용할 수 있다!

2016년 8월 22일 월요일

JSONP에 관하여...

jsonp라는 놈을 처음 접한건 instagram API에 대해서 스터디 하고 있었을 때였다. jquery의 ajax매소드($.ajax(인자))를 써서 api를 써보는데, datatype속성을 json으로 하면 브라우저가 크로스도메인 에러를 뿌린다.. 에러내용은 아래와 같다.

XMLHttpRequest cannot load http://받는주소. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://보낸주소' is therefore not allowed access.

즉, 도메인이 달라서 안된다는 것인데, 이것저것 찾아보다가 몇시간을 허비했다... 결국 찾은 해결책은 datatype속성을 jsonp로만 바꿔주면 되는 것이었다!

이는 교차출처(Cross-Origin) HTTP요청에 대한 정책때문이었다. ajax요청을 받는 서버의 헤더에 크로스도메인 요청을 승인(CORS)이 되어 있지 않으면 서로다른 도메인간에 ajax요청은 승인되지 않는다. 그리고 CORS가 되어 있더라도 ie8 같은 낮은버젼의 브라우저에서는 이를 지원하지 않는다. 그래서 나온 해결책이 jsonp다. 결국 jsonp는 우회해서 크로스도메인 요청을 할 수 있는 방식인 것이다.

jsonp가 생소할수 있지만, 사실 jsonp은 script태그를 생성하고 해당 url을 호출하는 방식이다. 우리가 아주 많이쓰고 있는 방식이다. 예를 들면 우리가 jquery js파일을 호출할때 아래와 같이 할 수 있다.

<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>

생각해보면 script태그로 호출하는 url주소는 도메인이 어디든지 영향을 받지 않는다. jsonp는 이 방법을 활용해서 해당 파일을 호출한다. 응답하는 파일의 내용에는 함수를 호출하는 자바스크립트 문장이 있으며, 인자로 json형태의 객체를 넘긴다. 예를 들면 다음과 같다.

callback([1, 2, {"name" : "owen"}])//여기서 함수명(callback)은 개발자가 정하기 나름이다.

그리고 이 함수에 대한 선언을 미리 해놓아서, jsonp으로 파일이 응답할때 해당 함수가 실행되도록 하는 것이다. 예를 들어.

function callback(res){console.log(res.name)}
//jsonp로 파일을 가져오면 위 'callback'함수가 실행된다.

Jquery의 ajax를 활용해서 jsonp를 사용하는건, 아래와 같이 쓴다.

$.ajax({
    dataType: "jsonp",
    url: "http://textsite.com/jsonp",
    type: "GET",
    data: {'s':s},
    success: function(data){
    }
});

아주쉽다. json을 jsonp로만 바꾸어주면 된다..
여기서 주의할 점은 위에서도 봤지만, 요청 받는 서버(http://textsite.com/jsonp)에서 자바스크립트 함수 호출하는 문장 형태로 값을 넘겨줄 준비가 되어 있어야 한다. 즉 아무 사이트나 json을 jsonp로 바꾼다고 값을 넘겨받을 수 있는건 아니라는 것이다. 다행히 요새 대부분의 sns, 포털사이트의 api들이 jsonp방식을 지원하고 있어서 큰 진행함에 있어서 큰 문제는 없다.

보다 정확히 jsonp를 이해하기 위해서 Jquery를 쓰지 말고 직접 만들어보자.

function getJSONP(url){
 if(url.indexOf("?") === -1) url +="?callback=cbFn"; //url에 파라미터가 있으면 '&' 없으면 '?'
 else url += "&callback=cbFn";
 var script = document.createElement('script');//스크립트 엘리먼트를 생성한다.
 window.cbFn = function(res){//호출될 함수를 미리 선언한다.
  try{
   console.log(res);
  }
  finally{
   delete window.cbFn;//임시로 추가한 함수므로 함수 실행이 끝나고 나서 지운다.
   script.parentNode.removeChild(script);//추가한 스크립트 테그도도 지운다.
  }
 }
 script.src = url;
 document.body.appendChild(script);//DOM에 script가 추가됨에 따라 파일 안의 함수(cnFn)가 호출된다.
}
getJSONP('jsonp.php');

그리고 호출되는 jsonp.php파일은 아래와 같이 작성하였다.

<?php echo $_GET['callback'];?>([
 [1,2,{"name" : "owen"}]
]);

결국 jsonp.php파일안의 cbFn([[1,2,{"name" : "owen"}]]) 를 통해 전역함수인 cbFn함수가 호출되면서 콘솔 함수(console.log(res);)가 실행되는 것이다~