vue

국비 코딩 풀스택 수업 8일차

비루블 2022. 6. 30. 12:25

오전일과

cmd 조작

vue.config.js (서버 등록, 공동 db등)

BoardPage.vue

BoardWritePage.vue

 

오후일과

BoardPage.vue

BoardContent.vue

 

 

ref에서는 계산 속성이 사용 불가능 했으나, reactive에서는 사용이 가능했다.


오전일과

 

cmd
npm install <= 꾸준히 한번씩 해주는게 좋음
npm run serve <= 서버 구동

 


reactive활용
state에 변수 선언하면 스크립트에서 바꿔서 페이지에 자동 반영


vue.config.js

서버 등록
읽어들이고 npm으로 서버를 불러들임
so config가 바뀌면 서버 껏다 켜야함(재구동 필수)
공공db도 여기에 넣음

 


게시판 작업(초기 설정)
index.js BoardPage 추가
컴포넌트 BoardPage 생성
App.vue 게시판 버튼 추가

 


BoardPage.vue
로그인 페이지는 사용자가 누르면 백엔드
보드는 키자마자 바로 백엔드에서 받아들여옴

<template>
    <div>
        <h3>게시판</h3>

        <button @click="handleNext()">글쓰기</button>
        <!-- {{state}} -->
        <table border = "1">
            <thead>
                <tr>
                    <th>번호</th>
                    <th>제목</th>
                    <th>작성자</th>
                    <th>조회수</th>
                    <th>날짜</th>
                </tr>
            </thead>
            <tbody>
                <!-- rows 모양 [{}, {}, {}] -->
                <tr v-for="tmp in state.rows" :key="tmp._id">
                    <td>{{tmp._id}}</td>
                    <td @click="handleContent(tmp._id)" style="cursor:pointer">{{tmp.title}}</td>
                    <td>{{tmp.writer}}</td>
                    <td>{{tmp.hit}}</td>
                    <td>{{tmp.regdate}}</td>
                </tr>
            </tbody>
        </table>

        <button v-for="tmp in state.pages" :key="tmp" @click="handlePage(tmp)">
            {{tmp}}
        </button>


    </div>
</template>

<script>
import axios from 'axios';
import { onMounted, reactive } from '@vue/runtime-core';
import { useRouter } from 'vue-router';

export default {
    setup () {

        // 1. 변수설정 및 import
        const router = useRouter();



        // 객체 만들기 (return필수)
        const state = reactive({
            tml:'aaa', // 테스트용
            rows : null, // 백엔드의 데이터 중에서 게시물 목록
            total :0, // 백엔드의 데이터 중에서 게시물 전체 개수
            pages :0, // 전체개수를 기반으로 계산된 페이지수
            page : 1, // 초기페이지 번호
        });
        
        // 백엔드로부터 게시판 목록 받아오기
        // 함수는 호출되기 전에는 실행되지 않음.
        const handleData = async() => {
            const url = `/board101/select.json?page=${state.page}&text=`;
            const headers = {"Content-Type":"application/json"};

            const response = await axios.get(url, {headers:headers});
            console.log(response.data);

            if(response.data.status === 200){
                state.rows = response.data.rows;
                state.total = response.data.total;
                // console.log(Math.floor((state.total-1)/10) +1);
                state.pages = Math.floor((state.total-1)/10) +1;
            }
        };

        // 페이지가 로딩되면 자동으로 호출됨
        onMounted( () => {
            handleData();
        });

        const handlePage = ( tmp ) => {
            // alert('페이지 클릭'+tmp);
            state.page = tmp;
            handleData();

        };


        const handleNext = () => {
            router.push({path:'boardw'});
        };

        const handleContent = async(no) => {
            console.log('handleContent', no);
            //조회수 1증가
            const url = `/board101/updatehit.json?no=${no}`;
            const headers = {"Content-Type":"application/json"};
            
            const response = await axios.get(url, {headers});

            console.log(response.data);
            if(response.data.staus ===200){
                //상세페이지로 이동
                router.push({path:'/boardc', query:{sendno : no}});
                
            }



        };





        return {state, handlePage, handleNext, handleContent};
    }
}
</script>

<style lang="css" scoped>

</style>

 

 

바로 호출하는 함수onMount

import { onMounted, reactive } from '@vue/runtime-core';

onMounted( () => {
    handleData();
});

 

 

백엔드로부터 게시판 목록 받아오기

