개발하다보면 내 환경은 잘되는데 성능이 느린 환경은 어떨까 궁금할 때가 있다. 그럴 때 사용하는  대표적인 도구 [WebPageTest] 가 있다.


다만, WebPageTest는 초기 로딩 성능을 주로 하고, 테스트 하는데 시간도 오래 걸리기 때문에 자주 실행해보는건 적당하지 않다. 가끔 확인이나 할 뿐. (물론 도구와 연결해서 주기적으로 할 수 있지만 바로 바로 확인해보고 싶은 경우가 많아서..)


근데 얼마 전에 크롬에서 Network throttling이란 기능이 생겼다. Network 탭에 가면 아래와 같이 Network을 조절할 수 있는 기능이 추가됐다.



거의 WebPageTest와 다르지 않게 많이 쓸 것 같은 대역폭과 레이턴시들이 존재하고 직접 상황에 맞는 설정을 추가할 수 있다.



물론 실제 환경이 아니지만 거의 비슷한 느낌을 받기 때문에 빠르게 네크워크가 안좋은 환경에서는 어떨까 확인하고 싶다면 확인해보는 것도 괜찮을 것 같다. 그리고 후진 폰에서는 인터랙션이 어떻게 동작할까? 이런 생각을 할 수 있는데 이걸 후진폰에서 직접 확인하는 방법이 좋긴 하지만, 귀찮기도 하고 빠르게 확인하기란 적절하지 않다.



이것도 최근에 크롬에서 CPU throttling이란 기능이 추가됐다. 현재는 바로 쓸 수 있는건 아니고  Setting > Experiments 에 가서 아래와 같이 활성화을 해야 한다.



그리고 타임라인 탭에 가면 Network와 같이 CPU로 throttling 할 수 있다.



직접 기기에서 보는 것과 다르긴 하지만, 써보니깐 편하게 느린 기기를 체험할 수 있다. 물론, 실제 기기랑 느린거랑은 좀 다르다.


실제 환경에서 확인하는게 더 정확하긴 하다. 하지만, 빠르게 확인하는 방법으로 이 두 기능은 꽤 괜찮은 방법이다.






Posted by 전용우
,

예전에 qunit에서 [code review] 받을 때 [Richard Gibson]이 알려준 내용인데 [jsmocktool] 관련해서 개발하다가 갑자기 생각하서 또 잊을까봐 정리한다.


당시 코드에서 regexp 객체에 flags가 있는지 확인하는 방법으로 속성에 접근해서 아래와 같이 사용했다.


1
return regexp.flags || regexp.toString().match(/[gimuy]*$/)[0];
cs


근데 Richard Gibson이 "나는 불필요한 동작을 하지 않기 위해 in연산자를 사용한다"며 in 연산자를 사용하길 바랬다. 첨에 "엇 뭐지?"라는 생각을 했지만, 잠깐 고민해보니 좋은 의견이였다. 그리고 오늘 글을 쓰기 위해 좀 더 조사를 해봤다.


먼저 속성이 있는지 확인하는 방법은 크게 3가지가 있다.


1. 동등 연산자( == ,=== )

1
2
if(o[k] === undefined){    
}
cs


2. hasOwnProperty 메서드

1
2
if(o.hasOwnProperty(v)){    
}
cs


3. in 연산자

1
2
if(v in o){    
}
cs



이 3가지중 어떤게 가장 빠를까? 한번 고민해보고 아래 글을 읽으면 재밌을거라 생각한다.


시점/브라우저 등 여러가지 상황에 따라 다를 수 있는데 V8(크롬)에서 보면 1번이 가장 빠르다. 왜 그런지는 V8 개발자인 [Vyacheslav Egorov]의 설명을 보면 알 수 있다. 요약하면, 1번의 경우는 흔히 얘기하는 hidden class로 특정 offset을 가지고 있고 반복 호출되면 [Inline Cache] 때문에 굉장히 빠르지만, hasOwnProperty와 in 연산자의 경우는 별도의 캐시를 하지 않고 매번 새로 찾기 때문에 느리다. [비교] (이 말은 키값(offset)이 고정되어 있지 않으면 차이가 없다는 말이다. 실제로 로직에서  키 값을 항상 변경해서 사용하면 성능에 크게 차이가 없다.)


