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);)가 실행되는 것이다~

2016년 8월 21일 일요일

Javascript의 valueOf, toString

Javascript의 Object객체의 내장 매쏘드 중,  자바스크립트 내부적으로는 많이 사용되지만, 사용자가 작성할때는 거의 사용되지 않는 것들이 있다. 그중에 대표적인 것이 toString, valueOf 이다.

내부적으로 toString이 사용되는 대표적인 예는 DOM프로퍼티인 innerHTML을 사용할 때이다.
var obj = {name : 'owen'};
document.getElementById('demo').innerHTML = obj;

이렇게 하면 자바스크립트는 demo아이디를 가진 엘리먼트에 "[object Object]"를 출력한다. 즉, 사용자는 사용하지 않았지만, 자바스크립트 내부적으로 toString 매쏘드를 사용해서 객체의 내용을 출력한 것이다.

valueOf가 가장 흔하게 사용되는 경우는 객체를 비교하는 연산자에서이다. ">, <, >=, <="등의 연산자가 객체간에 실행될때 자바스크립트 내부적으로는 valueOf 매쏘드를 호출한다.

이런 특성을 이용하면 흥미로운 예제를 만들 수 있다.
toString, valueOf은 결국 프로토타입으로부터 상속받은 매쏘드이기 때문에, 사용자가 같은 이름의 매쏘드를 선언함으로써 위 두 매쏘드를 원하는 형태로 바꿀수 있다는 것이다.

다음 예제를 보면... (자바스크립트의 프로토타입에 대해 잘 모르시는 분은 이해하기 어려울 수 있습니다.)

var students = [
  {name : 'owen', idx : 5},
  {name : 'jason', idx : 3},
  {name : 'justin', idx : 4}
];

var proto = {
  toString : function(){return this.name;},
  valueOf : function(){return this.idx}
};
var students2 = [];
for(var i = 0 ; i < students.length ; i++){
  var F = function(){};
  F.prototype = proto;
  students2[i] = new F();
  students2[i].name = students[i].name;
  students2[i].idx = students[i].idx;
}
console.log(students2[0] < students2[1]); //false
document.getElementById('demo').innerHTML = students2[0]; //owen 출력


  1. 이는 학교에서 학생들의 번호와 이름을 객체로 만드는 상황을 가정한 것이다. 첫번째로 학생들을 배열로 만들고 이름과 번호(idx)를 선언한다. 
  2. proto라는 객체를 만들어서 toString과 valueOf매쏘드를 새롭게 정의한다. 즉 toString은 학생의 이름을, valueOf는 학생의 번호를 가져오는 매쏘드가 된 것이다.
  3. for문을 실행시켜 새로운 배열을 생성하면서 각각의 요소에 toString과 valueOf를 프로토타입 상속을 통해 선언하고, 각이름과 idx값을 넣어준다.
  4. 이제 각요소의 idx프로퍼티나 name프로퍼티를 찾아가지 않아도 값비교나 이름 출력이 가능해 졌다.
이 예제는 크게 실용적으로 보이지는 않지만, 이런 자바스크립트 특성을 잘 활용하면 보다 효율적인 코드를 작성할 수 있을것 같다.^^

2016년 8월 18일 목요일

다른 퍼블리셔들의 사용패턴 설문조사

다른 퍼블리셔들은 어떻게 하는지 궁금한 것을이 있었다.
예를들어 에디터는 뭐를 쓸까? css 스타일은 한줄에 쓰나 나눠서 쓰나? 등등..

그러던 와중 css사용 패턴에 대한 설문조사가 있는 글을 발견했다.

https://www.sitepoint.com/results-ultimate-css-survey/

모수가 3,500명 정도 되고, 외국사람들을 대상으로 조사한 설문이다 보니 꽤 글로벌하게 의미있는 결과가 아닐까 싶다.

재미있었던것 몇가지가 있는데..


  1. css스타일을 멀티라인이 아닌, 1개 라인에 쓰는 사람이 훨신 많다는 것.
  2. css스타일링을 모바일 페이지 먼저 하는 사람이 많다는것
    (이건 외국이니까 가능한 것 같다. 우리는 미디어 쿼리 따위는 무시하는 ie8의 눈치를 봐야 한다.)
  3. 얼마전까지 썼던 aptana 에디터는 순위 안에도 없다는것
  4. 부트스트랩 css를 사용하는 사람이 43%로 제일 많다는것.
    (프레임워크 사용 안하는 사람은 20%...)
  5. CSS preprocessor(SASS, LESS등)을 사용하는 사람이 73%라는것.

