공부방

Vue Axios 본문

vue.js

Vue Axios

코딩 화이팅 2023. 5. 10. 11:41

AJAX

JavaScript는 Single thread가 하나이다.

  • 비동기식 JavaScript와  XML
  • 서버와 통신을 하기 위해서 XMLHttpRequest객체(옛날 방식)를 활용
  • JSON, XML, HTML 그리고 일반 텍스트 형식 등을 포함한 다양한 포맷을 주고 받을 수 있음
  • 페이지 전체를 '새로고침'하지 않고서도 수행되는 "비동기성"(일 부분만 업데이트 가능)

콜백

  • 함수를 매개변수로 전달하여 나중에 실행하도록 하는 것
  • 콜백이 중첩되면, 콜백 헬이 되어 해석하고 유지보수하기 힘든 코드가 될 우려(스파게트 코드)

Promise Object

  • 비동기 작업을 마치 동기 작업처럼 값을 반환해서 사용 형태
  • 미래의 완료 또는 실패와 그 결과 값을 나타냄
  • 미래의 어떤 상황에 대한 약속
  • new Promise(function(resolve, reject){})
  • resolve(성공 시)
  • reject(실패 시)

Promies Methods

  • .then(callback)
    • promise 객체를 리턴하고 두 개의 콜백 함수를 인수로 받는다.(이행했을 때, 거부했을 때)
    • 콜백 함수는 이전 작업의 성공 결과를 인자로 전달받음
  • .catch(callback)
    • .then이 하나라도 실패하면(거부되면) 동작 (예외 처리 구문 유사)
    • 이전 작업의 실패로 인해 생성된 error 객체는 catch 블록 안에서 사용 가능
  • .finally(callback)
    • Promise 객체 반환
    • 결과 상관없이 무조건 실행
  • 체이닝 가능

Axios

  • 브라우저와 node.js에서 사용할 수 있는 Promise 기반 HTTP 클라이언트 라이브러리
  • Vue에서 권고하는 HTTP 통신 라이브러리

Axios 설치

  • CDN : <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  • NPM 방식
    npm instaLL axios
  • CLI 방식
    npm i axios
<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <title>Document</title>
  </head>
  <body>
    <h1>Dog API</h1>
    <button id="btn">불러와</button>
    <img src="" alt="" id="dog-img" />
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <!-- cdn 방식으로 axios삽입 -->
    <script>
      const URL = "https://dog.ceo/api/breeds/image/random";
      랜덤으로 강아지 사진을 가져오는 사이트

      // 기본 방식
      // function getDog() {
      //   const xhr = new XMLHttpRequest();

      //   xhr.onreadystatechange = function() {
      // onreadystatechange : 콜백함수 지정
      //     if(xhr.readyState == xhr.DONE){
      //       if(xhr.status == 200){
      //         // console.log(JSON.parse(xhr.response).message)
      //         // console.log(JSON.parse(xhr.response)['message'])
      //         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();
      // }

      // axios방식
      function getDog() {
        axios.get(URL).then((response) => {
          const imgSrc = response.data.message;
          document.querySelector("#dog-img").setAttribute("src", imgSrc);
        });
      }

      const btn = document.querySelector("#btn");
      btn.addEventListener("click", getDog);
    </script>
  </body>
</html>

불러와를 누르면

강아지 사진들이 랜덤으로 나오고 불러와를 누를 때마다 다른 강아지 사진들이 나온다.

유투브 검색하여 썸네일 받아오기

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <title>Youtube 검색</title>
  </head>

  <body>
    <h1>Youtube 검색</h1>
    <input type="text" id="search" placeholder="검색어를 입력해주세요." />
    <button id="search-btn">검색</button>
    <hr />
    <h2>검색 결과</h2>
    <ul id="list"></ul>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <script>
      const URL = "https://www.googleapis.com/youtube/v3/search";
      const API_KEY = "AIzaSyDUyMjPPHmd5LrlnchjptolFtKd7pHCZiE";

      const btn = document.querySelector("#search-btn");

      btn.addEventListener("click", () => {
        const value = document.querySelector("#search").value;

        axios({
          url: URL,
          method: "GET",
          params: {
            key: API_KEY,
            part: "snippet",
            q: value,
            type: "video",
            maxResults: 20,
          },
        })
          .then((res) => {
            return res.data.items;
          })
          .then((res) => {
            const ulTag = document.querySelector("#list");

            for (let i = 0; i < res.length; i++) {
              let liTag = document.createElement("li");
              let imgTag = document.createElement("img");

              imgTag.src = res[i].snippet.thumbnails.default.url;
              liTag.appendChild(imgTag);
              ulTag.appendChild(liTag);
            }

            //아래와 같이 순회할 수도 있다.
            // res.forEach(element => {
            //   console.log(element)
            // });
          })
          .catch((err) => {
            console.log(err);
          });
      });
    </script>
  </body>
