document.write을 사용하면 돔 랜더링이 멈추기 때문에 느리게 보인다. 그래서document.write을 대신하기 위한 다양한 방법이 나와있다. 하지만, 광고의 경우 다양한 곳에 삽입되기 때문에 대부분은 document.write을 사용한다.

이 문제를 해결하고자 최근에 웹성능의 본좌인 사우더스 아저씨가 HTML import을 활용한 방법을 제안했다.[링크] (HTML import는 본인과 크게 상관도 없고 아직 완성도가 있는 스팩인데 이런걸 활용하다니 좀 놀랐다.)

HTML import를 간단히 설명하면  Web Components을 사용하기 위한 방법으로 link태그를 이용하여 html을 로딩하는 방법이다.[링크]

자세한 내용은 원문[링크]을 참고하고 요약하면 사우더스 아저씨는 아래와 같이  import안에서 document.write를 하는 방법을 제안했다.

main.html

<link rel="import" href="ad.html">

<div id="ad_area" style="width: 480px; height: 60px; margin-left: 2em; margin-bottom: 2em;"></div>

<script>
var link = document.querySelector('link[rel=import]');
var content = link.import.querySelector('#ad');
document.getElementById('ad_area').appendChild(content.cloneNode(true));
</script>

ad.html
<div id="ad" style="background: #E99; border: 2px; font-size: 2em; text-align: center; padding: 8px;">
<script>
document.write("IMPORTED CONTENT<div style='font-size=0.8em;'>(using document.write)</div>");
</script>

근데 문제가 있다.

1. import는 동기로 로딩된다.[링크]

2. import한 html에서 document.write을 하면 main페이지에서 context로 실행되는 문제가 있다.[링크]

그래서 이를 해결하고자 아래와 같이 스크립트로 import을 삽입하고 ad.html에서 document.write을 재정의하여 현재 context로 실행하도록 했다.

main.html

<div id="ad_area" style="width: 480px; height: 60px; margin-left: 2em; margin-bottom: 2em;"></div>

<script>
  var link = document.createElement('link');
  link.rel = 'import';
  link.onload = function() {
    var link = document.querySelector('link[rel=import]');
    var content = link.import.querySelector('#ad');
    document.getElementById('ad_area').appendChild(content.cloneNode(true));
  };
  link.href = "ad.html";
  document.getElementsByTagName('head')[0].appendChild(link);
</script>

ad.html

<div id="ad" style="background: #E99; border: 2px; font-size: 2em; text-align: center; padding: 8px;">
<script>
document.write = function(msg) {
    document.currentScript.ownerDocument.write(msg);
};
document.write("IMPORTED CONTENT<div style='font-size=0.8em;'>(using document.write)</div>");
</script>


이렇게 document.write를 사용하면서 돔의 랜더링을 지연하지 않도록 개선할 수 있다.[링크]

물론 import의 안에서 실행되는 context의 이슈등 문제가 있어 당장 사용하기 힘들지만 아이디어는 좋은 것 같다.

그리고 사우더스는 HTML import가 기본으로 랜더링을 막지 말고 동작해야 한다고 제안했지만 이 문제는 쉽지 않다. 이 문제에 대해서는 다음에 알아보자. 또한 위의 방법은 오해가 있기 때문에 반드시 [링크]을 추가로 읽기 바란다.


관련글 

알고보면 랜더링을 막지 않는 import

Posted by 전용우
,

가끔 node.js로 코딩을 하곤 하는데 자주하는게 아니라 대충 실행하고 확인해서 크게 귀찮지 않았다. 근데 오늘 좀 많이 코딩할 일이 있어서 해보니 파일 수정하고 서버 재시작하는게 너무 불편하다.

당연히 변경된걸 자동으로 반영하는 도구가 있을것 같아 찾아보니 친절하게 많은 도구를 알려줘서 제일 처음[링크]에 나온 supervisor를 사용해봤다.

