BOM이란?
BOM는 Browser Object Model의 약자로 문서 이외의 모든 것을 제어 하기 위해 브라우저가 제공하는 추가 객체를 나타낸다
대표적으로 navigator와 location이 존재한다
navigator 객체는 브라우저의 운영체제에 대한 정보를 제공하고
location 객체는 현재 URL을 읽고 새로운 URL로 변경도 가능하게 해준다
이 외의 BOM의 추가 객체들도 살펴보자
location
우선 http://localhost:5500/index.html?type=listing&page=2#title 의 링크에서 진행한다고 가정해보자
location.href : 현재의 주소를 그대로 반환한다
여기선 http://localhost:5500/index.html?type=listing&page=2#title를 그대로 반환한다
location.protocol : http혹은 https를 반환한다
현재는 http 로 시작하기 때문에 http를 반환한다
http와 https의 차이는 보안의 차이이다
s는 security를 가리킨다
https는 http를 확장한 버전 혹은 더욱 안전한 버전을 가리킨다
location.host : host의 이름과 port number를 반환한다
현재는 localhost:5500 반환한다
location.port : port number를 반환한다
현재는 5500을 반환한다
location.search : ? 부터 시작하는 query문을 반환한다
현재는 ?type=listing&page=2를 반환한다
params라고도 이야기 한다
이 부분을 백엔드로 넘겨 서버간 통신을 한다
const urlParams = new URLSearchParams(location.search);
처럼 입력하게 되면 type과 page가 저장된 객체로 할당된다
location.hash : #에 작성된 부분을 반환한다
현재는 #title을 반환한다
location에 hash를 추가하기도 한다
이때 페이지 내부에 해당 부분으로 바로 이동하게 만들어준다
location.replace('url') : 작성된 url로 이동시켜주는 함수이다
location.reload() : 현재 페이지를 새로고침해주는 함수이다
navigator
navigator.platform : 사용자가 현재 동작하고 있는 OS를 반환한다

navigator.userAgent : 사용자 에이전트 문자열을 반환한다
크롬의 경우 다음과 같다

navigator.language : 해당 페이지에 적용된 언어를 반환한다

navigator.onLine : 현재 온라인(네트워크와 연결 가능) 상태인지 확인한다

navigator.geoloaction : 사용자의 현재 위치 정보에 접근하여 반환한다
이때 객체로 반환하며 latitude와 longitude는 getCurrentPosition() 함수로 출력할 수 있다
navigator.mediaDevice : 사용자의 카메라에 접근하여 송출할 수 있다
이 기능을 온라인 스트리밍 서비스가 가능하다
navigator는 이 외에도 정말 다양한 기능들이 내포되어있다
screen
screen.width : 이는 모니터의 너비를 반환한다screen.height : 모니터의 높이를 반환한다screen.availHeight : indicator를 제외한 높이를 반환한다screen.availWidth : indicator를 제외한 너비를 반환한다screen.orientation : 가로의 값이 큰지 세로의 값이 큰지 비교하고 가로가 크다면 landscape를 포함한 객체를 세로가 크다면 portrait을 포함한 객체를 반환한다현재 값이 아닌 모니터의 크기에 따라 달라진다

screen에는 포함되어 있지 않고 window에 내장된 현재 브라우저의 크기를 구하는 것도 가능하다
innerHeight : 현재 브라우저의 높이를 반환한다innerWidth : 현재 브라우저의 너비를 반환한다history
history.back() : 현재 위치에서 바로 전에 방문했던 페이지로 이동하는 함수이다
history.forward() : 현재 위치보다 바로 앞서 실행됐던 페이지로 이동하는 함수이다
history.go(num) : 현재 페이지를 기준으로 num에 기준만큼 이동한다
history.length() : history에 저장된 페이지의 개수를 나타내준다
history.pushState() : 상태를 저장하여 back과 forward를 작동하게 한다
이는 SPA에서 주로 사용된다
DOM이란?
DOM은 Document Object Model의 약자로 어떠한 행동을 부여하거나 값을 부를 때 우선 접근해야 하는 영역을 의미한다
querySelector()
지금까지 봤던 예제들에서 querySelector()가 종종 나왔었다
이는 DOM에 접근하는 함수이며 매개변수로 전달받은 값에 해당하는 node를 반환한다
const node = document.querySelector('name');
위의 예제와 같이 사용하며 document는 상위 context지정으로 바꿔줄 수 있다
const node = context.querySelector('name');
만약 querySelector대신 querySeletorAll을 사용하면 해당하는 모든 값을 저장할 수 있다
document
우선 document가 지정하는 곳을 살펴보자