설문조사가 영어로 되어 있어서 완전히 이해하기는 어렵다. 시간날때마다 천천히 봐야 겠다..

2016년 8월 15일 월요일

Javascript 클로저

올해 초까지만 해도 Javascript를 사용하면서 '클로저'라는 개념을 잘 모르고 있었다. javascript를 제대로 익히고 싶다는 마음으로 시작한 공부에서 가장 큰 이득은 클로저를 어느정도는 이해 할 수 있게 된 것이라고 생각한다. 그만큼 중요하다.

사전적으로 클로저는 외부함수(포함하고 있는)의 변수에 접근할 수 있는 내부 함수를 말한다. 클로저라는게 사실 유효 범위를 뜻하는 것이라 사실 우리가 알게 모르게 쓰고 있다. 자바스크립트를 어느정도 하는 사람은 아래 예제가 어렵지는 않을 것이다.

var num = 1;
function init(){
  var num = 2;
  return num;
}
console.log(init())//2;

자바스크립트가 변수를 찾아 나갈때는 유효범위의 우선 순위대로 찾아 나간다. init함수에서 num이라는 변수를 찾을 때, 가장 가까이 변수가 선언된 곳은 var num=2이므로 해당 값을 반환한다.

비슷 하지만 다음 예제를 보자.

var num = 1;
function init(){
  var num = 2;
  function getNum(){
    return num;
  }
  return getNum;
}
console.log(init()())//2;

첫번째 예제와 거의 흡사하지만, 리턴을 변수가 아닌 함수로 넘겼다. 따라서 init()() 라는 조금 특이한 형태로 호출했다. init라는 함수를 호출해서 리턴받은 함수를 다시 호출한 것이다. 이 부분이 제일 중요한데, 왜냐하면 내부 함수(getNum)를 해당 내부함수의 외부함수(init)가 아닌 전역 영역에서 호출했다는 것이다. 또 호출된 내부 함수(getNum)의 유효범위가, 전역 영역에서 호출되었으메도 불구하고, 외부함수(init) 안의 변수에 접근해서 값을 가져왔다는 것이다.

외부함수(init)가 호출되고 함수의 해석이 끝날때, 리턴된 값이 함수로 남아 있어서 해당 함수에 접근할 수 있다면, 외부함수의 변수들은 사라지지 않고 남아있다고 한다. 그리고 자바스크립트의 유효범위는 함수가 호출될 때가 아닌, 정의될 때를 기준으로 작동된다고 한다.

이 개념을 잘 이해하고 있으면 다양한 방법으로 사용이 가능하다. 간단한 예제로, 숫자를 1씩 더하는 함수를 만들어 보자. 우리가 보통 사용하는 방법은 아래와 같다.

var num = 0;
function inc(){
 num++;
 return num;
}
console.log(inc());//1
console.log(inc());//2
console.log(inc());//3

함수안에 변수를 선언하면 함수가 실행될때 마다 변수가 초기화 되므로 전역 영역에 변수를 정의한다. 하지만 전역 영역에 변수를 정의하고 사용하는 것은 퍼포먼스를 고려할때 좋은 방법이 아니다.
이를 클로저를 사용하면 다음과 같이 바꿀수 있다.

function inc(){
 var cnt = 0;
 return function(){
  cnt++;
  return cnt;
 };
};
var incFn = inc();

console.log(incFn());//1
console.log(incFn());//2
console.log(incFn());//3

실제로 사용해보니, 개인적으로는 엘리먼트 요소를 외부 함수에 미리 정의해두고, 내부함수에 접근해서 외부함수의 엘리먼트를 호출하는 형태를 많이 쓰게 되었다. 함수가 호출될때마다 엘리먼트를 찾지 않아도 되니 퍼포먼스 측면에서 효율적이었다.

2016년 8월 12일 금요일

프레임에 이미지 꽉 채우기 Part2(Javascript)