하지만, 비교에서 보면 최근에 hasOwnProperty은 [개선]되어서 지금은 hasOwnProperty도 실제로 비슷한 성능을 내고 있으며 [in 연산자] 역시 언젠가 빠를 것 같다.


다시 돌아와서. 지금까지 보면 1번이 여러 상황에서 적절한 것 같은데 왜 Richard Gibson은 `in`연산자를 사용하라고 했을까?

단순 객체에 속성을 확인하는건 1번이 빠를진 몰라도, 일반적으로 FE을 개발할 때 DOM등 브라우저에 있는 native 객체들에 위의 비교문을 많이 사용한다. 그래서 속성을 읽을 때 단순히 값을 반환하는 경우도 있겠지만, 내부적으로 복잡한 연산이 들어가기 때문에 오히려 1번이 가장 느릴 경우가 많다.


예를 들어. 특정 객체에 offsetHeight라는 속성이 있는지 확인한다면, 1번의 경우는 [forced layout]이 발생하여 엄청나게 느리지만, 2번이나 3번의 경우는 속성이 있는지만 확인하기 때문에 불필요한 forced layout이 발생하지 않는다.


이렇게 단순히 자바스크립트 객체의 속성을 확인하는 방법으로 좋을지 모르지만, DOM이나 기타 native 객체들은 2, 3번(주로 3을 선호한다. 이유는 아래에)을 사용하는게 오히려 좀 더 나은 practice라고 생각하며 아마도 Richard Gibson이 이런 의미로 얘기한 것 같다.