chrome기준 현재 tab의 전체를 가리킨다
그렇다면 이 tab을 가리키는데 어떻게 HTML에 접근이 가능하냐? 라는 의문이 든다
이때 document는 documentElement를 HTML로 갖는다

따라서 document의 내부에 HTML이 종속되어있고, 이를 활용해 body와 같은 태그 요소에 접근할 수 있다는 의미이다
node 탐색
우선 내용에 대한 언급을 하기 전에 가정을 하나 하고 들어가자
태그의 구성은 다음과 같다
<span class = 'a'>
<span class = 'a-1'></span>
<span class = 'a-2'></span>
<span class = 'a-3'></span>
</span>
<span class = 'b'>
<span class = 'b-1'></span>
<span class = 'b-2'></span>
<span class = 'b-3'></span>
</span>
<span class = 'c'></span>
<span class = 'd'></span>
우선 구성은 a class를 가진 태그 내부에 a-1 a-2 a-3 class를 가진 자식 요소(span)들이 존재하고 형제는 b c d class를 가진 span태그들이 존재한다
또한 b도 a와 구성이 비슷하다
b class를 가진 span태그의 node를 추출하겠다
const b = document.querySelector('.b');
형제 노드 찾기
const c = b.nextSibling;
const a = a.previousSibling;
만약 위와 같은 방식으로 탐색을 하면 textNode와 commentNode 등을 포함해서 탐색하기 때문에 태그를 찾으려면 반복문을 돌려야 할 수도 있다
만약 elementNode만 찾고 싶다면
const c = b.nextElementSibling;
const a = b.previousElementSibling;
을 사용해 탐색할 수 있다
console.log(c); // <span class="c"></span>
console.log(a); // <span class="a"> ... </span>
부모 노드 찾기
우선 b의 자식 요소인 b-1을 가져오도록 하자
그 다음 부모 노드를 찾아보자
const b1 = document.querySelector('.b-1');
console.log(b1.parentNode); // <span class="b"></span>
parentNode로 부모 노드 탐색이 가능하다
자식 노드 찾기
이번엔 b의 자식노드들을 찾아보자
두 가지 방법을 살펴보겠다
console.log(b.childNodes); // [text, span.b-1, text, span.b-2, text, span.b-3, text]
이때 elementNode를 포함한 모든 자식 node들을 가져온다
조건문으로 소거하는 방법도 가능하다
(elementNode의 number가 1인것을 활용)
또한 firstChild와 lastChild를 이용해 자식의 처음과 마지막 node를 구할 수 있다
console.log(b.children); // [span.b-1, span.b-2, span.b-3]
childNodes대신 children을 사용하면 elementNode들만 추출이 가능하다
getElementBy${명령어}();
기존에 사용하던 querySelector()와 유사하게 동작한다
하지만 차이점은 tagName과 className 등으로 작동하며 elementNode만 추출한다는 점이다
그리고 모든 것을 확인하며 찾는 querySelecor와 다르게 선언된 id나 class만 탐색하기 때문에
효율이 좋다
주의해야할 점은 class혹은 name 혹은 tag들은 중복해서 사용이 가능하지만 id는 유일하게 사용한다
따라서 elementNode가 id를 제외한 곳에선 여러 개가 반환이 가능하기 때문에 복수형으로 사용해야한다
id 주의점
id가 선언된 tag들은 querySelector혹은 getElementById를 이용해 node를 추출하지 않아도 바로 사용이 가능하다

