비동기 함수를 지원하는 라이브러리
XMLHttpRequest (XHR) 객체는 웹 브라우저와 서버 간에 데이터를 비동기적으로 교환하기 위해 사용
XMLHttpRequest는 오래된 API이기 때문에, 대부분의 현대 웹 애플리케이션은 fetch API
나 다른 라이브러리(예: Axios
)를 사용하여 HTTP 요청 수행
1. XMLHttpRequest 객체 생성
const xhr = new XMLHttpRequest();
2. 요청 초기화
xhr.open(method, url, async);
3. 응답 처리
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 400) {
console.log(xhr.responseText);
} else {
console.error('서버에서 에러 응답:', xhr.statusText);
}
};
xhr.onerror = function() {
console.error('요청 중 에러 발생');
};
4. 요청 보내기
xhr.send();
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="video">
<iframe width="640" height="360" src="https://www.youtube.com/embed/phuiiNCxRMg" title="aespa 에스파 'Supernova' MV" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div>
<button id="load">서버에서 정보 불러오기</button>
<ul id="content"></ul>
<script>
// 버튼 클릭 이벤트
document.getElementById('load').addEventListener('click', e => {
// 서버에 비동기 통신 요청을 보내야 함.
// 게시물 정보를 가져오기
console.log('click!!');
const xhr = new XMLHttpRequest();
// 요청 방식과 요청 URL을 적음
const url = 'https://jsonplaceholder.typicode.com';
xhr.open('GET', `${url}/users/1/posts`);
// 요청 전송
xhr.send();
// 응답 정보 확인
xhr.onload = e => {
// 서버가 응답한 데이터는 JSON이라는 문자열 포맷의 데이터다.
// JSON은 자바스크립트가 아니다.
// JSON -> JS로 변환해야 함.
// JSON.parse() : JSON -> JS
// JSON.stringify() : JS -> JSON
const response = xhr.response;
const boardList = JSON.parse(response);
console.log(boardList[0]);
// 화면에 렌더링
const $ul = document.getElementById('content');
let liTag = '';
boardList.forEach(b => {
liTag += `
<li>
#글번호: ${b.id}, 제목: ${b.title}, 작성자id: ${b.userId}
</li>
`;
});
$ul.innerHTML += liTag;
};
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.video {
width: 300px;
}
.video iframe {
width: 100%;
}
</style>
</head>
<body>
<div class="video">
<iframe src="https://www.youtube.com/embed/phuiiNCxRMg" title="aespa 에스파 'Supernova' MV" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div>
<div>
글제목: <input type="text" id="title">
</div>
<div>
글내용: <textarea id="content"></textarea>
</div>
<div>
<button id="add">등록</button>
</div>
<script>
document.getElementById('add').addEventListener('click', e => {
// 서버에 post 요청 보내는 방법
const xhr = new XMLHttpRequest();
const url = 'https://jsonplaceholder.typicode.com';
// 요청정보 설정
xhr.open('POST', `${url}/posts`);
// 요청 보내기 : post요청은 서버에 보낼 데이터(payload)를 추가해야 함
const payload = {
title: document.getElementById('title').value,
body: document.getElementById('content').value,
userId: 1
};
// 요청 헤더에 payload의 mime type을 명시해야 함.
xhr.setRequestHeader('content-type', 'application/json');
// 읽은데이터는 순수자바스크립트이므로 서버에 보낼 때는
// JSON으로 변환하여 보내야 한다.
xhr.send(JSON.stringify(payload));
// 응답 확인
xhr.onload = e => {
console.log(xhr);
};
});
</script>
</body>
</html>
여러 비동기 연산을 순서대로 처리해야 할 때 발생하는 현상
>
형태를 띄게됨get(`${url}/users`, (response) => {
const userId = response[1].id;
get(`${url}/users/${userId}/posts`, (response) => {
const postId = response[2].id;
get(`${url}/posts/${postId}/comments`, (response) => {
console.log(response)
}); // 해당 글의 댓글 목록 요청
}); // 해당 회원이 쓴 글 목록 요청
}); // 회원 정보 조회
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="show">Promise요청!</button>
<script>
// HTTP 비동기 요청을 위한 함수
// 비동기 호출의 순서를 보장하기 위해 callback대신 promise사용
function get(url) {
// promise는 자바스크립트 비동기 통신(ajax)의 순서를 보장하고
// 데이터 처리를 용이하게 하기 위한 api입니다.
// resolve: 요청에 성공했을 때 실행할 함수
// reject: 요청에 실패했을 때 실행할 함수
const promise = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.send();
xhr.onload = e => {
if (xhr.status === 200) {
const response = JSON.parse(xhr.response);
resolve(response);
} else {
reject(`error!!`);
}
};
});
return promise;
}
document.getElementById('show').addEventListener('click', e => {
// 버튼을 클릭하면 1. 두번째 회원을 조회하여
// 해당 회원이 쓴 2. 글의 목록을 조회한 후
// 해당 글의 3. 댓글들을 조회한다.
const url = 'https://jsonplaceholder.typicode.com';
// promiseState
// 1. pending: 요청대기중
// 2. fulfilled: 요청 성공 -> resolve() 함수 실행
// 3. rejected: 요청 실패 -> reject() 함수 실행
get(`${url}/users`)
.then(res => get(`${url}/users/${res[1].id}/posts`))
.then(res => get(`${url}/posts/${res[2].id}/comments`))
.then(res => console.log(res))
});
</script>
</body>
</html>
네트워크 요청을 수행하기 위한 인터페이스를 제공
XMLHttpRequest에 비해 더 유연하고 강력하며, Promise 기반의 구조로 설계
// 서버 URL
const URL = 'https://jsonplaceholder.typicode.com/posts';
const URL2 = 'http://localhost:8383/api/v1/replies/30';
// ul 태그 가져오기
const $postUl = document.querySelector('.posts');
// 화면에 게시물을 렌더링하는 함수
const renderPosts = postList => {
postList.forEach(post => {
// 게시물 하나를 li태그로 만들어서 ul에 집어넣기
// 템플릿 태그 가져오기
const $template = document.getElementById('single-post');
// 템플릿 태그에서 li태그 추출
// true : 아래있는 것 모두 가져옴, false : li태그만 가져옴
const $li = document.importNode($template.content, true);
// console.log($li);
$li.querySelector(('h2')).textContent = post.title;
$li.querySelector(('p')).textContent = post.body;
$postUl.appendChild($li);
})
}
// 서버에서 게시물 정보 받아오기
fetch(URL)
.then(res => res.json())
.then(json => {
console.log(json);
// 게시물 정보 화면에 그리기
renderPosts(json);
});
json.data.movies
인 것에 주의// 서버 URL
const URL = "https://yts.mx/api/v2/list_movies.json";
// 정렬 URL
const sortYear =
"https://yts.mx/api/v2/list_movies.json?sort_by=year&order_by=desc";
const sortLike =
"https://yts.mx/api/v2/list_movies.json?sort_by=like_count&order_by=desc";
const sortDownload =
"https://yts.mx/api/v2/list_movies.json?sort_by=download_count&order_by=desc";
// 태그 가져오기
const $movieList = document.querySelector(".movie-list");
// 초기 영화정보 로딩하는 함수
const renderMovies = (movieList) => {
$movieList.innerHTML = "";
movieList.forEach((movie) => {
const $template = document.getElementById("movie-post");
// 템플릿 태그에서 movie 클래스 태그 추출
const $movie = document.importNode($template.content, true);
$movie.querySelector(".img-box > img").src = movie.large_cover_image;
$movie.querySelector(".inner .title").textContent = movie.title;
$movie.querySelector(".inner .year").textContent = movie.year;
$movie.querySelector(".inner .rating").textContent = movie.rating;
$movieList.appendChild($movie);
});
};
// 서버에서 게시물 정보 받아오기
fetch(URL)
.then((res) => res.json())
.then((json) => {
console.log(json);
// 게시물 정보 화면에 그리기
renderMovies(json.data.movies);
});
document.getElementById("s_rate").addEventListener("click", (e) => {
fetch(sortDownload)
.then((res) => res.json())
.then((json) => {
console.log(json.data.movies);
renderMovies(json.data.movies);
});
});
document.getElementById("s_year").addEventListener("click", (e) => {
fetch(sortYear)
.then((res) => res.json())
.then((json) => {
console.log(json.data.movies);
renderMovies(json.data.movies);
});
});
document.getElementById("s_like").addEventListener("click", (e) => {
fetch(sortLike)
.then((res) => res.json())
.then((json) => {
console.log(json.data.movies);
renderMovies(json.data.movies);
});
});
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<script src="practice.js" defer></script>
<style>
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
a {
color: inherit;
text-decoration: none;
}
.movie-list {
width: 80%;
margin: 0 auto;
}
.movie-list .movie {
float: left;
width: 23%;
height: 500px;
margin-right: 2%;
margin-bottom: 10px;
border: 1px solid gray;
box-sizing: border-box;
padding: 10px;
}
.movie-list .movie .img-box {
width: 100%;
height: 70%;
overflow: hidden;
}
.movie-list .movie .img-box img {
width: 100%;
}
.movie-list .movie .inner {
padding: 25px 15px;
}
.movie-list .movie .inner * {
font-size: 1.3em;
font-weight: 700;
margin-bottom: 5px;
}
.clearfix::after {
content: '';
display: block;
clear: both;
}
header {
width: 100%;
padding: 10px 120px;
box-sizing: border-box;
border-bottom: 2px solid gray;
margin-bottom: 20px;
display: flex;
justify-content: space-between;
}
header h1 {
flex: 1;
}
header .gnb {
flex: 1;
margin-top: 10px;
}
header .gnb ul {
display: flex;
justify-content: space-evenly;
align-items: center;
}
</style>
</head>
<body>
<header>
<h1>WhatSsa!!</h1>
<nav class="gnb">
<ul>
<li><a id="s_rate" href="#">다운로드순</a></li>
<li><a id="s_year" href="#">발매연도순</a></li>
<li><a id="s_like" href="#">좋아요순</a></li>
</ul>
</nav>
</header>
<!-- 동적으로 렌더링될 태그를 템플릿화 -->
<template id="movie-post">
<div class="movie">
<div class="img-box">
<img src="" alt="표지사진">
</div>
<div class="inner">
<div class="title"></div>
<div class="year"></div>
<div class="rating"></div>
</div>
</div>
</template>
<div class="movie-list clearfix">
<!-- <div class="movie">
<div class="img-box">
<img src="https://yts.mx/assets/images/movies/doctor_who_the_day_of_the_doctor_2013/large-cover.jpg" alt="표지사진">
</div>
<div class="inner">
<div class="title">제목</div>
<div class="year">개봉년도</div>
<div class="rating">평점</div>
</div>
</div> -->
</div>
<!-- Modal -->
<div class="modal fade" id="detailModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">아바타</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<img class="desc-image" src="" alt="" style="width:200px;float: left; margin-right: 20px;">
<p class="movie-description"></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
</body>
</html>