[JavaScript] Node 객체


Node 객체


1. 소개

Node 객체는 DOM에서 시조와 같은 역할을 한다. 다시 말해서 모든 DOM 객체는 Node 객체를 상속 받는다. Node 객체의 위상을 그림으로 나타내면 아래와 같다. 

모든 엘리먼트, 텍스트, 주석등 HTML코드의 모든 문서의 구성요소들의 객체는 공통적으로 Node라는 객체를 상속받는다.

Node객체는 firstChild, lastChild, nextSibling등의 프로퍼티를 가지고 있어서 이를 이용해서 각각의 Node들을 기준으로해서 자식이 누구인지, 형제가 누구인지 알 수 있다.



2. 주요기능

Node 객체의 주요한 임무는 아래와 같다.


(1) 관계

엘리먼트는 서로 부모, 자식, 혹은 형제자매 관계로 연결되어 있다. 각각의 Node가 다른 Node와 연결된 정보를 보여주는 API를 통해서 문서를 프로그래밍적으로 탐색할 수 있다.

- Node.childNodes//유사배열

- Node.firstChild

- Node.lastChild

- Node.nextSibling

- Node.previousSibling

- Node.contains()

- Node.hasChildNodes()


(2) 노드의 종류

Node 객체는 모든 구성요소를 대표하는 객체이기 때문에 각각의 구성요소가 어떤 카테고리에 속하는 것인지를 알려주는 식별자를 제공한다. 

- Node.nodeType

- Node.nodeName


(3) 값

Node 객체의 값을 제공하는 API

- Node.nodeValue

- Node.textContent


(4) 자식관리

Node 객체의 자식을 추가하는 방법에 대한 API

- Node.appendChild()

- Node.removeChild()


3. Node 관계 API

Node 객체는 Node 간의 관계 정보를 담고 있는 일련의 API를 가지고 있다. 다음은 관계와 관련된 프로퍼티들이다.

- Node.childNodes: 자식노드들을 유사배열에 담아서 리턴한다.

- Node.firstChild: 첫번째 자식노드

- Node.lastChild: 마지막 자식노드

- Node.nextSibling: 다음 형제 노드

- Node.previousSibling: 이전 형제 노드


아래는 위의 API를 이용해서 문서를 탐색하는 모습을 보여준다.

<body id="start">

<ul>

    <li><a href="./532">html</a></li> 

    <li><a href="./533">css</a></li>

    <li><a href="./534">JavaScript</a>

        <ul>

            <li><a href="./535">JavaScript Core</a></li>

            <li><a href="./536">DOM</a></li>

            <li><a href="./537">BOM</a></li>

        </ul>

    </li>

</ul>

<script>

var s = document.getElementById('start');

console.log(1, s.firstChild); // #text(<body>태그와 <ul>태그 사이의 공백(Node타입은 text)

var ul = s.firstChild.nextSibling

console.log(2, ul); // ul

console.log(3, ul.nextSibling); // #text

console.log(4, ul.nextSibling.nextSibling); // script

console.log(5, ul.childNodes); //text, li, text, li, text, li, text

console.log(6, ul.childNodes[1]); // li(html)

console.log(7, ul.parentNode); // body

</script>

</body>




4. 노드 종류 API

노드 작업을 하게 되면 현재 선택된 노드가 어떤 타입인지를 판단해야 하는 경우가 있다. 이런 경우에 사용할 수 있는 API가 nodeType, nodeName이다. 

- Node.nodeType: node의 타입을 의미한다. 

- Node.nodeName: node의 이름 (태그명을 의미한다.)


(1) Node Type

노드의 종류에 따라서 정해진 상수가 존재한다. 아래는 모든 노드의 종류와 종류에 따른 값을 출력하는 예제다.

for(var name in Node){

   console.log(name, Node[name]);

}

실행결과)

ELEMENT_NODE 1 (엘리먼트 노드의 값은 1-->상수)

ATTRIBUTE_NODE 2 

TEXT_NODE 3 

CDATA_SECTION_NODE 4 

ENTITY_REFERENCE_NODE 5 

ENTITY_NODE 6 

PROCESSING_INSTRUCTION_NODE 7 

COMMENT_NODE 8 

DOCUMENT_NODE 9 

DOCUMENT_TYPE_NODE 10 

DOCUMENT_FRAGMENT_NODE 11 

NOTATION_NODE 12 

DOCUMENT_POSITION_DISCONNECTED 1 

DOCUMENT_POSITION_PRECEDING 2 

DOCUMENT_POSITION_FOLLOWING 4 

DOCUMENT_POSITION_CONTAINS 8 

DOCUMENT_POSITION_CONTAINED_BY 16 

DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC 32





숫자는 외우기 힘들다.. 그래서 대안은....


아래 예제는 노드 종류 API를 이용해서 노드를 처리하는 예제다. 어떤함수가 실행될 때 그 함수가 자기 자신을 호출하는 것을 재귀함수라고 하는데 본 예제는 재귀 함수의 예를 보여준다.

<!DOCTYPE html>

<html>

<body id="start">

<ul>

    <li><a href="./532">html</a></li> 

    <li><a href="./533">css</a></li>

    <li><a href="./534">JavaScript</a>

        <ul>

            <li><a href="./535">JavaScript Core</a></li>

            <li><a href="./536">DOM</a></li>

            <li><a href="./537">BOM</a></li>

        </ul>

    </li>

</ul>

<script>

function traverse(target, callback){

// 첫번째 인자: 우리가 조회하려고 하는 루트 엘리먼트 지정 

//두번째 인자: 각각의 엘리먼트를 조회할 때 마다 그 순번에 해당하는 엘리먼트를 사용자가 작성한 함수의 인자로 전달(함수가 전달된다.--> 콜백)

    if(target.nodeType === 1){

    //nodeType이 1(Element)인 경우만 로직 수행--> 텍스트 노드를 배제하기 위함

    //if(target.nodeName === 'A')//a태그만 출력하기 위함

        callback(target);

  // callback 함수가 실행 위함(매개변수는 document.getElementById('start'))

        var c = target.childNodes;

        for(var i=0; i<c.length; i++){

            traverse(c[i], callback); 

// 더이상 자식노드가 나오지 않을 때 까지 출력      

        }   

    }

}


traverse(document.getElementById('start'), function(elem){

    console.log(elem);

});

</script>

</body>

</html>


5. 노드 변경 API


(1) 노드 추가

노드의 추가와 관련된 API들은 아래와 같다.

- appendChild(child): 노드의 마지막 자식으로 주어진 엘리먼트 추가

- insertBefore(newElement, referenceElement): appendChild와 동작방법은 같으나 두번째 인자로 엘리먼트를 전달 했을 때 이것 앞에 엘리먼트가 추가된다.


노드를 추가하기 위해서는 추가할 엘리먼트를 생성해야 하는데 이것은 document 객체의 기능이다. 아래 API는 노드를 생성하는 API이다.

- document.createElement(tagname): 엘리먼트 노드를 추가한다.

- document.createTextNode(data): 텍스트 노드를 추가한다. 


<ul id="target">

    <li>HTML</li>

    <li>CSS</li>

</ul>

<input type="button" onclick="callAppendChild();" value="appendChild()" />

<input type="button" onclick="callInsertBefore();" value="insertBefore()" />

<script>

    function callAppendChild(){

        var target = document.getElementById('target');

        var li = document.createElement('li');//<li></li> 태그를 만든다.(text가 비어있음)

        var text = document.createTextNode('JavaScript');//text노드를 만든다.

        li.appendChild(text); // 위의 두개 코드를 결합한다.--> <li>JavaScript</li>

        target.appendChild(li); // <ul>태그 자식인 <li>태그의 맨 끝에 추가된다.

    }

 

    function callInsertBefore(){

        var target = document.getElementById('target');

        var li = document.createElement('li');

        var text = document.createTextNode('jQuery');

        li.appendChild(text);

        target.insertBefore(li, target.firstChild);//<ul>태그 자식인 <li>태그의 맨 앞에 추가된다.

    }

</script>

<li> 앞쪽에 추가된다.--> <ul>,<li>태그 사이의 text 노드 영역에 추가된다.


(2) 노드 제거

노드 제거를 위해서는 아래 API를 사용한다. 이 때 메소드는 삭제 대상의 부모 노드 객체의 것을 실행해야 한다는 점에 유의하자.


- removeChild(child)

<ul>

    <li>HTML</li>

    <li>CSS</li>

    <li id="target">JavaScript</li>

</ul>

<input type="button" onclick="callRemoveChild();" value="removeChild()" />

<script>

    function callRemoveChild(){

        var target = document.getElementById('target');

        target.parentNode.removeChild(target);// target의 부모Node로 갔다가 다시 그 부모의 자식인 target을 삭제--> DOM이  비판받는 이유

    }

</script>


(3) 노드 바꾸기

노드 바꾸기에는 아래 API가 사용된다.


- replaceChild(newChild, oldChild)

