5 — 애플리케이션 제작 파트 — API 구현
source: categories/study/vue-beginner-lv3/vue-beginner-lv3_5.md
5.1 axios를 이용한 api 호출
npm i axios
# OR
yarn add axios
API를 받아와서 데이터를 뿌릴 때 - 설계를 어떻게 해야될까?
src/views/NewsView.vue
<template>
<div>
<div v-for="user in users">{{user}}</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: "NewsView",
data() {
return {
users: []
}
},
created() {
axios.get('users')
.then(response => this.users = response.data)
.catch()
}
}
</script>
<style scoped>
</style>
위와 같이해도 충분히 개발은 가능하지만, 제가 처음에 말씀드린 것처럼 페이지 역할을 하는 컴포넌트는
최대한 라우팅이라던지 어떤 데이터를 패치해오는 로직을 넣지 않는 것이 좋습니다.
라우팅이나 데이터 패치 로직이 들어가는 설계는 좋지 않습니다.
왜냐하면 위와 같은 설계들이 처음에 기획들이 완고하게 굳어져있고 정~말 100% 그 뒤에 바뀌지 않는다는 확신이 있다면 위와 같이 설계해도 문제가 없는데,
결국에는 views
에 들어가는 페이지 컴포넌트들이 레벨이 깊어진다던지, 아니면 설계가 바뀌었을 때 유연하게 대응하시려면
최대한 views
에 있는 컴포넌트들은 라우팅 정보만 담고있고
위와 같이 데이터를 불러오거나 하는 로직들은 별도의 컴포넌트로 등록하는게 좋습니다.
따라서 결론을 지으면 위와 같이 views
페이지 컴포넌트에 데이터 패치 로직이 들어가는게 아니라
<template>
<div>
<div v-for="user in users">{{user}}</div>
</div>
</template>
<script>
export default {
name: "NewsView",
components: {
}
}
</script>
<style scoped>
</style>
위와 같이 components
를 등록할겁니다.
일단은 위 views
페이지 컴포넌트에 데이터 로직을 넣고 나중에 어떤게 어떻게 어려워지는지 살펴보고
이를 components
로 수정해보도록 하겠습니다.
src/views/NewsView.vue
<template>
<div>
<div v-for="(user, index) in users" v-bind:key="index">{{user}}</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: "NewsView",
data() {
return {
users: []
}
},
created() {
axios.get('users')
.then(response => this.users = response.data)
.catch()
}
}
</script>
<style scoped>
</style>
src/views/NewsView.vue 제대로된 API 적용
<template>
<div>
<div v-for="(user, index) in users" v-bind:key="index">{{user}}</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: "NewsView",
data() {
return {
users: []
}
},
created() {
// axios는 Promise 기반: axios가 new Promise()로 되어있기 때문에 그 뒤에 then, catch를 체이닝할 수 있는 것
axios.get('https://api.hnpwa.com/v0/news/1.json')
.then(response => this.users = response.data) // 화살표 함수는 this가 실행컨텍스트에 정의되지 않는다. 상위 this를 찾아간다. 때문에 이렇게 작성해도됨
// 만약 function 일반 함수였다면 this를 const vm = this; 로 하고 vm.users로 function 안에 작성해야한다.
.catch(error => console.log(error))
}
}
</script>
<style scoped>
</style>
타이틀만 뽑도록
<template>
<div>
<div v-for="(user, index) in users" v-bind:key="index">{{user.title}}</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: "NewsView",
data() {
return {
users: []
}
},
created() {
// axios는 Promise 기반: axios가 new Promise()로 되어있기 때문에 그 뒤에 then, catch를 체이닝할 수 있는 것
axios.get('https://api.hnpwa.com/v0/news/1.json')
.then(response => this.users = response.data) // 화살표 함수는 this가 실행컨텍스트에 정의되지 않는다. 상위 this를 찾아간다. 때문에 이렇게 작성해도됨
// 만약 function 일반 함수였다면 this를 const vm = this; 로 하고 vm.users로 function 안에 작성해야한다.
.catch(error => console.log(error))
}
}
</script>
<style scoped>
</style>
5.2 [실습 안내] axios의 api 함수 구조화 방법 및 실습 안내
src/api/index.js 폴더 및 파일 만들기
// package.json에 명시된 라이브러리는 이렇게 "axios"라고만 적어도 알아서 node_modules 폴더 안에 있는 axios 라이브러리를 가져온다.
// axios는 Promise 기반: axios가 new Promise()로 되어있기 때문에 그 뒤에 then, catch를 체이닝할 수 있는 것
import axios from "axios";
// 1. HTTP Request & Response와 관련된 기본 설정
const config = {
baseUrl: 'https://api.hnpwa.com/v0/',
}
// 2. API 함수들을 정리
const fetchNewsList = () => {
return axios.get(`${config.baseUrl}news/1.json`);
}
export {
fetchNewsList,
}
src/views/NewsView.vue
<template>
<div>
<div v-for="(user, index) in users" v-bind:key="index">{{user.title}}</div>
</div>
</template>
<script>
// // 컴포넌트마다 이런식으로 작성하면 중복되는 코드가 많아진다.
// // 첫번째 아래부분 axios를 쓰는 컴포넌트라면 다 중복될 코드
// import axios from 'axios';
//
// export default {
// name: "NewsView",
// data() {
// return {
// users: []
// }
// },
// created() {
// // 두번째 아래부분 - 엄청 많이 중복될 가능성이 높다.
// // 화살표 함수는 this가 실행컨텍스트에 정의되지 않는다. 상위 this를 찾아간다. 때문에 이렇게 작성해도됨
// // 만약 function 일반 함수였다면 this를 const vm = this; 로 하고 vm.users로 function 안에 작성해야한다.
// axios.get('https://api.hnpwa.com/v0/news/1.json')
// .then(response => this.users = response.data)
// .catch(error => console.log(error))
// }
// }
import {fetchNewsList} from "../api";
export default {
name: "NewsView",
data() {
return {
users: [],
}
},
created() {
fetchNewsList()
.then(response => this.users = response.data)
.catch(error => console.log(error))
}
}
</script>
<style scoped>
</style>
AskView, JobsView 내가 푼 답
src/api/index.js
// package.json에 명시된 라이브러리는 이렇게 "axios"라고만 적어도 알아서 node_modules 폴더 안에 있는 axios 라이브러리를 가져온다.
// axios는 Promise 기반: axios가 new Promise()로 되어있기 때문에 그 뒤에 then, catch를 체이닝할 수 있는 것
import axios from "axios";
// 1. HTTP Request & Response와 관련된 기본 설정
const config = {
baseUrl: 'https://api.hnpwa.com/v0/',
}
// 2. API 함수들을 정리
const fetchNewsList = () => {
return axios.get(`${config.baseUrl}news/1.json`);
}
const fetchJobsList = () => {
return axios.get(`${config.baseUrl}jobs/1.json`);
}
const fetchAskList = () => {
return axios.get(`${config.baseUrl}ask/1.json`);
}
export {
fetchNewsList,
fetchJobsList,
fetchAskList,
}
src/views/JobsView.vue
<template>
<div>
<div v-for="(job, index) in jobs" v-bind:key="index">{{job}}</div>
</div>
</template>
<script>
import {fetchJobsList} from "../api";
export default {
name: "NewsView",
data() {
return {
jobs: [],
}
},
created() {
fetchJobsList()
.then(response => this.jobs = response.data)
.catch(error => console.log(error))
}
}
</script>
<style scoped>
</style>
src/views/AskView.vue
<template>
<div>
<div v-for="(ask, index) in asks" v-bind:key="index">{{ask}}</div>
</div>
</template>
<script>
import {fetchAskList} from "../api";
export default {
name: "NewsView",
data() {
return {
asks: [],
}
},
created() {
fetchAskList()
.then(response => this.asks = response.data)
.catch(error => console.log(error))
}
}
</script>
<style scoped>
</style>
5.3 [실습] JobsView와 AskView 구현
data
를 요청하는 로직은 보통 created
또는 beforeMount
라이프사이클 훅에서 처리합니다.
그 이유는 만약 mounted
훅에 data
를 요청하는 로직을 넣게되면,
컴포넌트가 화면에 렌더링 된 후, mounted
로 data
를 요청하고 다시 받아온 data
를 통해 재렌더링을 합니다.
화면이 다시 렌더링 되는 것이죠.
그걸 방지하기위해 렌더링 되기 전인 created
또는 beforeMount
에 보통 data
요청 로직을 넣는 것입니다.
5.4 자바스크립트 this 4가지와 화살표 함수의 this
5.4.1 전역 this(= window) 전역객체를 가리킨다
// 브라우저
this; // Window
// node
this; // Global
5.4.2 function 안에서의 this
function sum() {
console.log(this);
return a + b;
}
sum(10, 20); // Window
5.4.3 생성자 함수로 만든 인스턴스의 this
function Vue(el) {
console.log(this);
this.el = el;
}
new Vue('#app'); // Vue {} // 생성된 인스턴스를 가리킨다.
5.4.4 비동기 처리에서의 this
const obj = {
syncFunc() {
console.log(this);
},
asyncFunc() {
setTimeout(function () {
console.log(this);
})
}
}
obj.syncFunc(); // {syncFunc: ƒ, asyncFunc: ƒ}
obj.asyncFunc(); // Window {window: Window, self: Window, document: document, name: '', location: Location, …}
const obj = {
syncFunc() {
console.log(this);
},
asyncFunc() {
setTimeout(() => {
console.log(this);
})
}
}
obj.syncFunc(); // {syncFunc: ƒ, asyncFunc: ƒ}
obj.asyncFunc(); // {syncFunc: ƒ, asyncFunc: ƒ}
// function 일반 함수는 실행 콘텍스트에 this가 바인딩되어있다. 보통 자기 자신을 실행시킨 객체를 this 바인딩에 담는다.
// 즉 위의 syndFunc, asyncFunc 함수의 각 실행 콘텍스트에는 this가 바인딩되어있다.
// 하지만 setTimeout 안의 콜백함수로 들어가있는 화살표 함수는 원래 this가 바인딩되지 않는다.
// 부모의 this를 물려받는다.
// 즉, asyncFunc 함수의 this 바인딩을 물려받는다.
// 현재 asyncFunc의 this 바인딩엔 해당 함수를 실행시키는 객체 obj가 담겨있으므로 화살표 함수의 this도 obj를 가리키게된다.
5.5 자바스크립트 비동기 처리 1 - callback
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>callback</title>
</head>
<body>
<h1>jQuery ajax</h1>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
function fetchData() {
// 1
var result = [];
// 2
$.ajax({
url: 'https://api.hnpwa.com/v0/news/1.json',
success: function (data) {
console.log('데이터 호출 결과', data);
result = data;
}
})
// 3
console.log('함수 결과', result);
}
fetchData();
// 함수 결과 []
// 데이터 호출 결과 [{...}, {...}, ..., {...}]
</script>
</body>
</html>
callback 지옥 - nested
function a() {
function b() {
function c() {
// ...
}
c();
}
b();
}
5.6 자바스크립트 비동기 처리 2 - Promise
new Promise((resolve, reject) => {
if (true) {
resolve();
} else {
reject();
}
})
.then() // resolve 성공하면
.catch() // reject 실패하면
// 제이쿼리 ajax에 Promise 사용하기
function callAjax() {
return new Promise((resolve, reject) => {
$.ajax({
url: 'https://api.hnpwa.com/v0/news/1.json',
success: function () {
resolve(data);
}
})
})
}
function fetchData() {
var result = [];
callAjax()
.then(function (data) {
console.log('데이터 호출 결과', data);
result = data;
console.log('함수 결과', result);
})
}
fetchData();