const handleData  = async() => {
    const url = `/board101/select.json?page=${state.page}&text=`;
    const headers = {"Content-Type" : "application/json"};

    const response = await axios.get(url, {headers:headers});
}

 

axios에서 
아래는 오브젝트 타입이기 때문에
{headers}
{headers : headers} 그냥 이렇게 적으십쇼

짧은게 좋지만, 배열 상태에 따라 하나만 적어도 되는데 아직까지 개념이 안잡힘.

 

===================================================================

reactive
변수를 받아오고
변수를 쉽게 쓰기 위해서 const state를 만듬(임시로라도 먼저 만들고 시작)
(객체 만들기)
페이지 로딩하자마자 rows와 total이 변경됨.(if문과 연동시킴 status ===200)


테이블 만들기
중요
<tr v-for="tmp in state.rows" :key="tmp._id">
key는 겹치면 안되기때문에 , 고유한 것을 적어야함.
rows 모양 [{}, {}, {}]
tmp{   } 모양 tmp에 해당하는 것을 넣어라는 뜻
v-for 만듬 위 rows의 모양만 반복문이 가능


페이지 개수
console.log(Math.floor((state.total-1)/10) +1);
pages 정의 (전체개수를 기반으로 계산된 페이지수)
Math.floor 소수점 버리기

페이지 개수 버튼 만들기

밑 두개는 같은 것임
1.
<button v-for="tmp in state.pages" :key="tmp"
            @click="handlePage()">
            {{tmp}}
</button>


2.
<button v-for="tmp in state.pages" :key="tmp" @click="handlePage(tmp)">
            {{tmp}}
        </button>

===================================

페이지 버튼 클릭시 작동하는 원리 요약

===================================
const page로 페이지 초기선언 및 기본 페이지로 만들고(1)
url을 페이지로 바뀔 수 있게 변수(${state.page})로 바꿈.
버튼에는 handlePage함수가 내포
handlePage 클릭시 tmp가 변함.
state.page = tmp로 
state.page가 정의되고
handleData 함수를 불러오면 page가 버튼 불렀던 번호로 바뀐 후 작동
(원하는 페이지로 이동)
===================================

handlePage함수 만듬

const page : 1 //초기선언, 초기페이지 번호입력

(로딩시 처음 화면은 1페이지여야하기 때문)

<handleData>
const url = `/board101/select.json?page=${state.page}&text=`;

<handlePage>

const handlePage = ( tmp ) => {
    // alert('페이지 클릭'+tmp);
    state.page = tmp;
    handleData();
}


====================================

글쓰기 버튼 클릭시 동작 

 

<button @click="handleNext()">글쓰기</button>

1. 버튼 설정

 

import { useRouter } from 'vue-router';


const router = useRouter();

2. 라우터 설정

const handleNext = () => {
    router.push({path:'boardw'});
}

3. BoardWritePage.vue로 이동

 

boardWritePage는 아래서 설명



BoardWritePage.vue

v-model은 reactive와 연결(데이터를 받아서 백엔드로 넘길때 사용)

ref는 그냥 const 선언(유효성 검사할때 사용)

<template>
    <div>
        <h3>글쓰기</h3>
        <input type="text" ref="title" placeholder="제목" v-model="state.title" /><br/>
        <textarea rows="6" ref="content" placeholder="내용"  v-model="state.content"></textarea><br/>
        <input type="text" ref="writer" placeholder="작성자"  v-model="state.writer"/><br/>
        <button @click="handleWrite()">글쓰기</button>
    </div>
</template>

<script>
import { reactive, ref } from '@vue/reactivity'
import axios from 'axios';
import { useRouter } from 'vue-router';

export default {
    setup () {
        const router = useRouter();

        const title = ref(); //document.byid로 개체 찾는
        const content = ref(); //document.byid로 개체 찾는
        const writer = ref(); //document.byid로 개체 찾는

        const state = reactive({
            title : '',
            content: '',
            writer : '',
        });

        //글쓰기 버튼 클릭시 호출됨
        const handleWrite = async() => {
            // 제목, 내용, 작성자 유효성 검사 자리 생략

            if(state.title === ''){
                alert('아이디를 입력하세요.')
                title.value.focus();
                return false;
            }
            if(state.content === ''){
                alert('내용을 입력하세요.')
                content.value.focus();
                return false;
            }
            if(state.writer === ''){
                alert('작성자를 입력하세요.')
                writer.value.focus();
                return false;
            }

            const url = `/board101/insert.json`;
            const headers = {"Content-Type" : "application/json"}
            const body = {
                title : state.title,
                content : state.content,
                writer : state.writer,
            }
            const response = await axios.post(url, body, {headers:headers});
            console.log(response.data);
            if(response.data.status === 200){
                router.push({path:'/board'});
            }
        };

        return {state, handleWrite, title, content, writer}
    }
}
</script>