<ul>

    <li>HTML</li>

    <li>CSS</li>

    <li id="target">JavaScript</li>

</ul>

<input type="button" onclick="callReplaceChild();" value="replaceChild()" />

<script>

    function callReplaceChild(){

        var a = document.createElement('a');

        a.setAttribute('href', 'http://opentutorials.org/module/904/6701');

        a.appendChild(document.createTextNode('Web browser JavaScript'));

 

        var target = document.getElementById('target');

        target.replaceChild(a,target.firstChild);//a: 교체대상 태그, target.firstChild: 교체하려고 하는 내용

    }

</script>


6. 문자열로 노드 제어

노드 변경 API에서는 여러 메소드를 이용해서 노드를 제어하는 방법에 대해서 알아봤다. 그런데 이 방식은 복잡하고 장황하다. 좀 더 편리하게 노드를 조작하는 방법을 알아보자.


(1) innerHTML

innerHTML는 문자열로 자식 노드를 만들 수 있는 기능을 제공한다. 또한 자식 노드의 값을 읽어올 수도 있다. 

<ul id="target">

    <li>HTML</li>

    <li>CSS</li>

</ul>

<input type="button" onclick="get();" value="get" />

<input type="button" onclick="set();" value="set" />

<script>

    function get(){

        var target = document.getElementById('target');

        alert(target.innerHTML); // id가 target의 하위 HTML 태그의 text노드 값 까지 그대로 화면에 출력된다.

    }

    function set(){

        var target = document.getElementById('target');

        target.innerHTML = "<li>JavaScript Core</li><li>BOM</li><li>DOM</li>";// 기존의 노드가 새롭게 변경한 코드로 변경된다.--> id가 target의 하위태그를 문자열로 지정할 수 있다.

    }

</script>

                                                         

(2) outerHTML

outerHTML은 선택한 엘리먼트를 포함해서 처리된다.

<ul id="target">

    <li>HTML</li>

    <li>CSS</li>

</ul>

<input type="button" onclick="get();" value="get" />

<input type="button" onclick="set();" value="set" />

<script>

    function get(){

        var target = document.getElementById('target');

        alert(target.outerHTML);

    }

    function set(){

        var target = document.getElementById('target');

        target.outerHTML = "<ol><li>JavaScript Core</li><li>BOM</li><li>DOM</li></ol>";

    }

</script>

innerHTML: 자신을 제외한 하위 엘리먼트가 대상 

outerHTML: 자신을 포함한 하위 엘리먼트까지 대상


(3) innerText, outerText

innerHtml, outerHTML과 다르게 이 API들은 값을 읽을 때는 HTML 코드를 제외한 문자열을 리턴하고, 값을 변경할 때는 HTML의 코드를 그대로 추가한다.

<ul id="target">

    <li>HTML</li>

    <li>CSS</li>

</ul>

<input type="button" onclick="get();" value="get" />

<input type="button" onclick="set();" value="set" />

<script>

    function get(){

        var target = document.getElementById('target');

        alert(target.innerText);

    }

    function set(){

        var target = document.getElementById('target');

        target.innerText = "<li>JavaScript Core</li><li>BOM</li><li>DOM</li>";//태그 내용값(text)만 출력되는 것이 아니라 그 태그까지 출력된다.

    }

</script>


(4) insertAdjacentHTML()

좀 더 정교하게 문자열을 이용해서 노드를 변경하고 싶을 때 사용한다.

<ul id="target">

    <li>CSS</li>

</ul>

<input type="button" onclick="beforebegin();" value="beforebegin" />

<input type="button" onclick="afterbegin();" value="afterbegin" />

<input type="button" onclick="beforeend();" value="beforeend" />

<input type="button" onclick="afterend();" value="afterend" />

<script>

    function beforebegin(){

        var target = document.getElementById('target');

        target.insertAdjacentHTML('beforebegin','<h1>Client Side</h1>');

    }

    function afterbegin(){

        var target = document.getElementById('target');

        target.insertAdjacentHTML('afterbegin','<li>HTML</li>');

    }

    function beforeend(){

        var target = document.getElementById('target');

        target.insertAdjacentHTML('beforeend','<li>JavaScript</li>');

    }

    function afterend(){

        var target = document.getElementById('target');

        target.insertAdjacentHTML('afterend','<h1>Server Side</h1>');

    }

</script>



                               

'WebFont-end > JavaScript' 카테고리의 다른 글

[JavaScript] Text 객체  (0) 2014.12.23
[JavaScript] Document 객체  (0) 2014.12.23
[JavaScript] Element 객체  (0) 2014.12.22
[JavaScript] HTMLCollection 객체  (0) 2014.12.22
[JavaScript] HTMLElement 객체  (0) 2014.12.22

[JavaScript] Element 객체


Element 객체


1. 소개

Element 객체는 엘리먼트를 추상화한 객체다. HTMLElement 객체와의 관계를 이해하기 위해서는 DOM의 취지에 대한 이해가 선행되야 한다. 

DOM은 HTML만을 제어하기 위한 모델이 아니다. HTML이나 XML, SVG, XUL과 같이 마크업 형태의 언어를 제어하기 위한 규격이기 때문에 Element는 마크업 언어의 일반적인 규격에 대한 속성을 정의하고 있고, 각각의 구체적인 언어(HTML,XML,SVG)를 위한 기능은 HTMLElement, SVGElement, XULElement와 같은 객체를 통해서 추가해서 사용하고 있다.


2. 다른 객체들과의 관계

DOM의 계층구조에서 Element 객체의 위치는 아래와 같다.



※ Element와 HTMLElement를 구분하는 이유

DOM이라는 것이 HTML만 제어하는 것이 아니다.--> 마크업 언어(XML, HTML, SVG 등등)< > 를 제어하기 위해 표준이 DOM이다.(마크업 언어는 Element가 있다.)

(엘리먼트가 HTML에만 있는 것이 아니다.)


3. 주요기능

(1)  식별자

문서내에서 특정한 엘리먼트를 식별하기 위한 용도로 사용되는 API

- Element.classList

- Element.className

- Element.id

- Element.tagName


(2) 조회

엘리먼트의 하위 엘리먼트를 조회하는 API

- Element.getElementsByClassName

- Element.getElementsByTagName

- Element.querySelector

- Element.querySelectorAll


(3) 속성

엘리먼트의 속성을 알아내고 변경하는 API

- Element.getAttribute(name)

- Element.setAttribute(name, value)

- Element.hasAttribute(name);

- Element.removeAttribute(name);


4. 식별자 API

엘리먼트를 제어하기 위해서는 그 엘리먼트를 조회하기 위한 식별자가 필요하다. 

HTML에서 엘리먼트의 이름과 id 그리고 class는 식별자로 사용된다. 식별자 API는 이 식별자를 가져오고 변경하는 역할을 한다.


(1) Element.tagName

해당 엘리먼트의 태그 이름을 알아낸다. 태그 이름을 변경하지는 못한다.

<ul>

    <li>html</li>

    <li>css</li>

    <li id="active" class="important current">JavaScript</li>

</ul>

<script>

console.log(document.getElementById('active').tagName)//읽기전용

</script>


(2) Element.id

문서에서 id는 단 하나만 등장할 수 있는 식별자다. 아래 예제는 id의 값을 읽고 변경하는 방법을 보여준다. 

<ul>

    <li>html</li>

    <li>css</li>

    <li id="active">JavaScript</li>

</ul>

<script>

var active = document.getElementById('active');

console.log(active.id);

active.id = 'deactive';// id 값을 deactive로 변경

console.log(active.id);

</script>


(3) Element.className

클래스는 여러개의 엘리먼트를 그룹핑할 때 사용한다.

<ul>

    <li>html</li>

    <li>css</li>

    <li id="active">JavaScript</li>

</ul>

<script>

var active = document.getElementById('active');

// class 값을 변경할 때는 프로퍼티의 이름으로 className을 사용한다.

active.className = "important current";

console.log(active.className);

// 클래스를 추가할 때는 아래와 같이 문자열의 더한다.

active.className += " readed"


(4) Element.classList

className에 비해서 훨씬 편리한 사용성을 제공한다.

<ul>

    <li>html</li>

    <li>css</li>

    <li id="active" class="important current">JavaScript</li>

</ul>

<script>

function loop(){

    //var active = document.getElementById('active');

    for(var i=0; i<active.classList.length; i++){

        console.log(i, active.classList[i]);

    }

}

// 클래스를 추가

</script>

<input type="button" value="DOMTokenList" onclick="console.log(active.classList);" />

<input type="button" value="조회" onclick="loop();" />

<input type="button" value="추가" onclick="active.classList.add('marked');" />

<input type="button" value="제거" onclick="active.classList.remove('important');" />

<input type="button" value="토글" onclick="active.classList.toggle('current');" />


DOMTokenList: class= a,b,c라면 각각의 a,b,c를 담아놓은 객체가 바로 DOMTokenList이다. -->유사배열



5. 조회 API

