복습의 시간이다.
XMLHttpRequest
객체를 활용🔷 XMLHttpRequest 객체
🔷 AJAX 프로그래밍 순서
🔷 순차적인 비동기 처리하기
1. Async Callbacks
addEventListener()
의 두 번째 인자2. Promise-Style
🔷 Promise Object
new Promise(function (resolve, reject) { })
🔷 Promise Methods
.then(callback)
.catch(callback)
.finally(callback)
💡 체이닝이 가능하다.
🔷 fetch API
BOM (Browser Object Model)
객체 중의 하나.fetch()
메서드를 사용함fetch()
메서드는 HTTP 응답을 나타내는 Response 객체를 래핑한 Promise 객체를 반환🔷 async & await
🔷 async
🔷 await
async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("완료!"), 1000)
});
let result = await promise;
alert(result);
}
f();
🔷 브라우저와 node.js에서 사용할 수 있는 Promise 기반 HTTP 클라이언트 라이브러리
🔷 특징
XMLHttpRequests
생성🔷 axios 설치
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
npm install axios
🔷 axios API
🔷 Vanilla JS에서 XMLHttpRequest
vs Fetch
vs Axios
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Dog API</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<h1>Dog API</h1>
<button id="btn1">가져오기(XMLHttpRequest)</button>
<button id="btn2">가져오기(Fetch)</button>
<button id="btn3">가져오기(Axios)</button>
<img src="" alt="" id="dog-img" />
<script>
const URL = "https://dog.ceo/api/breeds/image/random";
function getDog1() {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
//완벽하게 통신이 끝이 났을 때
if (xhr.readyState == xhr.DONE) {
if (xhr.status == 200) {
const imgSrc = JSON.parse(xhr.response).message;
const imgTag = document.querySelector("#dog-img");
imgTag.src = imgSrc;
//만약 존재하지 않는 속성을 넣고 싶다면
//imgTag.setAttribute("src", imgSrc);
}
}
}
xhr.open("GET", URL);
xhr.send();
}
const btn1 = document.querySelector('#btn1');
btn1.addEventListener('click', getDog1);
function getDog2() {
fetch(URL)
.then((response) => {
return response.json()
})
.then((imgData) => {
const imgSrc = imgData.message;
document.querySelector("#dog-img").setAttribute("src", imgSrc);
})
}
const btn2 = document.querySelector('#btn2');
btn2.addEventListener('click', getDog2);
function getDog3() {
axios.get(URL)
.then((response) => {
const imgSrc = response.data.message;
document.querySelector("#dog-img").setAttribute("src", imgSrc);
})
}
const btn3 = document.querySelector('#btn3');
btn3.addEventListener('click', getDog3);
</script>
</body>
</html>
시간은 좀 걸려도 사진은 잘 불러온다.
🔷 Vue에서 Fetch
vs Axios
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Vue</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<button @click="getCat">냥냥펀치1(Fetch)</button>
<button @click="getCat2">냥냥펀치2(Axios)</button>
<hr>
<img :src="catImageSrc" />
</div>
<script>
const { createApp, ref, onMounted } = Vue
const URL = 'https://api.thecatapi.com/v1/images/search'
const app = createApp({
setup() {
const catImageSrc = ref('')
const getCat = function () {
fetch(URL)
.then((response) => response.json())
.then((imgData) => {
catImageSrc.value = imgData[0].url
})
.catch((error) => {
console.log("실패했다")
})
}
const getCat2 = function () {
axios({
url: URL,
method: 'GET'
})
.then((response) => {
catImageSrc.value = response.data[0].url
})
.catch((error) => {
console.log("실패했다")
})
}
onMounted(() => {
getCat2()
})
return { catImageSrc, getCat, getCat2 }
}
})
app.mount('#app')
</script>
</body>
</html>
고양이가 좀 더 귀엽다...
axios가 더 간결한 코드로 같은 출력을 띄우는 것을 확인 가능하다.
🔷 YouTube API 를 활용한 실습
Google에서 Youtube Data API를 검색하여 사용한다.
사용자 인증 정보를 만들어 API 키를 발급받고 저장한다.
⚙ 프로젝트 구조
🖥 App.vue
<template>
<div>
<header>
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/youtube">Youtube</RouterLink>
</nav>
</header>
<router-view></router-view>
</div>
</template>
<script setup>
</script>
<style scoped>
</style>
🖥 YoutubeView.vue
<template>
<div>
<h2>YoutubeView</h2>
<YoutubeVideoSearch />
<hr>
<YoutubeVideoDetail />
<hr>
<YoutubeVideoList />
</div>
</template>
<script setup>
import YoutubeVideoDetail from '../components/youtube/YoutubeVideoDetail.vue';
import YoutubeVideoList from '../components/youtube/YoutubeVideoList.vue';
import YoutubeVideoSearch from '../components/youtube/YoutubeVideoSearch.vue';
</script>
<style scoped>
</style>
🖥 HomeView.vue
<template>
<div>
<h2>HomeView</h2>
</div>
</template>
<script setup>
</script>
<style scoped>
</style>
🖥 router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import YoutubeView from '../views/YoutubeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/youtube',
name: 'youtube',
component: YoutubeView
},
]
})
export default router
여기까진 이전의 내용과 별 다름이 없다.
🖥 stores/youtube.js
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
import axios from 'axios'
export const useYoutubeStore = defineStore('youtube', () => {
const videos = ref([]);
const selectedVideo = ref(null);
const youtubeSearch = (keyword) => {
const URL = 'https://www.googleapis.com/youtube/v3/search';
const API_KEY = '발급받은 API Key';
axios({
url: URL,
method: "GET",
params: {
key: API_KEY,
part: "snippet", //part: youtube data api를 받을 때 매개변수 필수 속성
q: keyword,
type: 'video',
maxResults: 10,
}
})
.then((response) => {
videos.value = response.data.items;
})
.catch(() => {})
}
const clickVideo = (video) => {
selectedVideo.value = video;
}
return { youtubeSearch, videos, clickVideo, selectedVideo}
})
youtubeSearch
: axios를 이용해 Youtube search api를 받는다. keyword 입력을 받으면 해당 keyword를 쿼리스트링 안에 넣어 영상을 꺼내온다. params에서 part
는 필수이며 나머진 선택적 요소이다.
💡 이번엔 안했지만 catch를 통해 예외 처리를 해주는 것이 좋다.
🖥 YoutubeVideoList.vue
<template>
<div>
<h4>검색 결과</h4>
<ul>
<YoutubeVideoListItem
v-for="video in store.videos"
:key="video.id.videoId"
:video="video"
/>
</ul>
</div>
</template>
<script setup>
import { useYoutubeStore } from '@/stores/youtube'
import YoutubeVideoListItem from './YoutubeVideoListItem.vue';
const store = useYoutubeStore();
</script>
<style scoped>
</style>
💡 api를 불러오는데 성공하면 가져온 video의 속성을 확인해볼 수 있다. 확인해보면 video의 id 객체에 videoId 속성이 들어있다. 이를 key로 이용한다.
🖥 YoutubeVideoListItem.vue
<template>
<li @click="clickVideo()">
<img :src="video.snippet.thumbnails.default.url">
<span>{{video.snippet.title}}</span>
</li>
</template>
<script setup>
import { useYoutubeStore } from '@/stores/youtube'
const store = useYoutubeStore();
const props = defineProps({
video: {
type: Object,
required: true,
}
});
const clickVideo = () => {
store.clickVideo(props.video);
}
</script>
<style scoped>
</style>
비디오를 클릭하면 해당 비디오를 불러온다.
💡 video의 snippet 객체 안에 필요한 대다수의 정보가 들어있다.
🖥 YoutubeVideoSearch.vue
<template>
<div>
<h4>검색 컴포넌트</h4>
<input type="text" v-model="keyword">
<Button @click="search">검색</Button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import {useYoutubeStore} from '@/stores/youtube'
const keyword = ref('')
const store = useYoutubeStore();
const search = () => {
store.youtubeSearch(keyword.value);
}
</script>
<style scoped>
</style>
keyword를 받아 store로 넘겨 맞는 영상을 불러온다.
🖥 YoutubeVideoDetail.vue
<template>
<div v-if="store.selectedVideo">
<h4>영상 상세보기</h4>
<iframe
width="560"
height="315"
:src="videoURL"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
></iframe>
</div>
</template>
<script setup>
import { useYoutubeStore } from '@/stores/youtube'
import { computed } from 'vue';
const store = useYoutubeStore();
const videoURL = computed(() => {
const videoID = store.selectedVideo.id.videoId;
return `https://www.youtube.com/embed/${videoID}`
});
</script>
<style scoped>
</style>
검색한 video가 있을 시 해당 영상의 Id를 바탕으로 url을 불러온다.
💡 iframe 양식은 유튜브 영상에서
공유 -> 퍼가기
시에 얻을 수 있다.
🖨 결과
검색 결과의 max를 10개까지 설정했으므로 결과는 10개까지 뜬다. 5개가 minimum이다.
이제 야스오 장인 axios 장인이 되어보자.
다시 보면 어려운 것이 없다.
인생이 그러한 것이 아닐까...?