본문 바로가기
제가 쓴 책/HTML5, CSS3 and JavaScript

8. DOM(Document Object Model)

by edupicker(체르니) 2013. 8. 6.

8. DOM(Document Object Model)


기본적으로 웹문서는 HTML XML과 같은 마크업 언어로 작성된 문서로 익스플로러나 Firefox 같은 웹 브라우저는 이렇게 HTML이나 XML 같은 마크업 언어로 작성된 문서들을 여러분이 쉽게 이해할 수 있도록 바꾸는 작업을 수행합니다. 그래서 여러분들을 이러한 브라우저를 통해 문자열에서부터 그림, 테이블 등을 볼 수 있게 되는 것입니다.
그리고 이러한 해석 과정에서 HTML 페이지를 하나의 트리(Tree) 형태의 모델로 저장하는데 이를 DOM(Document Object Model)이라고 합니다.
DOM
HTML 문서를 하나의 트리(Tree) 형태로 나타낼 수 있는데 트리는 노드(node)들로 구성됩니다. 여기서 노드(node) DOM 트리에 존재하는 요소(Element), 속성(Attribute), 텍스트(Text), HTML 문서 전체를 나타내는 Document 등을 노드라고 합니다.
DOM
이라는 개념이 접근하기에 따라서 어려울 수도 있는데 여기서 설명하는 대로 잘 따라 오시면 그리 어렵지 않게 DOM의 개념을 이해할 수 있을 것입니다. 그리고 이 개념은 XML DOM을 이해하는데 거의 그대로 적용되는데 이에 대한 부분은 인터넷에서 검색을 통한 자료나 제 2 번째 책 예제로 배우는 XML & LINQ 완벽가이드를 참고하시면 좋을 것 같습니다.
이해를 돕기 위해서 예를 들어 다음과 같은 HTML 문서가 있다고 가정합니다.


<!DOCTYPE HTML>
<html>
<head>
  <title>Node
이해하기</title>
  <script type="text/javascript">
  </script>
</head>
<body>
  <h1>Node
에 대하여</h1>
  <p>Node
HTML의 요소(Element), 속성(Attribute) 등을 나타냅니다.</p>
</body>
</html>

위의 문서를 “NodeEx.html”로 저장하고 IE9를 통해 열어보면 다음과 같이 나타납니다.



이제 이 HTML 문서를 DOM 트리(Tree) 형태로 나타내면 다음과 같습니다.



위의 그림에서 보는 것처럼 루트 노드(root node) html 요소가 되고 이 루트 노드의 자식 노드로 첫 번째 자식(child) 노드는 head 요소, 두 번째 자식 노드는 body 요소가 됩니다. 그리고 head 요소와 body 요소를 나타내는 노드들은 서로 형제지간(sibling)입니다.

다음으로 head 요소를 나타내는 노드의 첫 번째 자식 노드는 title 요소이고 두 번째 자식 노드는 script 요소입니다. 역시 title, script 요소를 나타내는 노드들은 서로 형제지간(sibling)입니다.
마지막으로 body 요소를 나타내는 노드의 첫 번째 자식 노드는 h1 요소이고 두번째 자식 노드는 p 요소이며 마찬가지로 h1 요소와 p 요소는 서로 형제지간(sibling)입니다.



8.1 Node의 주요 속성
JavaScript
를 이용하여 HTML DOM을 다루는데 있어 노드(node)와 관련된 주요 속성들을 나타내면 다음과 같습니다.


속성

설명

nodeName

노드의 이름

nodeType

노드의 타입을 나타내며 읽기 전용입니다.

nodeValue

노드의 값을 나타냄. 일반적으로 해당 요소(Element) 값을 의미

parentNode

현재 노드의 부모 노드를 반환

firstChild

첫 번째 자식 노드 반환

lastChild

마지막 자식 노드 반환

previousSibling

바로 앞 형제 노드 반환

nextSibling