당연하지만, getter의 경우도 1번에서는 동작하지만, 2, 3의 경우는 동작하지 않는다. 참고로 hasOwnProperty는 1, 3번 약간은 다르다. hasOwnProperty은 prototype chaining하지 않고 속성을 찾기 때문에 기대했던 생각과 다른 결과를 만날 수 있다. [참고


요약하면,


단순 오브젝트를 확인하는건 속성에 접근하여 확인하는게 빠르지만, 브라우저의 객체나 DOM은 in/hasOwnProperty가 괜찮은 방법인 것 같다.


ps. Code HighLight을 하기 위해 보통 markup.su/highlighter/ 을 사용했는데 관리를 안하는지 다른 것을 찾아보다가 http://colorscripter.com/ 을 찾았는데 완전 좋네. 



참고 URL.

http://stackoverflow.com/questions/21763617/why-is-getting-a-member-faster-than-calling-hasownproperty

https://bugs.chromium.org/p/v8/issues/detail?id=2472#c8

https://bugs.chromium.org/p/v8/issues/detail?id=2743

Posted by 전용우
,

크롬 개발자 도구에서 스크립트 디버깅을 하다보면 breakpoint 사용이 가끔 불편할 때가 있다. 대표적으로 breakpoint에서 멈출때 특정 위치로 점프하고 싶다면, 원하는 위치에 breakpoint을 설정하고 next을 눌러 이동하는 경우이다.


이게 생각보다 불편한다. 대부분 임시로 breakpoint을 설정한 경우라 그대로 두면 엄청 많아져서 관리도 안되기 때문에 나중에 원하지 않아도 계속 걸려 귀찮다. 그래서 다시 해제해야 하는데 이것도 귀찮다.


이 때, 사용하는 기능이 아래 링크에서 설명하는   "continue to here" 다. 이 기능을 사용하면 breakpoint을 설정하지 않고 원하는 위치로 이동하기 때문에 breakpoint가 많아지지 않고 다시 해제할 필요가 없다.


https://twitter.com/kaycebasques/status/755817600249044992




만약, 이 기능을 사용해보지 않았다면, 한번쯤 사용해보면 좋은 기능임을 알게될 것이다. 



[깨알팁 시리즈]

Posted by 전용우
,

모바일 웹에서 성능을 높이는 방법으로 GPU가속을 사용하기 위해 translateZ(0)와 같은 hack코드를 넣곤 했다. 이후에 will-change가 나와서 hack을 대신해서 사용했다. 둘 다 GPU가속을 위해 사용했는데 굳이 will-change을 사용하는 이유는  "브라우저가 효과적으로 GPU가속을 사용할 수 있기" 때문이였다.


문서에는 브라우저마다 will-change을 다르게 사용한다고 하지만, 실제로 크롬에서는 will-change가 어떤 영향을 주는지 코드로 확인하고 싶었다. 아쉽게도 당시 코드를 봤을 때 딱히 크롬이 스마트하게 판단하는지는 찾지 못했다. 그렇게 내가 아직 잘 모르는구나 하는 생각을 하고 지나갔다.


그리고 몇 일전에 will-change: scroll-position, content이 성능에 어떤 영향을 받는지 문의한 글이 있었는데 이 글을 보고 예전 생각이 나서 정리한다. 


일단 scroll-position는 스크롤의 위치가 변경될 수 있음을 알려주는 힌트이고 content는 엘리먼트가 변경될 수 있음을 알려주는 힌트다. [링크]


답변 내용은 현재까진 will-change: scroll-position은 어떤 영향을 미치지 않는다. will-change: content의 경우는  content을 가진 엘리먼트의 자식 엘리먼트가 will-change: <hint>을 가지면 변경할 가능성이 있다고 판단하여 자식 엘리먼트는 composited layer을 만들지 않는다. 


예제를 보면 이해하기 쉬운데 아래 두개만 composited layer을 만든다.

<div style="will-change: scroll-position"></div>

<div style="will-change: contents"></div>

<div style="will-change: transform"></div> // <--------- composited layer

<div style="will-change: contents">
    <div style="will-change: transform"></div>
</div>

<div style="will-change: scroll-position">
    <div style="will-change: transform"></div> // <--------- composited layer
</div>


blink코드를 보면 더 정확히 할 수 있다.

if (style.hasWillChangeCompositingHint() && !style.subtreeWillChangeContents())
        reasons |= CompositingReasonWillChangeCompositingHint;


그리고 will-change에 들어갈 수 있는 style은 [opacity, transform, webkit-transform, top, left, bottom, right]이다.


switch (rareNonInheritedData->m_willChange->m_properties[i]) {
        case CSSPropertyOpacity:
        case CSSPropertyTransform:
        case CSSPropertyAliasWebkitTransform:
        case CSSPropertyTop:
        case CSSPropertyLeft:
        case CSSPropertyBottom:
        case CSSPropertyRight:
            return true;
        default:
            break;
}


이걸 보면서, 예전에 궁금했던 점이 조금 이해됐다. 현재는 content속성에 대해서만 되어 있지만, 나중에는 scroll-position에 대해서도 최적화가 들어갈 것 같고 이후에는 W3C에 나와 있는 것 처럼 최적화가 되지 않을까 생각된다.




Posted by 전용우
,

먼저, 잡담부터 시작.

예전에 태훈님한데 egghead을 지나가듯 들었는데 정수형이 다시 알려줘서 redux 강의를 들었다. redux강의 하나를 들었는데 다른 강의를 보지 않아도 돈을 내고 들을만 하다고 생각한다.

내가 본 건 [getting-started-with-redux] 인데  영상의 컨셉이 redux가 왜 필요한지 부터 시작해서 redux을 간단하게 만들면서 적용하는 과정이다. 영어긴 한데 누가 [정리한 글]도 있고 주로 코딩을 하기 때문에 보는데 무리가 없었다.


다시 돌아와서, 내가 이 강의를 들으면서 고민했던 부분을 정리하려고 한다. 이미 전반적인 내용을 정리한 글은 많아서 난 좀 이슈 위주로 정리. 


- stateless functional components의 존재

 : 난 이게 왜 필요한지 한참을 고민했다. 말그대로 상태가 없는 함수형 컴포넌트다. 여기서는 기본적으로 상태을 최소한으로 유지하고 각 컴포넌트가 가지고 있기 보다 container에서 관리하기 때문에 가능하면 상태를 관리하지 않도록 한다. 근데 이게 생각보다 잘 안되는지 기능적으로 개발할 때 상태를 가지지 못하게 한게 이 컴포넌트인 것 같다. 이건 이해했는데, 이게 성능이 빠르다고 하는게 이해가 안됬다. 왜냐하면, 이 컴포넌트는 그냥 함수라 기본적인 life cycle이 없다. 그래서  "shouldComponentUpdate"를 활용해서 내가 virtual dom의 비교을 smart하게 할 수 없었다. 그리고 코드를 봐도 왜 빠른지 알 수가 없었는데 [링크]을 통해 이해했다. 정리하면 아직 옵티마이제이션되는건 없고 미래에 될 거다 이다. 그리고 pureRender나 shouldComponentUpdate가 오/남용이 많은가 보다.


- Provider와 connect의 존재

: 동영상을 따라면서 동영상처럼 한 JS에 작성했던게 아니고 클래스별로 잘라서 사용했는데, 이게 문제가 redux의 store을 각 컴포넌트에서 접근해서 사용하기가 너무 귀찬고 매번 prop으로 넘기는데 이게 뭔가 싶었다. 한참 짜증이 났는데 일단 끝까지 보자라는 생각으로 봤더니 끝나기 몇 회 전에 이 문제를 해결하는 방법을 알려줬다. 공통적으로 가지고 다니는 store나 route같은 객체가 있다면 Provider나 connect가 써야 한다. 잊지는 않겠지만, 난 이거 나중에 알아서 좀 짜증났음. 그리고 Provider을 사용할 때 context가 들어오는 시점이 construct가 아니라 "componentWillMount"부터 들어와서 좀 삽질했다.


- arrow function

: 이게 뭔지는 알았는데 실제로 사용하다보니 react에는 이게 2가지 패턴이 있다. 바로 반환하는 경우는 {}을 사용하지 않고 그냥 ()을 사용한다. 좀 더 정확히 말하면 => 는 바로 반환할 때는 별도의 brace을 넣지 않고 할 수 있는데 jsx때문에 ()을 넣는다. 근데 난 이게 정말 눈에 안뜀. 이것도 몇 번 삽질.    

const Footer = ({ currentFilter, onFilterClick }) => ( <p> <FilterLink filter='SHOW_ALL'>All</FilterLink> </p> ); const Footer = ({ currentFilter, onFilterClick }) => ( // 이렇게 쓰고 몇 번 헤멤 doSomething(); return ( <p> <FilterLink filter='SHOW_COMPLETED'>Completed</FilterLink> </p> ); );



- destructuring

: ES6의 문법중에 , 초반에 눈에 잘 안들어왔던 문법이 destructuring이다. 익숙해지면 완전 편한데 처음에 좀 걸리적거렸음.

{
  filter,
  currentFilter,
  children,
  store
}

{...todo}

[...state, todo(undefined, action)]

{ ...state, completed: !state.completed}



- es6/7은 꿀

: destructuring, import, arrrow...을 es5로 개발한다고 생각하면 좀 답답했을것 같다. 그리고 babel에 "stage-0" preset을 사용하면 es7나 제안중인 [static property]도 사용할 수 있다는건 흥미로웠다. 다만, 실무에 써보라고 한다면 스킬이 아직 좀 부족한것 같다. 그리고 아직 트랜스파일링 된 코드를 보는게 눈에 잘 들어온다. -_-;


- export이슈

: export하는 부분을 빼먹은 적이 많아서 아래 메세지를 여러번 만났다. 이것 때문에 나도 삽질 좀 많이 함.

Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components). Check the render method of `TodoApp`.

Uncaught Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. Check the render method of `TodoApp`.


- 폴더 구조

: 어떻게 폴더 구조를 가져가야 할 지 고민이 된다. 찾아보니 몇 가지 제안([링크], [링크]...)들이 있던데, 아직 내가 실제로 만들어보지 않아서 그런지 뭔가 공감이 안된다. -__-;



전반적으로 이 강의를 듣고 나서 react+redux을 어떻게 활용해야 할 지에 대한 감은 잡혔다. 다만, 많이 만들어보지 않아서 그런지 의문점들은 몇 개 있다. 

예를 들면, 스케일이 커지면 redux처럼 하나의 store로 가는게 좋은 방법인지 잘 모르겠다. 그 밖에 Presentational Component의 단위를 가능하면 작게 쪼개길 기대하는데 이게 뭔가 난 부자연스럽다. 왠지 virtual dom때문에 하는 작업처럼 느낌..., 객체가 커지면 새로 만드는 비용은 크게 문제가 없는지 등등 디테일은 좀 경험이 필요할 것 같다.

여튼 egghead,io강의는 훌륭함,



Posted by 전용우
,

기술 지원할 때 많은 코드를 보는데 가끔 (append|prepend...)To 관련 메서드을 활용하지 못해 좀 읽기 어렵게 개발하는 경우를 봐서 생각난 김에 정리한다.

(append|prepend...)To 관련 기능은 대부분의 라이브러리에 있지만, 여기선 [jQuery]을 예를 들어 설명한다. 

보통 append, prepend... VS appendTo, prependTo...의 차이를 모르는 사람은 거의 없다.

간단히 설명하면 append는 부모 엘리먼트에 자식 엘리먼트를 마지막 자식 노드로 추가하는 메서드이다. 그래서 $(parent).append(child)는 parent가 앞이고 child가 뒤이고, appendTo는 $(child).appendTo(parent) 처럼 반대다.

그럼.. 예를 들어, 아래와 같은 기능을 구현한다고 생각해보자.

.layer라는 클래스를 가진 엘리먼트를 #parent에 마지막 자식 노드로 추가하는데 fadeIn되어야 한다.
(IE 하위 버전을 지원하기 위해 CSS transform은 사용하지 않는다고 가정하자.)

이런 기능을 구현한다고 하면 내가 본 코드의 패턴 중 하나는 아래와 같다.

var layer = $("<div class='layer'>");
$("#parent").append(layer);
layer.fadeIn();

때론 jQuery을 chaining하기 위해 아래와 같이 사용하곤 한다.

$("#parent").append("<div class='layer'>").find(".layer").fadeIn();

여기서 .layer가 여러 개이면 아래와 같이 개발하는 경우도 있다.

$($("#parent").append("<div class='layer'>").find(".layer")[0]).fadeIn()

마지막을 제외하곤(마지막은 코드 읽기가 어렵다) 위의 방법이 모두 나쁘니깐 개선하면 좋겠다라는 의미로  얘기하기보다  xxxxTo관련 메서드로 아래와 같이 할 수도 있다 정도로 이해해주면 좋겠다.

보통 나는 아래처럼 작성한다.

$("<div class='layer'>").appendTo("#parent").fadeIn();

위의 예로 개발한 경우 xxxxTo을 모르는 경우가 거의 없었지만, 이런식으로 할 수 있다고 제안하면 새롭게 느끼는걸 알 수 있었다. 

정리하면 xxxxTo는 단순히 부모와 자식의 위치를 바꿔서 엘리먼트를 추가할 때  사용한다기 보다 등록하는 엘리먼트을 기준으로 추가 작업을 하기 위해 사용하는 메서드로 알고 있으면 좀 더 다양하게 활용할 수 있다.


마지막으로 실제 사례를 찾고 싶어서 [github]에서 찾아봤는데 비슷한 사례가 있어 소개한다.
아.. github은 advance search가 안되서 정말 아쉽다. 그립다 code.google.com이여.

// 실제 코드
$('body').append('<div class="fyr"></div>').find('div.fyr').findYourRep({apis: 'represent'});

// 개선 코드
$('<div class="fyr"></div>').appendTo('body').findYourRep({apis: 'represent'});


Posted by 전용우
,

며칠 전에 angular 코드를 보다가 design note을 찾으러 위키를 갔는데 우연히 [performance]을 봤다.(design note는 [다른 곳]에서 찾음.)

다른 내용들은 일반적인 내용인데 내가 의아하게 느낀 부분이 자식노드를 탐색할 때 `DOM.childNodes`이 `node.nextSibling`보다 느리다는 얘기다. 그 예로 [jsperf링크]가 있어서 확인해보니 실제로 nextSibling이 빨랐고 몇 개 브라우저에서 좀 더 확인해봤는데 모두 nextSibling이 항상 빨랐다.


"왜 그럴까?" 잠깐 고민해봤는데 딱히 아이디어가 안떠올라 blink의 구현로직을 찾아보기로 했다.


먼저 clideNodes부분을 보니 Container노드(parentNode)일 때 그 안에 childeNode을 찾아 nodelist로 만든 다음 반환하는데 뭔가 특별해보이진 않는다.

PassRefPtrWillBeRawPtr<NodeList> Node::childNodes()
{
    if (isContainerNode())
        return ensureRareData().ensureNodeLists().ensureChildNodeList(toContainerNode(*this));
    return ensureRareData().ensureNodeLists().ensureEmptyChildNodeList(*this);
}

그럼 nextSibling은 뭘까 찾아봤다.

Node* previousSibling() const { return m_previous; }
Node* nextSibling() const { return m_next; }

엇. 이거 뭐지 그냥 반환만 하네... 그래서 찾아보니 nextSibling, previousSibling을 호출할 때는 뭔가 찾는게 아니라 아래와 같이 Node가 변경되면 값을 업데이트하고 호출할 때는 위와 같이 그냥 반환한다.

void ContainerNode::insertBeforeCommon(Node& nextChild, Node& newChild)
{
    Node* prev = nextChild.previousSibling();
    nextChild.setPreviousSibling(&newChild);
    if (prev) {
        prev->setNextSibling(&newChild);
    } else {
        m_firstChild = &newChild;
    }
    newChild.setParentOrShadowHostNode(this);
    newChild.setPreviousSibling(prev);
    newChild.setNextSibling(&nextChild);
}

void ContainerNode::appendChildCommon(Node& child)
{
    child.setParentOrShadowHostNode(this);

    if (m_lastChild) {
        child.setPreviousSibling(m_lastChild);
        m_lastChild->setNextSibling(&child);
    } else {
        setFirstChild(&child);
    }

    setLastChild(&child);
}

즉, childNodes을 찾을 때는 하위 노드를 찾아서 nodelist로 만든 다음 반환하기 때문에 비용이 좀 드는 것 같고, nextSibling은 그냥 바로 노드를 반환하기 때문에 비용이 적게 드는 것 같다. 

갑자기 비슷한 children이 생각나서 이건 어떻게 구현되어 있을까 하고 찾아봤는데 아래와 같이 구현되어 있다. 

PassRefPtrWillBeRawPtr<HTMLCollection> ContainerNode::children()
{
    return ensureCachedCollection<HTMLCollection>(NodeChildren);
}

이건 우리가 알고 있는 [live nodelist]인 것 같아 아마도 성능이 좋지 않을까 하고 비교해봤는데 결과는 nextSibling보다는 느리지만, childNodes보다는 빨랐다. [링크]


처음 가지고 있던 의구심을 해결하고 nextSibling, previousSibling, lastChild, firstChild는 이미 계산되어 가지고 있다는 사실을 알게됐다.


ps. github에서 유명한 프로젝트에 childNodes을 사용하는 부분을 찾아서 pr줍기를 시도했지만... 테스트 코드를 제외하곤 거의 없어 아쉬었다. 하지만, 우연히 버그같은 코드를 발견해서 [pr 줍기 성공]. :)





