KEMBAR78
Vue.js 기초 실습.pptx
Vue.js 기초 실습
한국폴리텍대학 스마트금융과
도입
source : https://www.appsdevpro.com/blog/vue-vs-react/
MVVM (Model, View, ViewModel)
View Model
View Model
DOM Listener
Data Binding
Javascript Object
준비물
IDE : VS Code 또는 ATOM, 기타
- VS Code : Volar extension 설치
크롬 : 개발자 도구와 Vue.js Extenstion (필요시)
Node.js (Install Node.js version 16.0 or higher)
Hello World
Vue 애플리케이션 만들기
https://ko.vuejs.org/guide/quick-start.html#creating-a-vue-application
index.html  main.js  App.vue
CDN에서 Vue 사용
https://codepen.io/vuejs-examples/pen/eYQpQEG
Vue = Component
App.vue
HelloWorld.vue
TheWelcome.vue
WelcomeItem.vue
WelcomeItem.vue
WelcomeItem.vue
WelcomeItem.vue
WelcomeItem.vue
Counter 예제 (1/3)
Counter 예제 (2/3)
Counter 예제 (3/3)
템플릿 문법 : v-bind
템플릿 문법 : Boolean 속성
템플릿 문법 : 여러 속성 바인딩
템플릿 문법 : JavaScript 표현식 사용
반응형 기초 : 반응형 상태 선언
Composition API에서 반응형 상태를 선언하는 권장 방법은 ref() 함수를 사용하는 것입니다:
컴포넌트가 처음 렌더링될 때, Vue는 렌더링 과정에서 사용된 모든 ref를 추적합니다. 나중에 ref가 변경되면, 이를 추적하는 컴포넌트
에 대해 재렌더링을 트리거합니다.
https://ko.vuejs.org/guide/essentials/reactivity-fundamentals.html#ref
반응형 기초 : reactive
 ref와 reactive 차이 알아보기