조회 API는 엘리먼트를 조회하는 기능이다. 조회 방법에 대해서는 이미 여러차례 살펴봤기 때문에 이번 시간에 알아볼 내용은 조회 대상을 제한하는 방법에 대한 것이다. 

지금까지 document.getElementsBy* 메소드를 통해서 엘리먼트를 조회했다. document 객체는 문서 전체를 의미하는 엘리먼트이기 때문에 document의 조회 메소드는 문서 전체를 대상으로 엘리먼트를 조회한다. Element 객체 역시도 getElementsBy* 엘리먼트를 가지고 있는데 Element 객체의 조회 메소드는 해당 엘리먼트의 하위 엘리먼트를 대상으로 조회를 수행한다. 

<ul>

    <li class="marked">html</li>

    <li>css</li>

    <li id="active">JavaScript

        <ul>

            <li>JavaScript Core</li>

            <li class="marked">DOM</li>

            <li class="marked">BOM</li>

        </ul>

    </li>

</ul>

<script>

    var list = document.getElementsByClassName('marked');

    console.group('document');

    for(var i=0; i<list.length; i++){

        console.log(list[i].textContent);

    }

    console.groupEnd();

     

    console.group('active');

    var active = document.getElementById('active');     

    var list = active.getElementsByClassName('marked');

    for(var i=0; i<list.length; i++){

        console.log(list[i].textContent);

    }

    console.groupEnd();

</script>

실행결과)


6. 속성 API

속성은 HTML에서 태그명만으로는 부족한 부가적인 정보라고 할 수 있다. 이 속성을 어떻게 제어하는가 알아보자. 


속성을 제어하는 API는 아래와 같다. 각각의 기능은 이름을 통해서 충분히 유추할 수 있을 것이다.

- Element.getAttribute(name)

- Element.setAttribute(name, value)

- Element.hasAttribute(name);

- Element.removeAttribute(name);


<a id="target" href="http://opentutorials.org">opentutorials</a>

<script>

var t = document.getElementById('target');

console.log(t.getAttribute('href')); //http://opentutorials.org

t.setAttribute('title', 'opentutorials.org'); // title 속성의 값을 설정한다.

console.log(t.hasAttribute('title')); // true, title 속성의 존재여부를 확인한다.

t.removeAttribute('title'); // title 속성을 제거한다.

console.log(t.hasAttribute('title')); // false, title 속성의 존재여부를 확인한다.--> true, false 리턴

</script>


(2) 속성과 프로퍼티

모든 엘리먼트의 (HTML)속성은 (JavaScript 객체의) 속성과 프로퍼티로 제어가 가능하다. 예제를 보자.

<p id="target">

    Hello world

</p>

<script>

    var target = document.getElementById('target');


    // attribute 방식

    target.setAttribute('class', 'important');

    // property 방식

    target.className = 'important';

</script>

setAttribute('class', 'important')와 className = 'important'는 같은 결과를 만든다. 하지만 전자는 attribute 방식(속성이라고 부르겠다)이고 후자는 property 방식이다.

property 방식은 좀 더 간편하고 속도도 빠르지만 실제 html 속성의 이름과 다른 이름을 갖는 경우가 있다. 그것은 자바스크립트의 이름 규칙 때문이다.

심지어 속성과 프로퍼티는 값이 다를수도 있다. 아래 코드를 실행한 결과는 속성과 프로퍼티의 값이 꼭 같은 것은 아니라는 것을 보여준다.

<a id="target" href="./demo1.html">ot</a>

<script>

//현재 웹페이지가 http://localhost/webjs/Element/attribute_api/demo3.html 일 때 

var target = document.getElementById('target');

// http://localhost/webjs/Element/attribute_api/demo1.html 

console.log('target.href', target.href);

// ./demo1.html 

console.log('target.getAttribute("href")', target.getAttribute("href"));

</script>


'WebFont-end > JavaScript' 카테고리의 다른 글

[JavaScript] Document 객체  (0) 2014.12.23
[JavaScript] Node 객체  (1) 2014.12.23
[JavaScript] HTMLCollection 객체  (0) 2014.12.22
[JavaScript] HTMLElement 객체  (0) 2014.12.22
[JavsScript] 제어 대상을 찾기  (0) 2014.12.21

[JavaScript] HTMLCollection 객체


HTMLCollection 객체


HTMLCollection은 리턴 결과가 복수인 경우에 사용하게 되는 객체다. 유사배열로 배열과 비슷한 사용방법을 가지고 있지만 배열은 아니다. 

HTMLCollection의 목록은 실시간으로 변경된다. 아래 코드를 보자.

<!DOCTYPE html>

<html>

<body>

<ul>

    <li>HTML</li>

    <li>CSS</li>

    <li id="active">JavaScript</li>

</ul>

<script>

console.group('before');

var lis = document.getElementsByTagName('li');

for(var i = 0; i < lis.length; i++){

    console.log(lis[i]);

}

console.groupEnd();

console.group('after');

lis[1].parentNode.removeChild(lis[1]);

for(var i = 0; i < lis.length; i++){

    console.log(lis[i]);

}

console.groupEnd();

</script>

</body>

</html>

실행결과)


'WebFont-end > JavaScript' 카테고리의 다른 글

[JavaScript] Node 객체  (1) 2014.12.23
[JavaScript] Element 객체  (0) 2014.12.22
[JavaScript] HTMLElement 객체  (0) 2014.12.22
[JavsScript] 제어 대상을 찾기  (0) 2014.12.21
[JavaScript] 전역객체 window  (0) 2014.12.21

[JavaScript] HTMLElement 객체


HTMLElement 객체


1. HTMLElement

getElement* 메소드를 통해서 원하는 객체를 조회했다면 이 객체들을 대상으로 구체적인 작업을 처리해야 한다. 

이를 위해서는 획득한 객체가 무엇인지 알아야 한다. 그래야 적절한 메소드나 프로퍼티를 사용할 수 있다.

아래 코드는 getElement*의 리턴 값을 보여준다. 

<ul>

    <li>HTML</li>

    <li>CSS</li>

    <li id="active">JavaScript</li>

</ul>

<script>

    var li = document.getElementById('active');

    console.log(li.constructor.name);// 리턴된 li 라는 객체의 생성자함수 이름을 알 수 있다.---> HTMLLIElement----->단수

    var lis = document.getElementsByTagName('li');

    console.log(lis.constructor.name);// HTMLCollection(유사배열:여러개의 엘리먼트 담고 있다.)----->복수

</script>

실행결과)

HTMLLIElement (li 엘리먼트 태그)

HTMLCollection (여러개의 엘리먼트를 담고 있다.)

이것을 통해서 알 수 있는 것은 아래와 같다.

- document.getElementById : 리턴 데이터 타입은 HTMLLIELement

- document.getElementsByTagName : 리턴 데이터 타입은 HTMLCollection

즉 실행결과가 하나인 경우 HTMLLIELement, 복수인 경우 HTMLCollection을 리턴하고 있다. 


2. HTMLElement

실행결과가 하나인 엘리먼트들을 좀 더 살펴보자.

<a id="anchor" href="http://opentutorials.org">opentutorials</a>

<ul>

    <li>HTML</li>

    <li>CSS</li>

    <li id="list">JavaScript</li>

</ul>

<input type="button" id="button" value="button" />

<script>

    var target = document.getElementById('list');

    console.log(target.constructor.name);

 

    var target = document.getElementById('anchor');

    console.log(target.constructor.name);

 

    var target = document.getElementById('button');

    console.log(target.constructor.name);

</script>

실행결과)

HTMLLIElement

HTMLAnchorElement

HTMLInputElement

이를 통해서 알 수 있는 것은 엘리먼트의 종류에 따라서 리턴되는 객체가 조금씩 다르다는 것이다. 각각의 객체의 차이점을 알아보자. 링크는 DOM의 스팩이다.

- HTMLLIElement

- HTMLAnchroElement

- HTMLInputElement


(1) HTMLLIElement

interface HTMLLIElement : HTMLElement {

           attribute DOMString       type;

           attribute long                 value;

};


(2) HTMLAnchroElement

interface HTMLAnchorElement : HTMLElement {

           attribute DOMString       accessKey;

           attribute DOMString       charset;

           attribute DOMString       coords;

           attribute DOMString       href;

           attribute DOMString       hreflang;

           attribute DOMString       name;

           attribute DOMString       rel;

           attribute DOMString       rev;

           attribute DOMString       shape;

           attribute long                 tabIndex;

           attribute DOMString       target;

           attribute DOMString       type;

                         void              blur();

                         void              focus();

};

엘리먼트 객체에 따라서 프로퍼티가 다르다는 것을 알 수 있다. 하지만 모든 엘리먼트들은 HTMLElement를 상속 받고 있다. 


interface HTMLLIElement : HTMLElement {

interface HTMLAnchorElement : HTMLElement {



(3) HTMLElement

interface HTMLElement : Element {

           attribute DOMString       id;