설치는 언제나 npm으로.

npm install supervisor -g

실행 방법도 언제나 간단.

supervisor web.js

확장자들로 설정할 수 있는데 난 web.js만 확인하고 있으면 되서 적용해봤는데....


완전 편하네. -_-;






Posted by 전용우
,

작년에는 Web Components관련해서 아무것도 없어서 가능성만 보고 발표했는데 올해는 그나마 라이브러리도 나오고 해서 좀 더 얘기할게 많았다.

Polymer랑 Brick에 대해서도 할 얘기가 많았지만 시간상 못하고 왔는데 언제 한번 자세히 정리해봐야지...


발표자료.


소스 코드는 아래서.

https://github.com/mixed/webcomponentscode/

Posted by 전용우
,

폴리머 프로젝트(Polymer Project)[링크]는 웹컴포넌트(WebComponents)을 기반으로 만든 라이브러리다.

근데 이 글은 폴리머 프로젝트가 어떤 프로젝트인지를 얘기하려고 쓴 글이 아니다. 그래서 웹컴포넌트가 뭔지는 알필요가 없기 때문에 자세히 설명하지 않는다. 그냥 컴포넌트 라이브러리구나 정도로 생각하면 된다.

본론으로 들어가기 전에 내가 왜 이글을 쓰게 됬는지 사전 배경이 필요해서 좀 주저리 주저리 적어본다. 나는 오랜시간 동안 자바스크립트 프레임워크를 개발하고 있는데 몇 년전 부터 고민이 내가 만들고 있는 프레임워크를 비롯하여 흔히 말하는 자바스크립트 프레임워크의 설계방법이 썩 좋은 방법이 아니라고 생각했다.

대부분의 자바스크립트 프레임워크는 크게 2가지 역활을 한다고 생각한다.

1. 브라우저마다 다르게 동작하는 기능들을 동일하게 동작하도록 하는 역활.

2. 좀더 쉽게 개발할 수 있도록 다양한 API을 제공하는 역활.

내가 알고 있는 현존하는 프레임워크들은 이 두가지가 코드로 섞여있다. 물론 아닐 수 있는데 찾진 못했다. 

1번을 비유를 하자면 성장과 같다. 시간이 지날수록 문제점을 발견하고 수정하면서 조금씩 성장하게 된다. 반면에 2번은 옷과 같다고 생각한다. 평상시에 캐주얼하게 잘 입는 사람이 옷을 잘입는다고 할 수 있지만 모든 상황에서 옷을 잘입는다고 하기 어렵다. 

근데 패션을 모를 때는 유행하는 옷을 입으면 옷 잘입는 것 같았는데 패션책을 보면서 눈을 뜨게 되면 때론 필요없는 악세사리는 빼버리기도 하고 상황에 맞게 내 뜻대로 입고 싶어 진다. 하지만 이미 옷은 몸과 붙어있어서 갈아입을 수 없게 된다.

이 문제는 내가 프레임워크를 개발하면 똑같이 겪었다. 역량이 낮을때는 알아서 해주는 라이브러리가 좋다가 어느 순간 역량이 높아지만 직접 하나씩 제어하고 싶어진다. 그리고 1번은 결코 버릴 수 없지만 2번은 버릴 수 있어 하는데 쉽지 않다. 

그래서 가끔 팀원들과 나중에는 저 2개를 반드시 분리하자고 했다. 물론 분리했을 때의 한계와 문제점들을 알기에 전체적으로 서둘러서 진행하진 못하고 나중에 모바일 정도만 적용해보자고 생각했다.


다시 돌아와서, 폴리머 프로젝트는 정확히 두가지가 분리되어 있다.

아래가 폴리머의 아키텍쳐이다. 보면 platform.js가 위에서 얘기한 1번이고 polymer.js가 2번에 속한다. 즉, 2번은 언제든지 버릴 수 있고 1번만 사용할 수 있다.

