SlideShare uma empresa Scribd logo
1 de 34
Baixar para ler offline
12
DOM modification
Secrets of the JavaScript Ninja
• HTML 문자들을 페이지에 넣기
           • 엘리먼트 복제
           • 엘리먼트 제거
           • 엘리먼트 텍스트 조작


이 챕터에서 다루는 내용:
12.0 들어가며




                   DOM을 순차적으로 처리하는데는
                  일반적인 과정의 JavaScript 코드가 필요




주로 새로운 노드를 계속해서 document에 추가, 복제, 제거하는데 도움이 되는 코드들이 있겠다.
12.1 Injecting HTML



             insertAdjacentHTML
                      1. 오직 IE에만 존재 (따라서 대안책이 필요)
                      2. IE의 구현은 매우 buggy (일부 엘리먼트에서
                         만 동작)




우선 효과적인 방법으로 HTML 문자열을 document 임의의 위치에 넣는걸 보자.
Internet Explorer에 있는 API (W3C HTML 5 스펙에 포함: http://www.w3.org/TR/html5/apis-in-html-documents.html#insertadjacenthtml) 하지만 몇 가지 문제가 있다.
12.1 Injecting HTML


           1. 임의의, 유효한, HTML/XHTML 문자열을
              DOM 구조체로 변환한다.
           2. 가능하면 효과적으로 DOM 구조체를 임
              의의 위치에 넣는다.
           3. 문자열의 inline script를 실행한다.




때문에 처음부터 다시 깔끔한 API를 만들어야한다.
12.1.1 Converting HTML to DOM


            •    HTML 문자열이 확실하게 HTML/XHTML에 대
                 해 유효한지 확인 (최대한 valid 하도록 조작)

            •    문자열엔 감싸여진 마크업이 필요

            •    innerHTML 을 사용하여 dummy DOM 엘리먼트
                 에 추가

            •    DOM 노드를 다시 뽑아냄




딱히 마법과 같은게 아니라 우리가 익히 알고 있는 정밀한 도구: innerHTML 를 사용한다.
Pre-Process XML/HTML


             •    문맥에 따라 달라지는 경우

             •    jQuery는 <table/> 같은 XML 스타일 엘리먼트를
                  지원

             •    브라우저에서는 (IE 같은) 일부분의 HTML 엘리
                  먼트만 XML 스타일만 동작

             •    pre-parse 통해 예방




string을 미리 가공하는 준비 단계
Pre-Process XML/HTML

// Listing 12.1 : http://jsbin.com/izisar
var tags = /^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i;
function convert(html){
  return html.replace(/(<(w+)[^>]*?)/>/g, function(all, front, tag){
    return tags.test(tag) ?
      all :
      front + "></" + tag + ">";
  });
}
assert( convert("<a/>") === "<a></a>", "Check anchor conversion." );
assert( convert("<hr/>") === "<hr/>", "Check hr conversion." );
HTML Wrapping




            •    특정 parent에 문자열을 바로 넣을 수 있다.

            •    허나 모든 환경에서 보장되지 않는다.




대다수의 HTML 엘리먼트는 꼭 컨테이너 엘리먼트가 있어야한다. (예를 들어 option 태그) 두 가지 해결책을 보자.
HTML Wrapping

             •     적합한 마크업으로 감싸져있을 땐 어느 컨테이
                   너 엘리먼트에든 넣을 수 있다.


            option, optgroup : <select multiple="multiple">...</select>
            legend : <fieldset>...</fieldset>
            thead, tbody, tfoot, colgroup, caption : <table>...</table>
            tr : <table><thead>...</thead></table> ,
                 <table><tbody>...</tbody></table> ,
                 <table><tfoot>...</tfoot></table>
            td, th : <table><tbody><tr>...</tr></tbody></table>
            col : <table><tbody></tbody><colgroup>...</colgroup></table>
            link, script : <div>...</div>




■   multiple select 를 사용한 이유는 inject 과정에서 자동으로 첫번째 option이 선택되는걸 방지하기 위해.
■   col 태그를 위해 추가적인 tbody가 없으면 생성되지 않을 수 있다.
■   link, script 경우 IE에서 앞뒤에 노드없이 innerHTML 사용하면 엘리먼트를 생성해내지 못한다.
Generating the DOM
                    // Listing 12.2 : http://jsbin.com/ohevux
                    function getNodes(htmlString){
                      var map = {
                         "<td": [3, "<table><tbody><tr>", "</tr></tbody></table>"],
                         "<option": [1, "<select multiple='multiple'>", "</select>"]
                         // a full list of all element fixes
                      };

                        var name = htmlString.match(/<w+/),
                          node = name ? map[ name[0] ] : [0, "", ""];

                        var div = document.createElement("div");
                        div.innerHTML = node[1] + htmlString + node[2];

                        while ( node[0]-- )
                          div = div.lastChild;

                        return div.childNodes;
                    }

                    assert( getNodes("<td>test</td><td>test2</td>").length === 2,
                      "Get two nodes back from the method." );
                    assert( getNodes("<td>test</td>")[0].nodeName === "TD",
                      "Verify that we're getting the right node." );


앞의 wrapping 을 직접 적용해보자.
IE에서는 아래의 버그가 있다.
 1.   빈 table에 tbody를 추가해버린다.
 2.   innerHTML으로 입력 된 문자열의 행간 공백을 모두 제거해버린다.
지금까지 과정을 거침으로써 이제 document에 추가할 준비가 되었다.
12.1.2 Inserting into the Document


          DOM Fragments
                   • W3C DOM 스펙이며 모든 브라우저에서 지원
                   • DOM 노드들을 담아 둘 수 있는 컨테이너를 제공
                   • 장점1 : 간단한 명령으로 inject, clone 가능
                   • 장점2 : 반복하여 inject, clone 가능



우선 효과적인 방법으로 HTML 문자열을 document 임의의 위치에 넣는걸 보자.
Internet Explorer에 있는 API (W3C HTML 5 스펙에 포함: http://www.w3.org/TR/html5/apis-in-html-documents.html#insertadjacenthtml) 하지만 몇 가지 문제가 있다.
// Listing 12.3 : http://jsbin.com/uyuwuz/2
                      // <div id="test"><b>Hello</b>, I'm a ninja!</div>
                      // <div id="test2"></div>

                      window.onload = function(){
                        function insert(elems, args, callback){
                          if ( elems.length ) {
                            var doc = elems[0].ownerDocument || elems[0],
                              fragment = doc.createDocumentFragment(),
                              scripts = getNodes( args[0], doc, fragment ),
                              first = fragment.firstChild;

                                   if ( first ) {
                                     for ( var i = 0; elems[i]; i++ ) {
                                       callback.call( root(elems[i], first),
                                         i > 0 ? fragment.cloneNode(true) : fragment );
                                     }
                                   }
                               }
                           }

                           var divs = document.getElementsByTagName("div");

                           insert(divs, ["<b>Name:</b>"], function(fragment){
                             this.appendChild( fragment );
                           });

                           insert(divs, ["<span>First</span> <span>Last</span>"],
                             function(fragment){
                               this.parentNode.insertBefore( fragment, this );
                             });
                      };


jQuery 코드를 참조해보면, fragment가 재생성 되어 함수에 전달되는 걸 확인할 수 있다.
12.1.2 Inserting into the Document


        // Listing 12.4 : http://jsbin.com/uyuwuz/2
        function root( elem, cur ) {
          return elem.nodeName.toLowerCase() === "table" &&
            cur.nodeName.toLowerCase() === "tr" ?
            (elem.getElementsByTagName("tbody")[0] ||
              elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
            elem;
        }




마지막으로 사용자가 직접 table 넣기를 시도할 경우 tbody를 매핑하는 식으로 관리해준다.
12.1.3 Script Execution



   • 인라인 스크립트 엘리먼트가 document에
     추가되면 실행이 되어야 할 것이다.

   • 가장 좋은 방법은 document에 추가되기
     전에 script 들을 분리시켜 놓는 방법이다.
12.1.3 Script Execution
       // Listing 12.5 : http://jsbin.com/atujam
       for ( var i = 0; ret[i]; i++ ) {
         if ( jQuery.nodeName( ret[i], "script" ) &&
             (!ret[i].type ||
               ret[i].type.toLowerCase() === "text/javascript") ) {
           scripts.push( ret[i].parentNode ?
             ret[i].parentNode.removeChild( ret[i] ) :
             ret[i] );
         } else if ( ret[i].nodeType === 1 ) {
           ret.splice.apply( ret, [i + 1, 0].concat(
             jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
         }
       }




ret (생성 될 DOM 노드), scripts (스크립트들을 fragment로 모음)의 2가지 배열로 분리. 이제 교묘한 방법으로 스크립트들을 실행해보자.
Global Code Evaluation



   • 사용자가 정의한 인라인 스크립트들은
     global context로 실행 된다.

   • Andrea Giammarchi 가 착안한 스크립트 실
     행법을 이용한다. - document에 script 엘리
     먼트를 붙였다 때어내는 방식
Global Code Evaluation
           // Listing 12.6 : http://jsbin.com/orevuz
           function globalEval( data ) {
             data = data.replace(/^s+|s+$/g, "");

               if ( data ) {
                 var head = document.getElementsByTagName("head")[0] ||
                     document.documentElement,
                   script = document.createElement("script");

                   script.type = "text/javascript";
                   script.text = data;

                   head.insertBefore( script, head.firstChild );
                   head.removeChild( script );
               }
           }




"어때요? 참~ 쉽죠?" 이제 이를 활용하여 동적 로딩까지 되는 코드를 만들 수 있습니다.
Global Code Evaluation
                        // Listing 12.7 : http://jsbin.com/uvinos
                        function evalScript( elem ) {
                          if ( elem.src )
                            jQuery.ajax({
                               url: elem.src,
                               async: false,
                               dataType: "script"
                            });
                          else
                            jQuery.globalEval( elem.text || "" );

                            if ( elem.parentNode )
                              elem.parentNode.removeChild( elem );
                        }



           NOTE
           실행이 완료 된 스크립트는 DOM에서 제거 합니다.
           (나중에 의도치 않는 이중 실행을 방지)


"어때요? 참~ 쉽죠?" 이제 이를 활용하여 동적 로딩까지 되는 코드를 만들 수 있습니다.
12.2 Cloning Elements



   • 엘리먼트 복제(DOM cloneNode 메소드
     사용)는 모든 브라우저에서 직접 사용 된
     다.

   • IE는 3가지 절망적인 단계를 거쳐야한다.
12.2 Cloning Elements

   •   첫째, 엘리먼트 복제를 하면 이에 따른 모든 이벤트
       헨들러를 복제한다.

       // Listing 12.8 : http://jsbin.com/atagec
       var div = document.createElement("div");

       if ( div.attachEvent && div.fireEvent ) {
         div.attachEvent("onclick", function(){
           // Cloning a node shouldn't copy over any
           // bound event handlers (IE does this)
           jQuery.support.noCloneEvent = false;
           div.detachEvent("onclick", arguments.callee);
         });
         div.cloneNode(true).fireEvent("onclick");
       }
12.2 Cloning Elements



             •    둘째, 복제 된 엘리먼트에서 이벤트 헨들러를 제거하
                  면 본래 엘리먼트쪽이 제거된다.

             •    셋째, 이를 해결하는 단계는 또다른 엘리먼트에 넣은
                  다음, innerHTML으로 읽어오고, 그리고 다시 DOM 노
                  드로 변환하는 것이다.




3 - 이 때 또다른 IE 버그 : innerHTML (또는 outerHTML) 읽어올 때 항상 정확한 엘리먼트 속성을 유지하지는 않는다. 때문에 XML DOM 확인 분기가 추가.
12.2 Cloning Elements
// Listing 12.9 : http://jsbin.com/etegeh
function clone() {
  var ret = this.map(function(){
    if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
      var clone = this.cloneNode(true),
        container = document.createElement("div");
      container.appendChild(clone);
      return jQuery.clean([container.innerHTML])[0];
    } else
      return this.cloneNode(true);
  });

    var clone = ret.find("*").andSelf().each(function(){
      if ( this[ expando ] !== undefined )
        this[ expando ] = null;
    });

    return ret;
}
12.3 Removing Elements



            • 연결되어 있는 이벤트 헨들러를 모두 정
                 리해야 한다. 이 단계는 IE의 메모리릭 때
                 문에 매우 중요하다.

            • 연결되어 있는 외부 데이터를 정리해야
                 한다.




DOM에서 엘리먼트 제거하기는 간단하다. (removeChild 바로 사용) 제거하는 과정엔 주로 2단계가 필요하다.
...
위 내용들은 추후 이벤트 챕터에서 다루기로 하자.
12.3 Removing Elements
         // Listing 12.10 : http://jsbin.com/ivaguq
         function remove() {
           // Go through all descendants and the element to be removed
           jQuery( "*", this ).add([this]).each(function(){
             // Remove all bound events
             jQuery.event.remove(this);

               // Remove attached data
               jQuery.removeData(this);
             });

             // Remove the element (if it's in the DOM)
             if ( this.parentNode )
               this.parentNode.removeChild( this );
         }




위의 포인트들이 엘리먼트 - 자손도 포함한 제거 과정에서 진행되는 jQuery 코드를 보자.
12.3 Removing Elements

                    // Listing 12.11 : http://jsbin.com/uzuhuc
                    // Remove the element (if it's in the DOM)
                    if ( this.parentNode )
                      this.parentNode.removeChild( this );

                    if ( typeof this.outerHTML !== "undefined" )
                      this.outerHTML = "";




          NOTE
          기억하세요! 항상 DOM을 tidy하게 유지해야 나중에 메모리 이슈를
          덜어줍니다.




또다른 고려사항은 정리 후에 실제로 DOM에서 엘리먼트가 없어졌는지 봐야한다. (역시 IE에서만 예외적) 잘 동작하는 해결책으로는 IE에서 제공하는 outerHTML 을 활용하는
방법.
12.4 Text Contents




  • W3C-compliant 브라우저에서는 textContent
    속성 사용 (자식, 자손 노드 모두 적용)

  • IE에서는 innerText, textContent 속성 사용
// Listing 12.12 : http://jsbin.com/olanub/2
               // <div id="test"><b>Hello</b>, I'm a ninja!</div>
               // <div id="test2"></div>

               window.onload = function(){
                 var b = document.getElementById("test");
                 var text = b.textContent || b.innerText;

                    assert( text === "Hello, I'm a ninja!",
                      "Examine the text contents of an element." );
                    assert( b.childNodes.length === 2,
                      "An element and a text node exist." );

                    if ( typeof b.textContent !== "undefined" ) {
                      b.textContent = "Some new text";
                    } else {
                      b.innerText = "Some new text";
                    }

                    text = b.textContent || b.innerText;

                    assert( text === "Some new text", "Set a new text value." );
                    assert( b.childNodes.length === 1,
                      "Only one text nodes exists now." );
               };



Note : textContent/innerText 속성 사용 시 내부의 본래 엘리먼트 구조는 사라진다.
12.4 Text Contents



  • 여기서 발생하는 gotchas
   1. 엘리먼트가 사라지는 경우 앞서 언급했던 메모리릭
      발생
   2. whitespace 크로스브라우징 처리 경우 최악
Setting Text



   • 내부의 엘리먼트가 비워지고,
      새로운 텍스트가 입력된다.

   • 내부의 컨텐츠가 비워진다. - Listing 12.10
Setting Text
              // Listing 12.13 : http://jsbin.com/otosuv/2
              // <div id="test"><b>Hello</b>, I'm a ninja!</div>
              // <div id="test2"></div>

              window.onload = function(){
                var b = document.getElementById("test");

                   // Replace with your empty() method of choice
                   while ( b.firstChild )
                     b.removeChild( b.firstChild );

                   // Inject the escaped text node
                   b.appendChild( document.createTextNode( "Some new text" ) );

                   var text = b.textContent || b.innerText;

                   assert( text === "Some new text", "Set a new text value." );
                   assert( b.childNodes.length === 1,
                     "Only one text nodes exists now." );
              };



HTML과 텍스트 입력의 차이점 : HTML 고유 문자가 escape 된다. 때문에 createTextNode 메소드를 사용이 필요하다.
Getting Text



            • endline 관련 문제들 때문에 textContent/
                 innerText 사용은 하지 않는다.

            • 대신 텍스트 노드의 값들을 직접 읽어오
                 는 것이 정확한 값을 가져오겠다.




1 - (해당 문제를 딱히 신경 쓸 필요 없다면 그냥 사용하는게 간단)
// Listing 12.14 : http://jsbin.com/olanof/2
// <div id="test"><b>Hello</b>, I'm a ninja!</div>
// <div id="test2"></div>

window.onload = function(){
  function getText( elem ) {
    var text = "";

         for ( var i = 0, l = elem.childNodes.length; i < l; i++ ) {
           var cur = elem.childNodes[i];

             // A text node has a nodeType === 3
             if ( cur.nodeType === 3 )
               text += cur.nodeValue;

             // If it's an element we need to recurse further
             else if ( cur.nodeType === 1 )
               text += getText( cur );
         }

         return text;
     }

     var b = document.getElementById("test");
     var text = getText( b );

     assert( text === "Hello, I'm a ninja!",
       "Examine the text contents of an element." );
     assert( b.childNodes.length === 2,
       "An element and a text node exist." );
};
12.5 Summary


   • DOM 조작에 있어 어려운점과 이를 해결
    하는 방법을 포괄적으로 살펴 보았다.

   • 크로스브라우징 이슈를 설명하고 실제
    로 구현하는게 훨씬 힘들다.

   • 잘 동작하는 통합 된 솔루션을 만들기 위
    해 노력해야겠다.

Mais conteúdo relacionado

Mais procurados

Html5 web workers
Html5 web workersHtml5 web workers
Html5 web workersWoo Jin Kim
 
Angular2 router&http
Angular2 router&httpAngular2 router&http
Angular2 router&httpDong Jun Kwon
 
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기NAVER Engineering
 
처음배우는 자바스크립트, 제이쿼리 #1
처음배우는 자바스크립트, 제이쿼리 #1처음배우는 자바스크립트, 제이쿼리 #1
처음배우는 자바스크립트, 제이쿼리 #1성일 한
 
처음배우는 자바스크립트, 제이쿼리 #4
처음배우는 자바스크립트, 제이쿼리 #4처음배우는 자바스크립트, 제이쿼리 #4
처음배우는 자바스크립트, 제이쿼리 #4성일 한
 
[D2 오픈세미나]5.robolectric 안드로이드 테스팅
[D2 오픈세미나]5.robolectric 안드로이드 테스팅[D2 오픈세미나]5.robolectric 안드로이드 테스팅
[D2 오픈세미나]5.robolectric 안드로이드 테스팅NAVER D2
 
처음배우는 자바스크립트, 제이쿼리 #3
처음배우는 자바스크립트, 제이쿼리 #3처음배우는 자바스크립트, 제이쿼리 #3
처음배우는 자바스크립트, 제이쿼리 #3성일 한
 
자바야 놀자 PPT
자바야 놀자 PPT자바야 놀자 PPT
자바야 놀자 PPTJinKyoungHeo
 
[11]Android DataBinding : 기초에서 고급까지
[11]Android DataBinding : 기초에서 고급까지[11]Android DataBinding : 기초에서 고급까지
[11]Android DataBinding : 기초에서 고급까지NAVER Engineering
 
ECMAScript 6의 새로운 것들!
ECMAScript 6의 새로운 것들!ECMAScript 6의 새로운 것들!
ECMAScript 6의 새로운 것들!WooYoung Cho
 
파이썬 언어 기초
파이썬 언어 기초파이썬 언어 기초
파이썬 언어 기초beom kyun choi
 
ReactJS | 서버와 클라이어트에서 동시에 사용하는
ReactJS | 서버와 클라이어트에서 동시에 사용하는ReactJS | 서버와 클라이어트에서 동시에 사용하는
ReactJS | 서버와 클라이어트에서 동시에 사용하는Taegon Kim
 
[115] clean fe development_윤지수
[115] clean fe development_윤지수[115] clean fe development_윤지수
[115] clean fe development_윤지수NAVER D2
 

Mais procurados (20)

Hacosa jquery 1th
Hacosa jquery 1thHacosa jquery 1th
Hacosa jquery 1th
 
Html5 web workers
Html5 web workersHtml5 web workers
Html5 web workers
 
Angular2 router&http
Angular2 router&httpAngular2 router&http
Angular2 router&http
 
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
 
처음배우는 자바스크립트, 제이쿼리 #1
처음배우는 자바스크립트, 제이쿼리 #1처음배우는 자바스크립트, 제이쿼리 #1
처음배우는 자바스크립트, 제이쿼리 #1
 
처음배우는 자바스크립트, 제이쿼리 #4
처음배우는 자바스크립트, 제이쿼리 #4처음배우는 자바스크립트, 제이쿼리 #4
처음배우는 자바스크립트, 제이쿼리 #4
 
[D2 오픈세미나]5.robolectric 안드로이드 테스팅
[D2 오픈세미나]5.robolectric 안드로이드 테스팅[D2 오픈세미나]5.robolectric 안드로이드 테스팅
[D2 오픈세미나]5.robolectric 안드로이드 테스팅
 
처음배우는 자바스크립트, 제이쿼리 #3
처음배우는 자바스크립트, 제이쿼리 #3처음배우는 자바스크립트, 제이쿼리 #3
처음배우는 자바스크립트, 제이쿼리 #3
 
4-2. ajax
4-2. ajax4-2. ajax
4-2. ajax
 
Redux
ReduxRedux
Redux
 
자바야 놀자 PPT
자바야 놀자 PPT자바야 놀자 PPT
자바야 놀자 PPT
 
[11]Android DataBinding : 기초에서 고급까지
[11]Android DataBinding : 기초에서 고급까지[11]Android DataBinding : 기초에서 고급까지
[11]Android DataBinding : 기초에서 고급까지
 
Node.js 심화과정
Node.js 심화과정Node.js 심화과정
Node.js 심화과정
 
ECMAScript 6의 새로운 것들!
ECMAScript 6의 새로운 것들!ECMAScript 6의 새로운 것들!
ECMAScript 6의 새로운 것들!
 
Jquery핵심노토
Jquery핵심노토Jquery핵심노토
Jquery핵심노토
 
파이썬 언어 기초
파이썬 언어 기초파이썬 언어 기초
파이썬 언어 기초
 
ReactJS | 서버와 클라이어트에서 동시에 사용하는
ReactJS | 서버와 클라이어트에서 동시에 사용하는ReactJS | 서버와 클라이어트에서 동시에 사용하는
ReactJS | 서버와 클라이어트에서 동시에 사용하는
 
[115] clean fe development_윤지수
[115] clean fe development_윤지수[115] clean fe development_윤지수
[115] clean fe development_윤지수
 
4-1. javascript
4-1. javascript4-1. javascript
4-1. javascript
 
Node.js 기본과정
Node.js 기본과정Node.js 기본과정
Node.js 기본과정
 

Semelhante a Secrets of the JavaScript Ninja - Chapter 12. DOM modification

First Step In Ajax Korean
First Step In Ajax KoreanFirst Step In Ajax Korean
First Step In Ajax KoreanTerry Cho
 
Okjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 TestOkjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 Testbeom kyun choi
 
알아봅시다, Polymer: Web Components & Web Animations
알아봅시다, Polymer: Web Components & Web Animations알아봅시다, Polymer: Web Components & Web Animations
알아봅시다, Polymer: Web Components & Web AnimationsChang W. Doh
 
Web Components - Part.I, @KIG 30th
Web Components - Part.I, @KIG 30thWeb Components - Part.I, @KIG 30th
Web Components - Part.I, @KIG 30thChang W. Doh
 
Meteor React Tutorial 따라하기
Meteor React Tutorial 따라하기Meteor React Tutorial 따라하기
Meteor React Tutorial 따라하기Jiam Seo
 
웹성능최적화 20130405
웹성능최적화 20130405웹성능최적화 20130405
웹성능최적화 20130405주형 전
 
[114]angularvs react 김훈민손찬욱
[114]angularvs react 김훈민손찬욱[114]angularvs react 김훈민손찬욱
[114]angularvs react 김훈민손찬욱NAVER D2
 
자바스크립트 프레임워크 살펴보기
자바스크립트 프레임워크 살펴보기자바스크립트 프레임워크 살펴보기
자바스크립트 프레임워크 살펴보기Jeado Ko
 
Html5 앱과 웹사이트를 보다 빠르게 하는 50가지
Html5 앱과 웹사이트를 보다 빠르게 하는 50가지Html5 앱과 웹사이트를 보다 빠르게 하는 50가지
Html5 앱과 웹사이트를 보다 빠르게 하는 50가지yongwoo Jeon
 
웹개발자가 알아야할 기술
웹개발자가 알아야할 기술웹개발자가 알아야할 기술
웹개발자가 알아야할 기술jaypi Ko
 
Front-end Development Process - 어디까지 개선할 수 있나
Front-end Development Process - 어디까지 개선할 수 있나Front-end Development Process - 어디까지 개선할 수 있나
Front-end Development Process - 어디까지 개선할 수 있나JeongHun Byeon
 
7가지 동시성 모델 람다아키텍처
7가지 동시성 모델  람다아키텍처7가지 동시성 모델  람다아키텍처
7가지 동시성 모델 람다아키텍처Sunggon Song
 
Polymer따라잡기
Polymer따라잡기Polymer따라잡기
Polymer따라잡기Han Jung Hyun
 
android_thread
android_threadandroid_thread
android_threadhandfoot
 

Semelhante a Secrets of the JavaScript Ninja - Chapter 12. DOM modification (20)

[Codelab 2017] ReactJS 기초
[Codelab 2017] ReactJS 기초[Codelab 2017] ReactJS 기초
[Codelab 2017] ReactJS 기초
 
First Step In Ajax Korean
First Step In Ajax KoreanFirst Step In Ajax Korean
First Step In Ajax Korean
 
Okjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 TestOkjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 Test
 
알아봅시다, Polymer: Web Components & Web Animations
알아봅시다, Polymer: Web Components & Web Animations알아봅시다, Polymer: Web Components & Web Animations
알아봅시다, Polymer: Web Components & Web Animations
 
Web Components - Part.I, @KIG 30th
Web Components - Part.I, @KIG 30thWeb Components - Part.I, @KIG 30th
Web Components - Part.I, @KIG 30th
 
Meteor React Tutorial 따라하기
Meteor React Tutorial 따라하기Meteor React Tutorial 따라하기
Meteor React Tutorial 따라하기
 
Xe hack
Xe hackXe hack
Xe hack
 
3-2. selector api
3-2. selector api3-2. selector api
3-2. selector api
 
웹성능최적화 20130405
웹성능최적화 20130405웹성능최적화 20130405
웹성능최적화 20130405
 
[114]angularvs react 김훈민손찬욱
[114]angularvs react 김훈민손찬욱[114]angularvs react 김훈민손찬욱
[114]angularvs react 김훈민손찬욱
 
자바스크립트 프레임워크 살펴보기
자바스크립트 프레임워크 살펴보기자바스크립트 프레임워크 살펴보기
자바스크립트 프레임워크 살펴보기
 
Html5 앱과 웹사이트를 보다 빠르게 하는 50가지
Html5 앱과 웹사이트를 보다 빠르게 하는 50가지Html5 앱과 웹사이트를 보다 빠르게 하는 50가지
Html5 앱과 웹사이트를 보다 빠르게 하는 50가지
 
웹개발자가 알아야할 기술
웹개발자가 알아야할 기술웹개발자가 알아야할 기술
웹개발자가 알아야할 기술
 
Front-end Development Process - 어디까지 개선할 수 있나
Front-end Development Process - 어디까지 개선할 수 있나Front-end Development Process - 어디까지 개선할 수 있나
Front-end Development Process - 어디까지 개선할 수 있나
 
Nest js 101
Nest js 101Nest js 101
Nest js 101
 
7가지 동시성 모델 람다아키텍처
7가지 동시성 모델  람다아키텍처7가지 동시성 모델  람다아키텍처
7가지 동시성 모델 람다아키텍처
 
Spring Boot 2
Spring Boot 2Spring Boot 2
Spring Boot 2
 
Polymer따라잡기
Polymer따라잡기Polymer따라잡기
Polymer따라잡기
 
Web workers
Web workersWeb workers
Web workers
 
android_thread
android_threadandroid_thread
android_thread
 

Último

A future that integrates LLMs and LAMs (Symposium)
A future that integrates LLMs and LAMs (Symposium)A future that integrates LLMs and LAMs (Symposium)
A future that integrates LLMs and LAMs (Symposium)Tae Young Lee
 
MOODv2 : Masked Image Modeling for Out-of-Distribution Detection
MOODv2 : Masked Image Modeling for Out-of-Distribution DetectionMOODv2 : Masked Image Modeling for Out-of-Distribution Detection
MOODv2 : Masked Image Modeling for Out-of-Distribution DetectionKim Daeun
 
캐드앤그래픽스 2024년 5월호 목차
캐드앤그래픽스 2024년 5월호 목차캐드앤그래픽스 2024년 5월호 목차
캐드앤그래픽스 2024년 5월호 목차캐드앤그래픽스
 
Merge (Kitworks Team Study 이성수 발표자료 240426)
Merge (Kitworks Team Study 이성수 발표자료 240426)Merge (Kitworks Team Study 이성수 발표자료 240426)
Merge (Kitworks Team Study 이성수 발표자료 240426)Wonjun Hwang
 
Console API (Kitworks Team Study 백혜인 발표자료)
Console API (Kitworks Team Study 백혜인 발표자료)Console API (Kitworks Team Study 백혜인 발표자료)
Console API (Kitworks Team Study 백혜인 발표자료)Wonjun Hwang
 
Continual Active Learning for Efficient Adaptation of Machine LearningModels ...
Continual Active Learning for Efficient Adaptation of Machine LearningModels ...Continual Active Learning for Efficient Adaptation of Machine LearningModels ...
Continual Active Learning for Efficient Adaptation of Machine LearningModels ...Kim Daeun
 

Último (6)

A future that integrates LLMs and LAMs (Symposium)
A future that integrates LLMs and LAMs (Symposium)A future that integrates LLMs and LAMs (Symposium)
A future that integrates LLMs and LAMs (Symposium)
 
MOODv2 : Masked Image Modeling for Out-of-Distribution Detection
MOODv2 : Masked Image Modeling for Out-of-Distribution DetectionMOODv2 : Masked Image Modeling for Out-of-Distribution Detection
MOODv2 : Masked Image Modeling for Out-of-Distribution Detection
 
캐드앤그래픽스 2024년 5월호 목차
캐드앤그래픽스 2024년 5월호 목차캐드앤그래픽스 2024년 5월호 목차
캐드앤그래픽스 2024년 5월호 목차
 
Merge (Kitworks Team Study 이성수 발표자료 240426)
Merge (Kitworks Team Study 이성수 발표자료 240426)Merge (Kitworks Team Study 이성수 발표자료 240426)
Merge (Kitworks Team Study 이성수 발표자료 240426)
 
Console API (Kitworks Team Study 백혜인 발표자료)
Console API (Kitworks Team Study 백혜인 발표자료)Console API (Kitworks Team Study 백혜인 발표자료)
Console API (Kitworks Team Study 백혜인 발표자료)
 
Continual Active Learning for Efficient Adaptation of Machine LearningModels ...
Continual Active Learning for Efficient Adaptation of Machine LearningModels ...Continual Active Learning for Efficient Adaptation of Machine LearningModels ...
Continual Active Learning for Efficient Adaptation of Machine LearningModels ...
 

Secrets of the JavaScript Ninja - Chapter 12. DOM modification

  • 1. 12 DOM modification Secrets of the JavaScript Ninja
  • 2. • HTML 문자들을 페이지에 넣기 • 엘리먼트 복제 • 엘리먼트 제거 • 엘리먼트 텍스트 조작 이 챕터에서 다루는 내용:
  • 3. 12.0 들어가며 DOM을 순차적으로 처리하는데는 일반적인 과정의 JavaScript 코드가 필요 주로 새로운 노드를 계속해서 document에 추가, 복제, 제거하는데 도움이 되는 코드들이 있겠다.
  • 4. 12.1 Injecting HTML insertAdjacentHTML 1. 오직 IE에만 존재 (따라서 대안책이 필요) 2. IE의 구현은 매우 buggy (일부 엘리먼트에서 만 동작) 우선 효과적인 방법으로 HTML 문자열을 document 임의의 위치에 넣는걸 보자. Internet Explorer에 있는 API (W3C HTML 5 스펙에 포함: http://www.w3.org/TR/html5/apis-in-html-documents.html#insertadjacenthtml) 하지만 몇 가지 문제가 있다.
  • 5. 12.1 Injecting HTML 1. 임의의, 유효한, HTML/XHTML 문자열을 DOM 구조체로 변환한다. 2. 가능하면 효과적으로 DOM 구조체를 임 의의 위치에 넣는다. 3. 문자열의 inline script를 실행한다. 때문에 처음부터 다시 깔끔한 API를 만들어야한다.
  • 6. 12.1.1 Converting HTML to DOM • HTML 문자열이 확실하게 HTML/XHTML에 대 해 유효한지 확인 (최대한 valid 하도록 조작) • 문자열엔 감싸여진 마크업이 필요 • innerHTML 을 사용하여 dummy DOM 엘리먼트 에 추가 • DOM 노드를 다시 뽑아냄 딱히 마법과 같은게 아니라 우리가 익히 알고 있는 정밀한 도구: innerHTML 를 사용한다.
  • 7. Pre-Process XML/HTML • 문맥에 따라 달라지는 경우 • jQuery는 <table/> 같은 XML 스타일 엘리먼트를 지원 • 브라우저에서는 (IE 같은) 일부분의 HTML 엘리 먼트만 XML 스타일만 동작 • pre-parse 통해 예방 string을 미리 가공하는 준비 단계
  • 8. Pre-Process XML/HTML // Listing 12.1 : http://jsbin.com/izisar var tags = /^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i; function convert(html){ return html.replace(/(<(w+)[^>]*?)/>/g, function(all, front, tag){ return tags.test(tag) ? all : front + "></" + tag + ">"; }); } assert( convert("<a/>") === "<a></a>", "Check anchor conversion." ); assert( convert("<hr/>") === "<hr/>", "Check hr conversion." );
  • 9. HTML Wrapping • 특정 parent에 문자열을 바로 넣을 수 있다. • 허나 모든 환경에서 보장되지 않는다. 대다수의 HTML 엘리먼트는 꼭 컨테이너 엘리먼트가 있어야한다. (예를 들어 option 태그) 두 가지 해결책을 보자.
  • 10. HTML Wrapping • 적합한 마크업으로 감싸져있을 땐 어느 컨테이 너 엘리먼트에든 넣을 수 있다. option, optgroup : <select multiple="multiple">...</select> legend : <fieldset>...</fieldset> thead, tbody, tfoot, colgroup, caption : <table>...</table> tr : <table><thead>...</thead></table> , <table><tbody>...</tbody></table> , <table><tfoot>...</tfoot></table> td, th : <table><tbody><tr>...</tr></tbody></table> col : <table><tbody></tbody><colgroup>...</colgroup></table> link, script : <div>...</div> ■ multiple select 를 사용한 이유는 inject 과정에서 자동으로 첫번째 option이 선택되는걸 방지하기 위해. ■ col 태그를 위해 추가적인 tbody가 없으면 생성되지 않을 수 있다. ■ link, script 경우 IE에서 앞뒤에 노드없이 innerHTML 사용하면 엘리먼트를 생성해내지 못한다.
  • 11. Generating the DOM // Listing 12.2 : http://jsbin.com/ohevux function getNodes(htmlString){ var map = { "<td": [3, "<table><tbody><tr>", "</tr></tbody></table>"], "<option": [1, "<select multiple='multiple'>", "</select>"] // a full list of all element fixes }; var name = htmlString.match(/<w+/), node = name ? map[ name[0] ] : [0, "", ""]; var div = document.createElement("div"); div.innerHTML = node[1] + htmlString + node[2]; while ( node[0]-- ) div = div.lastChild; return div.childNodes; } assert( getNodes("<td>test</td><td>test2</td>").length === 2, "Get two nodes back from the method." ); assert( getNodes("<td>test</td>")[0].nodeName === "TD", "Verify that we're getting the right node." ); 앞의 wrapping 을 직접 적용해보자. IE에서는 아래의 버그가 있다. 1. 빈 table에 tbody를 추가해버린다. 2. innerHTML으로 입력 된 문자열의 행간 공백을 모두 제거해버린다. 지금까지 과정을 거침으로써 이제 document에 추가할 준비가 되었다.
  • 12. 12.1.2 Inserting into the Document DOM Fragments • W3C DOM 스펙이며 모든 브라우저에서 지원 • DOM 노드들을 담아 둘 수 있는 컨테이너를 제공 • 장점1 : 간단한 명령으로 inject, clone 가능 • 장점2 : 반복하여 inject, clone 가능 우선 효과적인 방법으로 HTML 문자열을 document 임의의 위치에 넣는걸 보자. Internet Explorer에 있는 API (W3C HTML 5 스펙에 포함: http://www.w3.org/TR/html5/apis-in-html-documents.html#insertadjacenthtml) 하지만 몇 가지 문제가 있다.
  • 13. // Listing 12.3 : http://jsbin.com/uyuwuz/2 // <div id="test"><b>Hello</b>, I'm a ninja!</div> // <div id="test2"></div> window.onload = function(){ function insert(elems, args, callback){ if ( elems.length ) { var doc = elems[0].ownerDocument || elems[0], fragment = doc.createDocumentFragment(), scripts = getNodes( args[0], doc, fragment ), first = fragment.firstChild; if ( first ) { for ( var i = 0; elems[i]; i++ ) { callback.call( root(elems[i], first), i > 0 ? fragment.cloneNode(true) : fragment ); } } } } var divs = document.getElementsByTagName("div"); insert(divs, ["<b>Name:</b>"], function(fragment){ this.appendChild( fragment ); }); insert(divs, ["<span>First</span> <span>Last</span>"], function(fragment){ this.parentNode.insertBefore( fragment, this ); }); }; jQuery 코드를 참조해보면, fragment가 재생성 되어 함수에 전달되는 걸 확인할 수 있다.
  • 14. 12.1.2 Inserting into the Document // Listing 12.4 : http://jsbin.com/uyuwuz/2 function root( elem, cur ) { return elem.nodeName.toLowerCase() === "table" && cur.nodeName.toLowerCase() === "tr" ? (elem.getElementsByTagName("tbody")[0] || elem.appendChild(elem.ownerDocument.createElement("tbody"))) : elem; } 마지막으로 사용자가 직접 table 넣기를 시도할 경우 tbody를 매핑하는 식으로 관리해준다.
  • 15. 12.1.3 Script Execution • 인라인 스크립트 엘리먼트가 document에 추가되면 실행이 되어야 할 것이다. • 가장 좋은 방법은 document에 추가되기 전에 script 들을 분리시켜 놓는 방법이다.
  • 16. 12.1.3 Script Execution // Listing 12.5 : http://jsbin.com/atujam for ( var i = 0; ret[i]; i++ ) { if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) { scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] ); } else if ( ret[i].nodeType === 1 ) { ret.splice.apply( ret, [i + 1, 0].concat( jQuery.makeArray(ret[i].getElementsByTagName("script"))) ); } } ret (생성 될 DOM 노드), scripts (스크립트들을 fragment로 모음)의 2가지 배열로 분리. 이제 교묘한 방법으로 스크립트들을 실행해보자.
  • 17. Global Code Evaluation • 사용자가 정의한 인라인 스크립트들은 global context로 실행 된다. • Andrea Giammarchi 가 착안한 스크립트 실 행법을 이용한다. - document에 script 엘리 먼트를 붙였다 때어내는 방식
  • 18. Global Code Evaluation // Listing 12.6 : http://jsbin.com/orevuz function globalEval( data ) { data = data.replace(/^s+|s+$/g, ""); if ( data ) { var head = document.getElementsByTagName("head")[0] || document.documentElement, script = document.createElement("script"); script.type = "text/javascript"; script.text = data; head.insertBefore( script, head.firstChild ); head.removeChild( script ); } } "어때요? 참~ 쉽죠?" 이제 이를 활용하여 동적 로딩까지 되는 코드를 만들 수 있습니다.
  • 19. Global Code Evaluation // Listing 12.7 : http://jsbin.com/uvinos function evalScript( elem ) { if ( elem.src ) jQuery.ajax({ url: elem.src, async: false, dataType: "script" }); else jQuery.globalEval( elem.text || "" ); if ( elem.parentNode ) elem.parentNode.removeChild( elem ); } NOTE 실행이 완료 된 스크립트는 DOM에서 제거 합니다. (나중에 의도치 않는 이중 실행을 방지) "어때요? 참~ 쉽죠?" 이제 이를 활용하여 동적 로딩까지 되는 코드를 만들 수 있습니다.
  • 20. 12.2 Cloning Elements • 엘리먼트 복제(DOM cloneNode 메소드 사용)는 모든 브라우저에서 직접 사용 된 다. • IE는 3가지 절망적인 단계를 거쳐야한다.
  • 21. 12.2 Cloning Elements • 첫째, 엘리먼트 복제를 하면 이에 따른 모든 이벤트 헨들러를 복제한다. // Listing 12.8 : http://jsbin.com/atagec var div = document.createElement("div"); if ( div.attachEvent && div.fireEvent ) { div.attachEvent("onclick", function(){ // Cloning a node shouldn't copy over any // bound event handlers (IE does this) jQuery.support.noCloneEvent = false; div.detachEvent("onclick", arguments.callee); }); div.cloneNode(true).fireEvent("onclick"); }
  • 22. 12.2 Cloning Elements • 둘째, 복제 된 엘리먼트에서 이벤트 헨들러를 제거하 면 본래 엘리먼트쪽이 제거된다. • 셋째, 이를 해결하는 단계는 또다른 엘리먼트에 넣은 다음, innerHTML으로 읽어오고, 그리고 다시 DOM 노 드로 변환하는 것이다. 3 - 이 때 또다른 IE 버그 : innerHTML (또는 outerHTML) 읽어올 때 항상 정확한 엘리먼트 속성을 유지하지는 않는다. 때문에 XML DOM 확인 분기가 추가.
  • 23. 12.2 Cloning Elements // Listing 12.9 : http://jsbin.com/etegeh function clone() { var ret = this.map(function(){ if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) { var clone = this.cloneNode(true), container = document.createElement("div"); container.appendChild(clone); return jQuery.clean([container.innerHTML])[0]; } else return this.cloneNode(true); }); var clone = ret.find("*").andSelf().each(function(){ if ( this[ expando ] !== undefined ) this[ expando ] = null; }); return ret; }
  • 24. 12.3 Removing Elements • 연결되어 있는 이벤트 헨들러를 모두 정 리해야 한다. 이 단계는 IE의 메모리릭 때 문에 매우 중요하다. • 연결되어 있는 외부 데이터를 정리해야 한다. DOM에서 엘리먼트 제거하기는 간단하다. (removeChild 바로 사용) 제거하는 과정엔 주로 2단계가 필요하다. ... 위 내용들은 추후 이벤트 챕터에서 다루기로 하자.
  • 25. 12.3 Removing Elements // Listing 12.10 : http://jsbin.com/ivaguq function remove() { // Go through all descendants and the element to be removed jQuery( "*", this ).add([this]).each(function(){ // Remove all bound events jQuery.event.remove(this); // Remove attached data jQuery.removeData(this); }); // Remove the element (if it's in the DOM) if ( this.parentNode ) this.parentNode.removeChild( this ); } 위의 포인트들이 엘리먼트 - 자손도 포함한 제거 과정에서 진행되는 jQuery 코드를 보자.
  • 26. 12.3 Removing Elements // Listing 12.11 : http://jsbin.com/uzuhuc // Remove the element (if it's in the DOM) if ( this.parentNode ) this.parentNode.removeChild( this ); if ( typeof this.outerHTML !== "undefined" ) this.outerHTML = ""; NOTE 기억하세요! 항상 DOM을 tidy하게 유지해야 나중에 메모리 이슈를 덜어줍니다. 또다른 고려사항은 정리 후에 실제로 DOM에서 엘리먼트가 없어졌는지 봐야한다. (역시 IE에서만 예외적) 잘 동작하는 해결책으로는 IE에서 제공하는 outerHTML 을 활용하는 방법.
  • 27. 12.4 Text Contents • W3C-compliant 브라우저에서는 textContent 속성 사용 (자식, 자손 노드 모두 적용) • IE에서는 innerText, textContent 속성 사용
  • 28. // Listing 12.12 : http://jsbin.com/olanub/2 // <div id="test"><b>Hello</b>, I'm a ninja!</div> // <div id="test2"></div> window.onload = function(){ var b = document.getElementById("test"); var text = b.textContent || b.innerText; assert( text === "Hello, I'm a ninja!", "Examine the text contents of an element." ); assert( b.childNodes.length === 2, "An element and a text node exist." ); if ( typeof b.textContent !== "undefined" ) { b.textContent = "Some new text"; } else { b.innerText = "Some new text"; } text = b.textContent || b.innerText; assert( text === "Some new text", "Set a new text value." ); assert( b.childNodes.length === 1, "Only one text nodes exists now." ); }; Note : textContent/innerText 속성 사용 시 내부의 본래 엘리먼트 구조는 사라진다.
  • 29. 12.4 Text Contents • 여기서 발생하는 gotchas 1. 엘리먼트가 사라지는 경우 앞서 언급했던 메모리릭 발생 2. whitespace 크로스브라우징 처리 경우 최악
  • 30. Setting Text • 내부의 엘리먼트가 비워지고, 새로운 텍스트가 입력된다. • 내부의 컨텐츠가 비워진다. - Listing 12.10
  • 31. Setting Text // Listing 12.13 : http://jsbin.com/otosuv/2 // <div id="test"><b>Hello</b>, I'm a ninja!</div> // <div id="test2"></div> window.onload = function(){ var b = document.getElementById("test"); // Replace with your empty() method of choice while ( b.firstChild ) b.removeChild( b.firstChild ); // Inject the escaped text node b.appendChild( document.createTextNode( "Some new text" ) ); var text = b.textContent || b.innerText; assert( text === "Some new text", "Set a new text value." ); assert( b.childNodes.length === 1, "Only one text nodes exists now." ); }; HTML과 텍스트 입력의 차이점 : HTML 고유 문자가 escape 된다. 때문에 createTextNode 메소드를 사용이 필요하다.
  • 32. Getting Text • endline 관련 문제들 때문에 textContent/ innerText 사용은 하지 않는다. • 대신 텍스트 노드의 값들을 직접 읽어오 는 것이 정확한 값을 가져오겠다. 1 - (해당 문제를 딱히 신경 쓸 필요 없다면 그냥 사용하는게 간단)
  • 33. // Listing 12.14 : http://jsbin.com/olanof/2 // <div id="test"><b>Hello</b>, I'm a ninja!</div> // <div id="test2"></div> window.onload = function(){ function getText( elem ) { var text = ""; for ( var i = 0, l = elem.childNodes.length; i < l; i++ ) { var cur = elem.childNodes[i]; // A text node has a nodeType === 3 if ( cur.nodeType === 3 ) text += cur.nodeValue; // If it's an element we need to recurse further else if ( cur.nodeType === 1 ) text += getText( cur ); } return text; } var b = document.getElementById("test"); var text = getText( b ); assert( text === "Hello, I'm a ninja!", "Examine the text contents of an element." ); assert( b.childNodes.length === 2, "An element and a text node exist." ); };
  • 34. 12.5 Summary • DOM 조작에 있어 어려운점과 이를 해결 하는 방법을 포괄적으로 살펴 보았다. • 크로스브라우징 이슈를 설명하고 실제 로 구현하는게 훨씬 힘들다. • 잘 동작하는 통합 된 솔루션을 만들기 위 해 노력해야겠다.