           attribute DOMString       title;

           attribute DOMString       lang;

           attribute DOMString       dir;

           attribute DOMString       className;

};


3. DOM Tree

모든 엘리먼트는 HTMLElement의 자식이다. 따라서 HTMLElement의 프로퍼티를 똑같이 가지고 있다. 동시에 엘리먼트의 성격에 따라서 자신만의 프로퍼티를 가지고 있는데 이것은 엘리먼트의 성격에 따라서 달라진다. HTMLElement는 Element의 자식이고 Element는 Node의 자식이다. Node는 Object의 자식이다. 이러한 관계를 DOM Tree라고 한다.

이 관계를 그림으로 나타내면 아래와 같다. 


'WebFont-end > JavaScript' 카테고리의 다른 글

[JavaScript] Element 객체  (0) 2014.12.22
[JavaScript] HTMLCollection 객체  (0) 2014.12.22
[JavsScript] 제어 대상을 찾기  (0) 2014.12.21
[JavaScript] 전역객체 window  (0) 2014.12.21
[JavaScript] Object Model  (0) 2014.12.21

불위야 비불능야


맹자께서 말씀하시기를, 불위야 비불능야(不爲也 比不能也) 라고 하셨다. 



'하지 않는 것이지, 하지 못하는 것이 아니다.'라는 의미이다.



세상 이치가 다 그런 것... 포기하고 체념하면 아무것도 이룰 수 없고, 하고자 작정하고 덤비면 뜻을 이룰 수 있다.



'낙서장' 카테고리의 다른 글

Nocturne  (0) 2014.12.29
故 신해철_ 저 당당함  (0) 2014.12.09
블로그 개설  (0) 2014.11.25

[Spring] XML 스키마 기반의 POJO 클래스를 이용한 AOP 구현

XML 스키마 기반의 POJO 클래스를 이용한 AOP 구현


1. XML 스키마 기반 AOP 


XML 스키마를 이용해서 AOP를 구현하는 과정은 다음과 같다.

1 관련 .jar파일을 클래스패스에 추가한다.

2 공통 기능을 제공하는 Advice 클래스를 구현한다.

3 XML 설정 파일에서 <aop:config>를 이용해서 Aspect를 설정한다. Advice를 이떤 Pointcut에 적용할지를 지정하게 된다.


(1) 공통 기능을 제공한 Advice 클래스를 작성

ProfilingAdvice.java

import org.aspectj.lang.ProceedingJoinPoint; 

public class ProfilingAdvice {

// ProfilingAdvice 클래스는 Around Advice를 구현한 클래스이다.

public Object trace(ProceedingJoinPoint joinPoint) throws Throwable {

// trace() 메서드는 ProceedingJoinPoint 타입의 joinpoint 파라미터를 전달받는데, 이 파라미터를 이용해서 Around Advice를 구현할 수 있게 된다.

String signatureString = joinPoint.getSignature().toShortString();

System.out.println(signatureString + " 시작");

long start = System.currentTimeMillis();

try { // Advice가 적용될 대상 객체를 호출 하기 전의 시간을 구해서 대상 객체를 메서드 호출 실행 시간을 출력

Object result = joinPoint.proceed();

return result;

} finally {

long finish = System.currentTimeMillis();

System.out.println(signatureString + " 종료");

// Advice가 적용될 대상 객체를 호출한 후에 시간을 구해서 대상 객체를 메서드 호출 실행 시간을 출력

System.out.println(signatureString + " 실행 시간 : " + (finish - start) + "ms");

}

}

}

ProfilingAdvice 클래스는 Around Advice를 구현한 클래스이며, trace() 메서드는 Advice가 적용될 대상 객체를 호출하기 전과 후에 시간을 구해서 대상 객체의 메서드 호출 실행 시간을 출력한다.


(2) XML 파일에 Advice를 빈으로 등록하고 Aspect를 설정

acQuickStart.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:aop="http://www.springframework.org/schema/aop" 

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

            http://www.springframework.org/schema/aop

            http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

--> XML 스키마를 이용해서 AOP를 구현하려면 aop 네임스페이스를 추가해주어야 한다.


<!-- Advice 클래스를 빈으로 등록 -->

<bean id="performanceTraceAdvice" class="madvirus.spring.chap05.aop.pojo.ProfilingAdvice" />

<!-- Aspect 설정: Advice를 어떤 Pointcut에 적용할 지 설정 -->

<aop:config>

<aop:aspect id="traceAspect" ref="performanceTraceAdvice">

<aop:pointcut id="publicMethodexpression="execution(public * madvirus.spring.chap05..*(..))" />

<!-- excution 명시자는 Advice를 적용할 패키지, 클래스 그리고 메서드를 표현할 때 사용된다. -->

<aop:around pointcut-ref="publicMethod" method="trace" />

</aop:aspect>

</aop:config>

<!-- <aop:config>, <aop:aspect>, <aop:pointcut>, <aop:around> 태그를 이용해서 AOP 설정을 할 수 있다.(aop 네임스페이스를 추가) 

madvirus.spring.chap05 패키지 및 그 하위 패키지에 있는 모든 public 메서드를 Pointcut으로 설정하고, 이들 Pointcut에 Around Advice로 performTraceAdvice 빈 객체의 trace() 메서드를 적용한다. 따라서 writeArticeService 빈의 public 메서드나 articleDao 의 public 메서드가 호출되면 ProfilingAdvice 클래스의 trace() 메서드가 Around Advice로 적용된다.--> 


<bean id="writeArticleService" class="madvirus.spring.chap05.board.service.WriteArticleServiceImpl">

<constructor-arg>

<ref bean="articleDao" />

</constructor-arg>

</bean>


<bean id="articleDao" class="madvirus.spring.chap05.board.dao.MySQLArticleDao" />


<bean id="memberService" class="madvirus.spring.chap05.member.service.MemberServiceImpl" />

</beans>

Advice 클래스를 적용하려면 일단 XML 설정 파일에 Advice 클래스를 빈으로 등록해주어야 한다.


(3) Advice가 Pointcut으로 지정한 메서드에 적용되는 지의 여부를 확인하기 위한 클래스

import madvirus.spring.chap05.board.Article;

import madvirus.spring.chap05.board.service.WriteArticleService;

import madvirus.spring.chap05.member.Member;

import madvirus.spring.chap05.member.service.MemberService;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;


public class MainQuickStart {


public static void main(String[] args) {

String[] configLocations = new String[] { "acQuickStart.xml" };

ApplicationContext context = new ClassPathXmlApplicationContext(configLocations);


WriteArticleService articleService = (WriteArticleService) context.getBean("writeArticleService");

articleService.write(new Article());


MemberService memberService = context.getBean("memberService", MemberService.class);

memberService.regist(new Member());

}

}

MainQuickStart 클래스는 acQuickStart.xml 파일을 이용해서 ApplicationContext를 생성한 뒤 writeArticleService 빈의 write() 메서드를 호출하고, memberService 빈의 regist() 메서드를 호출하고 있다.

앞서 acQuickStart.xml 설정 파일을 설명할 때 madvirus.spring.chap05 패키지 및 그 하위 패키지에 있는 빈 객체의 public 메서드를 호출하면 ProfilingAdvice 클래스의 trace() 메서드가 Around Advice로 사용된다고 했는데 실제로 실행해보면 다음과 같은 결과가 출력된다.


WriteArticleService.write() 시작

WriteArticleServiceImpl.write() 메서드 실행

ArticleDao.insert() 시작

MySQLArticleDao.insert() 실행

ArticleDao.insert() 종료

ArticleDao.insert(..) 실행시간:0ms

WriteArticleService.write(..): 종료

WriteArticleService.write(..): 실행 시간:0ms

MemberService.regist(..): 시작

MemberServiceImpl.regist(): 메서드 실행

MemberService.regist(..): 종료

MemberService.regist(..): 실행 시간:0ms


위 코드에서 굵게 표시한 부분이 ProfilingAdvice 클래스의 trace() 메서드에서 출력한 내용이다. 실행 결과를 보면 실제 빈 객체의 메서드가 호출되기 전/후로 trace() 메서드에서 실행한 내용이 출력되는 것을 확인할 수 있다. 위 실행결과를 통해서 눈 여겨 볼 부분은 WriteArticleServiceImpl,MySQLServiceImpl 등 클래스를 변경하지 않고(즉, 핵심 로직 코드의 변경 없이) 공통 기능을(즉, 메서드 실행 시간을 출력해주는 기능을) 추가했다는 점이다. 이는 설정 파일만 변경하면 기존 코드의 변경 없이 공통 기능을 추가하거나 변경할 수 있는 AOP의 장점을 잘 보여주고 있다.


2. XML 스키마를 이용한 AOP 설정


AOP를 설정하기 위한 aop 네임스페이스 및 aop 네임스페이스와 관련된 XML 스키마가 추가되었다. aop 네임스페이스와 관련된 XML 스키마는 다음과 같이 <beans> 태그에 명시할 수 있다.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:aop="http://www.springframework.org/schema/aop" 

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