언제든지 polymer.js가 마음에 안든다면 platform.js는 사용하여 개발하면 된다.

근데 polymer프로젝트가 좀 아쉬운 점은 platform.js는 반드시 polyfill만 존재해야만 한다. 그렇지 않으면 언젠가는 1번과 2번이 섞이게 된다고 생각한다. 하지만 polyfill이 아닌 기능들이 있어 아쉽다.


    

Posted by 전용우
,

최근에 헤드리스 브라우저를 사용할 일이 있어서 찾아봤다.

제일 처음 생각난게 가장 유명한 phantomjs[링크]. 근데 난 node.js을 사용해야하기 때문에 직접 사용하진 못하고 proxy을 사용해야했다. 그래서 일단 스킵.

다음으로 찾은건 zombiejs[링크].
node.js로 만들어졌기 때문에 바로 설치해서 실행해봤다.

이게 왠걸 유니코드를 지원하지 않는다. -_-;

다시 돌아가서 phantomjs을 사용하기로.

프록시해주는 라이브러리를 찾아보니 phantomjs-node[링크], phantom-proxy[링크] 두 개가 있었다.

둘 중에 phantomjs-node가 유명한 것 같아서 써보려고 하니 문제는 phantomjs의 경로를 설정을 해야한다. 근데 난 cafe24을 사용하기 때문에 경로를 따로 설정할 수 없어 결국에 phantom-proxy로 결정했다.


좀 더 써봐야 알겠지만, 크게 문제가 되진 않네.

Posted by 전용우
,

V8 빌드하기

프로그래밍 2013. 8. 23. 02:01

V8은 크로미엄에 비해 더 간단하지만 나중을 위해 간단히 적어본다.

V8역시 mac을 기준으로 작성한다.


1. 소스 코드 받기

앞서 크로미엄[링크]에서 삽집하게된 이유가 V8을 먼저 빌드했기 때문이다. -_-

V8은 크로미엄과 다르게 파일을 빠르게 checkout[링크]받을 수 있기 때문에 난 당연히 비슷할 줄 알고 checkout받았는데 생각해보니 너무 무식했다.

git clone git://github.com/v8/v8.git v8 && cd v8

2. 코드 동기화

V8는 git을 이용해서 쉽게 최신 파일을 받을 수 있다.

git pull --rebase origin master


3. 빌드

의존파일을 빌드하기

make dependencies

본인에 환경에 맞게 빌드 설정한다. 난 x64로 실행.

build/gyp_v8 -Dtarget_arch=x64

크로미엄과 같이 xcodebuild로 빌드한다. 그리고 난 debug모드에 공유 라이브러리로 빌드한다.

xcodebuild -project build/all.xcodeproj -configuration Debug -Dcomponent=shared_library


4. 실행

정상적으로 빌드를 하면 "v8/xcodebuild/Debug"에 파일들을 확인할 수 있다. shell을 이용하여 인터렉티브하게 실행해볼 수 있고, d8로 스크립트파일을 실행할 수 있다.


자세한 내용은 링크를 참고하면 된다.


Posted by 전용우
,

사실 알고보면 별거 아닌데 대박 삽질해서 다음 빌드때 잊지 않기 위해 정리한다. 난 mac에 설치했는데 타 OS도 큰 차이 없는것 같다.

1. 소스 코드 받기

처음엔 svn으로 받아오려고 했는데 일단 시간도 정말 오래걸리고 중간에 자꾸 끊기는 현상이 발생했다.몇일 개삽질하다가 그냥 소스코드를 내려받음.(링크) 이것도 오래 걸리긴 하는데 끊김이 없다. 나는 여기서 빌드하기 위한 시간의 99% 소비했다.;;


2. 코드 동기화

크로미엄은 정말 수시로 코드가 변하기 때문에 최신 코드를 보려면 동기화를 해야 하는데  이때 gclient을 이용한다.  아래와 같이 실행한다.