<style lang="css" scoped>

.container {
    width : 700px;
    padding : 20px;
    border : 1px solid #cccccc;
}

</style>

 

index에서 매핑(페이지 연결)

BoardWritePage
input, textarea 입력
컨테이너 스타일 입력
state 리액트 선언

div부분
v-model로 변수적용(const state = reactive 실시간 적용)

 

 


await axios.post(서버기입, 바디, 헤더) 업로드
response (post 업로드 하는 것이기때문)
const response = await axios.post(url, body, {headers:headers});
url에 body를 첨부하여 보냄

 


글쓰기 완료후
status 200 확인후
게시판으로 보냄(router)

import { useRouter } from 'vue-router';
const router = useRouter();

라우터를 사용할꺼면 위 코드를 입력하여야함.(import 포함)
router.push({path:'/board'});

<script>
import { reactive, ref } from '@vue/reactivity'
import axios from 'axios';
import { useRouter } from 'vue-router';

export default {
    setup () {
        const router = useRouter();

        const state = reactive({
            title : '',
            content: '',
            writer : '',
        });

        //글쓰기 버튼 클릭시 호출됨
        const handleWrite = async() => {
            // 제목, 내용, 작성자 유효성 검사 자리 생략
            
            const url = `/board101/insert.json`;
            const headers = {"Content-Type" : "application/json"}
            const body = {
                title : state.title,
                content : state.content,
                writer : state.writer,
            }
            const response = await axios.post(url, body, {headers:headers});
            console.log(response.data);
            if(response.data.status === 200){
                router.push({path:'/board'});
            }
        };

        return {state, handleWrite, title, content, writer}
    }
}
</script>



===================================

 

아래는 유효성 검사를 위한 것.

개체찾기 (연결) reference 레퍼런스 ref

<div>
    <h3>글쓰기</h3>
    <input type="text" ref="title" placeholder="제목" v-model="state.title" /><br/>
    <textarea rows="6" ref="content" placeholder="내용"  v-model="state.content"></textarea><br/>
    <input type="text" ref="writer" placeholder="작성자"  v-model="state.writer"/><br/>
    <button @click="handleWrite()">글쓰기</button>
</div>
const title = ref(); //document.byid로 개체 찾는 것
const content = ref(); //document.byid로 개체 찾는 것
const writer = ref(); //document.byid로 개체 찾는 것

handleWrite
유효성 검사(div부분 ref 기입 필요 + const선언 필요)

if(state.title === ''){
    alert('아이디를 입력하세요.')
    title.value.focus();
    return false;
}
if(state.content === ''){
    alert('내용을 입력하세요.')
    content.value.focus();
    return false;
}
if(state.writer === ''){
    alert('작성자를 입력하세요.')
    writer.value.focus();
    return false;
}

 


오후일과

BoardPage.vue

상세페이지 확인

 <td @click="handleContent(tmp._id)" style="cursor:pointer">{{tmp.title}}</td>

handleContent

조회수 증가 성공하면 상세페이지로 이동(query 정보를 다음페이지로 보내줌)

if(response.data.staus ===200){
    //상세페이지로 이동
    router.push({path:'/boardc', query:{sendno : no}});

}

boardc에 넘버를 보내줌

 

아래는 오류가 계속나서 강사님께 물었더니, axios put을 이용하고 있었고 + body기입했었음.

axios.get을 사용해야함 게시물을 가져오는 것이니까(가져올때는 body를 쓰지 않음)


-BoardContent.vue

목록으로 이동할때 받아온 쿼리, 이전글, 다음글 정보, 게시글 내용 불러오기

수정, 삭제, 이전글, 다음글, 목록 (수정은 내일 알려주심)