            http://www.springframework.org/schema/aop

            http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

...

<beans>


aop 네임스페이스와 관련된 XML 스키마를 지정한 뒤에, 다음과 같이 <aop:config> 태그를 사용하여 AOP 관련 정보를 설정할 수 있다.

<bean id="performanceTraceAdvice" class="madvirus.spring.chap05.aop.pojo.ProfilingAdvice" />


<aop:config>

<aop:aspect id="traceAspect1" ref="performanceTraceAdvice">

<aop:pointcut id="publicMethod"

expression="execution(public * madvirus.spring.chap05.board..*(..))" />

<aop:around pointcut-ref="publicMethod" method="trace" />

</aop:aspect>


<bean id="writeArticleService" class="madvirus.spring.chap05.board.service.WriteArticleServiceImpl"/>

위 코드에서 <aop:*>에 해당하는 태그는 다음과 같은 정보를 설정한다.


<aop:config>: AOP 설정 정보임을 나타낸다.

<aop:aspect>: Aspect를 설정한다.

<aop:pointcut>: Pointcut을 설정한다.

<aop:around>: Around Advice를 설정한다. 이외에도 다양한 Advice를 설정할 수 있다.


<aop:aspect> 태그의 ref 속성은 Aspect로서 기능을 제공할 빈을 설정할 때 사용된다. 위 코드의 경우 "performanceTraceAdvice" 빈이 Aspect 기능을 제공한다고 명시하고 있다.

위 코드를 보면 "traceAspect"가 MemberServiceImpl 클래스가 구현한 모든 인터페이스의 public 메서드에 Around Advice로 적용되며, 이때 Aspect의 구현 클래스인 ProfilingAdvice의 trace() 메서드가 

호출된다는 것을 쉽게 유추할 수 있다. XML 스키마 기반의 AOP 설정은 이렇게 설정 파일만 보더라도 어렵지 않게 어떤 코드에 어떤 Aspect가 어떤 타입의 Advice로 적용되는지를 파악할 수 있다.


cp.) Aspect 설정

Aspect 설정에서 <aop:aspect> 태그는 한 개의 Aspect를 설정한다. <aop:aspect> 태그의 ref: 속성에는 공통 기능을 구현하고 있는 빈을 전달한다.

<aop:config>

<aop:aspect id="traceAspect1" ref="performanceTraceAdvice">

<aop:pointcut id="publicMethod" expression="execution(public * madvirus.spring.chap05.board..*(..))" />

<aop:around pointcut-ref="publicMethod" method="trace" />

</aop:aspect>

<aop:aspect id="traceAspect2" ref="performanceTraceAdvice">

<aop:around pointcut="execution(public * madvirus.spring.chap05.member..*(..))" method="trace" />

</aop:aspect>

</aop:config>

Advice를 적용할 Pointcut은 <aop:pointcut> 태그를 이용하여 설정한다. <aop:pointcut> 태그의 id 속성은 Pointcut을 정의하는 AspectJ의 표현식을 입력 받는다.

Advice를 표현하는 태그에는 <aop:around>를 비롯하여 각 타입의 Advice를 정의하기 위해 다양한 태그를 제공하고 있다.


 태 그

 설 명

 <aop:before>

 메서드 실행 전에 적용되는 Advice를 정의한다.

 <aop:returning>

 메서드가 정상적으로 실행된 후에 적용되는 Advice를 정의한다.

 <aop:after-throwing>

 메서드가 예외를 발생시킬 때 적용되는 Advice를 정의한다.

 try-catch 블록에서 catch와 유사하다.

 <aop:after>

 메서드가 정상적으로 실행되는지 또는 예외를 발생시키는지 여부에 상관  없이 적용되는 Advice를 정의한다. try-catch-finally에서 finally 불록과 유사하다.

 <aop:around>

 메서드 호출 이전, 이후, 예외 발생 등 모든 시점에 적용 가능한 Advice를 정의한다.


각 태그는 pointcut 속성 또는 pointcut-ref 속성을 사용하여 Advice가 적용될 Pointcut을 지정한다. pointcut-ref 속성은 <aop:config> 태그를 이용하여 설정한 Pointcut을 참조할 때 사용되며, pointcut 속성은 직접 AspectJ 표현식을 이용하여 Pointcut을 지정할 때에 사용된다. 

Advice의 각 태그는 Pointcut에 포함되는 대상 객체의 메서드가 호출될 때, <aop:aspect> 태그의 ref 속성으로 지정한 빈 객체에서 어떤 메서드를 실행할 지를 지정한다. 예를 들어, 위 코드의 경우 madvirus.spring.chap05.member 패키지 및 그 하위 패키지의 public 메서드가 호출될 때 performTraceAdvice 빈의 trace 메서드가 호출되도록 설정하고 있다.


3. Advice 타입 별 클래스 작성


(1) Before Advice

Before Advice를 사용하려면 <aop:before> 태그를 이용하여 Before Advice를 설정하면 된다.

<aop:config>

...

<aop:aspect id ="loggingAspect" ref="logging">

<aop:before pointcut-ref="publicMethod" method="before" />

</aop:aspect>

</aop:config>


Aspect로 사용될 빈 클래스는 다음과 같이 <aop:before> 태그의 method 속성에 명시한 메서드를 구현해주면 된다.

public void before(){

//대상 객체의 메서드 실행 이전에 적용할 기능 구현

...

}


대상 객체 및 호출되는 메서드에 대한 정보나 전달되는 파라미터에 대한 정보가 필요한 경우 org.aspectj.lang.JoinPoint 타입의 파라미터를 메서드에 전달한다.

public void before(JoinPoint joinPoint){

// 대상 객체의 메서드 실행 이전에 적용할 기능 구현

...

}


Before Advice를 구현한 메서드는 일반적으로 리턴 타입이 void인데, 그 이유는 리턴 값을 갖더라도 실제 Advice의 적용 과정에 아무런 영향이 없기 때문이다.

Before Advice를 위한 메서드 구현 시 주의해야 할 점은 메서드에서 예외를 발생시킬 경우 대상 객체의 메서드가 호출되지 않게 된다는 점이다.

cp.) Before Advice에서 예외를 발생시키면 대상 객체의 메서드가 호출되지 않기 때문에, 메서드를 실행 하기 전에 접근 권한을 검사해서 접근 권한이 없을 경우 예외를 발생시키는 기능을 구현하는 데 Before Advice가 적합하다.


(2) After Returning Advice
After Returning Advice는 대상 객체의 메서드가 정상적으로 실행된 후에 공통 기능을 적용하고 싶을 때 사용되는 Advice로서, 다음과 같이 <aop:after-returning> 태그를 이용하여 After Returning Advice를 설정한다.
<aop:config>
...
<aop:aspect id ="loggingAspect" ref="logging">
<aop:after-returning pointcut-ref="publicMethod" method="afterReturning" />
</aop:aspect>
</aop:config>

Aspect로 사용될 빈 클래스는 아래 코드처럼 <aop:after-returning> 태그에 명시한 메서드를 구현한다.
public void afterReturning(){
// 대상 객체의 메서드가 정상적으로 실행된 이후에 적용할 기능 구현
...
}

Advice를 구현한 메서드에서 리턴 값을 사용하고 싶다면 returning 속성을 사용하여 리턴 값을 전달 받을 파라미터의 이름을 명시해 주면 된다.
<aop:after-returning pointcut-ref="publicMethod" method="afterReturning" returning="ret"/>

Advice를 구현한 메서드는 returning 속성에 명시한 이름을 갖는 파라미터를 이용해서 리턴 값을 전달 받게 된다.
public void afterReturning(Object ret){
// 대상 객체의 메서드가 정상적으로 실행된 이후에 적용할 기능 구현
...
}

만약 리턴 된 객체가 특정 타입인 경우에 한해서 메서드를 실행하고 싶다면 다음과 같이 한정하고 싶은 타입의 파라미터를 사용하면 된다.
pulbic void afterReturning(Article ret){
// 대상 객체의 메서드가 정상적으로 실행된 이후에 적용할 기능 구현
...
}

대상 객체 및 호출되는 매서드에 대한 정보나 전달되는 파라미터에 대한 정보가 필요한 경우 다음과 같이 org.aspectj.lang.JoinPoint를 파라미터로 추가한다.
pulbic void afterReturning(Article ret, Object ret){
// 대상 객체의 메서드가 정상적으로 실행된 이후에 적용할 기능 구현
...
}

cp.) 스프링 AOP <aop:after-returning> 태그의 ret 속성에 명시한 파라미터 이름과 실제 Advice 구현 메서드의 파라미터 이름이 일치하는 지 확인하기 위해 클래스의 디버그 정보를 사용한다.
만약 디버그 모드로 컴파일하지 않았다면 Advice 구현 메서드의 파라미터 개수가 한 개인 경우 해당 파라미터 ret 속성에 명시한 파라미터라고 가정한다.