</html>

Vue-Axios

views/YoutubeView.Vue
<template>
  <div>
    <h2>유튜브 전용페이지</h2>
    <!-- <youtube-search @search-input="search"></youtube-search>
    <youtube-search-result
        :videos="videos"
    ></youtube-search-result> -->
  </div>
</template>

<script>

export default {
  name: "YoutubeView",
  components: {
    // YoutubeSearch,
    // YoutubeSearchResult,
  },
  data() {
    return {
      videos: [],
    };
  },
};
</script>

<style scoped></style>
=================================================================
router/index.js
import Vue from "vue";
import VueRouter from "vue-router";
import HomeView from "../views/HomeView.vue";
import YoutubeView from "../views/YoutubeView.vue";

Vue.use(VueRouter);

const routes = [
  {
    path: "/",
    name: "home",
    component: HomeView,
  },
  {
    path: "/youtube",
    name: "youtube",
    component: YoutubeView,
  },
];

const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes,
});

export default router;
=================================================================
App.vue
<template>
  <div id="app">
    <nav>
      <router-link to="/">Home</router-link> |
      <router-link to="/youtube">Youtube</router-link>
    </nav>
    <!-- <h2>이거 무슨색?</h2> -->
    <router-view />
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

nav {
  padding: 30px;
}

nav a {
  font-weight: bold;
  color: #2c3e50;
}

nav a.router-link-exact-active {
  color: #42b983;
}
</style>

유부브 전용페이지 색 변경하기

views/YoutubeView.Vue
<template>
  <div>
    <h2>유튜브 전용페이지</h2>
    <!-- <youtube-search @search-input="search"></youtube-search>
    <youtube-search-result
        :videos="videos"
    ></youtube-search-result> -->
  </div>
</template>

<script>

export default {
  name: "YoutubeView",
  components: {
    // YoutubeSearch,
    // YoutubeSearchResult,
  },
  data() {
    return {
      videos: [],
    };
  },
};
</script>

<style>
h2 {
  color: red;
}
</style>

같은 페이지이지만 style속성을 주지 않았을 때 결과값은?

App.vue
<template>
  <div id="app">
    <nav>
      <router-link to="/">Home</router-link> |
      <router-link to="/youtube">Youtube</router-link>
    </nav>
    <h2>이거 무슨색?</h2>
    <router-view />
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

nav {
  padding: 30px;
}

nav a {
  font-weight: bold;
  color: #2c3e50;
}

nav a.router-link-exact-active {
  color: #42b983;
}
</style>

한 개로 다 같이 보기 때문에 style에 빨간색으로 색을 바꿔도 다른 곳에서 빨간색으로 바뀐다.

한 개만 style속성을 주기 위해

views/YoutubeView.Vue
<style scoped>
h2 {
  color: red;
}
</style>

scoped를 넣어주면 그 페이지에서만 적용이 된다.

밑에 검색창 만들기

components/YoutubeSearch.Vue
<template>
  <div>
    <h3>검색 컴포넌트</h3>
    <input type="text" v-model="keyword" />
    <button @click="search">검색</button>
  </div>
</template>

<script>
export default {
  name: "YoutubeSearch",
  data() {
    return {
      keyword: "",
    };
  },
};
</script>

<style>
</style>
===============================================================
views/YoutubeView.Vue
<template>
  <div>
    <h2>유튜브 전용페이지</h2>
    <youtube-search></youtube-search>
  </div>
</template>

<script>
import YoutubeSearch from "../components/youtube/YoutubeSearch.vue";

export default {
  name: "YoutubeView",
  components: {
    YoutubeSearch,
    // YoutubeSearchResult,
  },
  data() {
    return {
      videos: [],
    };
  },
};
</script>

<style scoped>
h2 {
  color: red;
}
</style>

Search.vue에서 데이터를 YoutubeView로 보내기

youtube/YoutubeSearch.vue
<template>
  <div>
    <h3>검색 컴포넌트</h3>
    <input type="text" v-model="keyword" />
    <button @click="search">검색</button>
  </div>