<template>
    <div>
        <h3>게시판상세</h3>
        {{ state }} <!-- 처음에 해놓는게 좋음 -->
        <div v-if="state.row">
            글번호 : {{state.row._id}} <br/>
            제목 : {{state.row.title}}
            <input type="text" v-model="state.row.title" />
            <br/>
            내용 : {{state.row.content}} <br/>
            작성자 : {{state.row.writer}} <br/>
            조회수 : {{state.row.hit}} <br/>
            등록일 : {{state.row.regdate}} <br/>


            <button @click="handleUpdate()">수정</button>
            <button @click="handleDelete()">삭제</button>
            <button v-if="state.prev !==0" @click="handlePrev()">이전글</button>
            <button v-if="state.next !==0" @click="handleNext()">다음글</button>
            <router-link to ="/board">
                <button>목록</button>
            </router-link>
        </div>
        <router-link to="/board">목록으로</router-link>
    </div>
</template>

<script>
import { reactive } from '@vue/reactivity'
import { useRoute, useRouter } from 'vue-router'
import { onMounted } from '@vue/runtime-core';
import axios from 'axios';
export default {
    setup () {
        const route = useRoute(); //query값 받기
        const router = useRouter(); //페이지 이동 import 필수
        console.log(route);

        const state = reactive({ // 초기선언
            no : Number(route.query.sendno), // 목록에서 전달되는 글번호
            prev : 0, // 이전글 정보
            next : 0, // 다음글 정보
            row : null, // 게시글 내용 1개
        });
        
        const handleData = async() => {
            const url = `board101/selectone.json?no=${state.no}`
            const headers = {"Content-Type":"application/json"};
            const response = await axios.get(url, {headers});
            console.log(response.data);
            if(response.data.status === 200){
                state.prev = response.data.prevNo;
                state.next = response.data.nextNo;
                state.row = response.data.result;
            }
        }
        onMounted( () => {
            handleData();
        });
        
        const handlePrev = async() => {
            state.no = state.prev;
            handleData(); // 여기는 no값을 가지고 내용을 가져옴.
        };
        const handleNext = async() => {
            state.no = state.next;
            
            handleData(); // 여기는 no값을 가지고 내용을 가져옴.
        };

        const handleDelete = async() => {
            if(confirm('삭제할까요?')){
                const url = `board101/delete.json?no=${state.no}`;
                const headers = {"Content-Type":"application/json"};
                const response = await axios.delete(url, {headers});
                if(response.data.status === 200){
                    alert('삭제되었습니다.')
                    router.push({path:'board'});
            }
                

                
            }
        }

        const handleUpadte = async() =>{
            // 컴포넌트 없음
            // BoardUpdatepage.vue 생성
            // 라우트 연결
            router.push({path:'/boardu', query :{sendno: state.no} });
        }
        return {state, handlePrev, handleNext, handleDelete, handleUpadte}
    }
}
</script>

<style lang="scss" scoped>

</style>

const route = useRoute(); //query값 받기
no : Number(route.query.sendno)
route.query.+키

 

보드페이지에서 보드컨텐츠로 넘어올때 쿼리

router.push({path:'/boardc', query:{sendno : no}});

 

BoardContent 같은 것 만들때는
{{state}} 처음에 해놓는고 작업하는게 좋음.

 

 

v-if 쓰는 이유
데이터가 있을경우에만 표기
데이터를 받기전에 화면에 표기하기때문에 v-if를 없이 화면표기할때
오류 발생

 

handleData
상세 데이터 불러오는 펑션

onMounted

바로 실행하게끔 마운트

const handleData = async() => {
    const url = `board101/selectone.json?no=${state.no}`
    const headers = {"Content-Type":"application/json"};
    const response = await axios.get(url, {headers});
    console.log(response.data);
    if(response.data.status === 200){
        state.prev = response.data.prevNo;
        state.next = response.data.nextNo;
        state.row = response.data.result;
    }
}
onMounted( () => {
    handleData();
});

 


참고

v-for key

https://kamang-it.tistory.com/631

 

[Web][Performance][Vue] v-for과 key, 그리고 성능사이의 관계

참고: Programming/JavaScript-Vue Usage/JavaScript-Vue [Web][Performance][Angular][React][Vue] Angular vs React vs Vue 속도 비교 [Vue-06]반복문(v-for) v-for을 사용하다보면 생각보다 요놈이 문제 많은..

kamang-it.tistory.com

https://dev-josh.tistory.com/28

 

[Vue] Vue3 ref, reactive 사용해보기

이번에 vue3에서 composition api가 추가됨으로써 vue2 사용자 시점에서 ref와 reactive에 대하여 간단한 예제 소스를 작성해 보았다. 1. data()와 ref() 기존 vue2에서 데이터를 핸들링 할때는 사실 반응형이

dev-josh.tistory.com

ref 와 reactive 차이점

 

어렵다..