Posted by 전용우
,

한 2년 전인가 구글 개발자들이 회사에 온 적이 있는데 당시에 내가 했던 질문이 "DOM/Event Listener Breakpoint는 유용한 기능인데 항상 라이브러리에서 멈추기 때문에 활용도가 떨어지는데 개선할 계획이 있냐?" 였다.

그 때 paul irish가 "아. 그거 조만간 나올 예정이다"라고 했다.

그리고 나서 작년 겨울인가 디버깅하다가 갑자기 생각나서 그 기능 개발되었나 하고 찾아보니 적용이 되어 있어서 사용하고 있는데 생각보다 사람들이 이 기능을 몰라서 잠깐 설명할까 한다.


예를 들어, 어느날 서비스를 담당하는 개발자가 휴가를 갔다. 근데 버그가 발견되어서 내가 대신 처리해야 하는 상황이다.

버그는 특정 버튼을 누를 때 레이아웃이 이상하게 된다고 가정하자.

이런 상황에서 일반적으로 내가 해당 코드를 모르기 때문에 js을 바로 찾진 못하고 버튼 엘리먼트의 아이디, 클래스, 이벤트명등의 단서를 가지고 js파일을 찾는다. 그리고 거기에 debugger을 걸면서 해당 이벤트가 발생하는 시점을 확인하면서 디버깅을 한다.