gclient sync

만약에 gclient가 없다면 [링크]을 통해 설치하면 된다. 근데, 문제는 동기화하는데 시간이 무지 걸린다. 그래서 내가 받고 싶은 폴더만 받으려면 소스 코드 루트에 .gclient에 설정하면 된다.

나도 왠만하면 하루에 한번씩 하려고 했으나 하루사이에도 엄청 변경되서 요즘엔 거의 동기화 안한다. -_-;

fetch chromium --nosvn=True

1시간 20분 정도 걸림.

3. 빌드하기

최근 맥에서 기본 빌드가 ninja로 변경됬다.[링크]

그래서 아래와 같이 ninja을 이용하여 빌드하면 된다.

 ninja -C out/Debug chrome

근데 만약에 xcode로 빌드하고 싶다면 project를 xcode로 만들면 된다.

xcode로 빌드하는 방법은 build폴더에 있는 gyp_chromium파일에서 ninja를 xcode로 변경하거나 GYP_GENERATORS 환경변수를 xcode로 변경하고 gyp_chromium.py을 실행시켜 xcode프로젝트를 만든 후 아래와 같이 빌드하면 된다

xcodebuild -project chrome.xcodeproj -configuration Debug -target chrome

target을 All 지정하면 모두 빌드하기 때문에 완전 느리다. 그래서 원하는 타켓을 지정하면 된다. 난 chrome만 쓸거니깐 chrome만 빌드.


4. 실행하기

빌드가 정상적으로 완료하면 xcode로 빌드했으면 xcodebuild/Debug, ninja로 빌드했으면 out/Debug에 있는 Chromium.app을 open하면 된다.


보다 자세한 내용은 [링크]를 참고.


ps. 다음엔 비교적 삽질을 덜한 V8빌드하기. 빌드하면 아무것도 안해도 뭔가 한 것 같은 느낌이 ㅋ

Posted by 전용우
,

IE에서는 한 CSS파일에 4095개의 선택자까지만 적용되고 이후는 적용이 안된다.(링크)

그래서 네트워크비용을 줄이기 위해 CSS파일을 합치려면 최대 선택자가 4095개여야 한다.

넘는 경우에는 모두 합치고 CSS을 xhr로 가져와 적용하는 사례들도 있다.


근데 문제는 XHR은 크롬에서 리소스를 가져오는 순서중 뒤에 속한다.

(아마 타 브라우저도 비슷한것 같다.)

크롬에서 리소스를 내려받는 순서는 HTML -> CSS -> JS -> 이미지 순이다.

좀더 자세한 순서는 CachedResourceLoader.cpp(링크)에 보면 알 수 있다.


그래서 xhr은 JS을 받으면 실행하기 때문에 

link를 사용했더라면 html -> css ->  js였지만 xhr을 사용하게 되면 html -> js -> css로 되어 네트워크 비용이 감소할지 모르지만 사용자가 느끼는 체감 속도는 더 느릴 수 있다.


이런 경우는 걍 2개로 나누어서 받는게 더욱 효율적일 수 있다.

SPDY의 경우 적게는 4배에서 많게는 8배 정도 빠르다고 한다.(링크)

Posted by 전용우
,

최근(?) 자바스크립트에서는 모듈 패턴(Module Pattern)을 많이 사용한다.

이렇게 많이 사용하는 이유는 개인적으로 AMD의 영향이 크지 않을까 생각한다. (AMD의 좀 안좋은 인식이 있는데 이건 나중에 시간이 되면 쓰도록 하고....)

뭐.. 어쨌건 모듈 패턴을 사용하는 이유야 다양하겠지만 대부분은 지금의 자바스크립트에서 지원하지 않는 private 속성을 사용하기 위함이 큰 것 같다.(ECMA6에서는 뭐가 있는것 같은데..)