(3) After Throwing Advice

After Throwing Advice는 대상 객체의 메서드가 예외를 발생시킨 경우에 적용되는 Advice로서 다음과 같이 <aop:config-throwing> 태그를 이용하여 설정한다.

<aop:config>

...

<aop:aspect id ="loggingAspect" ref="logging">

<aop:after-throwing pointcut-ref="publicMethod" method="afterTrowing" />

</aop:aspect>

</aop:config>


Advice 구현 클래스는 다음과 같이 <aop:after-returning> 태그에 명시한 메서드를 구현한다.

public void afterThrowing(){

// 대상 객체의 메서드가 예외를 발생시킨 경우에 적용할 기능 구현

...

}


대상 객체의 메서드가 발생시킨 예외 객체가 필요한 경우 throwing 속성에 예외 객체를 전달받을 파라미터의 이름을 명시하면 된다.

<aop:after-throwing pointcut-ref="publicMethod" method="afterTrowing" throwing="ex"/>


Advice 구현 메서드에서 발생된 예외를 사용하려면 <aop:after-throwing> 태그의 throwing 속성에 명시한 이름을 갖는 파라미터를 추가하면 된다.

public void afterThrowing(Trowable ex){

// 대상 객체의 메서드가 예외를 발생시킨 경우에 적용할 기능 구현

...

}


만약 특정 타입의 예외에 대해서만 처리하고 싶다면, Throwable이나 Exception이 아니라 처리하고 싶은 예외 타입을 파라미터로 지정하면 된다. 예를 들어, 아래 코드는 발생된 예외가 ArticleNotFoundException인 경우에만 호출된다.

public void afterThrowing(AtricleNotFoundException ex){

// 대상 객체의 메서드가 예외를 발생시킨 경우에 적용할 기능 구현

...

}


대상 객체 및 호출되는 메서드에 대한 정보나 전달되는 파라미터에 대한 정보가 필요한 경우 다음과 같이 org.aspectj.lang.JoinPoint를 파라미터로 추가한다.

public void afterThrowing(JoinPoint joinPoint, Exception ex){

// 대상 객체의 메서드가 예외를 발생시킨 경우에 적용할 기능 구현

...

}


(4) After Advice

After Advice는 대상 객체의 메서드가 정상적으로 실행되었는지 아니면 예외를 발생시켰는지의 여부에 상관없이 메서드 실행이 종료된 이후에 적용되는 Advice로서 try-catch-fanally

블록에서 finally 블록에서 finally 블록과 비슷한 기능을 수행한다. 다음과 같이 <aop:after> 태그를 이용하여 After Advice를 설정한다.


<aop:config>

...

<aop:aspect id ="loggingAspect" ref="logging">

<aop:after pointcut-ref="publicMethod" method="afterFinally" />

</aop:aspect>

</aop:config>


Aspect로 사용될 빈 클래스는 다음과 같이 <aop:after> 태그에 명시한 메서드를 구현해주면 된다. 이 때 메서드는 파라미터를 갖지 않는다.

public void afterFinally(){

...

}


대상 객체 및 호출되는 메서드에 대한 정보나 전달되는 파라미터에 대한 정보가 필요한 경우 다음과 같이  org.aspectj.lang.JoinPoint를 파라미터로 명시하면 된다.

public void afterFinally(JoinPoint joinPoint){

...

}


(5) Around Advice

Around Advice는 앞서 살펴 본 Before, After Returning, After Throwing, After Advice를 모두 구현할 수있는 Advice로서, 다음과 같이 <aop:around> 태그를 이용하여 Around Advice를 설정한다.

<bean id ="cache" class="madvirus.spring.chap05.aop.pojo.ArticleCacheAdvice"/>

<aop:config>

...

<aop:aspect id ="cacheAdpect" ref="cache">

<aop:around method="cache" pointcut="execution(public * *..ReadArticleService.*(..))" />

</aop:aspect>

</aop:config>


Around Advice를 구현한 메서드는 org.aspectj.lang.ProceedingJoinPoint를 반드시 첫 번째 파라미터로 지정해야 한다. 그렇지 않을 경우 스프링은 예외를 발생시킨다.

다음 코드는 Around Advice의 구현 예를 보여주고 있다.

package madvirus.spring.chap05.aop.pojo;

import java.util.HashMap;

import java.util.Map;

import madvirus.spring.chap05.board.Article;

import org.aspectj.lang.ProceedingJoinPoint;


public class ArticleCacheAdvice {

private Map<Integer, Article> cache = new HashMap<Integer, Article>();

// 첫 번째 파라미터로 ProceedingJoinPoint를 전달받고 있다. ProceedingJoinPoint의 procced() 메서드를 호출하면 프록시 대상 객체의 실제 메서드를 호출하게 된다.

// 따라서 ProceedingJoinPoint.proceed() 메서드를 호출하기 전과 후에 필요한 작업을 수행할 수 있다.

public Article cache(ProceedingJoinPoint joinPoint) throws Throwable {

Integer id = (Integer) joinPoint.getArgs()[0];

Article article = cache.get(id);

if (article != null) {

System.out.println("[ACA] 캐시에서 Article[" + id + "] 구함");

return article;

}

Article ret = (Article) joinPoint.proceed();

if (ret != null) {

cache.put(id, ret);

System.out.println("[ACA] 캐시에 Article[" + id + "] 추가함");

}

return ret;

}

}

위 코드는 대상 객체의 메서드 호출 전후에 캐시 기능을 삽입하였다. proceed() 메서드를 호출하기 전에 Map에 ID에 해당하는 Article 객체가 존재하는지 검사한다.


[Spring] AOP 개요


AOP 개요


Aspect Oriented Programming, 줄여서 AOP는 문제를 바라보는 관점을 기준으로 프로그래밍하는 기법을 말한다. AOP는 문제를 해결하기 위한 핵심 관심 사항과 전제에 적용되는 공통 관심 사항을 기준으로 프로그래밍 함으로써 공통 모듈을 여러 코드에 쉽게 적용할 수 있도록 도와 준다.


AOP를 구현하는 다양한 방법이 존재하지만, 기본적인 개념은 공통 관심 사항을 구현한 코드를 핵심 로직을 구현한 코드 안에 삽입한다는 것이다.



AOP 기법에서는 핵심 로직을 구현한 코드에서 공통 기능을 직접적으로 호출하지 않는다. 핵심 로직을 구현한 코드를 컴파일 하거나, 컴파일 된 클래스를 로딩하거나, 또는 로딩한 클래스의 객체를 생성할 때 AOP가 적용되어 핵심 로직 구현 코드 안에 공통 기능이 삽입된다.


AOP 프로그래밍에서는 AOP 라이브러리 공통 기능을 알맞게 삽입해주기 때문에 개발자는 게시글 쓰기나 목록 읽기와 같은 핵심 로직을 구현할 때 트랜잭션 적용이나 보안 검사와 같은 공통 기능을 처리하기 위한 코드를 핵심 로직 코드에 삽입할 필요가 없다. 핵심 로직을 구현한 코드에 공통 기능 관련 코드가 포함되어 있지 않기 때문에 적용해야 할 공통 기능이 변경되더라도 핵심 로직을 구현한 코드를 변경할 필요가 없다. 단지, 공통 기능 코드를 변경한 뒤 핵심 로직 구현 코드에 적용만 하면 된다.


1. AOP 용어


(1) Advice: 언제 공통 관심 기능을 핵심 로직에 적용할 지를 정의하고 있다. 예를 들어, '메서드를 호출하기 전'(언제)에 '트랜잭션을 시작한다.'(공통기능)기능을 적용한다는 것을 정의하고 있다.

조인 포인트에 삽입되어져 동작할 수 있는 코드를 '어드바이스'라 한다. 관점으로서 분리되고 실행시 모듈에 위빙된 구체적인 처리를 AOP에서는 Advice라고 한다. Advice를 어디에서 위빙하는지는 뒤에 나오는 PointCut이라는 단위로 정의한다. 또한 Advice가 위빙되는 인스턴스를 '대상객체'라고 한다.


cp.) 스프링의 Advice 타입

- Around Advice: Joinpoint 앞과 뒤에서 실행되는 Adcvice

- Before Advice: Joinpoint 앞에서 실행되는 Advice

- After Returning Advice: Jointpoint 메서드 호출이 정상적으로 종료된 뒤에 실행되는 Advice

- After Throwing Advice: 예외가 던져질 때 실행되는 Advice

- Introduction:  클래스에 인터페이스와 구현을 추가하는 특수한 Advice


(2) JoinPoint: Advice를 적용 가능한 지점을 의미한다. 메서드 호출, 필드 값 변경 등이 Joinpoint에 해당한다.

클래스의 인스턴스 생성 시점', '메소드 호출 시점', '예외 발생 시점'과 같이 어플리케이션을 실행할 때 특정 작업이 시작되는 시점을 '조인포인트'라고 한다. 실행시의 처리 플로우에서 Advice를 위빙하는 포인트를 JointPoint라고 한다. 구체적으로는 메서드 호출이나 예외발생이라는 포인트를 Joinpoint라고 한다.