만약에 click이 발생하는 시점에 호출되는 함수를 알 수 있다면 아마 좀 더 쉽게 디버깅을 할 수 있을 것이다.

이게 바로 source tab에 있는 Event Listener Breakpoints다.


위와 같이 click은 선택하면 click이벤트가 발생했을때 해당 함수에 break point가 걸린다.

근데 문제는 대부분이 라이브러리를 사용하기 때문에 아래와 같이 라이브러리 코드에서 멈춘다.



그러면 다음 다음을 누르면서 해당 함수를 찾아가야 하니 생각보다 편하지 않다.

이때, 사용하는게 "Blackbox JavaScript Source Files"다.

크롬 개발자 도구에서 "설정 -> General -> Sources"에 가면 아래와 같이 "Manage framework blackboxing..."을 눌러 라이브러리를 등록한다.



그리고 나서 다시 실행하면 아까와는 다르게 아래와 같이 내가 작성한 코드에서 break point가 걸린다.


이제 부터 "Event Listener Breakpoints"는 너무 편해진다.

그리고 디버깅할 때 등록한 라이브러리 코드는 디버깅 대상에서 제외된다. 이것도 완전 편하다. 항상 다음 함수로 넘어가기를 누르면 라이브러리 코드로 가서 들어갔다 나왔다를 반복해야 하는데 이런 문제도 없다. 