a class를 가진 span에 id='a'를 할당하고 바로 출력한 모습이다
또한 id는 고유하다고 했지만 HTML에 id를 중복해서 사용해도 문제 없이 작동은 된다
하지만 이는 권장하진 않는다
matches와 closest
matches를 이용하면 예상한 값과 같은지에 대한 결과를 보여준다
이는 id 혹은 class와 같은 속성들을 포함해 태그 이름을 넣어서 비교할 수 있다
const a = document.querySelector('.a');
a.matches('span'); // true
a.matches('.a'); // true
a.matches('#a'); // true
closest를 사용해 가장 가까운 설정한 값을 탐색해준다
예를 들어 a-1 태그와 가장 가까운 span태그를 검색해달라고 요청할 수 있다는 것이다
이때 주의할 점은 탐색 대상은 자기 자신도 포함한다는 점과
형제 노드는 탐색 대상으로 넣지 않고 직계만 탐색 대상으로 한다는 점이다
만약 검색에 실패했다면 null을 반환한다
이는 이벤트 위임(delegation)에 주로 사용된다
const a1 = document.querySelector('.a-1');
console.log(a1.closest('span')); // <span class="a-1"> ... </span>
console.log(a1.closest('a-3')); // null -> 형제 노드 찾지 못함
console.log(a1.closest('.b')); // null -> 직계 이외에는 찾지 못함

사실 DOM node는 이 그림 하나로 요약할 수 있다
하지만 각각의 것들이 무슨 역할을 하는지 모르기에 하나하나 뜯어보겠다
EventTarget
루트에 있는 추상 class이다
추상이라고 언급한 이유는 이는 객체들이 실제로 만들어지진 않는 class이기 때문이다
DOM node의 베이스에 존재하기 때문에 DOM node에서 이벤트를 사용할 수 있다
Node
DOM node의 베이스 역할을 한다
여기서 노드 탐색을 진행한다(parentNode, nextSiblingNode 등등)
때문에 이를 상속받는 class들이 여럿 존재한다(text class, comment class, element class)
이도 역시 추상 class이다
Element
DOM 구성을 위한 베이스 클래스이다
위에서 언급한 Node의 노드 탐색 중 nextElementSibling과 같은 요소 전용 탐색을 도와주는 property나 method가 이를 기반으로 한다
또한 HTML에서 지원하는 XML,SVG를 구성하는 요소(HTMLElement, XMLElement, SVGElement)의 베이스 클래스 역할도 수행한다
HTMLElement
HTML요소의 베이스 노드 역할을 한다
HTMLElement를 상속받는 class들은 다음과 같다
- HTMLInputElement – <input> 요소에 대응하는 클래스
- HTMLBodyElement – <body> 요소에 대응하는 클래스
- HTMLAnchorElement – <a> 요소에 대응하는 클래스 등등
DOM Node의 이름 확인
우선 객체는 constructor를 기본적으로 내장하고 있다
따라서 이를 활용하여 이름을 출력할 수 있다
const a = document.querySelector('.a');
console.log(a.constructor.name);
결과는 다음과 같다

