5. 0827_ Vue 더 알아보기
* ubi폴더의 customer-index.html을 싱글페이지로 쪼개는 작업
* onclick은 html에서 작동하고, vue에서는 작동하지 않는다. --> @click 으로 작성해야 함
Ex07
- 터미널 yarn.cmd serve 서버 실행
- 포트넘버 브라우저에 띄우기
<CustomerList.vue>
- 추가하기 버튼 누를 때 넘어가도록 하기
온클릭 --> @클릭 으로
함수넣기 showAddPage() {}
<CustomerAdd.vue>
card-body 안의 이름, 나이, 전화번호 input 태그마다 속성 코드 추가 : v-model="item.name"
script태그 추가, 안에 객체 생성 추가
- 수정 버튼 기능 추가
온클릭 --> @클릭 으로
index로 변수 지정, item.index와 함수()안에 index
함수넣기 showModifyPage() {}
수정하고 다시 list페이지로 넘어갈 수 있도록 add페이지에 mounted설정
<input> 이름, 나이, 전화번호 태그 속성에
ref="nameInput" --> id같은 역할을 함
this.$refs.nameInput.focus()
this.$refs.ageInput.focus()
this.$refs.mobileInput.focus()
ubi폴더의 > <customer-index.html> 파일업로드
<form id="FILE_FORM" method="post" enctype="multipart/form-data">
<label>파일 업로드</label>
<input type="file" id="pictureInput">
<img src="person.png" id="pictureImage">
<input type="button" value="업로드하기" onclick="uploadFile()">
</form>
function uploadFile() {
const form = $('#FILE_FORM')[0] //제이쿼리로 폼태그를 찾아서 넣어줌
const formData = new FormData(form)
formData.append('photo', $('#pictureInput')[0].files[0]) //url에 컨트롤러로 받아준다(?)
axios.post(
'/profile/upload', //url등을 안쓰고 그냥 쓰면 된다
formData,
{
headers: {
'Content-Type':'multipart/form-data;charset=utf-8;'
},
onUploadProgress: (e) => { //업로드 진행상황을 알게 해주는 함수, e:event
console.log('onUploadProgress 호출됨')
}
}
).then((response) => {
console.log(`응답 : ${response.data}`)
}).catch((err) => {
console.log(`에러 : ${err}`)
})
}
- 파일선택 버튼을 눌러서 이미지 파일을 선택하고 업로드하기 버튼을 누르면
코드 수정하고 나서 다시 업로드하기 버튼을 누르면
<div>
<img id="imageOutput" src="person.png">
</div>
console.log(`응답 : ${JSON.stringify(response.data)}`)
$('#imageOutput').attr('src', response.data.output.filename)
코드 추가 후 다시 업로드하기 버튼 누르면 이미지가 뜬다!
- 이미지파일 선택했을 때 미리보기
document.ready() {}안에 코드 추가하고, 함수 만들기
$('#pictureInput').change(() => {
previewPicture()
function previewPicture() {
const input = $('#pictureInput')[0]
if (input.files && input.files[0]) {
const reader = new FileReader()
reader.onload = (event) => { //비동기로 실행, 파일을 읽어서 event로 넘어옴
$('#pictureImage').attr('src', event.target.result)
}
reader.readAsDataURL(input.files[0]) //읽으라고 명령
}
}
- 이 아이콘 이미지를 서버에서 가져온 이미지로 넣어보기
- 추가 페이지에서 정보와 이미지 추가하고 저장버튼을 누르면 넘어오도록 파일업로드 부분 코드 전부 추가하기
saveItem함수 코드 수정
function saveItem() {
//1. 파일 업로드
uploadFile((err, filename) => {
if (err) {
return
}
//2. 고객정보 추가 / 수정
if (mode == 'add') {
requestAdd(filename) //파일을 추가하는 경우
} else if (mode == 'modify') {
requestModify()
}
showPage(0)
})
}
uploadFile함수 코드 수정
function uploadFile(callback) {
const form = $('#FILE_FORM')[0] //제이쿼리로 폼태그를 찾아서 넣어줌
const formData = new FormData(form)
formData.append('photo', $('#pictureInput')[0].files[0]) //url에 컨트롤러로 받아준다(?)
axios.post(
'/profile/upload', //url등을 안쓰고 그냥 쓰면 된다
formData,
{
headers: {
'Content-Type':'multipart/form-data;charset=utf-8;'
},
onUploadProgress: (e) => { //업로드 진행상황을 알게 해주는 함수, e:event
console.log('onUploadProgress 호출됨')
}
}
).then((response) => {
console.log(`응답 : ${JSON.stringify(response.data)}`)
$('#imageOutput').attr('src', response.data.output.filename)
callback(null, response.data.output.filename)
}).catch((err) => {
console.log(`에러 : ${err}`)
callback(err, null)
})
}
requestAdd 함수 코드 수정
function requestAdd(filename) {
console.log('requestAdd 함수 호출됨')
const name = $('#nameInput').val()
const age = $('#ageInput').val()
const mobile = $('#mobileInput').val()
axios({
method: 'post',
url: '/customer',
data: {
name: name,
age: age,
mobile: mobile,
filename: filename
},
responseType:'json'
}).then((response) => {
console.log(`응답 : ${JSON.stringify(response.data)}`)
requestList(1, 3)
}).catch((err) => {
console.log(`에러 : ${err}`)
})
}
- 과제,,
ubi폴더에 > users-controller.js 생성
-
- database폴더에 > sql 폴더에 > 파일 생성 : users_sql.js
module.exports = {
users_find: {
sql: "select id, name, age, mobile, filename \
from test.customer \
where id = :id" //:id 는 파라미터임 (아이디를 다른데에서 넣어준다~)
}
}
- postman을 실행
- users라는 콜렉션을 만들어 준다.
- find라는 request를 만들고 get방식을 이용하여 주소를 가져온다.
- 응답 부분 코드 작성
const util= require('../util/util')
const param = require('../util/param')
const Database = require('../database/database_mysql')
/**
* @constroller(path="/users")
*/
class Users {
constructor() {
this.database = new Database('database_mysql')
}
/**
*
* @RequestMapping(path="/find", method="get")
*/
async find(req, res) {
const params = param.parse(req)
const queryParams = {
sqlName: 'users_find',
params: params,
paramType: {}
}
try {
const rows = await this.database.execute(queryParams)
console.log(`sql 실행 결과 : ${JSON.stringify(rows)}`)
util.sendRes(res, 200, 'ok', rows)
} catch(err) {
console.log(`sql 실행 시 에러 : ${err}`)
util.sendError(res, 400, `Error -> ${err}`)
}
}
}
module.exports = Users
- postman에서 응답이 온다.
- ,,,
[최종 코드]
public > index.html
<!DOCTYPE html>
<html lang="">
<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">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<!-------------부트스트랩을 위한 CSS 파일 불러오기-------------->
<!-- Font Awesome -->
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.11.2/css/all.css">
<!-- Bootstrap core CSS -->
<link href="./mdb/css/bootstrap.min.css" rel="stylesheet">
<!-- Material Design Bootstrap -->
<link href="./mdb/css/mdb.min.css" rel="stylesheet">
<!------------------------------------------------------------->
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<!------------부트스트랩을 위한 JS 파일 불러오기------------->
<!-- JQuery -->
<script type="text/javascript" src="./mdb/js/jquery.min.js"></script>
<!-- Bootstrap tooltips -->
<script type="text/javascript" src="./mdb/js/popper.min.js"></script>
<!-- Bootstrap core JavaScript -->
<script type="text/javascript" src="./mdb/js/bootstrap.min.js"></script>
<!-- MDB core JavaScript -->
<script type="text/javascript" src="./mdb/js/mdb.min.js"></script>
<!------------------------------------------------------------------>
</body>
</html>
src > main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import VueAxios from 'vue-axios'
import Axios from 'axios'
createApp(App).use(store).use(router).use(VueAxios, Axios).mount('#app')
scr > App.vue
<template>
<div>
<div id="nav">
<router-link to="/">고객목록</router-link> |
<router-link to="/CustomerAdd">고객추가</router-link>
</div>
<router-view/>
</div>
</template>
<style>
#app {
text-align: center;
color: #2c3e50;
}
#nav {
padding: 30px;
}
#nav a {
font-weight: bold;
color: #2c3e50;
}
#nav a.router-link-exact-active {
color: #42b983;
}
</style>
src > router > index.js
import { createRouter, createWebHistory } from 'vue-router'
import CustomerList from '../views/CustomerList.vue'
import CustomerAdd from '../views/CustomerAdd.vue'
const routes = [
{
path: '/',
name: 'CustomerList',
component: CustomerList
},
{
path: '/CustomerAdd',
name: 'CustomerAdd',
component: CustomerAdd
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
src > views > CustomerList.vue
<template>
<div class="home">
<h1>고객목록 입니다 </h1>
<div class="container-fluid">
<div class="card" style="margin: 1em;">
<div class="card-body">
<!-- 리스트-->
<ul class="list-group" id="list1">
<li class="list-group-item active">
<div class="row">
<div class="col-sm-1 text-center align-self-center">
<img src="../assets/group.png" style="width: 2em">
</div>
<div class="col-sm-2 text-center align-self-center">
<span class="font-weight-bold">이름</span>
</div>
<div class="col-sm-2 text-center align-self-center">
<span class="font-weight-bold">나이</span>
</div>
<div class="col-sm-3 text-center align-self-center">
<span class="font-weight-bold">전화번호</span>
</div>
<div class="col-sm-4 text-center align-self-center">
<span class="font-weight-bold">기능</span>
</div>
</div>
</li>
<!-- v-for : 데이터를 반복해서 여러 번 만들어 줌 -->
<li v-for="(item, index) in items" :key="item.id" class="list-group-item waves-effect" >
<div class="row">
<div class="col-sm-1 text-center align-self-center">
<img src="../assets/person.png" style="width: 2em">
</div>
<div class="col-sm-2 text-center align-self-center">
<span class="font-weight-bold">{{ item.name }}</span>
</div>
<div class="col-sm-2 text-center align-self-center">
<span class="font-weight-bold">{{ item.age }}</span>
</div>
<div class="col-sm-3 text-center align-self-center">
<span class="font-weight-bold">{{ item.mobile }}</span>
</div>
<div class="col-sm-4 text-center align-self-center">
<button type="button" class="btn btn-outline-default btn-sm waves-effect font-weight-bold" @click="showModifyPage(index)">수정</button>
<button type="button" class="btn btn-outline-danger btn-sm waves-effect font-weight-bold" @click="deleteItem(index)">삭제</button>
</div>
</div>
</li>
</ul>
</div>
<div class="card-footer">
<div id="pagination">
<nav aria-label="Page navigation example">
<ul class="pagination pagination-circle pg-blue">
<li class="page-item" :class="{ 'disabled' : pagination.page == 1 }">
<a v-if="pagination.page == 1" class="page-link" href="#">First</a>
<a v-else @click="requestList(1, pagination.perPage)" class="page-link" href="#">First</a>
</li>
<li class="page-item" :class="{ 'disabled' : pagination.page == 1 }">
<a v-if="pagination.page == 1" class="page-link" aria-label="Previous" href="#">
<span aria-hidden="true">«</span>
<span class="sr-only">Previous</span>
</a>
<a v-else @click="requestList(pagination.page-1, pagination.perPage)" class="page-link" aria-label="Previous" href="#">
<span aria-hidden="true">«</span>
<span class="sr-only">Previous</span>
</a>
</li>
<!-- v-for, v-if, v-else :key가 의미하는 것 : v-bind :class속성(active가 true라면)-->
<li v-for="item in pagination.pages" :key="item.pageNo" class="page-item" :class="{ 'active' : item.pageActive }">
<a v-if="item.pageActive" class="page-link" href="#">{{ item.pageNo }}</a>
<a v-else @click="requestList(item.pageNo, pagination.perPage)" class="page-link" href="#">{{ item.pageNo }}</a>
</li>
<li class="page-item" :class="{ 'disabled' : pagination.page >= pagination.pageCount }">
<a v-if="pagination.page < pagination.pageCount" @click="requestList(pagination.page + 1, pagination.perPage)" class="page-link" aria-label="Next" href="#">
<span aria-hidden="true">»</span>
<span class="sr-only">Next</span>
</a>
<a v-else class="page-link" aria-label="Next" href="#">
<span aria-hidden="true">»</span>
<span class="sr-only">Next</span>
</a>
</li>
<li class="page-item" :class="{ 'disabled' : pagination.page >= pagination.pageCount }">
<a v-if="pagination.page < pagination.pageCount" @click="requestList(pagination.pageCount, pagination.perPage)" class="page-link" href="#">Last</a>
<a v-else class="page-link" href="#">Last</a>
</li>
</ul>
</nav>
</div>
<div class="row" style="margin-top: 1em;">
<div class="col-sm-8">
</div>
<div class="col-sm-4 text-right">
<button type="button" class="btn btn-outline-primary waves-effect" @click="showAddPage()">추가하기</button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Home',
components: {
},
data: () => ({
items: [],
pagination: { //데이터 넣어줌
page:0,
perPage:0,
total:0,
pages:[],
pageCount:0
}
}),
methods : {
requestList(page, perPage) {
console.log(`requestList 함수 호출됨 : ${page}, ${perPage}`)
this.axios({
method: 'get',
url: `http://localhost:8001/customer?page=${page}&perPage=${perPage}`,
responseType: 'json'
}).then((response) => {
console.log(`응답 : ${JSON.stringify(response.data)}`)
this.items = response.data.output.body
this.setPagination(response.data.output.header)
}).catch((err) => {
console.log(`에러 : ${err}`)
})
},
//원래 제이쿼리 해줬던 부분을 다 없애고 setPagination 이용함 --> 코드 양이 더 줄음
setPagination(header) { //테이블을 만드는 데 필요한 데이터만 계산해준다(?)
console.log(`setPagination 호출됨 : ${JSON.stringify(header)}`)
let curPage = Number(header.page)
let perPage = Number(header.perPage)
let totalRecords = header.total
console.log(`header : ${curPage}, ${perPage}, ${totalRecords}`)
let pageCount = Math.ceil(totalRecords / perPage)
console.log(`pageCount : ${pageCount}`)
// 한 번에 보여줄 페이지 개수
let unitPage = 10
// 시작 페이지의 인덱스
let startPageIndex = Math.floor((curPage-1)/unitPage) * unitPage
let endPage = pageCount - startPageIndex
if (endPage > unitPage) {
endPage = startPageIndex + unitPage
} else {
endPage = startPageIndex + endPage
}
console.log(`시작 페이지 인덱스와 끝 페이지 : ${startPageIndex}, ${endPage}`)
const pages = []
for (let i = startPageIndex; i < endPage; i++) {
if ((i+1) == curPage) {
pages.push({
pageNo: i+1,
pageActive: true
})
} else {
pages.push({
pageNo: i+1,
pageActive: false
})
}
}
//pagination에 데이터를 넣어줌
this.pagination.page = curPage
this.pagination.perPage = perPage
this.pagination.total = totalRecords
this.pagination.pages = pages
this.pagination.pageCount = pageCount
},
showAddPage() {
this.$router.push('/CustomerAdd')
},
showModifyPage(index) {
console.log(`showModifyPage 호출됨 : ${index}`)
const params = this.items[index]
this.$router.push({name:'CustomerAdd', params:params})
},
deleteItem(index) {
console.log(`deleteItem 호출됨 : ${index}`) //몇 번째 인덱스가 넘어오게 할지 적어줘야함
this.axios({
method:'delete',
url:`http://localhost:8001/customer/${this.items[index].id}`, //여기에 요청파라미터를 ?붙이는 게 안돼서 data를 따로 넣어야 함
data: {
},
responseType:'json'
}).then((response) => {
console.log(`응답 : ${JSON.stringify(response.data)}`)
// 고객목록 새로고침
let page = this.pagination.page
if (this.items.length == 1) {
page -= 1
}
if (page == 0) {
page = 1
}
const perPage = 3
this.requestList(page, perPage) //화면이 바뀔 필요는 없어서 데이터만 바꿔지게 함
}).catch((err) => {
console.log(`에러 : ${err}`)
})
}
},
mounted() { // 제이쿼리의 ready랑 같은역할
console.log('mounted 함수 호출됨')
const page = 1
const perPage = 3
this.requestList(page, perPage)
}
}
</script>
src > views > CustomerAdd.vue
<template>
<div class="about">
<h1>고객추가 페이지입니다</h1>
<div class="container-fluid">
<div class="card" style="margin: 3em;">
<div class="card-header">
<div class="font-weight-bold">고객 추가</div>
</div>
<div class="card-body">
<form class="text-center" style="color:#888888; padding: 4em;" action="#">
<div class="md-form form-group mt-5">
<input type="hidden" class="form-control" id="idInput">
</div>
<div class="md-form form-group mt-5">
<input ref="nameInput" v-model="item.name" type="text" class="form-control" id="nameInput" placeholder="">
<label for="nameInput">이름</label>
</div>
<div class="md-form form-group mt-5">
<input ref="ageInput" v-model.number="item.age" type="text" class="form-control" id="ageInput" placeholder="">
<label for="ageInput">나이</label>
</div>
<div class="md-form form-group mt-5">
<input ref="mobileInput" v-model="item.mobile" type="text" class="form-control" id="mobileInput" placeholder="">
<label for="mobileInput">전화번호</label>
</div>
</form>
<div class="row" style="margin-top: 3em;">
<div class="col-sm-1">
</div>
<div class="col-sm-5">
<button class="btn btn-outline-primary waves-effect" @click="saveItem()">저장</button>
</div>
<div class="col-sm-5">
<button class="btn btn-outline-primary waves-effect" @click="goBack()">닫기</button>
</div>
<div calss="col-sm-1">
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default { //객체(붕어빵) 넣어주기
name:'CustomerAdd',
data: () => ({
mode:'',
item: {
id:0, //비어있는 것도 값이 있는 것임 (0이나 ''대신에 undefined로 적어도 됨)
name:'',
age:0,
mobile:''
}
}),
methods: {
saveItem() {
console.log('CustomerAdd 안에서 saveItem 함수 호출됨.')
//이제 mode로 구분할 것임
if (this.mode == 'modify') {
this.requestModify()
} else {
this.requestAdd()
}
},
requestAdd() {
console.log('requestAdd 함수 호출됨')
this.axios({
method:'post',
url:'http://localhost:8001/customer', //여기에 요청파라미터를 ?붙이는 게 안돼서 data를 따로 넣어야 함
data: {
name: this.item.name,
age: this.item.age,
mobile: this.item.mobile
},
responseType:'json'
}).then((response) => {
console.log(`응답 : ${JSON.stringify(response.data)}`)
// 고객목록 화면으로 이동
this.$router.push('/')
}).catch((err) => {
console.log(`에러 : ${err}`)
})
},
requestModify() {
console.log('requestModify 함수 호출됨')
this.axios({
method:'put',
url:`http://localhost:8001/customer/${this.item.id}`,
data: {
name: this.item.name,
age: this.item.age,
mobile: this.item.mobile
},
responseType:'json'
}).then((response) => {
console.log(`응답 : ${JSON.stringify(response.data)}`)
// 고객목록 화면으로 이동
this.$router.push('/')
}).catch((err) => {
console.log(`에러 : ${err}`)
})
},
goBack() {
this.$router.push('/') //이 경로로 이동할 수 있다
}
},
mounted() {
console.log(`CustomerAdd의 mounted 함수 호출됨 : ${JSON.stringify(this.$route.params)}`)
if (typeof(this.$route.params) == 'undefined') { //params가 있는지 없는지 변수 크기로 확인해줌
this.mode = 'add'
} else {
this.mode = 'modify'
this.item = this.$route.params //한 사람에 대한 고객정보를 넣어줌
}
this.$refs.nameInput.focus()
this.$refs.ageInput.focus()
this.$refs.mobileInput.focus()
}
}
</script>