** 이 글은  프레임에 이미지 꽉 채우기 Part1(CSS)에 이어진 글입니다.

이번에는 자바스크립트를 이용한 방식이다.

Javascript로 꽉 채우기.

우선 소스...


imgAlign이라는 함수를 만들었다.

함수가 길고 복잡하니 함수에 대한 설명은 하지 않고 사용법에 대해서만 기록한다.

우선 css스타일이 있어야 한다.

img.h100p {
    height: 100% !important;
    width: auto !important;
    position: relative
}
img.w100p {
    width: 100% !important;
    height: auto !important;
    position: relative;
}

자바스크립트로 width와 height값을 처리하지 않고 클래스를 add, remove 해서 위치를 맞춘다. 그게더 속도가 빠르다고 해서 이렇게 한건데.. 실상은 잘 모르겠다.

함수 실행할때 인자는 여러 방법으로 넘길 수 있다. 인자로는 이미지를 싸고있는 박스를 css selector방식으로 넘긴다.

imgAlign('.imgBx'); //jquery로 씌워서 해당 element를 찾음
imgAlign(['.imgBx', '.imgBx2']); //배열을 돌면서 jquery로 씌워서 해당 element를 찾음
imgAlign($('.imgBx')); //이미 jquery로 싸여 있으므로 바로 element를 찾음
imgAlign('.imgBx', {resize : ture}); //리사이즈 될때마다 위치를 재 계산함.



프레임에 이미지 꽉 채우기 Part1(CSS)

코딩을 하다 보면 특정 프레임 안에 이미지를 꽉 채워야 할 때가 종종 있다.
여기서 꽉체운다는 것은, 프레임의 가로/세로 보다 이미지의 가로/세로가 크다면 세로가 100%로가 되고, 작다면 가로가 100%가 되는것을 의미한다.

이 것과 관련된 글을 찾아보니 css-tricks에 잘 정리가 되어 있는것을 발견했다.

https://css-tricks.com/perfect-full-page-background-image/

위 페이지에서 여러가지 방법을 제시하고 있는데 그 중 유용한 2가지 방법에 대해서 포스팅 하려고 한다.

CSS로 꽉 채우기.

이 방법은 완벽하진 않다. 하지만, javascript를 쓰지않고 구현할 수 있다는 점에서 매력적이다.

우선 링크...
See the Pen 이미지 꽉 채우기(css) by owen (@wooner) on CodePen.


여기서의 핵심 스타일 소스는 아래와 같다.

.gallery_list_artwork .img> a {
    display: block;
    position: absolute;
    top: -50%;
    left: -50%;
    width: 200%;
    height: 200%;
}
.gallery_list_artwork .img img {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    min-width: 50%;
    min-height: 50%;
    margin: auto;
}

프레임 영역 안의 프레임(위 에서 a태그)를 absolute로 띄우고 크기를 프레임에 200%로 넗힌다음 top, left를 50%씩 줘서 가운데로 위치하게 했다.
그 다음 이미지의 최소 길이와 최소 높이를 50%씩 줘서 안쪽 프레임 넓이의 반절로 길이를 조절하고, top, bottom, left, right를 0을 주고,  margin을 auto를 줘서 가운대로 위치 시켰다. 이렇게 하면 가로든, 세로든 프레임에 딱 맞도록 이미지가 위치하게 된다.
이 방법의 한계는 이미지의 원본 크기가 프레임의 크기보다 2배 이상 클때는 역시 이미지가 프레임을 넘어가게 된다.

더 큰 이미지에 맞추려면 프레임의 안쪽 프레임의 넓이를 더 넓히면 된다. 예를 들어..

.gallery_list_artwork .img> a {
    display: block;
    position: absolute;
    top: -25%;
    left: -25%;
    width: 400%;
    height: 400%;
}
.gallery_list_artwork .img img {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    min-width: 25%;
    min-height: 25%;
    margin: auto;
}

하지만 이미지가 얼마나 크게 될지 예측할 수 없는 경우도 있고, 저렇게 영역을 400%씩 넗히는 것도 뭔가 찜찜하다...

다음은 자바스크립트로 맞추는 방법을 게시하겠다.

객체와 배열 구분하기.