나는 사실 모듈 패턴을 선호하지 않는 편이다. 그 이유 중 하나가 코드 리딩이 힘들다.

왜냐하면 모듈 패턴은 함수로 감싼 형식이라 구현부를 볼 때 함수의 파라메터가 있다면 제일 마지막 부분을 확인해야 알 수 있고 클로져를 생각보다 많이 사용해 코드 리딩도 좀 힘들다. 그래서 난 개인적으로 모듈 패턴을 사용할 때는 파라메터를 넣지 않기를 선호하며 클로져도 적게 쓰려고 한다.

그리고 모듈 패턴을 쓰면 private의 사용이 많아지고 private이 많아질 수록 테스트는 만들기 힘들어진다. 물론, private를 테스트하기 힘든건 모듈 패턴의 문제는 아니다.

이런 경우 대체적으로 private함수는 직접 테스트하기 보다는 private함수를 사용하는 public함수를 테스트한다.

나도 보통은 그렇게 하는데 한편으론 맘에 안들때도 있다.

예를 들면 아래의 코드형식이다.

(function(){
    var global = this;
    function Stub(vName, sType){
        this.stubMethod = new StubMethod();
    }
    Stub.prototype.with_param = function(){
        return this.stubMethod;
    }

    //private
    function StubMethod(iStub){
    }
    StubMethod.prototype.and_return = function(vReturn){
    }

    global.stub = global.Stub = Stub;
})();

위에 코드를 보면 StubMethod는 외부에서 생성해서 사용하지 않기 때문에 외부에 노출하지 않는다.

그리고 with_param의 반환 값이 StubMethod인지 테스트해야 하는 코드를 아래와 같이 작성하려고 한다.

test("with_param의 반환 값은 StubMethod 인스턴스여야 한다.",function(){
    //Given
    var stubInstance = stub("Stub");
    //When
    var stubMethod = stubInstance.with_param();
    //Then
    ok(stubMethod instanceof StubMethod);
 });

근데 여기서 StubMethod는 외부에 노출하지 않았기 때문에 위와 같은 테스트가 불가능하다.

그래서 테스트하는 방법을 고민해봤는데 몇 가지가 떠올랐다.

첫 번째. 반환 값을 가지고 add_return을 사용하여 테스트한다.(간접적으로)

두 번째.  반환 값에서 add_return메서드가 있는지 확인하는 테스트를 작성해야 한다(덕타이핑과 같이)

세 번째. 키워드를 넘겨서 테스트 중이면 외부에서도 StubMethod를 접근할 수 있게 한다. (이건 좀 구린것 같다.)

처음 고민한건 add_return 함수의 기능을 테스트하면 StubMethod인지 동시에 테스트되는 방법이다. 물론 간접적으로 테스트 되지만 반환 값이 StubMethod인지 테스트하는 부분이 코드로 표현되지 않아서 좀 별루라고 생각했다.

이것 저것 고민하다가 결국엔 덕타이핑으로 판단했지만 여전히 찜찜함은 남는다.

test("with_param은 반환 값이 StubMethod 인스턴스여야 한다.",function(){
    //Given
    var stubInstance = stub("Stub");
    //When
    var stubMethod = stubInstance.with_param();
    //Then
    // ok(stubMethod instanceof StubMethod);
    // StubMethod는 private으로 접근할 수 없어 덕타이핑으로 판단한다.
    equal(typeof stubMethod.and_return,"function");
});


이와 관련한 질문도 많고 답변도 봤는데 나의 고민을 해결해주는 글은 없었다. 그리고 유사한 패턴이 jQuery의 Deferred, Promise인 것 같아서 코드와 테스트 케이스를 보니 위에 경우랑은 좀 차이가 있었다.

결론은 위와 같이 했지만 이런 경우는 어떻게 처리해야 할지 아직 고민이 된다.