계산된 속성(computed)
계산된 속성(computed)
<script setup>
import { reactive, computed } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery',
'Vue 5 - The Mystery',
'Vue 6 - The Mystery',
'Vue 7 - The Mystery'
]
})
// 계산된 ref
const publishedBooksMessage = computed(() => {
return author.books.length > 3 ? 'Yes' : 'No'
})
const funcBtn = (()=>{
author.books.pop()
})
</script>
<template>
<p>책을 많이 가지고 있다:</p>
<span>{{ publishedBooksMessage }}</span>
<p></p>
<button @click="funcBtn">버튼</button>
</template>
클래스와 스타일 바인딩
조건부 렌더링
리스트 렌더링
추가 실습 : https://ko.vuejs.org/guide/essentials/list.html
이벤트 핸들링
입력 바인딩
추가 실습 : https://ko.vuejs.org/guide/essentials/forms.html
라이프싸이클
Component 실습
실습 환경 구성
npm install -g @vue/cli
vue create vue-project2
cd vue-project2
npm run serve
컴포넌트
image: https://ko.vuejs.org/guide/essentials/component-basics.html
컴포넌트
App.vue
HelloWorld.vue
App.vue
HelloWorld.vue
컴포넌트 : props (1/3)
App.vue
HelloWorld.vue
App.vue
HelloWorld.vue
컴포넌트 : props (2/3)
부모 컴포넌트에서 버튼 클릭시,
부모 컴포넌트의 count 값을 자식 컴포넌트에 전달하고,
전달받은 값을 자식 컴포넌트 화면에 출력한다.
컴포넌트 : props (3/3)
App.vue
HelloWorld.vue
컴포넌트 : emit event (1/2)
App.vue
HelloWorld.vue
컴포넌트 : emit event (2/2)
1. Child Component에 버튼 추가하고, 버튼 클릭시 상위 컴포넌트에 메시지 전달
컴포넌트 : Slot
https://ko.vuejs.org/guide/essentials/component-basics.html#content-distribution-with-slots
Router
Router
Router
Kopo 라는 메뉴 만들고, 해당 메뉴 클릭시 Kopo 컴포넌트 호출하게 코드 추가하기
Axios
axios 설치 및 import
vue2 style 준비 (전역 설정)
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import axios from 'axios'
// createApp(App).use(router).mount('#app')
const app = createApp(App)
app.config.globalProperties.axios=axios
app.use(router).mount('#app')
main.js
vue2 style (사용)
사용하고자 하는 컴포넌트에서...
HelloWorld.vue
export default {
name: 'HelloWorld',
methods: {
testAxios() {
this.axios.
get('https://api.coindesk.com/v1/bpi/currentprice.json').
then(response => (console.log(response)))
},
btnclicked(){
this.testAxios();
}
}
}
vue3 style 준비 (전역 설정)
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import axios from 'axios'
// createApp(App).use(router).mount('#app')
const app = createApp(App)
app.provide('$axios', axios);
app.use(router).mount('#app')
main.js
vue3 style (사용)
사용하고자 하는 컴포넌트에서...
import { inject } from 'vue'
setup(props, ctx) {
const axios = inject('$axios');
function sendmymessage () {
ctx.emit('send-message',childmsg)
axios.
get('https://api.coindesk.com/v1/bpi/currentprice.json').
then(response => (console.log(response)))
}
return{
sendmymessage
}
}
HelloWorld.vue
CORS 관찰
CORS 관찰
만약에?
아마도, backend 응답 헤더에 아래와 같은 속성이 없을 수도?
CORS 조치
Vue 개발을 위한 조치 => Production 환경에서는 BackEnd 에서 조치가 필요
function sendmymessage () {
axios.
get('/v1/bpi/currentprice.json').
then(response => (console.log(response)))
const params = {
name: "morpheus",
job: "leader",
};
axios.post("/api/users",params,{}
).then((res)=>{
//성공했을경우
console.log("성공",res)
}).catch((res)=>{
//실패했을경우
console.error("실패",res)
})
}
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
proxy: {
'/v1': {
target: "https://api.coindesk.com",
changeOrigin: true
},
'/api': {
target: "https://reqres.in",
changeOrigin: true
}
}
}
})
vue.config.js
async await
Do you know 콜백지옥?
btnclicked(){
this.testAxios();
this.testAsync();
},
async testAsync() {
const resultValue = await this.requestPost();
this.requestGet(resultValue);
},
requestGet(arg) {
console.log(`[START] axios Get,
Call Back From Previos Axios Post = ${arg} `);
this.axios
.get(`https://reqres.in/api/users?/page=1`, {})
.then((res) => {
console.log("[END] axios Get ");
this.getData = res.data;
})
.catch((res) => {
console.error(res);
});
},
requestPost() {
const params = {
name: "morpheus",
job: "leader",
};
console.log("[START] axios Post");
return this.axios
.post(`https://reqres.in/api/users`, params, {})
.then((res) => {
console.log("[END] axios Post");
this.postData = res.data;
return "Success";
})
.catch((res) => {
console.error(res);
return "Failure";
});
},
},
Pinia
Pinia
Vue의 상태 관리 모듈
npm install pinia --save
https://pinia.vuejs.org/introduction.html
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import axios from 'axios'
import { createPinia } from 'pinia'
const app = createApp(App)
const pinia = createPinia()
app.config.globalProperties.axios=axios
app.provide('$axios', axios);
app.use(pinia)
app.use(router).mount('#app')
Pinia
폴더와 파일 생성
import { ref } from 'vue'
import { defineStore } from "pinia"
export const useCounterStore = defineStore('counter', () => {
const count = ref(1)
function increment() {
count.value++
console.log("increment called")
}
return { count, increment }
})
counter.js
Pinia
HelloWorld.vue
Pinia
HelloWorld.vue
실습
실습
https://github.com/wonyongHwang/vue3-todo.git
https://github.com/wonyongHwang/simple-todo-list.git
- localStorage 저장
- backend  database 저장
조별 실습 과제
Vue Project
- 화면 정의
- R&R, WBS, ...
설치
https://www.creative-tim.com/product/vue-material-kit
App.vue
router > index.js
로그인 예제 (준비)
setup() {
const myemail = ref('a@a.com')
const mypwd = ref('1234')
const refmyemail = ref(null);
function func_signin() {
console.log("sign in ",myemail,mypwd)
console.log(refmyemail._value.value)
}
onMounted(() => {
setMaterialInput();
})
return{
myemail,
mypwd,
func_signin,
refmyemail
}
},
로그인 예제 (v-model)
<div class="card-body">
<!-- submit.prevent added by hwy -->
<form role="form" class="text-start" @submit.prevent>
<!-- code modified by hwy -->
<div class="input-group input-group-outline my-3">
<input v-model="myemail" id="email" class="form-control form-control-md" type="email" ref="refmyemail"/>
</div>
<div class="input-group input-group-outline my-3">
<input v-model="mypwd" id="password" class="form-control form-control-md" type="password"/>
</div>
<div class="text-center">
<MaterialButton
class="my-4 mb-2"
variant="gradient"
color="success"
@click="func_signin"
fullWidth
>Sign in</MaterialButton>
</div>
로그인 예제 (결과확인)
로그인 예제 (ref 살펴보기)
setup() {
const refmyemail = ref(null);
function func_signin() {
console.log(refmyemail._value.value)
}
return{
func_signin,
refmyemail
}
<div class="input-group input-group-outline my-3">
<input v-model="myemail" id="email" class="form-control form-control-md"
type="email" ref="refmyemail"/>
</div>
로그인 예제 (axios 및 router 적용)
import { ref, inject } from 'vue'
import { useRouter } from 'vue-router'
setup() {
const $client = inject("$axios");
const router = useRouter()
async function func_signin() {
const resp = await $client.get("/api/delayHello");
if(resp.status == 200){
router.push('/')
}else{
}
}
rest call 정상 응답시 최상위 경로로 이동
 로그인 관련 Backend에서 응답 받은 정보의 저장 및 관리는?
분석
컴포넌트 배치 확인
그리드 시스템(부트스트랩)
https://getbootstrap.kr/docs/5.0/layout/grid/
PresentationView.vue
분석
분석
분석
PresentationView.vue
분석
PresentationView.vue
PresentationCounter.vue
DefaultCounterCard.vue
진행 방향
컴포넌트 구성도 작성
기획 & 화면 정의, R&R, WBS (교수와 협의)
* axios, pinia, mitt 등 활용
형상관리(Git)
빌드 및 실서버 배포
mitt
컴포넌트간 이벤트 전달
Vue2 : EventBus
Vue3 : mitt
npm install --save mitt
main.js
이벤트 발송
AboutView.vue
이벤트 수신
PresentationView.vue
결과 확인
AboutView.vue를 호출  이벤트 발송 (mount)  이벤트 수신
mock
준비
npm install axios-mock-adapter --save
npm install axios --save
main.js
mock 코드 준비
import MockAdapter from "axios-mock-adapter";
function parseQueryString(url) {
const queryString = url.replace(/.*?/, "");
const result = {};
if (queryString === url || !queryString) {
return result;
}
const urlParams = new URLSearchParams(queryString);
urlParams.forEach((val, key) => {
if (Object.prototype.hasOwnProperty.call(result, key)) {
result[key] = [result[key], val];
} else {
result[key] = val;
}
});
return result;
}
export default {
mocked(axiosClient) {
const mock = new MockAdapter(axiosClient, { onNoMatch: "passthrough" });
mock.onGet("/api/hello").reply(200, { hello: "world" });
mock.onGet(//api/hello2?.*/).reply((config) => {
const params = parseQueryString(config.url);
return [200, { config, params }];
});
mock.onPost("/api/formPost").reply((config) => {
//const formData = config.data;
const name = config.data.get("name");
return [200, { config, name: name }];
});
mock.onPost("/api/postJson").reply((config) => {
const data = JSON.parse(config.data);
return [200, { data }];
});
mock.onGet("/api/delayHello").reply((config) => {
const delay = 5000;
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([200, { hello: "world" }]);
}, delay);
});
});
}
};
mock.js
테스트
// mock
import { inject, reactive } from "vue";
const $client = inject("$axios");
const myModel = reactive({});
const btnClick1 = async () => {
const resp = await $client.get("/api/hello");
myModel.respCode = resp.status;
myModel.respBody = resp.data;
};
const btnClick2 = async () => {
const resp = await $client.get("https://httpbin.org/get");
//const resp = await $client.get("https://ifconfig.io/all.json");
myModel.respCode = resp.status;
myModel.respBody = resp.data;
};
const btnClick3 = async () => {
const resp = await $client.get("/api/hello2?name=taro"); // &name=taro
myModel.respCode = resp.status;
myModel.respBody = resp.data;
console.log(resp.data)
};
const postFormData = async () => {
const formData = new FormData();
formData.append("name", "taro");
formData.append("flag1", "true");
const resp = await $client.post("/api/formPost", formData, {});
myModel.respCode = resp.status;
myModel.respBody = resp.data;
};
<template>
<div class="container position-sticky z-index-sticky top-0">
<div class="row">
<!-- <div class="col-12">
<NavbarDefault :sticky="true" />
</div> -->
<button @click="btnClick1">Call API(Mock)</button>
<button @click="btnClick2">Call API</button>
<button @click="btnClick3">Call (Hello2)</button>
<button @click="postFormData">Call (postFormData)</button>
<button @click="postJson">Call (postJson)</button>
<button @click="delayHello">delayHello</button>
</div>
<div>
<dl>
<dt>code</dt>
<dd>{{ myModel.respCode }}</dd>
<dt>body</dt>
<dd>
<pre>{{ myModel.respBody }}</pre>
</dd>
</dl>
</div>
</div>
PresentationView.vue
테스트
source : https://github.com/ctimmerm/axios-mock-adapter
테스트
mock.js
빌드 및 배포
node
npm run build
copy dist folder to server directory
app.js
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const cors = require('cors');
const app = express();
var request = require('request');
app.use(bodyParser.json()); // json을 parse하도록 함
app.use(cors());
app.use(express.static("."));
// configuration =========================
app.set('port', process.env.PORT || 8030);
app.use( '/', express.static( path.join(__dirname, './dist') ));
app.get('/', function (req,res) {
res.sendFile(path.join(__dirname, './dist', 'index.html'));
});
app.listen(app.get('port'), () => {
console.log('Express server listening on port ' + app.get('port'));
});
npm install express
npm install cors
npm install request
node app.js
forever - nodeJS의 무중단 관리도구
○ forever 설치
npm install forever -g
○ forever 시작
forever start app.js
○ forever 종료
forever stop app.js (또는 forever list에서 확인한 pid)
○ forever 목록
forever list