자바스크립트를 작성하다 보면, 객체와 배열을 구분해야할 때가 있다.
근데 타입을 조사하는 함수인 typeof 를 실행해 보면 객체와 배열 모두 'object'를 반환한다.

console.log(typeof {});//"object"
console.log(typeof []);//"object"

이때 사용하는 방법은, 자바스크립트 객체의 매쏘드인 toString을 활용하는 것이다.
일단 소스는 아래와 같다.

위의 예제에서 typeof 대신 toString을 실행해보면 아래와 같이 나온다.

console.log(Object.prototype.toString.call({}))//;"[object Object]"
console.log(Object.prototype.toString.call([]));//"[object Array]"

위 예제에서 특이한 것은 toString매쏘드를 해당 객체(또는 배열)에 직접 실행 한 것이 아니라 Object객체의 프로토타입 프로퍼티로 실행을 시키고 call이라는 재귀 함수를 통해 toString을 해당 객체(또는 배열)의 매쏘드화 시켰다는 것이다.

일반적으로 우리가 사용하는 방법인 '[].toString()'과 이 방법의 차이는 무엇인가..?(이 방법으로 하면 배열을 콤마(,)로 구분된 문자열을 반환한다)

'[].toString()'의 toString은 배열('[]')의 프로토타입인 Array 객체로 부터 상속받아 사용하는 매쏘드이고,
'Object.prototype.toString.call([])'의 toString은 배열('[]')에 Object의 매쏘드인 toString()을 직접 정의시킨 매쏘드 인 것이다.

즉 Array객체의 toString을 사용하면 우리가 원하는 객체 타입을 반환하는 것이 아니라 배열을 문자열로 반환시켜 버리므로, Object객체의 toString을 사용하도록 강제로 설정했다고 볼 수 있다.

자 그럼 이제 객체의 class를 구분하는 함수를 만들어 보면..

function getClassType(obj){
  return Object.prototype.toString.call(obj).slice(8,-1);
}

console.log(getClassType([]));//"Array"
console.log(getClassType({}));//"Object"
console.log(getClassType(1));//"Number"
console.log(getClassType(new Date()));//"Date"

이처럼 반환된 문자열을 slice 매쏘드를 통해 잘라내어 class를 구할 수 있다.^^

자바스크립트 프로토 타입의 프로퍼티 할당

지난번 게시물에서 자바스크립트의 객체에서 특정 프로퍼티를 찾을 때, 프로토 타입 체인의 순서에 의해서 값을 찾아 나간다는 것을 배웠습니다.

그런데 이 프로토 타입 체인이 값을 "찾을 때" 뿐 아니라 값을 "할당 할 때"도 동일하게 작동 하는 것을 확인했습니다.

아래 소스를 보면..
var Test = function(){}
Test.prototype = {
    a : {
        aa : '1'
    }
}
var TestA = new Test();
var TestB = new Test();
TestB.a.aa= '2';
console.log(TestA.a.aa); //2
console.log(TestB.a.aa); //2


위에서 신기한 것은, TestB 객체에 직접 프로퍼티 값을 할당했는데도, Test 함수의 프로토 타입의 값이 바뀌었다는 것입니다. (이 내용이 잘 이해 안되시면 프로토 타입 체인에 대해서 찾아 보시기 바랍니다.^^)

이유는 처음에도 말한것 처럼 자바스크립트의 객체는 값을 할당할 때도 프로토 타입 체인을 따라가기 때문입니다.

TestB.a.aa= '2'; 을 실행했을때, TestB에는 a라는 프로퍼티가 없기 때문에 'aa'라는 프로퍼티를 생성 할 수가 없습니다. 보통은 자바스크립트가 error를 뿌리면서 실행이 중지되죠.
하지만 이 경우에는 자바스크립트는 프로토타입에 a라는 프로퍼티가 존재합니다. 따라서 프로토타입 체인 순서에 의해 Test객체의 prototype 프로퍼티에 있는 a.aa에 값을 할당하게 됩니다.
Test객체의 prototype 값이 바뀌었으므로 이를 상속받는 TestA객체도 영향을 받은 것이지요.

자바스크립트는 참 유연한 언어라는걸 또한번 느낄수 있었네요.