이 기능은 정말 디버깅할 때 강추한다.

내가 사용해보니 좀 노하우가 생겼는데 그 중 하나가 라이브러리 코드 뿐 아니라 의존성을 가지는 파일 중에 보지 않아도 되는 파일이 있다면 해당 파일을 등록하면 좀 더 디버깅이 간단해진다.


ps. 참. 아쉽게도 DOM break point은 black box을 사용할 수 없다.

[깨알팁 시리즈]

Posted by 전용우
,

[저번]에 이어 이번에는 크롬개발자 깨알팁 

가끔 아래와 같이 찍힌 로그를 복사하고 싶을 때가 있다.


이럴 때 보통 그냥 drag해서 선택한 다음에 ctrl + c , ctrl + v로 하는데 이러면 나중에 정리해야 하고 불편하다. 그 때는 copy라는 메서드를 쓰면 객체가 클립보드에 저장된다.

copy(obj);

근데 문제는 변수에 담기지 않은 상태 즉, 위에 처럼 이미 로깅된 객체를 어떻게 copy메서드를 쓴단 말인가? -_-;

이 때. 알고 있으면 도움되는 깨알팁.

내가 저장하고 싶은 객체에 오른쪽 마우스를 클릭하면 아래(Store as Global Variable)처럼 나옴.

