HTML 문서의 내용을 트리형태로 구조화하여 웹페이지와 프로그래밍 언어를 연결시켜주는 역할
노드(node) : 각각의 요소와 속성, 콘텐츠를 표현하는 단위
DOM 트리에 접근하기
document : 브라우저가 불러온 웹페이지 / DOM 트리의 진입점
document 객체를 통해 HTML 문서에 접근이 가능하다.
// 해당하는 Id를 가진 요소에 접근하기
document.getElementById();
// 해당하는 모든 요소에 접근하기
document.getElementsByTagName();
// 해당하는 클래스를 가진 모든 요소에 접근하기
document.getElementsByClassName();
// css 선택자로 단일 요소에 접근하기
// 페이지에 있는 selector 태그중 가장 앞에 나오는 태그만 나온다
document.querySelector("selector");
// css 선택자로 여러 요소에 접근하기
document.querySelectorAll("selector");
DOM 제어 명령어
이벤트 삽입
target.addEventListener( type, listener )의 문법 형태를 지닌다.
// 이벤트의 타입에는 click, mouseover, mouseout, wheel 등 다양한 이벤트를 감지합니다.
// listener 함수의 인수에는 이벤트에 대한 정보가 담겨있습니다.
const myBtn = document.querySelector("button");
myBtn.addEventListener('click', function(){
console.log("hello world");
})
클래스 제어
classList 객체를 통해 요소의 class 속성을 제어할 수 있다.
<button>Make me BLUE!</button>
const myBtn = document.querySelector("button");
myBtn.addEventListener('click', function(){
// blue 라는 클래스의 속성 값을 지정 할 수 있습니다.
myBtn.classList.add("blue");
// myBtn.classList.remove("blue"); 클래스를 제거합니다.
// myBtn.classList.toggle("blue"); 클래스를 토글합니다. 없으면 넣어주고, 있으면 제거합니다.
// myBtn.classList.contains("blue"); 해당하는 클래스가 있는지 확인합니다.
})
const myBtn = document.querySelector("button");
const myP = document.querySelector("p");
const myInput = document.querySelector("input");
myBtn.addEventListener('click', function(){
myP.textContent = myInput.value;
});
// input 요소에 'input' 이벤트를 연결하면 실시간으로 값이 반영되게 만들 수도 있습니다.
myInput.addEventListener('input', ()=>{
myP.textContent = myInput.value;
});
myP.innerHTML = "<strong>I'm Strong!!</strong>";
// innerHTML 은 요소(element) 내에 포함된 HTML 마크업을 가져오거나 설정합니다. 중요한 기능은 innerHTML로 값을 할당할 때, 마크업으로 변환할 수 있는 문자열이 있다면 마크업으로 만들어 보여준다는 것 입니다. 만약 그런 문자열이 없다면 그냥 문자열만 컨텐츠로 설정합니다.
// innerText 속성은 요소의 렌더링된 텍스트 콘텐츠를 나타냅니다. (렌더링된에 주목하세요. innerText는 텍스트 내에 문법적으로 처리가 가능한 텍스트가 있으면 처리가 끝난 결과물을 텍스트로 전달합니다.)
// textContent 속성은 노드의 텍스트 콘텐츠를 표현합니다. 컨텐츠를 단순히 텍스트로만 다룹니다.
const sayHi = document.querySelector('.sayHi');
// 열린태그 바로 앞에 해당 요소를 추가해라
sayHi.insertAdjacentHTML('beforebegin', '<span>안녕하세요 저는</span>');
// 열린태그 바로 뒤에 해당 요소를 추가해라 (첫 자식)
sayHi.insertAdjacentHTML('afterbegin', '<span>재현입니다</span>');
// 닫는태그 바로 앞에 해당 요소를 추가해라 (마지막 자식)
sayHi.insertAdjacentHTML('beforeend', '<span>면접오시면</span>');
// 닫는 태그 바로 뒤에 해당 요소를 추가해라
sayHi.insertAdjacentHTML('afterend', '<span>치킨사드릴게요</span>');
DOM 안에서 노드 탐색하기
<!-- 주석입니다 주석. -->
<article class="cont">
<h1>안녕하세요 저는 이런 사람입니다.</h1>
<p>지금부터 자기소개 올리겠습니다</p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Deserunt incidunt voluptates laudantium fugit, omnis
dolore itaque esse exercitationem quam culpa praesentium, quisquam repudiandae aut. Molestias qui quas ea iure
officiis.
<strong>감사합니다!</strong>
</article>
const cont = document.querySelector(".cont");
console.log(cont.firstElementChild); // 첫번째 자식을 찾습니다.
console.log(cont.lastElementChild); // 마지막 자식을 찾습니다.
console.log(cont.nextElementSibling); // 다음 형제요소를 찾습니다.
console.log(cont.previousSibling); // 이전 형제노드를 찾습니다.
console.log(cont.children); // 모든 자식요소를 찾습니다.
console.log(cont.childNodes); // 모든 자식노드를 찾습니다.
console.log(cont.parentElement); // 부모 요소를 찾습니다.
// 자기 자신부터 시작해 부모로 타고 올라가며 가장 가까운 cont 클래스 요소를 찾습니다. 단, 형제요소는 찾지 않습니다.
console.log(cont.querySelector('strong').closest('.cont').innerHTML);
<!-- 앵커의 기본 동작을 중지 -->
<a href="https://www.naver.com" class="link">네이버 링크입니다만..</a>
<script>
const link = document.querySelector('.link');
link.addEventListener('click', (event) => {
console.log('clicked');
event.preventDefault();
})
</script>
<!-- submit 의 기본 동작을 중지 -->
<form action="">
<button type="submit" class="submit">제출</button>
</form>
<script>
const submit = document.querySelector('.submit');
submit.addEventListener('click', (event) => {
console.log('clicked');
event.preventDefault();
})
</script>
stopPropagation
이벤트의 전파과정을 차단
부모자식 관계에 있는 요소들이 둘다 eventlistener가 달려있다면, 부모의 이벤트가 자식 이벤트에 의해 발생하는 경우를 막을 때 사용
Ajax
동기 vs 비동기
동기 : 코드의 순서에 따라 코드가 처리되는 것
비동기 : 코드의 순서와 별도로 동시에 코드 처리가 진행되는 것
보통 언제 발생할지 모르는 일을 처리할때 비동기를 사용한다.
console.log(1);
// setTimeout으로 콜백함수가 일정시간(100ms) 뒤에 실행하도록 코드를 작성합니다. 순서대로 실행되지 않습니다.(비동기적으로 실행). 이러한 비동기 실행 코드는 setInterval, addEventListener 와 같은 함수들이 있습니다.
setTimeout(() => console.log(2), 100);
[3, 4, 5].forEach(i => console.log(i));
console.log(6);
// XHR 객체를 생성합니다.
const requestObj = new XMLHttpRequest();
requestObj.open('GET', 'url'); // 요청을 초기화합니다. 통신방법과 요청을 발신할 대상의 주소를 전달합니다.
requestObj.onreadystatechange = () => { // readystate 가 변화하면 실행되는 이벤트리스너 입니다.
// readystate : 요청을 보내는 클라이언트의 상태를 의미합니다.
// readystate의 종류
// 0 (UNSENT) - XHR 객체가 생성되었지만 아직 초기화되지 않았습니다.
// 1 (OPENED) - open()함수가 호출되어 요청이 초기화되었습니다.
// 2 (HEADERS_RECEIVED) - send()함수가 호출되었습니다.
// 3 (LOADING) - 데이터를 다운받는 중 입니다.
// 4 (DONE) - 통신이 완료되었습니다.
if (requestObj.readyState == 4 && requestObj.status == "200") {
const result = requestObj.responseText;
}
};
requestObj.send(); // 서버로 요청을 보냅니다. send 메소드가 실행되어야만 우리가 위에서 설정한 내용들이 의미를 가지게 됩니다.
callback 지옥
callback 함수 : 함수의 인자로 들어오는 함수
callback 함수가 끊이지 않는걸 의미한다.
비동기 코드를 동기코드와 같이쓰는 것은 문제가 발생할 수 있다.
실행 순서를 보장할 수 없기 때문에
// sudo코드
const result = 비동기통신함수();
const result2 = 비동기통신함수2();
const total = result + result2;
// 이런 방식은 불가능합니다. result와 result2에 무슨 값이 들어있을지 생각해보세요.
비동기 방식에서 실행 순서를 보장하기위해 callback함수를 활용할 수 밖에 없고, callback 지옥에 빠지게 된다.
const total = 비동기통신함수(
input,
통신함수의결과를가공하는함수1 (
result,
비동기통신함수2(
통신함수의결과를가공하는함수2(
result,
result2
)
)
)
);
// 이렇게 비동기 함수가 끝나기 전에 중간 중간에 필요한 콜백함수를 실행시키며 사용할 수 밖에 없었습니다.
예시코드
<script>
'use strict';
class UserStorage {
// 사용자가 입력한 값과 유저 정보가 일치하는 유저를 찾습니다.
searchUser(userName, password, onSuccess, onError) {
const requestObj = new XMLHttpRequest();
requestObj.open('GET', 'users.json'); // 요청 초기화
requestObj.onreadystatechange = () => {
if (requestObj.readyState === 4 && requestObj.status === 200) {
const result = JSON.parse(requestObj.responseText).user.find((item) => {
return item.userName === userName && item.password === password
});
// 사용자가 입력한 정보와 일치하는 유저가 존재한다면
if (result) {
onSuccess(userName);
// 사용자가 입력한 정보와 일치하는 유저가 존재하지 않는다면
} else {
onError(new Error('user not found'));
}
}
}
requestObj.send();
}
// 유저에 맞는 인사말을 출력합니다.
sayHi(user, onSuccess, onError) {
const requestObj = new XMLHttpRequest();
requestObj.open('GET', 'greetings.json'); // 요청 초기화
requestObj.onreadystatechange = () => {
if (requestObj.readyState === 4 && requestObj.status === 200) {
const result = JSON.parse(requestObj.responseText).greetings.find((item) => {
return item.userName === user
});
// 사용자가 입력한 유저 이름과 일치하는 인사말이 존재한다면
if (result) {
onSuccess(result);
// 사용자가 입력한 유저 이름과 일치하는 인사말이 존재하지 않는다면
} else {
onError(new Error('no greetings'));
}
}
}
requestObj.send();
}
}
const userStorage = new UserStorage();
const userName = prompt('이름을 입력하세요.');
const password = prompt('비밀번호를 입력하세요.');
userStorage.searchUser(
userName,
password,
(name) => {
userStorage.sayHi(
name,
(result) => {
alert(`당신에게 인사합니다! ${result.userName}님 ${result.greetings}`);
},
(error) => {
console.log(error);
});
},
(error) => {
console.log(error);
}
);
</script>
Fetch API
XMLHttpRequest를 대체할 새로운 API
XHR과의 차이점 : fetch는 인스턴스를 만들지 않고, ‘약속’을 반환한다.
// 커피를 주문하는 프로미스 객체를 생성합니다. 생성자에는 약속을 지키기 위한 resolve와, 약속을 지키지 못했을 때를 대비한 reject 두 가지를 인자로 전달합니다.
// 프로미스 객체를 생성하는 순간 프로미스 생성자함수의 콜백 함수가 실행됩니다. 이를 실행자(executor)라 부릅니다.
const orderCoffee = new Promise((resolve, reject) => {
const requestObj = new XMLHttpRequest();
requestObj.open('GET', 'orderCoffee.txt');
requestObj.onreadystatechange = () => {
if (requestObj.readyState === 4) {
if (requestObj.status === 200) {
const result = requestObj.responseText;
// resolve 메소드가 실행되면 then 메소드가 자동으로 호출됩니다.
resolve(result);
} else {
// resolve 메소드 호출이 없는 상태에서 reject 메소드가 실행되면 catch 메소드가 자동으로 호출됩니다.
reject(new Error(`커피주문이 정상적으로 이뤄지지 않았습니다.: ${requestObj.status}`));
}
}
};
requestObj.send();
});
// 이 부분에 주목해주세요. then 메소드를 사용하면 비동기 코드를 마치 동기적인 코드처럼 작성할 수 있습니다. 앞에서 작성한 XHR 코드와 비교해보는것도 좋습니다.
// resolve 메소드가 실행될때 전달된 인자는 then 메소드의 콜백함수의 인자로 전달됩니다.
orderCoffee.then((asyncResult) => {
console.log(asyncResult);
console.log('약속이 이루어졌습니다.');
return asyncResult;
}).catch((error) => { // then 메소드는 프라미스 객체를 반환하기 때문에 catch 메소드를 이어서 쓰는것이 가능합니다.
// resolve 메소드와 마찬가지로 reject 메소드가 실행될때 전달된 인자는 catch 메소드의 콜백함수의 인자로 전달됩니다.
console.log(error);
})
Promise란?
무언가를 할 것이라 미리 정하는 행위
프로미스 객체는 약속을 지키기 위한 resolve와 약속을 지키지 못했을때를 대비한 reject 두가지를 인자로 전달한다.
resolve : 데이터 통신이 성공 했을때 실행
reject : 데이터 통신이 실패 했을때 실행
서버와 통신이 성공했을때 클라이언트에서 처리해야하는 내용이 then 뒤에 나옴
반대로 서버와 통신이 실패했을때 클라이언트에서 처리해야하는 내용은 catch 뒤에 나옴
async, await
async 키워드는 어떤 함수든 프로미스 객체를 반환하게 만들 수 있다.
await는 async 함수 안에서 promise 객체의 상태가 결정될 때까지 다음 코드를 대기시킨다. 그리고 프로미스 객체의 fulfilled 값을 반환한다.
await는 반드시 async 함수 안에서만 사용가능 하다.
async function message() {
const hello = await new Promise((resolve) => {
setTimeout(() => {
resolve('hello');
}, 100)
})
const world = await new Promise((resolve) => {
setTimeout(() => {
resolve('world');
}, 100)
})
console.log(`${hello} ${world}`);
}
message();