{{ str1 }} // aaa->zzz
{{ str2 }} // aaa
const obj = ref({v1:'aaa'})
let str1 = toRef(obj.value, "v1")
let str2 = obj.value.v1
setTimeout(() => {
obj.value.v1 = 'zzz'
}, 3000);
๐ ๊ฒฐ๊ณผ๊ฐ์ ๋ณด๊ฒ๋๋ฉด str1์ ๊ฐ์ zzz๋ก ๋ณ๊ฒฝ์ด ๋์ง๋ง, str2๋ zzz๋ก ๋ณ๊ฒฝ์ด ๋์ง ์๋๋ค.
์ด๋ str1์ toRef๋ฅผ ์ด์ฉํ์ฌ obj.value.v1 ๊ฐ์ ๊ฐ์ ์ฐธ์กฐํ๋ฏ๋ก ์๊ฐ์ด ๋ณ๊ฒฝ๋ ์ str1๊ฐ๋ ๋ณ๊ฒฝ๋๋ค.
const userId = ref({mid:''})
const toMid = toRef(userId.value, "mid")
๐ ์ด๋ฌํ ์ ์ ์ด์ฉํ์ฌ userId๊ฐ ๋ํ toRef๋ฅผ ์ด์ฉํ์ฌ ๊ฐ์ ์ฐธ์กฐ๋ฅผ ํ๋ค.