이를 선택하면 global영역에 임시 변수로 저장된다.


그리고 나서 copy(temp1); 하면 됨.


[깨알팁 시리즈]


Posted by 전용우
,

t3js

프로그래밍 2015. 4. 19. 23:07

토요일인가? 트위터에서 t3js[링크]라는 프레임워크를 만들었다는 트윗을 보고 이건 뭔가하고 주말에 와이프가 드라마를 보는 틈을 타 잠깐 살펴봤다.

t3js을 만든 사람이 니콜라스 자카스[링크]라고 다수의 JS책을 쓰고 JS관련 컨설팅을 하다가 최근에 프로필을 보니 BOX에서 수석 아키텍트를 하고 있다. 이 사람 낸 책 중 번역서는 대부분 봤는데[링크] 다 괜찮았던 책이고 아티클도 꾸준히 쓰는 편이라 신뢰감이 있었다.

먼저, 간단하게 설명을 한면 t3js는 아래 4가지를 알 필요가 있다.

Application - Module과 service을 관리하고 메시지를 전달한다. 모듈간의 커뮤니케이션과 라이프사이클등등 .(하나만 존재)

Module - 흔히 얘기하는 Component같은 역할이다. 돔에 이벤트를 바인딩하는등 application의 비지니스 로직을 담당한다.