Vue.js 기초 실습.pptx

  • 1.
  • 2.
  • 3.
    MVVM (Model, View,ViewModel) View Model View Model DOM Listener Data Binding Javascript Object
  • 4.
    준비물 IDE : VSCode 또는 ATOM, 기타 - VS Code : Volar extension 설치 크롬 : 개발자 도구와 Vue.js Extenstion (필요시) Node.js (Install Node.js version 16.0 or higher)
  • 5.
    Hello World Vue 애플리케이션만들기 https://ko.vuejs.org/guide/quick-start.html#creating-a-vue-application index.html  main.js  App.vue CDN에서 Vue 사용 https://codepen.io/vuejs-examples/pen/eYQpQEG
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
    템플릿 문법 :Boolean 속성
  • 12.
    템플릿 문법 :여러 속성 바인딩
  • 13.
    템플릿 문법 :JavaScript 표현식 사용
  • 14.
    반응형 기초 :반응형 상태 선언 Composition API에서 반응형 상태를 선언하는 권장 방법은 ref() 함수를 사용하는 것입니다: 컴포넌트가 처음 렌더링될 때, Vue는 렌더링 과정에서 사용된 모든 ref를 추적합니다. 나중에 ref가 변경되면, 이를 추적하는 컴포넌트 에 대해 재렌더링을 트리거합니다. https://ko.vuejs.org/guide/essentials/reactivity-fundamentals.html#ref
  • 15.
    반응형 기초 :reactive  ref와 reactive 차이 알아보기
  • 16.
  • 17.
    계산된 속성(computed) <script setup> import{ reactive, computed } from 'vue' const author = reactive({ name: 'John Doe', books: [ 'Vue 2 - Advanced Guide', 'Vue 3 - Basic Guide', 'Vue 4 - The Mystery', 'Vue 5 - The Mystery', 'Vue 6 - The Mystery', 'Vue 7 - The Mystery' ] }) // 계산된 ref const publishedBooksMessage = computed(() => { return author.books.length > 3 ? 'Yes' : 'No' }) const funcBtn = (()=>{ author.books.pop() }) </script> <template> <p>책을 많이 가지고 있다:</p> <span>{{ publishedBooksMessage }}</span> <p></p> <button @click="funcBtn">버튼</button> </template>
  • 18.
  • 19.
  • 20.
    리스트 렌더링 추가 실습: https://ko.vuejs.org/guide/essentials/list.html
  • 21.
  • 22.
    입력 바인딩 추가 실습: https://ko.vuejs.org/guide/essentials/forms.html
  • 23.
  • 24.
  • 25.
    실습 환경 구성 npminstall -g @vue/cli vue create vue-project2 cd vue-project2 npm run serve
  • 26.
  • 27.
  • 28.
    컴포넌트 : props(1/3) App.vue HelloWorld.vue App.vue HelloWorld.vue
  • 29.
    컴포넌트 : props(2/3) 부모 컴포넌트에서 버튼 클릭시, 부모 컴포넌트의 count 값을 자식 컴포넌트에 전달하고, 전달받은 값을 자식 컴포넌트 화면에 출력한다.
  • 30.
    컴포넌트 : props(3/3) App.vue HelloWorld.vue
  • 31.
    컴포넌트 : emitevent (1/2) App.vue HelloWorld.vue
  • 32.
    컴포넌트 : emitevent (2/2) 1. Child Component에 버튼 추가하고, 버튼 클릭시 상위 컴포넌트에 메시지 전달
  • 33.
  • 34.
  • 35.
  • 36.
    Router Kopo 라는 메뉴만들고, 해당 메뉴 클릭시 Kopo 컴포넌트 호출하게 코드 추가하기
  • 37.
  • 38.
  • 39.
    vue2 style 준비(전역 설정) import { createApp } from 'vue' import App from './App.vue' import router from './router' import axios from 'axios' // createApp(App).use(router).mount('#app') const app = createApp(App) app.config.globalProperties.axios=axios app.use(router).mount('#app') main.js
  • 40.
    vue2 style (사용) 사용하고자하는 컴포넌트에서... HelloWorld.vue export default { name: 'HelloWorld', methods: { testAxios() { this.axios. get('https://api.coindesk.com/v1/bpi/currentprice.json'). then(response => (console.log(response))) }, btnclicked(){ this.testAxios(); } } }
  • 41.
    vue3 style 준비(전역 설정) import { createApp } from 'vue' import App from './App.vue' import router from './router' import axios from 'axios' // createApp(App).use(router).mount('#app') const app = createApp(App) app.provide('$axios', axios); app.use(router).mount('#app') main.js
  • 42.
    vue3 style (사용) 사용하고자하는 컴포넌트에서... import { inject } from 'vue' setup(props, ctx) { const axios = inject('$axios'); function sendmymessage () { ctx.emit('send-message',childmsg) axios. get('https://api.coindesk.com/v1/bpi/currentprice.json'). then(response => (console.log(response))) } return{ sendmymessage } } HelloWorld.vue
  • 43.
  • 44.
    CORS 관찰 만약에? 아마도, backend응답 헤더에 아래와 같은 속성이 없을 수도?
  • 45.
    CORS 조치 Vue 개발을위한 조치 => Production 환경에서는 BackEnd 에서 조치가 필요 function sendmymessage () { axios. get('/v1/bpi/currentprice.json'). then(response => (console.log(response))) const params = { name: "morpheus", job: "leader", }; axios.post("/api/users",params,{} ).then((res)=>{ //성공했을경우 console.log("성공",res) }).catch((res)=>{ //실패했을경우 console.error("실패",res) }) } const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ transpileDependencies: true, devServer: { proxy: { '/v1': { target: "https://api.coindesk.com", changeOrigin: true }, '/api': { target: "https://reqres.in", changeOrigin: true } } } }) vue.config.js
  • 46.
    async await Do youknow 콜백지옥? btnclicked(){ this.testAxios(); this.testAsync(); }, async testAsync() { const resultValue = await this.requestPost(); this.requestGet(resultValue); }, requestGet(arg) { console.log(`[START] axios Get, Call Back From Previos Axios Post = ${arg} `); this.axios .get(`https://reqres.in/api/users?/page=1`, {}) .then((res) => { console.log("[END] axios Get "); this.getData = res.data; }) .catch((res) => { console.error(res); }); }, requestPost() { const params = { name: "morpheus", job: "leader", }; console.log("[START] axios Post"); return this.axios .post(`https://reqres.in/api/users`, params, {}) .then((res) => { console.log("[END] axios Post"); this.postData = res.data; return "Success"; }) .catch((res) => { console.error(res); return "Failure"; }); }, },
  • 47.
  • 48.
    Pinia Vue의 상태 관리모듈 npm install pinia --save https://pinia.vuejs.org/introduction.html import { createApp } from 'vue' import App from './App.vue' import router from './router' import axios from 'axios' import { createPinia } from 'pinia' const app = createApp(App) const pinia = createPinia() app.config.globalProperties.axios=axios app.provide('$axios', axios); app.use(pinia) app.use(router).mount('#app')
  • 49.
    Pinia 폴더와 파일 생성 import{ ref } from 'vue' import { defineStore } from "pinia" export const useCounterStore = defineStore('counter', () => { const count = ref(1) function increment() { count.value++ console.log("increment called") } return { count, increment } }) counter.js
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
    조별 실습 과제 VueProject - 화면 정의 - R&R, WBS, ...
  • 55.
  • 56.
    로그인 예제 (준비) setup(){ const myemail = ref('a@a.com') const mypwd = ref('1234') const refmyemail = ref(null); function func_signin() { console.log("sign in ",myemail,mypwd) console.log(refmyemail._value.value) } onMounted(() => { setMaterialInput(); }) return{ myemail, mypwd, func_signin, refmyemail } },
  • 57.
    로그인 예제 (v-model) <divclass="card-body"> <!-- submit.prevent added by hwy --> <form role="form" class="text-start" @submit.prevent> <!-- code modified by hwy --> <div class="input-group input-group-outline my-3"> <input v-model="myemail" id="email" class="form-control form-control-md" type="email" ref="refmyemail"/> </div> <div class="input-group input-group-outline my-3"> <input v-model="mypwd" id="password" class="form-control form-control-md" type="password"/> </div> <div class="text-center"> <MaterialButton class="my-4 mb-2" variant="gradient" color="success" @click="func_signin" fullWidth >Sign in</MaterialButton> </div>
  • 58.
  • 59.
    로그인 예제 (ref살펴보기) setup() { const refmyemail = ref(null); function func_signin() { console.log(refmyemail._value.value) } return{ func_signin, refmyemail } <div class="input-group input-group-outline my-3"> <input v-model="myemail" id="email" class="form-control form-control-md" type="email" ref="refmyemail"/> </div>
  • 60.
    로그인 예제 (axios및 router 적용) import { ref, inject } from 'vue' import { useRouter } from 'vue-router' setup() { const $client = inject("$axios"); const router = useRouter() async function func_signin() { const resp = await $client.get("/api/delayHello"); if(resp.status == 200){ router.push('/') }else{ } } rest call 정상 응답시 최상위 경로로 이동  로그인 관련 Backend에서 응답 받은 정보의 저장 및 관리는?
  • 61.
    분석 컴포넌트 배치 확인 그리드시스템(부트스트랩) https://getbootstrap.kr/docs/5.0/layout/grid/ PresentationView.vue
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
    진행 방향 컴포넌트 구성도작성 기획 & 화면 정의, R&R, WBS (교수와 협의) * axios, pinia, mitt 등 활용 형상관리(Git) 빌드 및 실서버 배포
  • 67.
  • 68.
    컴포넌트간 이벤트 전달 Vue2: EventBus Vue3 : mitt npm install --save mitt main.js
  • 69.
  • 70.
  • 71.
    결과 확인 AboutView.vue를 호출 이벤트 발송 (mount)  이벤트 수신
  • 72.
  • 73.
    준비 npm install axios-mock-adapter--save npm install axios --save main.js
  • 74.
    mock 코드 준비 importMockAdapter from "axios-mock-adapter"; function parseQueryString(url) { const queryString = url.replace(/.*?/, ""); const result = {}; if (queryString === url || !queryString) { return result; } const urlParams = new URLSearchParams(queryString); urlParams.forEach((val, key) => { if (Object.prototype.hasOwnProperty.call(result, key)) { result[key] = [result[key], val]; } else { result[key] = val; } }); return result; } export default { mocked(axiosClient) { const mock = new MockAdapter(axiosClient, { onNoMatch: "passthrough" }); mock.onGet("/api/hello").reply(200, { hello: "world" }); mock.onGet(//api/hello2?.*/).reply((config) => { const params = parseQueryString(config.url); return [200, { config, params }]; }); mock.onPost("/api/formPost").reply((config) => { //const formData = config.data; const name = config.data.get("name"); return [200, { config, name: name }]; }); mock.onPost("/api/postJson").reply((config) => { const data = JSON.parse(config.data); return [200, { data }]; }); mock.onGet("/api/delayHello").reply((config) => { const delay = 5000; return new Promise((resolve, reject) => { setTimeout(() => { resolve([200, { hello: "world" }]); }, delay); }); }); } }; mock.js
  • 75.
    테스트 // mock import {inject, reactive } from "vue"; const $client = inject("$axios"); const myModel = reactive({}); const btnClick1 = async () => { const resp = await $client.get("/api/hello"); myModel.respCode = resp.status; myModel.respBody = resp.data; }; const btnClick2 = async () => { const resp = await $client.get("https://httpbin.org/get"); //const resp = await $client.get("https://ifconfig.io/all.json"); myModel.respCode = resp.status; myModel.respBody = resp.data; }; const btnClick3 = async () => { const resp = await $client.get("/api/hello2?name=taro"); // &name=taro myModel.respCode = resp.status; myModel.respBody = resp.data; console.log(resp.data) }; const postFormData = async () => { const formData = new FormData(); formData.append("name", "taro"); formData.append("flag1", "true"); const resp = await $client.post("/api/formPost", formData, {}); myModel.respCode = resp.status; myModel.respBody = resp.data; }; <template> <div class="container position-sticky z-index-sticky top-0"> <div class="row"> <!-- <div class="col-12"> <NavbarDefault :sticky="true" /> </div> --> <button @click="btnClick1">Call API(Mock)</button> <button @click="btnClick2">Call API</button> <button @click="btnClick3">Call (Hello2)</button> <button @click="postFormData">Call (postFormData)</button> <button @click="postJson">Call (postJson)</button> <button @click="delayHello">delayHello</button> </div> <div> <dl> <dt>code</dt> <dd>{{ myModel.respCode }}</dd> <dt>body</dt> <dd> <pre>{{ myModel.respBody }}</pre> </dd> </dl> </div> </div> PresentationView.vue
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
    copy dist folderto server directory
  • 81.
    app.js const express =require('express'); const bodyParser = require('body-parser'); const path = require('path'); const cors = require('cors'); const app = express(); var request = require('request'); app.use(bodyParser.json()); // json을 parse하도록 함 app.use(cors()); app.use(express.static(".")); // configuration ========================= app.set('port', process.env.PORT || 8030); app.use( '/', express.static( path.join(__dirname, './dist') )); app.get('/', function (req,res) { res.sendFile(path.join(__dirname, './dist', 'index.html')); }); app.listen(app.get('port'), () => { console.log('Express server listening on port ' + app.get('port')); }); npm install express npm install cors npm install request
  • 82.
  • 83.
    forever - nodeJS의무중단 관리도구 ○ forever 설치 npm install forever -g ○ forever 시작 forever start app.js ○ forever 종료 forever stop app.js (또는 forever list에서 확인한 pid) ○ forever 목록 forever list

Editor's Notes

  • #5 Vue.js devtools
  • #6 <script setup>과 <script> 차이 https://mine-it-record.tistory.com/640
  • #17 https://joshua1988.github.io/vue-camp/syntax/computed.html#computed-%E1%84%89%E1%85%A9%E1%86%A8%E1%84%89%E1%85%A5%E1%86%BC-%E1%84%8B%E1%85%A8%E1%84%89%E1%85%B5
  • #41 {data: {…}, status: 200, statusText: '', headers: AxiosHeaders, config: {…}, …} config: {transitional: {…}, adapter: 'xhr', transformRequest: Array(1), transformResponse: Array(1), timeout: 0, …} data: {time: {…}, disclaimer: 'This data was produced from the CoinDesk Bitcoin P…hourly conversion rate from openexchangerates.org', chartName: 'Bitcoin', bpi: {…}} headers: AxiosHeaders {cache-control: 'max-age=15', content-length: '679', content-type: 'application/javascript', expires: 'Mon, 18 Sep 2023 12:50:07 UTC'} request: XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …} status: 200 statusText: "" [[Prototype]]: Object
  • #43 {data: {…}, status: 200, statusText: '', headers: AxiosHeaders, config: {…}, …} config: {transitional: {…}, adapter: 'xhr', transformRequest: Array(1), transformResponse: Array(1), timeout: 0, …} data: bpi: EUR: {code: 'EUR', symbol: '&euro;', rate: '26,640.5514', description: 'Euro', rate_float: 26640.5514} GBP: code: "GBP" description: "British Pound Sterling" rate: "22,851.4325" rate_float: 22851.4325 symbol: "&pound;" [[Prototype]]: Object USD: code: "USD" description: "United States Dollar" rate: "27,347.5961" rate_float: 27347.5961 symbol: "&#36;" [[Prototype]]: Object [[Prototype]]: Object chartName: "Bitcoin" disclaimer: "This data was produced from the CoinDesk Bitcoin Price Index (USD). Non-USD currency data converted using hourly conversion rate from openexchangerates.org" time: updated: "Sep 18, 2023 12:43:00 UTC" updatedISO: "2023-09-18T12:43:00+00:00" updateduk: "Sep 18, 2023 at 13:43 BST" [[Prototype]]: Object constructor: ƒ Object() hasOwnProperty: ƒ hasOwnProperty() isPrototypeOf: ƒ isPrototypeOf() propertyIsEnumerable: ƒ propertyIsEnumerable() toLocaleString: ƒ toLocaleString() toString: ƒ toString() valueOf: ƒ valueOf() __defineGetter__: ƒ __defineGetter__() __defineSetter__: ƒ __defineSetter__() __lookupGetter__: ƒ __lookupGetter__() __lookupSetter__: ƒ __lookupSetter__() __proto__: (...) get __proto__: ƒ __proto__() set __proto__: ƒ __proto__() [[Prototype]]: Object constructor: ƒ Object() hasOwnProperty: ƒ hasOwnProperty() isPrototypeOf: ƒ isPrototypeOf() propertyIsEnumerable: ƒ propertyIsEnumerable() toLocaleString: ƒ toLocaleString() toString: ƒ toString() valueOf: ƒ valueOf() __defineGetter__: ƒ __defineGetter__() __defineSetter__: ƒ __defineSetter__() __lookupGetter__: ƒ __lookupGetter__() __lookupSetter__: ƒ __lookupSetter__() __proto__: (...) get __proto__: ƒ __proto__() set __proto__: ƒ __proto__() headers: AxiosHeaders {cache-control: 'max-age=15', content-length: '679', content-type: 'application/javascript', expires: 'Mon, 18 Sep 2023 12:45:07 UTC'} request: XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …} status: 200 statusText: "" [[Prototype]]: Object constructor: ƒ Object() hasOwnProperty: ƒ hasOwnProperty() isPrototypeOf: ƒ isPrototypeOf() propertyIsEnumerable: ƒ propertyIsEnumerable() toLocaleString: ƒ toLocaleString() toString: ƒ toString() valueOf: ƒ valueOf() __defineGetter__: ƒ __defineGetter__() __defineSetter__: ƒ __defineSetter__() __lookupGetter__: ƒ __lookupGetter__() __lookupSetter__: ƒ __lookupSetter__() __proto__: (...) get __proto__: ƒ __proto__() set __proto__: ƒ __proto__()
  • #45 https://cors-anywhere.herokuapp.com
  • #47 https://icea.tistory.com/47
  • #58 <script> import { ref } from 'vue' import { onMounted } from "vue"; // example components import DefaultNavbar from "@/examples/navbars/NavbarDefault.vue"; import Header from "@/examples/Header.vue"; //Vue Material Kit 2 components import MaterialInput from "@/components/MaterialInput.vue"; import MaterialSwitch from "@/components/MaterialSwitch.vue"; import MaterialButton from "@/components/MaterialButton.vue"; // material-input import setMaterialInput from "@/assets/js/material-input"; export default {   components: {     DefaultNavbar,     Header,     MaterialInput,     MaterialSwitch,     MaterialButton,     setMaterialInput   },   setup() {     const myemail = ref('a@a.com')     const mypwd = ref('1234')     const refmyemail = ref(null);     function func_signin() {       console.log("sign in ",myemail,mypwd)       console.log(refmyemail._value.value)     }         onMounted(() => {       setMaterialInput();           })     return{       myemail,       mypwd,       func_signin,       refmyemail     }   }, } </script> <template>   <DefaultNavbar transparent />   <Header>     <div       class="page-header align-items-start min-vh-100"       :style="{         backgroundImage:           'url(https://images.unsplash.com/photo-1497294815431-9365093b7331?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1950&q=80)'       }"       loading="lazy"     >       <span class="mask bg-gradient-dark opacity-6"></span>       <div class="container my-auto">         <div class="row">           <div class="col-lg-4 col-md-8 col-12 mx-auto">             <div class="card z-index-0 fadeIn3 fadeInBottom">               <div                 class="card-header p-0 position-relative mt-n4 mx-3 z-index-2"               >                 <div                   class="bg-gradient-success shadow-success border-radius-lg py-3 pe-1"                 >                   <h4                     class="text-white font-weight-bolder text-center mt-2 mb-0"                   >                     Sign in                   </h4>                   <div class="row mt-3">                     <div class="col-2 text-center ms-auto">                       <a class="btn btn-link px-3" href="javascript:;">                         <i class="fa fa-facebook text-white text-lg"></i>                       </a>                     </div>                     <div class="col-2 text-center px-1">                       <a class="btn btn-link px-3" href="javascript:;">                         <i class="fa fa-github text-white text-lg"></i>                       </a>                     </div>                     <div class="col-2 text-center me-auto">                       <a class="btn btn-link px-3" href="javascript:;">                         <i class="fa fa-google text-white text-lg"></i>                       </a>                     </div>                   </div>                 </div>               </div>               <div class="card-body">                 <!-- submit.prevent added by hwy -->                 <form role="form" class="text-start" @submit.prevent>                   <!-- code modified by hwy -->                   <div class="input-group input-group-outline my-3">                     <input v-model="myemail" id="email" class="form-control form-control-md" type="email" ref="refmyemail"/>                   </div>                   <div class="input-group input-group-outline my-3">                     <input v-model="mypwd" id="password" class="form-control form-control-md" type="password"/>                   </div>                                     <!-- <MaterialInput                     :value="myemail"                     id="email"                     class="input-group-outline my-3"                     :label="{ text: 'Email', class: 'form-label' }"                     type="email"                   /> -->                   <!-- <MaterialInput                     id="password"                     class="input-group-outline mb-3"                     :label="{ text: 'Password', class: 'form-label' }"                     type="password"                     v-model="mypwd"                   /> -->                   <MaterialSwitch                     class="d-flex align-items-center mb-3"                     id="rememberMe"                     labelClass="mb-0 ms-3"                     checked                     >Remember me</MaterialSwitch                   >                   <div class="text-center">                     <MaterialButton                       class="my-4 mb-2"                       variant="gradient"                       color="success"                       @click="func_signin"                       fullWidth                       >Sign in</MaterialButton                     >                   </div>                   <p class="mt-4 text-sm text-center">                     Don't have an account?                     <a                       href="#"                       class="text-success text-gradient font-weight-bold"                       >Sign up</a                     >                   </p>                 </form>               </div>             </div>           </div>         </div>       </div>       <footer class="footer position-absolute bottom-2 py-2 w-100">         <div class="container">           <div class="row align-items-center justify-content-lg-between">             <div class="col-12 col-md-6 my-auto">               <div                 class="copyright text-center text-sm text-white text-lg-start"               >                 © {{ new Date().getFullYear() }}, made with                 <i class="fa fa-heart" aria-hidden="true"></i> by                 <a                   href="https://www.creative-tim.com"                   class="font-weight-bold text-white"                   target="_blank"                   >Creative Tim</a                 >                 for a better web.               </div>             </div>             <div class="col-12 col-md-6">               <ul                 class="nav nav-footer justify-content-center justify-content-lg-end"               >                 <li class="nav-item">                   <a                     href="https://www.creative-tim.com"                     class="nav-link text-white"                     target="_blank"                     >Creative Tim</a                   >                 </li>                 <li class="nav-item">                   <a                     href="https://www.creative-tim.com/presentation"                     class="nav-link text-white"                     target="_blank"                     >About Us</a                   >                 </li>                 <li class="nav-item">                   <a                     href="https://www.creative-tim.com/blog"                     class="nav-link text-white"                     target="_blank"                     >Blog</a                   >                 </li>                 <li class="nav-item">                   <a                     href="https://www.creative-tim.com/license"                     class="nav-link pe-0 text-white"                     target="_blank"                     >License</a                   >                 </li>               </ul>             </div>           </div>         </div>       </footer>     </div>   </Header> </template>
  • #75 parseQueryString 역할 : 물음표 앞에 문자열을 모두 없애버림 "/api/hello2?name=taro"  name=taro Object.prototype.hasOwnProperty.call 문장 테스트: - const resp = await $client.get("/api/hello2?name=taro&name=taro2");에 대한 응답 name: Array(2) 0: "taro" 1: "taro2" length: 2 - const resp = await $client.get("/api/hello2?name=taro")에 대한 응답 params: name: "taro"
  • #82 const express = require('express'); const bodyParser = require('body-parser'); const path = require('path'); const cors = require('cors'); const app = express(); var request = require('request'); app.use(bodyParser.json()); // json을 parse하도록 함 app.use(cors()); app.use(express.static(".")); // configuration ========================= app.set('port', process.env.PORT || 8030); app.use( '/', express.static( path.join(__dirname, './dist') )); app.get('/', function (req,res) { res.sendFile(path.join(__dirname, './dist', 'index.html')); }); app.listen(app.get('port'), () => { console.log('Express server listening on port ' + app.get('port')); });