(3) Pointcut: Joinpoint의 부분 집합으로서 실제로 Advice가 적용되는 Jointpoint를 나타낸다. 스프링에서는 정규 표현식이나 AspectJ 문법을 이용하여 Pointcut을 정의할 수 있다. 여러 개의 조인포인트를 하나로 결합한 것을 포인트 컷이라 한다.

하나 또는 복수의 Jointpoint를 하나로 묶은 것을 Pointcut 이라고 한다. Advice의 위빙 정의는 Pointcut을 대상으로 설정한다. 하나의 Pointcut에는 복수 Advice를 연결할 수 있다. 반대로 하나의 Advice를 복수 Pointcut에 연결하는 것도 가능하다.


(4) Weaving: Advice를 핵심 로직 코드에 적용하는 것을 weaving 이라고 한다. 즉 공통 코드를 핵심 로직 코드에 삽입하는 것이 weaving이다.

어드바이스를 핵심 로직 코드에 삽입하는 것을  위빙이라고 한다.


(5) Aspect: 여러 객체에 공통으로 적용되는 공통 관심 사항을 Aspect라고 한다. 트랜잭션이나 보안 등이 Aspect의 좋은 예이다.

여러 객체에 공통으로 적용되는 공통 관점 사항을 에스펙트라 한다.


(6) Target

핵심 로직을 구현하는 클래스를 말한다.


(7) advisor

어드바이스와 포인트컷을 하나로 묶어 취급한 것을 '어드바이저'라 부른다.

advisor와 Pointcut을 하나로 묶어 다루는 것을 Advisor라고 한다. Advisor는 스프링 AOP에만 있는 것인데, 관점 지향에서 관점을 나타내는 개념이라고 할 수 있다.


2. 세 가지 Weaving 방식

advice를 Weaving하는 방식에는 다음과 같이 세 가지 방식이 존재한다.


(1) 컴파일시에 Weaving 하기

컴파일 시에 코드를 삽입하는 방법은 AspectJ에서 사용하는 방식이다. 컴파일 방식에서는 핵심 로직을 구현한 자바 소스 코드를 컴파일할 때에 알맞은 위치에 공통 코드에 삽입한다.

따라서 AOP가 적용된 클래스 파일이 생성된다. 컴파일 방식을 제공하는 AOP 도구는 공통 코드를 알맞은 위치에 삽입할 수 있도록 도와주는 컴파일러나 IDE를 함께 제공한다.


(2) 클래스 로딩 시에 Weaving 하기

클래스를 로딩할 때에 AOP를 적용할 수도 있다. AOP 라이브러리는 JVM이 클래스를 로딩할 때 클래스 정보를 변경할 수 있는 에이전트를 제공한다. 이 에이전트는 로딩한 클래스의 바이너리 정보를 변경하여 알맞은 위치에 공통 코드를 삽입한 새로운 클래스 바이너리 코드를 사용하도록 한다. 즉 원본 클래스 파일은 변경하지 않고 클래스를 로딩할 때에 JVM이 변경된 바이트 코드를 사용하도록 함으로써 AOP를 적용한다.


(3) 런타임 시에 Weaving 하기

런타임 시에 AOP를 적용할 때에는 소스 코드나 클래스 정보 자체를 변경하지 않는다. 대신 프록시에 이용하여 AOP를 적용한다.

프록시 기반의 AOP는 핵심 로직을 구현한 객체에 직접 접근하는 것이 아니라 중간에 프록시를 생성하여 프록시를 통해서 핵심 로직을 구현한 객체에 접근하게 된다.

이때, 프록시의 핵심 로직을 실행하기 전 또는 후에 공통 기능을 적용하는 방식으로 AOP를 적용하게 된다. 프록시 기반에서는 메서드가 호출될 때에만 Advice를 적용할 수 있기 때문에 필드 값 변경과 같은 Jointpoint에 대해서는 적용할 수 없는 한계가 있다.


4. 프록시를 이용한 AOP 구현

스프링은 프록시를 이용하여 AOP를 구현하고 있다. 스프링은 Aspect의 적용 대상이 되는 객체에 대한 프록시를 만들어 제공하며, 대상 객체를 사용하는 코드는 대상 객체에 직접 접근하기 보다는 프록시를 통해서 간접적으로 접근하게 된다. 이 과정에서 프록시는 공통 기능을 실행한 뒤 대상 객체의 실제 메서드를 호출하거나 또는 대상 객체의 실제 메서드가 호출된 뒤에 공통 기능을 실행하게 된다.

스프링에서 어떤 대상 객체에 대해 AOP를 적용할 지의 여부는 설정 파일을 통해서 지정할 수 있으며, 스프링은 설정 정보를 이용하여 런타임에 대상 객체에 대한 프록시 객체를 생성하게 된다.

프록시 객체를 생성하는 방식은 대상 객체가 인터페이스를 구현하고 있으냐 없느냐 여부에 따라 달라진다.

대상 객체가 인터페이스를 구현하고 있다면, 스프링은 자바 리플렉션 API가 제공하는 java.lang.reflect.Proxy를 이용하여 프록시 객체를 생성한다. 이때 생성된 프록시 객체는 대상 객체와 동일한 인터페이스를 구현하게 되며, 클라이언트는 인터페이스를 통해서 필요한 메서드를 호출하게 된다. 하지만, 인터페이스를 기반으로 프록시 객체를 생성하기 때문에 인터페이스에 정의되어 있지 않은 메서드에 대해서는 AOP가 적용되지 않는 점에 유의해야 한다.

대상 객체가 인터페이스를 구현하고 있지 않다면, 스프링은 CGLIB를 이용하여 클래스에 대한 프록시 객체를 생성한다. CGLIB는 대상 클래스를 상속받아 프록시를 구현한다.

따라서, 대상 클래스가 final인 경우 프록시를 생성할 수 없으며, final인 메서드에 대해서는 AOP를 적용할 수 없게 된다.


[JavsScript] 제어 대상을 찾기

제어 대상을 찾기


문서를 자바스크립트로 제어하려면 제어의 대상에 해당되는 객체를 찾는 것이 제일 먼저 할 일이다. 문서 내에서 객체를 찾는 방법은 document 객체의 메소드를 이용한다. 


1. document.getElementsByTagName

문서 내에서 특정 태그에 해당되는 객체를 찾는 방법은 여러가지가 있다. getElementsByTagName은 인자로 전달된 태그명에 해당하는 객체들을 찾아서 그 리스트를 NodeList라는 유사 배열에 담아서 반환한다. 

NodeList는 배열은 아니지만 length와 배열접근연산자를 사용해서 엘리먼트를 조회할 수 있다.

<!DOCTYPE html>

<html>

<body>

<ul>

    <li>HTML</li>

    <li>CSS</li>

    <li>JavaScript</li>

</ul>

<script>

    var lis = document.getElementsByTagName('li');//li에 해당하는 각각의 태그들의 객체를 담은 유사배열을 리턴해 변수에 담는다.

    for(var i=0; lis.length; i++){

        lis[i].style.color='red'; //각각의 태그들의 style이 빨간색으로 바뀐다.  

    }

</script>

</body>

</html>


만약 조회의 대상을 좁히려면 아래와 같이 특정 객체를 지정하면 된다. 이러한 원칙은 다른 메소드에도 적용된다.

<!DOCTYPE html>

<html>

<body>

<ul>

    <li>HTML</li>

    <li>CSS</li>

    <li>JavaScript</li>

</ul>

<ol>

    <li>HTML</li>

    <li>CSS</li>

    <li>JavaScript</li>

</ol>

<script>

    var ul = document.getElementsByTagName('ul')[0];//첫번째 인덱스 값을 리턴한다.

    var lis = ul.getElementsByTagName('li');//ul의 하위 태그들 까지 영향을 미친다.

    for(var i=0; lis.length; i++){

        lis[i].style.color='red';   

    }

</script>

</body>

</html>



2. document.getElementsByClassName

class 속성의 값을 기준으로 객체를 조회할수도 있다.

<!DOCTYPE html>

<html>

<body>

<ul>

    <li>HTML</li>

    <li class="active">CSS</li>

    <li class="active">JavaScript</li>

</ul>

<script>

    var lis = document.getElementsByClassName('active');

    for(var i=0; i < lis.length; i++){

        lis[i].style.color='red';   

    }

</script>

</body>

</html>



3. document.getElementById

id 값을 기준으로 객체를 조회한다. 성능면에서 가장 우수하다.

--> 반드시 하나의 결과만 가져온다. --> 복수의 결과가 나오지 않는다.

<!DOCTYPE html>

<html>

<body>

<ul>

    <li>HTML</li>

    <li id="active">CSS</li>

    <li>JavaScript</li>

</ul>

<script>

    var li = document.getElementById('active');

    li.style.color='red';