</template>

<script>
export default {
  name: "YoutubeSearch",
  data() {
    return {
      keyword: "",
    };
  },
  methods: {
    search(){
        this.$emit('search-input', this.keyword)
    }
  },
};
</script>

<style>
</style>
===============================================================
views/YoutubeView.vue
<template>
  <div>
    <h2>유튜브 전용페이지</h2>
    <youtube-search @search-input="search"></youtube-search>
  </div>
</template>

<script>
import YoutubeSearch from "../components/youtube/YoutubeSearch.vue";

export default {
  name: "YoutubeView",
  components: {
    YoutubeSearch,
  },
  data() {
    return {
      videos: [],
    };
  },

  methods: {
    search(value) {
      console.log(value);
    },
  },
};
</script>

<style scoped>
h2 {
  color: red;
}
</style>

Youtube API키를 받아 검색을 한 동영상들의 정보들을 가져오기

.env.local
# 키를 다른 사람이 확인할 수도 있기 때문에 다른 곳에만 저장하고
# 협업한다는 거를 생각하여 이 파일은 따로 전송하지 않는다.
VUE_APP_YOUTUBE_API_KEY=AIzaSyDUyMjPPHmd5LrlnchjptolFtKd7pHCZiE
=================================================================
views/YoutubeView.Vue
<template>
  <div>
    <h2>유튜브 전용페이지</h2>
    <youtube-search @search-input="search"></youtube-search>
  </div>
</template>

<script>
import YoutubeSearch from "../components/youtube/YoutubeSearch.vue";
import axios from "axios";

export default {
  name: "YoutubeView",
  components: {
    YoutubeSearch,
  },
  data() {
    return {
      videos: [],
    };
  },

  methods: {
    search(value) {
      console.log(value);
      const URL = "https://www.googleapis.com/youtube/v3/search";
      const API_KEY = process.env.VUE_APP_YOUTUBE_API_KEY;
      axios({
        url: URL,
        method: "GET",
        params: {
          key: API_KEY,
          part: "snippet",
          q: value,
          type: "video",
          maxResults: 10,
        },
      })
        // 체이닝
        .then((res) => {
          return res.data.items;
        })
        .then((res) => {
          console.log(res);
          this.videos = res;
        })
        .catch((err) => console.log(err));
    },
  },
};
</script>

<style scoped>
h2 {
  color: red;
}
</style>

들어온 데이터들을 썸네일과 제목을 밑에 나열하기

views/YoutubeView.vue
<template>
  <div>
    <h2>유튜브 전용페이지</h2>
    <youtube-search @search-input="search"></youtube-search>
    <youtube-search-result :videos="videos"></youtube-search-result>
  </div>
</template>

<script>
import YoutubeSearch from "../components/youtube/YoutubeSearch.vue";
import YoutubeSearchResult from "../components/youtube/YoutubeSearchResult.vue";
import axios from "axios";

export default {
  name: "YoutubeView",
  components: {
    YoutubeSearch,
    YoutubeSearchResult,
  },
  data() {
    return {
      videos: [],
    };
  },

  methods: {
    search(value) {
      console.log(value);
      const URL = "https://www.googleapis.com/youtube/v3/search";
      const API_KEY = process.env.VUE_APP_YOUTUBE_API_KEY;
      axios({
        url: URL,
        method: "GET",
        params: {
          key: API_KEY,
          part: "snippet",
          q: value,
          type: "video",
          maxResults: 10,
        },
      })
        // 체이닝
        .then((res) => {
          return res.data.items;
        })
        .then((res) => {
          console.log(res);
          this.videos = res;
        })
        .catch((err) => console.log(err));
    },
  },
};
</script>

<style scoped>
h2 {
  color: red;
}
</style>
===============================================================
youtube/YoutubeSearchResult.vue
<template>
  <div>
    <h3>검색 결과</h3>
    <ul class="youtube-list">
      <li v-for="video in videos" :key="video.id.videoId">
        <img :src="video.snippet.thumbnails.default.url" />
        {{ video.snippet.title }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "YoutubeSearchResult",
  props: {
    videos: Array,
  },
};
</script>

<style scoped>
.youtube-list {
  text-align: left;
}
</style>

 

'vue.js' 카테고리의 다른 글

Vuex  (0) 2023.05.11
Vue Style Guide  (0) 2023.05.10
Vue Router  (0) 2023.05.09
Vue CLI  (0) 2023.05.08
Vue Component  (0) 2023.05.04