다음 형제 노드 반환

childNodes

자식 노드들을 반환

textContent

노드나 자식 노드의 Text 값을 가져오거나 설정할 때 사용

innerText

노드의 Text 값을 가져오거나 설정할 때 사용

innerHTML

요소(Element)의 내용(content)Text 값을 가져오거나 설정할 때 사용


표에서 소개한 속성들을 이해하기 위해서 예제를 해볼까요? 이제 “NodeEx.html”에서 <script> … </script> 블록 내에 다음과 같이 코드를 작성합니다.

  <script type="text/javascript">
    var documentNodes = document.documentElement;  //HTML --

    document.write(documentNodes.nodeName +"<br />");

    var htmlfirstChild = document.documentElement.firstChild;  //
첫 번째 자식노드 --

    document.write(htmlfirstChild.nodeName+"<br/>");

    var htmlnextSibling = document.documentElement.firstChild.nextSibling; // --

    document.write(htmlnextSibling.nodeName+"<br/>");

    var htmllastChild = document.documentElement.lastChild; //
마지막 자식노드 --

    document.write(htmllastChild.nodeName+"<br/>");

    var htmlchildNodes = document.documentElement.childNodes;  //--

    for (var i = 0, l = htmlchildNodes.length; i < l; i++) {

      document.write(htmlchildNodes[i].nodeName+"<br/>");

    }
  </script>

에서 document 객체는 window 객체(object)의 한 속성으로 DOM 트리(Tree)의 루트(root)를 나타내며 document 객체의 속성 중 documentElement 속성은 html 요소를 나타냅니다. 참고로
body
속성은 body 요소를 나타냅니다.
에서는 firstChild 속성을 이용하여 루트 노드의 첫 번째 자식 노드에 접근할 수 있으며 에서는 첫 번째 자식 노드 다음에 오는 노드에 접근하기 위해서 nextSibling 속성을 사용했습니다. 에서는 lastChild 속성을 이용하여 루트 노드의 마지막 자식 노드에 접근하는데 결과에서 보게 되면 알겠지만 루트 노드(html)에는 2개의 자식 노드만 존재하므로 , 의 결과가 동일하게 나타나게 됩니다.
마지막으로
에서는 childNodes 속성을 이용하여 루트 노드의 모든 자식 노드들을 받아서 for문을 이용하여 자식 노드의 수(length)만큼 반복하여 노드이름을 나타내도록 합니다.
위와 같이 코드를 추가한 후 저장하고 IE9를 통해서 보면 다음과 같이 나타납니다.





8.2 Node의 주요 메서드를 이용한 추가, 수정, 삭제


여러분들은 HTML 요소들을 각각 Node의 관점에서 보고 다음과 같은 Node의 주요 메서드를 이용하여 새로운 노드, 즉 새로운 요소(Element)를 추가, 수정, 삭제할 수 있습니다.


메서드

설명

insertBefore()

특정 자식노드 앞에 새로운 노드를 추가하거나 맨 마지막 자식 노드
뒤에 추가

appendChild()

기존 자식 노드의 다음에 새로운 newNode를 추가

replaceChild()

기존 oldNode newNode로 교체

removeChild()

자식 노드를 삭제

hasChildNodes()

노드가 자식 노드를 가지고 있으면 true, 없으면 false 반환

hasAttributes()

요소 노드가 속성노드를 가지고 있으면 true, 없으면 false 반환



8.2.1 insertBefore(), appendChild() 메서드를 이용한 노드 추가
insertBefore(), appendChild() 메서드는  대한 형식은 다음과 같이 정의하여 사용하게 됩니다.


insertBefore(newNode [, childNode])
appendChild(newNode)


