ㅎㅇ JS 관련 글도 쓸 생각이다.
완전 쌩 초보도 이해할 수 있게 쉽게 쓰려고 노력했음.
(이게 내 최선임)
DOM에 관련된 글이고
도대체 DOM이 뭔지에 대한 감을 잡는 간단한 글이다.
바로 본론
웹 개발을 만지다보면 DOM DOM 많이 들어봤을 텐데.
" Document Object Model "의 약자다
조선말로 " 문서 객체 모델 "이라는 뜻인데
웹 문서(Document)의 모든 요소를 프로그래밍에서 다룰 수 있는 객체(Object)로 만들고,
이러한 객체들을 계층화된 트리 구조의 모델(Model)로 정의하기 때문이다.
무슨 말인지 모르겠는데요.
그래서 Document / Object / Model 하나하나 알아볼 거다.
Object (객체)
객체는 정보와 기능이 담긴 상자라고 생각하면 된다.
객체는 대충 이런 느낌으로 생겼다. (별로 어려운 내용도 아니니 그냥 넘어가지 말고 주의 깊게 한 줄 한줄 읽는 거 추천)
let 강아지 = {
// 강아지의 정보
이름: "초코",
나이: 3,
색깔: "갈색",
// 강아지가 할 수 있는 행동
짖기: function() {
console.log("멍멍!");
},
앉기: function() {
console.log("앉았습니다");
}
}
// 강아지 정보 보기
console.log(강아지.이름); // "초코" 출력
console.log(강아지.나이); // 3 출력
// 강아지 행동시키기
강아지.짖기(); // "멍멍!" 출력
강아지.앉기(); // "앉았습니다" 출력
보면 강아지의 정보와 이 강아지가 할 수 있는 행동들이 모여 있다.
다시 말하지만 객체는 정보와 기능이 담긴 상자다.
오잉 그러면 저 강아지가 객체라는 상자라는 건가요?
ㅇㅇ 맞음
- 강아지 = 객체
- 강아지 정보 = 속성
- 강아지가 할 수 있는 행동 = 메소드
속성과 메소드는 추후에 블로그에 적을 수도 있지만
내 귀찮음이 성실함을 툭 치면 죽어버려서 본인이 직접 찾아보는 것을 추천.
간단하게
강아지.이름(속성) 하면 강아지의 객체에 저장된 이름이 나오고
강아지.짖기() (메소드) 하면 강아지의 객체에 저장된 행동을 함. 메소드는 뒤에 ()가 붙음 그게 실행한다는 뜻임.
객체 끝
MODEL(모델)
Model은 웹 문서를 프로그래밍적으로 다룰 수 있도록 만든 인터페이스이자 문서의 구조화된 표현 방식을 의미함.
뭐라는 거야
조금 더 쉽게
여기서 MODEL은 포괄적인 개념인데
- HTML을 JS로 조작할 수 있는 인터페이스이자. (리모컨 같은 거임)
- HTML의 구조화된 표현 방식을 의미한다는 거임. (지도 같은 거임)
저 두 가지 의미를 둘 다 포함하고 있음 리지도모컨임 ㅇㅇ
일단 구조화된 표현 방식인 트리 구조부터
이게 트리구조임
O ← 이것이 하나의 '매듭점(node)'
/ \ ← 이것들은 '연결선(edge)'
O O ← 다른 '매듭점들(nodes)'
그리고 저 하나하나를 노드(node)라고 부름.
매듭점, 연결점, 교차점 대충 이런 뉘앙스를 가짐.
저 동글뱅이들이 노드임.
이렇게 생긴 걸 트리 모델이라고 함.
근데 왜 이 구조를 써야 하는데요
맞는 말임 분명 다른 구조도 가능했을 거임 이런 식으로
<!-- 실제 HTML -->
<div>
<h1>제목</h1>
<p>내용</p>
</div>
// 1. 트리 모델
div
├── h1 ("제목")
└── p ("내용")
// 2. 리스트 모델 (다른 방식)
1. div 시작
2. h1 시작
3. "제목"
4. h1 끝
5. p 시작
6. "내용"
7. p 끝
8. div 끝
근데 트리구조를 채택하면 다음과 같은 이점이 있음:
- 부모-자식 관계를 직관적으로 표현
- 요소 찾기/수정이 쉬움
- HTML의 계층구조와 잘 맞음
ㅇㅇ 그래서 트리구조를 선택한 거임.
근데 도대체 누가 멀쩡한 HTML을 트리구조로 바꿔준다는 거임 자꾸
님이 보고 있는 이 브라우저가 그걸함.
이를파싱(Parsing)이라고 함.
정보를 대충 가공하는걸 파싱이라고 보면 됨.
그리고 그 파싱을 하는 놈들을 파서(Parser)라고 부름.
브라우저에는 엔진들이 있는데
그중 HTML 파서가 HTML을 파싱 해서 트리구조를 만듦.
참고로 CSS 파서가 CSS를 파싱 하면 CSSOM임 ㅋㅋ
JavaScript 엔진 (예: V8)
렌더링 엔진
이런 애들도 있음.
그럼 이제 또 의문이 생김.
도대체 누구 좋으라고 HTML을 파싱 해서 트리구조로 만드니 마니
이 난리부르스 다이아몬드 스텝을 밟고 있는 걸까?
정답은
1. 렌더링 엔진을 위해 (처음에 말한 지도 기능)
2. JS 조작을 위해. (처음에 말한 리모컨 기능임)
1. 은 위에서 언급한 렌더링 엔진이 만약 HTML 텍스트 그대로 사용하게 되면
허벌나게 뛰댕겨야됨.
- 부모-자식 관계 파악이 안돼서 폐륜이 발생할 수도 있고
- 스타일 상속 계산이 어려워서 재벌가처럼 상속분쟁이 일어날수도 있음.
- 레이아웃 계산도 어려워짐
암튼 렌더링 엔진이 힘들어함.
그래서 좀 효율적으로 뛰어다니라고 지도를 쥐어주는 거임.
(사실상 더 돌아다니기 편하게 맵자체를 변경한다는 표현이 조금 더 정확하지만)
결론 : 렌더링 편하자고 트리 구조로 바꾼 거임.
이론상 트리구조 없이 렌더링을 할 수 있음. 렌더링 엔진이 힘들 뿐 (렌엔 지켜 엉엉)
2. JS는 추후 설명함
NODE
아까 위에서 트리구조는 노드들을 가진다고 말했음.
1. 요소 노드(Element Node): <div>, <p> 등의 HTML 태그
2. 텍스트 노드(Text Node): 텍스트 내용
3. 속성 노드(Attribute Node): class, id 같은 속성
4. 문서 노드(Document Node): 문서 전체를 나타내는 노드
5. 주석 노드(Comment Node): HTML 주석
그래서 파싱 된 HTML은 이런 식으로 노드를 가지고 있음. 트리구조니깐.
밑은 더 자세한 예시임. (자세히 보셈)
// HTML 구조
<html>
<head>
<title>DOM 예시</title>
</head>
<body>
<div id="container">
<h1>제목</h1>
<p>텍스트 내용</p>
</div>
</body>
</html>
// 위 HTML의 DOM 트리 구조:
document (문서 노드)
└── html (요소 노드)
├── head (요소 노드)
│ └── title (요소 노드)
│ └── "DOM 예시" (텍스트 노드)
└── body (요소 노드)
└── div (요소 노드)
├── h1 (요소 노드)
│ └── "제목" (텍스트 노드)
└── p (요소 노드)
└── "텍스트 내용" (텍스트 노드)
이런 식으로 HTML이 트리구조로 변경되고
해당방식으로 노드들이 생김.
(변환된 트리구조 맨 위 주목)
어 근데 맨 위에 저 Document는 뭘까.
DOM
O(객체)에 대해서 설명했고
M(모델)에 대해서 설명했다.
마지막 남은 D(문서)
저 Document가 D다.
모델 끝
Document(문서)
Doucument는 객체다.
맨 위에 말한 그 object 객체.
지금 우리가 지지고 볶은 트리화 구조가 이 Document 객체에 들어간다.
결국 DOM이란
파싱 된 HTML이 트리 구조의 객체 모델로 변환되어
Document에 저장되어 이를 조작할 수 있는 인터페이스를 제공하는 것.
이라고 이해할 수 있다.
조금 더 자세히 알아보자
Document의 트리구조는 다음과 같다.
Document
├── DOCTYPE
└── <html>
├── <head>
│ ├── <title>
│ ├── <meta>
│ └── ...
└── <body>
├── <div>
├── <p>
└── ...
이 Document라는 객체 안에 트리 구조로 HTML이 들어가 있다.
실제 Document 객체의 구조는 어떻게 생겼을까?
document = {
documentElement: HTMLHtmlElement, // <html> 태그
head: HTMLHeadElement, // <head> 태그
body: HTMLBodyElement, // <body> 태그
// 주요 메서드들
getElementById(): Element,
getElementsByClassName(): HTMLCollection,
getElementsByTagName(): HTMLCollection,
querySelector(): Element,
querySelectorAll(): NodeList,
// 문서 관련 속성들
title: String,
URL: String,
documentURI: String,
characterSet: String,
// 이벤트 관련
addEventListener(): void,
removeEventListener(): void
// DOM 조작 메서드들
createElement(): Element,
createTextNode(): Text,
// ... 기타 여러 속성과 메서드들
}
대략적으로 이렇게 생겼다.
Document는 객체이기 때문에
위에 설명한 강아지처럼
속성과 메소드가 있다.
위에 언급한 객체의 메소드 사용법을 떠올려보자.
강아지를 짓게 하려면 강아지.짖기() 를 사용해야 했다.
강아지.짖기()
- 강아지 = 객체
- 짖기() = 메소드
지금 나는 html 태그를 하나 선택하고 싶다고 가정해 보자.
(그때 필요한 메소드는 querySelector() 다.)
document.querySelector('p')
- document = 객체
- querySelector() = 메소드
이러면 p 태그를 선택할 수가 있다.
이렇게 객체를 조작하는 방식으로 JS로 간편하게 우리는 HTML을 조작할 수 있다.
왜냐
우리가 document라는 객체를 만들고 그 안에 HTML을 파싱 해서 넣었을 뿐 아니라
그 안에 유용한 메소드들이 있기 때문이다.
(참고로 메서드들은 브라우저가 추가해 준다.)
이게 모델에서 언급한 리모컨 기능이다. (인터페이스)
JS를 통해 쉽게 HTML을 조작할 수 있다.
근데 이렇게 안 하면 얼마나 더럽고 귀찮게 조작해야 되는데요?
// 1. DOM 구조가 없다고 가정하면 (HTML이 그냥 문자열)
let html = "<div><h1>제목</h1><p>내용</p></div>";
// HTML을 수정하려면 문자열 처리를 해야 함
html = html.replace("<h1>제목</h1>", "<h1>새로운 제목</h1>");
html = html.replace("<p>내용</p>", "<p>새로운 내용</p>");
------------------------------------------------------------
// 2. DOM 구조일 때 (현재 브라우저처럼)
let 제목 = document.querySelector('h1');
let 내용 = document.querySelector('p');
제목.textContent = "새로운 제목";
내용.textContent = "새로운 내용";
1번처럼 코드가 혼파망이 되어버린다.
DOM이 있음에 감사하자.
다시 document 객체로 돌아와 보자.
document = {
documentElement: HTMLHtmlElement, // <html> 태그
head: HTMLHeadElement, // <head> 태그
body: HTMLBodyElement, // <body> 태그
// 주요 메서드들
getElementById(): Element,
getElementsByClassName(): HTMLCollection,
getElementsByTagName(): HTMLCollection,
querySelector(): Element,
querySelectorAll(): NodeList,
// 문서 관련 속성들
title: String,
URL: String,
documentURI: String,
characterSet: String,
// 이벤트 관련
addEventListener(): void,
removeEventListener(): void
// DOM 조작 메서드들
createElement(): Element,
createTextNode(): Text,
// ... 기타 여러 속성과 메서드들
}
뭐야 트리라더니 생긴 게 하나도 트리처럼 안 생겼잖아.
왜냐면 JS 객체를 표현하는 방식이 그렇게 보이지 않기 때문이다.
실제로는 트리 구조를 가진다.
더 자세한 예시를 보자
HTMLHtmlElement은 이렇게 생겼다.
HTMLHtmlElement = {
// 속성들
id: String,
className: String,
// ... 다른 속성들
// 자식 요소들이 여기에 저장됨
children: {
0: HTMLHeadElement, // <head>
1: HTMLBodyElement, // <body>
length: 2
}
}
children은 자식 요소들을 저장하는 곳이다
그중 자식 요소인 HTMLHeadElement 객체는 어떻게 생겼을까?
HTMLHeadElement 객체 구조
HTMLHeadElement = {
// HTMLElement로부터 상속받은 속성들
id: String,
className: String,
// head 요소의 자식들
children: {
0: HTMLMetaElement, // <meta>
1: HTMLTitleElement, // <title>
2: HTMLLinkElement, // <link>
length: 3
},
// 부모-자식 관계 속성/메서드
firstChild: Node,
lastChild: Node,
appendChild(): Node,
removeChild(): Node
}
보면 자식을 타고 들어가면 또 자식이 있고 또 자식이 있고 이런 식으로
트리 구조를 이루고 있다.
이를 조금 더 구조적으로 표현하면 다음과 같은 구조를 이루고 있다.
이래서 트리구조라는 거임.
다시 돌아와서
위에 나열한 Document(문서) / Object(객체) / Model(모델) 을 전부 연결해서 생각하면
D.O.M 이 DOM이 된다!
수미상관 간다.
DOM이 DOM인 이유는 다음과 같다.
웹 문서(Document)의 모든 요소를 프로그래밍에서 다룰 수 있는 객체(Object)로 만들고,
이러한 객체들을 계층화된 트리 구조의 모델(Model)로 정의하기 때문이다.
이제는 이 문장이 조금 더 친숙하게 당신에게 다가왔을 거라 믿는다.
P.S.
내가 글을 못써서 DOM의 장점을 다 못 적었다.
DOM이 어떤 방식으로 작동하는지
DOM이 뭔지 어느 정도 감이 잡혔다면 나는 그걸로 만족.
기회가 된다면 DOM을 쓰게 됨으로써 얻게 된 장점도 써보도록 하겠다.
(안쓰겠다는 뜻)
'JS' 카테고리의 다른 글
1-2 메모리구조 (0) | 2024.11.26 |
---|---|
1-1 JavaScript 변수 (1) | 2024.11.21 |