또한 상속 여부는 instanceof를 사용해 확인할 수 있다
HTML의 body요소는 DOM Node를 상속했는지 확인해보자
console.log(document.body instanceof EventTarget); // true
console.log(document.body instanceof Node); // true
console.log(document.body instanceof Element); // true
console.log(document.body instanceof HTMLElement); // true
console.log(document.body instanceof HTMLBodyElement); // true
console.log(document.body instanceof HTMLSpanElement); // false
DOM Node의 구성요소들에 포함이 되는 것을 확인할 수 있다
이때 span은 상속받지 않았기 때문에 false를 출력한다
또한 console.dir() 을 이용해 property를 더욱 확인하기 쉽게 출력할 수 있다
log와의 차이는 dir은 DOM 객체처럼 취급하기 때문에 그러하다
node 구분
전에 언급했었던 nodeType으로 구분하는 법은 한 번 다뤘으니 생략하겠다
이번엔 nodeName으로 node를 구분하는 법을 살펴보자
사용법은 간단하다 document.node.nodeName과 같이 사용하면 된다
const a = document.querySelector('.a');
console.log(a.nodeName); // SPAN
nodeName 말고 tagName으로도 사용이 가능하다
const a = document.querySelector('.a');
console.log(a.tagName); // SPAN
둘의 결과는 같으나 차이점은 있다
바로 nodeName은 모든 노드를 지원하고 tagName은 요소 노드에만 지원한다
innerHTML
innerHTML을 사용하면 HTML을 문자열로 가져와 자유롭게 원하는 값을 넣고 뺄 수 있다
document.body.innerHTML += 'hello';
와 같은 방식으로 사용된다
만약 문법을 틀렸다면 자동으로 수정도 가능하다
단, 이를 이용해 script태그를 넣었다면 HTML에는 삽입되지만 실행은 되진 않는다
주의해야할 점은 다음과 같다
innerHTML은 단순 추가가 아니라 새로운 값을 덮어 씌우는 것이다
(기존 내용 삭제 후 기존 내용 + 추가한 내용 할당)
이는 리로딩을 부르는 문제점이 존재하여 성능 저하를 유발할 수 있다
보안의 위험이 존재한다(XSS - cross-scripting)
textContent
textContent를 사용하게 되면 순수한 text에만 접근이 가능해진다
따라서 안전한 방법으로 오로지 text만 입력하는 것이 가능해진다
const a = document.querySelector('.a');
a.textContent = '안녕하세요';


textContent를 이용하여 다음과 같이 변한 것을 확인할 수 있다
hidden
참고로 이는 HTML에서도 JS에서도 사용이 가능하다
무언가를 보이지 않게 숨기는 역할을 할 때 사용한다
const a = document.querySelector('.a');
a.textContent = '안녕하세요';
a.hidden = true;

둘 다 한국어로 번역하면 '속성'이다 하지만 JS에선 명확한 차이가 존재한다
만약 브라우저가 웹페이지를 만나 parsing이 일어나면 DOM 객체를 생성한다
이때 elementNode에서 표준 HTML attribute가 DOM 객체의 property가 된다
하지만 항상 attribute가 property와 1대1로 매핑되진 않는다
즉, property로 전환되지 않는다는 의미이다
이는 비표준 속성으로 attribute가 작성되었을때다
예를 들어 body 태그 내부에 age라는 attribute를 지정했을때
이는 비표준 속성이다
따라서 document.body.age로는 추적할 수 없다
그렇다면 비표준 속성에 접근하기 위한 방법은 무엇이 있을까?
method를 살펴보자
그렇다면 비표준 속성을 해결하기 위한 다른 방법은 무엇이 있을까?
바로 data-를 붙이는 방법이다
이렇게 적용된 attribute는 document.body.attribute를 이용하여 확인이 가능하다
이와 같은 방법을 사용하는 이유는 HTML은 살아있는 언어이다
따라서 언제든 사용했던 비표준 속성이 표준으로 바뀌어 충돌이 일어날지 알 수 없다
data-를 이용하여 attribute를 할당했다면 dataset을 이용하여 불러오는 것과 수정하는 것도 가능하다
console.log(a.dataset.age) // 25
dataset은 호출될 시 DOMStringMap으로 출력된다
참고로 DOM property는 항상 문자열은 아니다
hidden만 살펴봐도 true 와 false가 할당되는 구조이다
attribute - HTML 내부 , 정적이라 값의 변화는 없다
property - DOM객체 내부 , 동적으로 작동하며 값의 변화가 있다
ex) checkbox 클릭 시 attribute는 변하지 않지만 property에는 checked가 추가된다