ps. 여전히 고민인 건 정말 private로 만들어야만 했는가? 그냥 public이면 되는거 아닌가? 외부에 노출시켜 발생하는 문제에 비해 테스트의 가치가 큰게 아닌가? 등 많은 고민이 되는데 잘 모르겠다.


ps. 정말 오랫만에 블로그를 썼다. 한동안 바쁘다는 이유로 안썼는데 이젠 조금씩 써보려고 하는데 얼마나 갈지는 잘 모르겠다. ㅎㅎ

Posted by 전용우
,
먼저 JS Test Driver(JDT)의 지난 글 참조.

오늘은 JDT의 커버리지 사용기를 소개 할까 한다. 오픈은 10월 말경에 한것 같다.

JDT의 커버리지 툴은 JDT의 플러그인 형식으로 지원 하며 ANTLR을 이용해서 자바스크립트 문법을 분석하여 만들었다. (폴더가 plugins인걸로 봐서 다른 플러그인들도 개발할 예정인듯)

현재 지원 범위
  1. 향후 어떨지는 모르지만 현재 지원하는 커버리지는 라인커버리지만 지원
  2. 기존의 이클립스 플러그인에서 사용은 못하고 커멘드로만 사용가능(전에 방식을 봐서는 향후 지원할 듯)
  3. 결과는 콘솔로 파일당 라인 커버리지를 확인 가능하고 dat파일로 export할수 있으며 LCOV을 이용하여 비주얼 하게 볼수 있음.
  4. 일부 파일을 제외하는 기능 없이 모든 파일의 커버리지를 파일당 측정.(가능하다고는 메일링에 그러는데 안되는것 같음)

사용방법은 간단하다.
먼저 원글을 보면 나와있는데 간단히 알아 보면

전에 적은 JDT사용기 처럼 셋팅 후
  1. JsTestDriver.jar가 있는 폴더에 plugins 폴더를 만든다.
  2. coverage.jar를 다운 받아서 plugins 폴더에 넣는다.
  3. 기존에 만든 conf파일에 커버리지에 필요한 정보를 추가로 넣는다.(jar경로는 자신에 맞게 수정)
  4. 서버 띄우고 capture 한다.
  5. 기존과 똑같이 커멘드로 실행 시키며 된다.
위와 같이 진행을 하면 아래와 같은 결과를 받을수 있다.

사용자 삽입 이미지


위에 이미지에서 볼수 있듯이 test의 대한 결과를 보여주고 test가 100%일 때
하단에 각 파일에 대한 라인 커버리지율을 보여 주고 있다.

장점으로는
  1. test, coverage를 별다른 설치없이 한번에 볼 수 있다는 것은 정말 큰 장점이다.(IDE에선 성능을 볼수 있다. 전에 말한 JDT의 기능은 이제 거의 다 개발한듯.)
  2. 데이터를 export할수 있어 외부 툴하고도 연동하기 쉽게 되어 있다.(export한 데이터를 허드슨과 연동 하는 등)
단점으로는
  1. 향후 어떨지는 모르지만 아직 까지 파일 당 라인 커버리지만 지원하는건 큰 메리트가 없다.(적어도 함수당 라인 커버리지를 보여주거나 clover처럼 브랜치와 복잡도도 보여주면 좋을것 같다.구문 분석을 했기 때문에 항후 가능할것 같다.)
  2. 보기 힘든 정보.(향후 플러그인과 합쳐져서 비쥬얼 하게 보여 준다면 좋을것 같다)
  3. 느린 속도.(버그 인지 어쩔수 없는지 모르지만 여러 브라우져에서 테스트 할때 같은 js파일을 매번 분석해서 파일이 많아지면 굉장히 느릴것 같다)

결론은 아직 까지는 부족하지만 지금껏 해왔던 행보로는 충분히 좋아 질것 같다.
자바스크립트 커버리지툴은 내가 알기로 현재 JSCoverage 밖에 없는데 또 다른 커버리지 툴이 생겨서 좋다.

 

Posted by 전용우
,