Behavior - Module의 중복되는 부분을 뽑아 behavior을 만든 후 moudule에서 사용한다.(사실 module의 공통 부분이라고 생각하면된다)

Service - 유틸리티 같은 코드를 집합이다.(비지니스 로직을 제외한 외부 라이브러리 등등)


이 4가지의 역할을 다이어그램으로 표시하면 아래와 같다.


http://t3js.org/


특징은 보는 것과 같이 module간 커뮤니케이션을 할 수 없고 Application을 통해서만 모뮬간 커뮤니션을 한다. 그리고 jQuery의 의존성을 가지며, 제거할 예정이고 [이슈]에 다양한 라이브러리를 사용할 수 있도록 제안하는 내용이 있어 어떻게 변할지는 모르겠다.

개인적으로 생각하는 이런 류의 프레임워크들에게 기대하는 포인트는 어떻게 메세지를  관리하는가 이다.

규모가 커지면 커질수록 메시지 관리 이슈가 너무 많다.

그래서 이런 문제를 사용자가 덜 겪도록 혹은 해결할 수 있도록 프레임워크에서 적절하게 제어해줘야하는데 t3js는 이런 문제를 실제로 고민했는지 모르지만, 내가 느끼기에 그런 고민을 한 것 같다.

그렇게 생각한 이유가 프레임워크를 만들면 기능을 넣고 싶은 유혹이 많은데... 대표적으로 메세지가 아니라 이벤트처럼 beforeA, afterA와 같은 걸 만들고 싶다. 근데 이걸 만들게 되는 순간 헬..  그리고 다수개의 모듈을 관리하는 객체를 만들어서 관리하고 싶어지는데.. 이것도 만드는 순간 처음에는 좋아보이지만 헬 열림.

여튼 전체적으로 최대한 간단하게 메세지를 처리하려고 한 디자인이 나뻐보이지 않는다. 그렇다고 세련됐다고 보기 힘들어 아쉽다.

그리고 다른 괜찮았던 점은 처음 코드를 볼 때 왜 module context라는 객체가 필요할까 라는 고민이 있었다.아래와 같이 module context객체는 사실상 application의 메서드를 호출하는 수준이다. 

broadcast: function(name, data) {
    this.application.broadcast(name, data);
},
getService: function(serviceName) {
    return this.application.getService(serviceName);
},

그래서 이건 뭘까라는 고민하다가 테스트 작성하는 [문서]를 보고 테스트 때문임을 알았다. 괜찮은 아이디어.


아쉬운 점은 module의 사용법이 굉장히 어색하다.

먼저, 사용 가능한 이벤트가 한정적이고, 아래와 같이 이벤트를 처리하는 방식이 [data-type]속성으로 처리하는데 너무 어색하다.

<footer id="footer" data-module="footer">
    <button id="clear-completed" data-type="clear-btn">Clear completed</button>
</footer>

Application.addModule('footer', function(context) {
    return {
        onclick: function(event, element, elementType) {
            if (elementType === 'clear-btn') {
                // Do something
            }

        }
    };
});

나라면 elementType을 굳이 로직으로 처리해야 하나라는 생각이 들었다. 이런건 사실 delegate을 써서 안에서 처리해야 하는 내용일 것 같은데 아쉬웠다.

코드가 간단해서 간만에 재미있게 봤네.

ps. 근데 왜 t3인거지?


Posted by 전용우
,