</script>

</body>

</html>



4. document.querySelector 

css 선택자의 문법을 이용해서 객체를 조회할수도 있다.

<!DOCTYPE html>

<html>

<body>

<ul>

    <li>HTML</li>

    <li>CSS</li>

    <li>JavaScript</li>

</ul>

<ol>

    <li>HTML</li>

    <li class="active">CSS</li>

    <li>JavaScript</li>

</ol>

 

<script>

    var li = document.querySelector('li');

    li.style.color='red';

    var li = document.querySelector('.active');

    li.style.color='blue';

</script>

</body>

</html>

 


5. document.querySelectorAll

querySelector과 기본적인 동작방법은 같지만 모든 객체를 조회한다는 점이 다르다.

<!DOCTYPE html>

<html>

<body>

<ul>

    <li>HTML</li>

    <li>CSS</li>

    <li>JavaScript</li>

</ul>

<ol>

    <li>HTML</li>

    <li class="active">CSS</li>

    <li>JavaScript</li>

</ol>

 

<script>

    var lis = document.querySelectorAll('li');//유사배열

    for(var name in lis){

        lis[name].style.color = 'blue';

    }

</script>

</body>

</html>

'WebFont-end > JavaScript' 카테고리의 다른 글

[JavaScript] HTMLCollection 객체  (0) 2014.12.22
[JavaScript] HTMLElement 객체  (0) 2014.12.22
[JavaScript] 전역객체 window  (0) 2014.12.21
[JavaScript] Object Model  (0) 2014.12.21
[JavaScript] arguments(유사 배열 객체)  (0) 2014.12.21

[JavaScript] 전역객체 window


전역객체 window


1. Window 객체

Window 객체는 모든 객체가 소속된 객체(DOM, BOM, JavaScriptCore)이고, 전역객체이면서, 창이나 프레임을 의미한다. `

window.Array(JavaScriptCore)

window.navigator(BOM)

window.document(DOM)


2, 전역객체

Window 객체는 식별자 window를 통해서 얻을 수 있다. 또한 생략 가능하다. Window 객체의 메소드인 alert을 호출하는 방법은 아래와 같다.

<!DOCTYPE html>

<html>

<script>

    alert('Hello world');// window라는 객체내의 메서드

    window.alert('Hello world');

</script>

<body>

 

</body>

</html>


아래는 전역변수 a에 접근하는 방법이다.  

<!DOCTYPE html>

<html>

<script>

    var a = 1;

    alert(a);

    alert(window.a);//undefined

</script>

<body>

 

</body>

</html>


객체를 만든다는 것은 결국 window 객체의 프로퍼티를 만드는 것과 같다.

<!DOCTYPE html>

<html>

<script>

    var a = {id:1};

    alert(a.id);

    alert(window.a.id);

</script>

<body>

 

</body>

</html>

예제를 통해서 알 수 있는 것은 전역변수와 함수가 사실은 window 객체의 프로퍼티와 메소드라는 것이다. 또한 모든 객체는 사실 window의 자식이라는 것도 알 수 있다. 

이러한 특성을 ECMAScript에서는 Global 객체라고 부른다. ECMAScript의 Global 객체는 호스트 환경에 따라서 이름이 다르고 하는 역할이 조금씩 다르다. 

웹브라우저 자바스크립트에서 Window 객체는 ECMAScript의 전역객체이면서 동시에 웹브라우저의 창이나 프레임을 제어하는 역할을 한다.


'WebFont-end > JavaScript' 카테고리의 다른 글

[JavaScript] HTMLElement 객체  (0) 2014.12.22
[JavsScript] 제어 대상을 찾기  (0) 2014.12.21
[JavaScript] Object Model  (0) 2014.12.21
[JavaScript] arguments(유사 배열 객체)  (0) 2014.12.21
[JavaScript] 클로저  (1) 2014.12.21

[JavaScript] Object Model


Object Model


자바스크립트를 통해서 브라우저를 제어하기 위해서는 자바스크립로 제어할 무엇인가가 준비되어야 한다. 그것이 바로 Object(객체)이다.

웹브라우저의 구성요소들은 하나 하나가 객체화되어 있다. 자바스크립트로 이 객체를 제어해서 웹브라우저를 제어할 수 있게 된다. 

이 객체들은 서로 계층적인 관계로 구조화되어 있다. BOM과 DOM은 이 구조를 구성하고 있는 가장 큰 틀의 분류라고 할 수 있다.

자바스크립트로 브라우저를 제어하기 위해서는 객체를 제어한다.--> 객체화(자바스크립트로 브라우저를 제어하기 위해서는 모든 것이 객체화 되어 있어야 한다.)

브라우저 또는 웹페이지를 제어하기 위해서는 객체가 필요하고 그 객체를 만드는 주체는 웹브라우저가 준비하고 개발자는 준비된 그 객체를 자바스크립트를 통해서 웹브라우저 또는 문서를 제어할 수 있다.

이 관계를 그림으로 나타내면 아래와 같다. 



1. window: 전역객체 또는 window(frame)을 제어하기 위한 객체


2. DOM(window.document): 웹 페이지에 있는 문서(태그<body>, <img> 등))를 제어한다. 


3. BOM: 현재 이 웹브라우저가 가리키는 URL이나, 경고창을 띄운다거나 하는 등의 window 객체의 프로퍼티에 저장되어 있다.


4. JavaScriptCore: 자바스크립트라는 언어를 통해서 브라우저나, node.js, 구글 스프레시트 등의 다양한 호스트 환경을 제어할 수 있다. 그 호스트 환경이 무엇이든 간에 공통으로 사용할 수 있는 것이다. 

     --> 공통으로 사용할 수 있는 언어가 바로 JavaScriptCore이다.

ex.) node.js에서는 DOM이나 BOM이 적용되지 않는다.


1. BOM(Browser Object Model)

웹페이지의 내용을 제외한 브라우저의 각종 요소들을 객체화시킨 것이다. 전역객체 Window의 프로퍼티(window.document 제외)에 속한 객체들이 이에 속한다.

BOM은 전역객체인 Window의 프로퍼티와 메소드들을 통해서 제어할 수 있다. 따라서 BOM은 Window 객체의 프로퍼티와 메소드의 사용법을 배우는 것이라고 해도 과언이 아닐 것이다.

<!DOCTYPE html>

<html>

<body>

<input type="button" onclick="alert(window.location)" value="alert(window.location)" />

<input type="button" onclick="window.open('bom.html')" value="window.open('bom.html')" />

</body>

</html>


2. DOM (Document Object Model)

웹페이지의 내용을 제어한다. window 객체의 프로퍼티인 document 프로퍼터에 할당된 Document 객체가 이러한 작업을 담당한다.

Window 객체가 창을 의미한다면 Document 객체는 윈도우에 로드된 문서를 의미한다고 할 수 있다. 

Document 객체의 프로퍼티는 문서 내의 주요 엘리먼트에 접근할 수 있는 객체를 제공한다.

<!DOCTYPE html>

<html>

<body>

<img src="https://s3-ap-northeast-1.amazonaws.com/opentutorialsfile/course/94.png" />

<img src="https://s3-ap-northeast-1.amazonaws.com/opentutorialsfile/course/94.png" />

<img src="https://s3-ap-northeast-1.amazonaws.com/opentutorialsfile/course/94.png" />

<img src="https://s3-ap-northeast-1.amazonaws.com/opentutorialsfile/course/94.png" />

<img src="https://s3-ap-northeast-1.amazonaws.com/opentutorialsfile/course/94.png" />

<script>

// body 객체

console.log(document.body);

// 이미지 객체들의 리스트

console.log(document.images);

</script>

</body>

</html>


또한 특정한 엘리먼트의 객체를 획득할 수 있는 메소드도 제공한다.

<!DOCTYPE html>

<html>

<body>

<img src="https://s3-ap-northeast-1.amazonaws.com/opentutorialsfile/course/94.png" />

<img src="https://s3-ap-northeast-1.amazonaws.com/opentutorialsfile/course/94.png" />

<img src="https://s3-ap-northeast-1.amazonaws.com/opentutorialsfile/course/94.png" />

<img src="https://s3-ap-northeast-1.amazonaws.com/opentutorialsfile/course/94.png" />

<img src="https://s3-ap-northeast-1.amazonaws.com/opentutorialsfile/course/94.png" />

<script type="text/javascript">

// body 객체

console.log(document.getElementsByTagName('body')[0]);

// 이미지 객체들의 리스트

console.log(document.getElementsByTagName('body'));

</script>

</body>

</html>


'WebFont-end > JavaScript' 카테고리의 다른 글

[JavsScript] 제어 대상을 찾기  (0) 2014.12.21
[JavaScript] 전역객체 window  (0) 2014.12.21
[JavaScript] arguments(유사 배열 객체)  (0) 2014.12.21
[JavaScript] 클로저  (1) 2014.12.21
[JavaScript] 유효범위  (0) 2014.12.21