먼저 insertBefore() 메서드는 위 형식에서 보는 것처럼 입력 파라미터로 newNode, childNode 모두 정의될 경우는 childNode 앞에 newNode 추가합니다만 입력 파라미터로 childNode가 입력되지 않을 경우 맨 마지막 자식 노드 다음에 newNode가 추가됩니다.
다음으로 appendChild() 메서드는 기존 자식 노드의 다음에 새로운 newNode를 추가합니다.
이제 두 메서드의 사용 방법을 예제를 통해 알아보기 위해 메모장을 열어서 다음과 같이 작성합니다.


<!DOCTYPE HTML>
<html>
<head>
  <title>insertBefore()
메서드 예제</title>
</head>
<body>
  <ul id="oulFruits">
    <li id="oliApple">
사과</li>
    <li id="olistrawberry">
딸기</li>
    <li id="oliGrape">
포도</li>
  </ul>
  <script type=”text/javascript”>
  </script>
</body>
</html>

위와 같이 작성한 후 “insertBeforeMethod.html”로 저장한 후 IE9를 통해 보면 다음과 같이 리스트 항목(list item)을 나타냅니다.



이제 위 결과에서 딸기항목 위에 수박항목을 insertBefore() 메서드를 이용하여 추가해볼까요?
딸기항목 위에 수박항목을 추가하기 위해서는 부모 노드인 ul 노드에 insertBefore() 메서드를 적용하면 됩니다. 그래서 위 코드에서 <script>… </script> 코드 블록에 다음과 같이 코드를 추가합니다.

  <script type="text/javascript">
    var newNode = document.createElement("li");
    newNode.innerText="
수박";
    //newNode.textContent=”
수박”;
    var retul = document.getElementById("oulFruits");
    retul.insertBefore(newNode, olistrawberry);
  </script>

위에서 새로운 노드를 생성하는데 있어서 createElement() 메서드를 이용했는데 이 createElement() 메서드는 document.createElement(“생성할 요소이름”) 형태로 새로운 요소 노드(Element Node)를 생성할 수 있습니다.
그리고 createElement() 메서드를 이용하여 생성된 li 요소 노드에 innerText 속성을 이용하여 해당 노드의 Text 값을 설정합니다. 물론 위 코드에서 주석처리된 대로 textContent 속성을 이용해서도 노드의 Text 값을 설정할 수 있습니다.
다음으로 getElementById() 메서드를 이용하여 id 속성값이 “oulFruits” ul 을 검색하고 검색된 ul 노드에 insertBefore() 메서드를 이용하여 생성한 li 요소 노드를 추가할 수 있습니다. 저장하고 IE9를 통해 확인하면 다음과 같이 원하는 위치에 노드가 추가되어 나타납니다.



만일 위의 insertBefore() 메서드를 “retul.insertBefore(newNode);” 형태로 사용하면 어떤 결과가 나올까요? Text값이 수박 li 노드는 Text값이 포도 li 노드 다음에 추가되어 나타납니다. 이는 appendChild() 메서드를 이용하여 추가하는 결과와 동일합니다.
위 코드에서 “// retul.insertBefore(newNode, olistrawberry);”와 같이 주석처리하고 그 밑에 다음과 같이 한 줄을 추가합니다.

retul.appendChild(newNode);

저장하고 IE9를 통해 확인하면 다음과 같이 수박노드가 맨 마지막에 나타나게 됩니다.





- 본 저작물은 본인이 2011년 상반기부터 2012년 여름 즈음까지 도서 출판을 목적으로 약 470 페이지(A4, 폰트 10)으로 작성한 원본 중 JavaScript에 대한 부분을 그대로를 공개하는 것으로 본 저작물에 대한 모든 권리는 본인(원철연)에서 있음을 알립니다.
개인적인 학습 목적으로 사용을 허용하며 온오프라인의 베포나 펌상업적인 용도의 사용은 삼가해주시기 바랍니다. 끝으로 학교나 비영리 단체에서의 경우 본 저작물을 비상업적인 용도로 활용하고자 할 경우 연락처를 비밀댓글로 남겨주시면 연락드리겠습니다.