[코딩온] 프론트엔드 입문 Day 26~27 (JS 표준 객체, DOM 접근, 이벤트, 쿼리스트링)
지난 이틀간 JS 표준 객체와 DOM에 대해서 배웠다. 수업 시간에 설명을 들을 때는 배운 내용들이 전부 초면인줄 알았는데, DOM에서 document 속성으로 요소를 선택하는 명령은 예전에 공부하면서 사용해봤던 거였다. 당시에는 뭔지도 모르고 사용했는데, 이제는 뭔지 알면서 사용할 수 있게 되었으니, 나날이 성장하고 있긴 하구나!🌱

JS의 데이터 타입
Day 23 후기에서도 설명했지만, JS의 데이터 타입은 '기본 자료형(원시 타입)'과 '객체 자료형(참조 타입)'으로 나뉜다.
- 객체 - 키(key)와 값(value)으로 이루어진 property의 집합
- 프로퍼티(property) - 객체를 이루는 데이터
- 메소드(method) - 프로퍼티 값으로 쓰는 함수. 동작을 의미한다.
let panda = {
name : "fubao";
birth : 2020;
gender : 'female';
home : 'Everland';
eat : function(){
return bamboo;
}
}
예를 들어, 위 코드에서
- property - let panda에 할당한 scope 내부, 판다에 대한 모든 정보
- 속성 - 프로퍼티 중에 명사로 짝을 이루는 값 (name, birth, gender, home..)
- 메소드 - key가 동사인 것 (eat..)
💡 여기서 잠깐!
- 기본 자료형 - 다른 변수에 값을 할당하거나 함수 인자를 넘길 때 값을 복사하여 전달한다. (Access by Value)
- 객체 자료형 - 메모리 주소의 참조값을 저장한다. (Access by Reference)
// 기본 자료형
let a = 10;
let b = a;
console.log('a:', a); // 10
console.log('b:', b); // 10
b = a;
a = 20;
console.log('a:', a); // 20
console.log('b:', b); // 10
저게 무슨 얘기인지, 텍스트만 읽고는 모르겠어서 실험을 해보았다.
- a에 10을 할당하고, b = a 라고 설정했을 때, 콘솔에는 a와 b 모두 10이 나온다.
- b = a 인 상태 그대로 a에 20을 할당하면, a가 변했으니 b도 변해야할 것 같지만, b는 여전히 10을 가지고 있다.
→ 처음에 b = a 라고 했을 때, b는 a의 값을 통째로 복사해서 자신의 '그릇'에 담았다.
→ 그러므로 a가 20으로 바뀌어도, b는 기존의 값인 10을 그대로 갖고 있는 것이다.
반면에 '객체 자료형'은 변수에 할당될 때, 값을 저장하는 메모리의 주소 값을 저장하고, 메모리 주소를 이용해서 변수의 값에 접근한다. 그렇기 때문에 Object 자료형 중에 배열 데이터를 const 키워드로 할당해도 요소의 추가/삭제가 자유로운 것이다!
JS 표준 객체
우선 예전에 내일배움카드로 공부했던 내용인 '객체의 종류'에 대해서 잠깐 정리해보도록 하겠다.✍
- 표준 내장 객체 - JS 엔진에 내장되어 있는 것. 문자, 날짜, 배열, 수학 등
- 브라우저 객체 모델(BOM, Browser Object Model)
→ 브라우저에 계층 구조로 내장되어 있는, 브라우저에 대한 모든 내용을 담고 있는 객체.
→ 브라우저에 관한 정보 제공이나 브라우저의 모양을 제어할 수 있다. - 문서 객체 모델(DOM, Document Object Model)
→ HTML 문서에 접근하기 위한 인터페이스
→ 문서 내의 모든 요소를 정의하고, 각각의 요소에 접근할 수 있다.
Date 객체
시간, 날짜에 대한 정보를 얻기 위해 사용한다.
// 현재 날짜 (시간까지 출력)
let now = new Date();
console.log(now); // Mon Mar 04 2024 21:06:04 GMT+0900 (한국 표준시)
// 1970년 1월 1일 기준으로 해당 ms만큼 지난 시간
let Jan_02_1970 = new Date(1000 * 3600 * 24);
console.log(Jan_02_1970);
// Fri Jan 02 1970 09:00:00 GMT+0900 (한국 표준시)
// new Date(year, month, date, hour, minute, seconds, ms)
console.log(new Date(2021, 2, 15));
// Mon Mar 15 2021 00:00:00 GMT+0900 (한국 표준시)
console.log(new Date(2021, 2, 15, 18, 30, 15));
// Mon Mar 15 2021 18:30:15 GMT+0900 (한국 표준시)
// date 객체 메소드: 년월일 등의 값을 얻을 수 있음
console.log(now.getFullYear()); // 2024
console.log(now.getMonth()); // 2 (0부터 시작하기 때문에 3월이라는 의미)
console.log(now.getDate()); // 4
console.log(now.getHours()); // 시간만 뽑아냄
console.log(now.getMinutes()); // 분
console.log(now.getSeconds()); // 초
console.log(now.getMilliseconds()); // ms
console.log(now.getDay()); // 요일 (0~6), 일요일이 0
- Date 객체에서는 밀리초(ms)라는 개념이 종종 사용된다.
1000ms가 1s이고, 1000ms * 3600이 1시간, 1000ms * 3600 * 24는 24시간이다. - new Date()의 경우, year는 네 자리를 입력해야한다.
month는 0이 1월이고 11이 12월이다. (zero-based numbering)
date는 1~31까지의 숫자가 나오는데 값이 없으면 1로 처리된다.
hour, minute, second, ms는 값이 없으면 0으로 처리된다. - getDay()도 마찬가지로 zero-based numbering으로 일요일부터 0으로 시작한다.
Math 객체
수학적인 상수와 함수를 위한 속성과 메소드.
웹 브라우저마다 다른 결과를 얻을 수 있어서, 정확한 결과가 필요하다면 사용하지 않는 것이 좋다.
console.log(Math.E); // 자연로그
console.log(Math.PI); // 원주율 출력
console.log(Math.SQRT2); // 2의 제곱근(루트)
// 메소드
console.log(Math.min(10, 2, 6, -50)); // 인자로 전달받은 값 중에 최소값 출력
console.log(Math.max(10, 2, 6, -50)); // 인자 중에 최대값 출력
console.log(Math.random()); // 인자 없음. 0<= x < 1 사이의 난수 생성. (소수점)
console.log(Math.round(3.4)); // 3, 소수를 반올림하여 정수로 변환
console.log(Math.floor(3.4)); // 3, 소수를 버려서 정수로 변환
console.log(Math.ceil(3.4)); // 4, 소수를 올려서 정수로 변환
DOM
앞서 설명했듯이, HTML 문서에 접근하기 위한 인터페이스.
쉽게 말해서, 문서를 브라우저가 이해할 수 있게 구성하여 메모리에 넣는 것, 또는 모든 요소와 텍스트를 각각의 객체로 만들어 그들의 부자관계를 트리 구조로 표현해놓은 것이다.
DOM Tree - DOM이 HTML을 아래 사진처럼 태그 트리 구조로 표현한 것.
트리를 이루는 요소 하나하나는 '노드(Node)'라고 부른다.
→ 모든 요소(element)는 node지만, 모든 node가 요소인 것은 아니다.
※ DOM Tree에 관한 자세한 설명 - https://ko.javascript.info/dom-nodes
DOM 트리
ko.javascript.info
DOM을 이용해서 할 수 있는 작업은,
- HTML 요소/속성의 추가 or 제거
- 문서의 모든 요소 or 속성 변경
- 문서의 모든 CSS 스타일 변경
- 새로운 HTML 이벤트를 추가하거나 모든 이벤트에 반응, 등이 있다.
Document 객체
HTML 요소에 접근하여 특정 행동을 할 때 사용하는 객체로서 요소의 추가, 삭제, 변경 등이 가능하다.
// 1. 속성
console.log(document); // html 문서 전체
console.log(document.documentElement);
console.log(document.head);
console.log(document.body);
console.log(document.URL);
console.log(document.domain);
- document - 예시 코드에도 있듯, HTML 문서 전체를 의미한다.
- documentElement - HTML 문서에 있는 모든 요소
- head - 헤드 태그와 그 이하에 있는 모든 요소
- body - 바디 태그와 그 이하에 있는 모든 요소
- URL - 열려있는 페이지의 전체 주소가 찍힌다 (http부터)
- domain - 페이지의 도메인 주소만 찍힌다 (ip주소, www..com)
// 2. 메소드
// getElementBy ~
console.log(document.getElementById('gold'));
console.log(document.getElementsByClassName('pink'));
console.log(document.getElementsByTagName('div'));
// CSS 선택자 불러오기
console.log(document.querySelector('#green'));
console.log(document.querySelector('div.pink'));
console.log(document.querySelector('body div'));
console.log(document.querySelectorAll('.others'));
console.log(document.querySelectorAll('div.pink')[2]); //인덱스로 접근
- 'getElementBy..' 라는 메소드를 통해 id, class, tag 이름으로 요소를 선택할 수 있다.
- querySelector - 해당하는 CSS 선택자 중에 일치하는 첫번째 요소만 불러온다.
- querySelectorAll - 해당되는 모든 요소를 불러온다.
💡 여기서 잠깐!
getElementBy~ 메소드는 콘솔에 HTML Collection으로 찍히고, querySelector는 NodeList로 찍힌다.
HTML Collection과 NodeList 모두 '유사 배열'이라는 공통점이 있지만,
getElementBy~ 는 JS에서 노드를 생성+삭제하는 변경을 감지하고, querySelector는 감지하지 못한다.
★ 유사 배열 - length 속성을 가지고 있어 [i]로 접근 가능. 표준 객체 메소드는 사용 불가, forEach는 사용 가능.
// 1. 태그 내부 컨텐츠 다루기
div1.innerText = '안녕하세요!';
div1.innerHTML = '<b>안녕</b>하세요!';
// 2. 속성 가져오기, 수정하기
let naver = document.getElementById('naver');
console.log(naver.getAttribute('href')); // 가져오기
// console.log(naver.href); -> 점 접근법으로 가져오기
naver.setAttribute('href', 'https://www.google.co.kr'); // 수정하기
- innerText - 요소 안의 텍스트를 가져오거나 입력된 문자열로 변경해주는 속성이다. 글자만 가져오기 때문에 해당 속성으로 string을 수정하면서 태그를 입력하면, 태그도 string으로 입력된다.
- innerHTML - 요소 안의 코드를 가져오거나 수정하는 속성. 태그까지 가져오기 때문에 여기서는 대체할 텍스트를 입력하면서 태그를 같이 입력하면 제대로 반영된다.
★ innerText와 innerHTML 모두 앞/뒤에 남는 공백 문자를 자동으로 제거해준다. - getAttribute() 메소드를 통해, 요소의 속성을 가져올 수 있다.
→ 위 코드에서는 naver라는 아이디를 가진 요소의 href 속성을 가져왔다. - setAttribute() 메소드로 요소의 속성을 수정할 수 있다.
→ 위 코드에서는 href 속성의 값을 구글로 변경했다.
// 3. 스타일 변경
let flowers = document.querySelectorAll('#flower li');
for (let li of flowers) {
// style 속성 지정 (개별적으로 할 때)
// li.style.backgroundColor = 'skyblue';
// li.style.color = 'green';
li.classList.add('changeStyle');
// changeStyle 이라는 클래스에 있는 코드 전부 실행
}
// 클래스 목록 가져오기
console.log(flowers[0].classList);
// 클래스 삭제하기
flowers[0].classList.remove('changeStyle');
/* 클래스 갖고 있는지 확인하기 -> true, false로 반환 (조건문에서 사용) */
console.log(flowers[0].classList.contains('changeStyle'));
// 클래스 토글하기 -> 해당 클래스가 있으면 제거, 없으면 추가
flowers[0].classList.toggle('changeStyle');
flowers[1].classList.toggle('changeStyle');
- classList로 시작하는 메소드로 선택한 요소에 클래스를 추가/제거하거나 존재하는지 체크할 수 있다.
→ 위 코드는 CSS에서 미리 changeStyle이라는 클래스로 지정한 속성들을 classList.add()로 한 번에 추가했다. - 클래스 목록을 가져오는 메소드는 해당 요소에 지정된 클래스를 전부 불러온다.
→ 위 코드에서는 flowers라는 목록의 0번째 요소가 가지고 있는 클래스를 전부 불러왔다. - remove로 지정된 클래스 삭제, contains로 지정된 클래스가 존재하는지 확인할 수 있고, toggle로 존재 여부를 확인하고 추가/제거할 수 있다.
// 4. 부모 자식 node
const parentEl = document.querySelector('#flower'); // ul
const childEl = document.querySelector('#flower .pink'); // ul > li.pink
// 자식 node 접근하기
console.log(parentEl.children); // ul의 자식 li들을 유사배열로 불러온다
console.log(parentEl.children[2]);
// 특정 자식요소 불러올 때 배열처럼 인덱스로 접근, 자식이 하나여도 동일
// 부모 node 접근하기 -> 요소 자체에 접근한다
console.log(childEl.parentNode); // childEl의 부모를 선택
// 형제 node 접근하기
console.log(childEl.previousElementSibling); // 위 노드 접근
console.log(childEl.parentNode.children[2].previousElementSibling);
console.log(childEl.nextElementSibling); // 아래 노드 접근
- 자식 node는 부모를 통해, 부모 node는 자식을 통해 접근할 수 있다.
- 선택된 형제 node가 가장 첫번째 자식이면, previousElementSibling을 사용했을 때 null이 나온다.
이런 상황을 피하기 위해서는 부모에게 갔다가 다시 자식에게 가는 방법을 사용할 수 있다.
// 5. 요소 추가, 생성, 삭제
// 요소 생성
const pEl = document.createElement('p');
pEl.innerHTML = 'JS로 만든 p 태그';
console.log(pEl);
// append(): pEl 요소 html에 추가
container.append(pEl);
const pEl2 = document.createElement('p');
pEl2.innerHTML = 'JS로 만든 p 태그 2';
container.append(pEl, pEl2); // 한 번에 추가하기
// appendChild()
const p2 = document.createElement('p');
p2.innerText = 'appendChild 사용해서 추가한 첫번째 p태그';
appendChild(p2);
// prepend()
const div2 = document.createElement('div');
div2.innerText = 'prepend로 추가한 첫번째 요소';
container.prepend(div2);
createElement()로 HTML 문서에 요소를 생성하고, append()로 만든 요소를 추가할 수 있다.
- createElement()로 만든 요소는 여러 번 삽입해도 한 번만 들어간다. (1개 밖에 안 만들었으니까)
- append()는 선택된 요소의 마지막 자식으로 추가. 여러 자식요소를 한 번에 추가할 수도 있다. 텍스트를 바로 추가하는 경우에는 부모 요소의 컨텐츠로 들어간다.
- appendChild() - 선택된 요소의 마지막 자식으로 추가. 한 번에 하나의 요소만 추가 가능하고, 텍스트 추가 불가
- prepend() - 선택된 요소의 첫번째 자식으로 추가
★ append, appendChild, prepend → 선택된 요소의 자식으로 (인자로 전달한 자식을) 추가
// before()
const h1 = document.querySelector('#h1');
const h3 = document.createElement('h3');
h3.innerText = 'before로 추가한 h3';
h1.before(h3);
// after()
const h2 = document.createElement('h2');
h2.innerText = 'after로 추가한 h2';
h1.after(h2);
// 요소 삭제
const deleteDiv = document.querySelector('.container div');
deleteDiv.remove();
- before() - 선택된 요소의 앞에 추가할 요소가 삽입된다.
- after() - 선택된 요소의 뒤에 추가할 요소가 삽입된다.
★ before, after → 선택된 요소의 형제로 인자로 전달한 요소를 추가 - remove() - 선택한 요소를 삭제한다.
addEventListener()
선택된 요소에 지정한 이벤트가 발생하면 약속된 명령이 실행되는 메소드.
변수에 담아서 사용하는 편이 낫다.
addEventLister()의 특징은,
- CSS의 hover처럼 특정 행동이 진행된 순간에만 이벤트가 발생하는게 아니라, 한번 발생하면 refresh 되기 전까지는 발생한 상태를 계속 유지한다.
- 하나의 요소에 여러 이벤트를 등록할 수 있다.
- 아래 예시 코드의 btn3처럼, 같은 기능을 재사용하는 경우에 함수를 선언하고 addEventListener()로 불러와서 사용할 수 있는데, 함수를 불러오는 경우에는 괄호를 작성하지 않는다. → 괄호를 작성하면 브라우저가 JS 파일을 위에서부터 읽으며 내려오다가 해당 코드를 만나면 이벤트 진행 전에 함수부터 실행시켜버리기 때문.
// 기본 형태. 익명함수와 함수표현식 사용해 변수에 저장하지 않고 작성.
const btn1 = document.querySelector('.btn-black');
btn1.addEventListener('click', function () {
alert('버튼1을 클릭했습니다!');
});
// 화살표 함수 사용해 container에 요소 추가
const btn2 = document.querySelector('.btn-pink');
btn2.addEventListener('click', () => {
const div = document.createElement('div');
div.innerText = '버튼2를 클릭해 추가된 div';
container.append(div);
});
// 함수 선언해서 이벤트 등록
const btn3 = document.querySelector('.btn-gold');
btn3.addEventListener('click', changeColor);
function changeColor() {
let divs = document.querySelectorAll('.container div');
divs.style.color = 'red';
}
// 이벤트가 적용된 자신을 선택하는 this 키워드
const btn4 = document.querySelector('.btn-skyblue');
btn4.addEventListener('click', changeBtnColor);
function changeBtnColor() {
this.style.backgroundColor = 'orange';
this.style.color = 'white';
}
addEventListener() 메소드에 추가할 수 있는 이벤트 종류 - https://homzzang.com/b/js-1130
★ HTML 문서에서 onclick 속성의 값으로 JS에서 작성한 함수를 연결하여 이벤트를 실행시키는 방법도 있다.
그러나 대체적으로 JS에서 바로 addEventLister()를 사용하는 편이 더 많다고 한다.
쿼리스트링 (Query String)
사용자가 입력 데이터를 전달하는 가장 단순한 방법.
- 물음표(identifier) 이후부터 주소가 끝나는 지점까지가 쿼리스트링이다.
- "파라미터(parameter)=value"의 형식으로 key와 value를 적는다.
- 파라미터가 여러 개일 경우, &(connector)로 연결해서 작성한다.
- 파라미터의 이름과 값은 API 명세서에 어떤 의미를 나타내는지가 작성되어 있다.
// 쿼리스트링 추출하기
console.log(window.location.search);
const urlParam = new URLSearchParams(window.location.search);
const productId = urlParam.get('id');
쿼리스트링을 추출하는 방법에는 두 가지가 있는데,
- window.location.search - 주소에서 쿼리스트링이 어떤 부분인지만 추출하는 것.
- URLSearchParams - 다양한 메소드를 사용해서 특정한 키와 값을 확인하거나 추가/삭제할 수 있다.
https://developer.mozilla.org/ko/docs/Web/API/URLSearchParams
URLSearchParams - Web API | MDN
URLSearchParams 인터페이스는 URL의 쿼리 문자열을 대상으로 작업할 수 있는 유틸리티 메서드를 정의합니다.
developer.mozilla.org
URL objects
javascript.info
여기까지가 지난 이틀간 배운 내용이었고, 어느덧 두번째 파트도 막바지에 접어들어 동기들과 함께 하는 팀 프로젝트가 다가오고 있다. 프로젝트가 어떻게 진행될지 두렵고 걱정되면서도, 어떤 페이지를 만들게 될지 기대되기도 한다. 이번에도 훌륭한 팀원들을 만나서, 좋은 결과를 낼 수 있었으면 좋겠다!