import { defineStore } from "pinia";
import { computed, ref, toRef } from "vue";
const useMember2 = defineStore('useMember2', ()=> {
const userId = ref({mid:''})
const toMid = toRef(userId.value, "mid") // ๊ธฐ์กด์ ref๋ฅผ ์ฌ์ฉํ์ฌ ์ฐธ์กฐํ๋ค๋ฉด 1๋์ค ๊น์ง ์ฐธ์กฐ๊ฐ ๋์ด ์ดํ ์ถ๋ ฅํ ๋ ์ ๋๋ก๋ ์ถ๋ ฅ์ด ๋ถ๊ฐ๋ฅํ๋ค.
// ์ฝ๋๋ป์ userId.value๊ฐ ์ค์์ mid๊ฐ์ ์ฐธ์กฐํ๊ฒ ๋ค๋ ๋ง.
const signin = (str) => {
userId.value.mid = str
localStorage.setItem("mid", str)
}
const signout = () => {
userId.value.mid = ''
localStorage.removeItem("mid")
}
const computedMid = computed(()=> { // computed๋ ๋ด๋ถ์ ๋ฐ์ํ ๊ฐ์ฒด๊ฐ ์ฌ์ฉ๋ ๋ ๊ทธ ๋ฐ์ํ ๊ฐ์ฒด ๊ฐ์ด ๋ณ๊ฒฝ์ด ๋๋ฉด ์ถ๋ ฅ๊ฐ๋ ๋ณ๊ฒฝ๋ ๊ฐ์ผ๋ก ์ถ๋ ฅ๋๋ค.
if(!localStorage.getItem("mid")) {
return ''
}
if(localStorage.getItem("mid")){
userId.value.mid = localStorage.getItem("mid")
}
if(toMid){
return toMid
}
return toMid
})
return {userId,signin,signout,computedMid}
})
export default useMember2
<script setup>
import { RouterLink, RouterView } from 'vue-router';
import useMember2 from './stores/useMember2';
const {signout,computedMid} = useMember2() //useMember.js ์์ member๋ณ์์ logout๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ฒ ๋ค.
</script>
<template>
<div>
<span><RouterLink to="/">Main</RouterLink></span> <!--Aํ๊ทธ ์ฒ๋ผ ์๊ธด ํ๋ฉด์ด๋-->
<span><RouterLink to="/about">About</RouterLink></span>
<span><RouterLink to="/todo">Todo</RouterLink></span>
<span v-if="computedMid===''"><RouterLink to="/login">Login</RouterLink></span>
<span v-if="computedMid!==''"><button @click="signout">Logout</button></span>
</div>
<div>
<hr/>
USERID : {{ computedMid }}
<hr/>
</div>
<RouterView></RouterView> <!--๋ผ์ฐํฐ๋ก ์ด์ฉ๋ ํ์ด์ง๋ฅผ ์ถ๋ ฅ-->
</template>
<style scoped>
span{
margin-left: 1em;
}
</style>
<template>
<div>
<h1>Login Page</h1>
<input type="text" v-model="midInput">
<button @click="handleClick">Login</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import useMember2 from '../stores/useMember2';
const {signin} = useMember2() // useMember.js์ login ๊ธฐ๋ฅ ์ฌ์ฉ
const midInput = ref('')
const handleClick = () => {
const mid = midInput.value // input text ๋ก ์
๋ ฅํ id ๊ฐ์ mid๋ณ์์ ๋ฃ๊ณ
signin(mid) // store์ ์ ์ฅ๋ signin๋ฉ์๋์ mid ์ธ์๋ฅผ ๋ฃ์ด ํธ์ถฃ
}
</script>
<style lang="scss" scoped>
</style>
<template>
<div>
<div v-if="loading" class="loadingDiv">
<h1>Loading.............</h1>
</div>
<ul>
<li v-for="t in result.content" :key="t.tno" @click="()=>handleClickMove(t.mno)">
{{ t }}
</li>
</ul>
<template v-for="(p,idx) in pageArr" :key="idx">
<span class="pageSpan" @click="() => handleClickPage(p.page)" > {{p.label}} </span>
</template>
</div>
</template>
<script setup>
import { getList } from '../../apis/todoAPI';
import useListData from '../../hooks/useListData';
const {loading, moveToRead, router, route, refresh, result, pageArr} = useListData(getList)
const handleClickPage = (pageNum) => {
const currentPage = route.query.page || 1 // page์ฟผ๋ฆฌ๊ฐ์ด ์์ผ๋ฉด ๊ฐ์ ธ์ค๊ณ ์์ผ๋ฉด 1
router.push({query: {page:pageNum} }).then(() => {
if(currentPage == pageNum){ // ํ์ฌ ํ์ด์ง ๋ฒํผ์ ๋๋ฅด๋ฉด
refresh.value = !refresh.value // refresh
}
})
}
const handleClickMove = ( tno )=> {
moveToRead(tno)
}
</script>
<style scoped>
.loadingDiv {
position: absolute;
top: 30vh;
left: 40vw;
width: 20vw;
height: 10vh;
background-color: aqua;
}
.pageSpan {
margin: 0.3em;
padding: 0.1em;
border: 1px solid black;
}
</style>
import { computed, onMounted, ref, watch } from 'vue';
import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
const useListData = (listFn) => {
const fn = listFn
const route = useRoute()
const router = useRouter()
const loading = ref(false)
const refresh = ref(false)
const result = ref({
content:[],
number:0,
size:10,
totalElements:0,
totalPages:0
})
const loadPageData = async (page) => {
loading.value = true
const data = await fn(page)
result.value = data
loading.value = false
}
const pageArr = computed(() => {
//ํ์ฌ ํ์ด์ง ๋ฒํธ
const currentPage = result.value.number + 1
//๋ง์ง๋ง ํ์ด์ง ๋ฒํธ - ์ฌ๋ฆผ( ํ์ด์ง๋ฒํธ/10.0 ) * 10
let lastPage = Math.ceil( currentPage/10.0 ) * 10
//์์ ํ์ด์ง ๋ฒํธ
const start = lastPage - 9
//์ด์ , ๋ค์
const prev = start !== 1
const next = result.value.totalPages > lastPage
if(result.value.totalPages < lastPage){
lastPage = result.value.totalPages
}
//ํ์ด์ง ๋ฒํธ์ ์ถ๋ ฅ์ ํ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ฐฐ์ด๋ก
const pageArr = []
//์ด์
if(prev){
pageArr.push({page: start -1, label:'์ด์ ' })
}
for(let i = start; i <= lastPage ; i++){
pageArr.push({page: i, label: i })
}
//๋ค์
if(next){
pageArr.push({page: lastPage + 1, label:'๋ค์' })
}
return pageArr
})
onMounted(() => { // ๋ฐ์ดํฐ๋ฅผ ๋ก๋ํ๋ ์ญํ
loadPageData(route.query.page || 1)
})
watch(refresh, ()=> { // refresh๊ฐ์ ๊ฐ์งํ์ฌ refresh๋๋ฉด loadPageDataํธ์ถ
loadPageData(route.query.page || 1)
})
onBeforeRouteUpdate((to, from, next) => { // ์ฌ์ฉ์๊ฐ ํ์ด์ง๋ฅผ ๋ณ๊ฒฝํ ๋ (ํ์ด์ง ๋ฒํธํด๋ฆญํด์ url๋ณ๊ฒฝ์)
loadPageData(to.query.page || 1)
next()
})
const moveToRead = (tno) => {
router.push(`/todo/read/${tno}`)
}
return {loading, moveToRead, router, route, refresh, result, pageArr}
}
export default useListData
export const getOne = async(mno) => {
const res = await axios.get(`${host}/${mno}`)
return res.data
}
<template>
<div>
{{ todo }}
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import { getOne } from '../../apis/todoAPI';
const route = useRoute()
const mno = route.params.mno // routers์์ url์ฃผ์์ mno๊ฐ์ ๋ฃ์๋ค์
const todo = ref({
mno:0,
title:'',
writer:'',
dueDate:''
})
onMounted(()=> {
console.log("mno: " + mno)
getOne(mno).then(data => todo.value = data) // api์์ ๋ฐ์ data๊ฐ์ ref๊ฐ์ ๋์
})
</script>
<style lang="scss" scoped>
</style>
<template>
<div>
<h1>Todo Read Page</h1>
<todoReadComponent></todoReadComponent>
</div>
</template>
<script setup>
import todoReadComponent from '../../components/todo/todoReadComponent.vue';
</script>
<style lang="scss" scoped>
</style>
export const postOne = async (obj) => { // ๋ฑ๋ก
const res = await axios.post(`${host}`, obj) // post๋ ์๋ ๋งค๊ฐ๋ณ์๊ฐ 2๊ฐ
return res.data
}
<template>
<div>
<h1>Todo Add Component</h1>
<div>
<input type="text" v-model="todo.title" >
</div>
<div>
<input type="text" v-model="todo.writer" >
</div>
<div>
<input type="date" v-model="todo.dueDate" >
</div>
<div>
<button @click="handleClick" >ADD</button>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { postOne } from '../../apis/todoAPI';
import { useRouter } from 'vue-router';
const router = useRouter()
const todo = ref({ // api์์์ obj๊ฐ
title:'',
writer: '',
dueDate: ''
})
const handleClick = async ()=> {
console.log(todo.value)
const res = await postOne(todo.value)
alert(res.mno)
}
</script>
<style lang="scss" scoped>
</style>
<template>
<div>
<h2>Todo Add Page</h2>
<TodoAddComponent></TodoAddComponent>
</div>
</template>
<script setup>
import TodoAddComponent from '../../components/todo/TodoAddComponent.vue';
</script>
<style lang="scss" scoped>
</style>
export const deleteOne = async (mno) => { // ์ญ์
const res = await axios.delete(`${host}/${mno}`)
return res.data
}
<template>
<div>
{{ todo }}
</div>
<div>
<button @click="handleClickDelete">DELETE</button>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { deleteOne, getOne } from '../../apis/todoAPI';
import useMember2 from '../../stores/useMember2';
const route = useRoute()
const router = useRouter()
const mno = route.params.mno
const {computedMid} = useMember2()
const handleClickDelete = () => {
deleteOne(mno)
router.replace("/todo/list")
}
const todo = ref({
mno:0,
title:'',
writer:'',
dueDate:''
})
onMounted(()=> {
console.log("mno: " + mno)
getOne(mno).then(data => todo.value = data)
})
</script>
<style lang="scss" scoped>
</style>
<template>
<div>
<h1>Todo Edit Page</h1>
<TodoEditComponent></TodoEditComponent>
</div>
</template>
<script setup>
import TodoEditComponent from '../../components/todo/TodoEditComponent.vue';
</script>
<style lang="scss" scoped>
</style>
export const putOne = async (todo) => { // ์์
const res = await axios.put(`${host}/${todo.mno}`, todo)
return res.data
}
<template>
<div v-if="error !== null">ERROR {{ error }}</div> <!--์๋ฌ๊ฐ ์์ผ๋ฉด ์๋ฌ๋ฉ์ธ์งํ์ -->
<div v-if="error === null">
<div>
<input type="text" v-model="todo.mno" readonly> <!--mno, wrtier๋ ์์ ๋ถ๊ฐ๋ฅ-->
</div>
<div>
<input type="text" v-model="todo.title" >
</div>
<div>
<input type="text" v-model="todo.writer" readonly >
</div>
<div>
<input type="date" v-model="todo.dueDate" >
</div>
<div>
<button @click="handleClickModify">MODIFY</button>
<button @click="handleClickDelete">DELETE</button>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { deleteOne, getOne, putOne } from '../../apis/todoAPI';
import useMember2 from '../../stores/useMember2';
const route = useRoute()
const router = useRouter()
const mno = route.params.mno
const {computedMid} = useMember2()
const handleClickModify = () => {
putOne(todo.value).then(result => {
router.replace(`/todo/read/${mno}`) // replace๋ฅผ ์ฌ์ฉํ๋ฉด ํ์ฌ ํ์ด์ง์ ํ์คํ ๋ฆฌ๊ฐ ์ญ์ ๋๋ฏ๋ก ์์ ๋ฐ ์ญ์ ์์๋ push ๋ณด๋จ replace
})
}
const handleClickDelete = () => {
deleteOne(mno)
router.replace("/todo/list")
}
const todo = ref({
mno:0,
title:'',
writer:'',
dueDate:''
})
const error = ref(null)
onMounted(()=> {
getOne(mno).then(data => todo.value = data).catch((err) => { // getOne์ด ์ฑ๊ณต์ ์ผ๋ก ๋๋ฉด then์ผ๋ก ๊ฐ์ง๋ง ์คํจํ๋ค๋ฉด catch๋ฌธ
error.value = err.response.data.message //error์ response์์ฑ์ค data์์ฑ์ message
})
})
</script>
<style lang="scss" scoped>
</style>
<template>
<div>
<h1>Todo Edit Page</h1>
<TodoEditComponent></TodoEditComponent>
</div>
</template>
<script setup>
import TodoEditComponent from '../../components/todo/TodoEditComponent.vue';
</script>
<style lang="scss" scoped>
</style>