본문 바로가기

국비과정/Vue

20230919 _[77일차]_01. Vue (4차)_게시판만들기

pdf _ vue 4차 참고

 

cmd에서 새로운 vue 프로젝트 만들자.

최상위에서 vue폴더로 이동 : cd c:\vue  

vue폴더 내부에 sep19폴더생성 : vue create sep19

전자정부 새로운 프로젝트 생성

설정은 아래 네가지만 추가

 

BoardController & BoardService & BoardDAO 생성

static 폴더 아래 mapper폴더 생성

boardMapper.xml 생성

mapper에서 namespace 경로설정 해준다.

이번에는 포트번호 3000으로 한다.

pdf에서 복붙해주고 db 정보만 내 계정으로 수정해준다.

build-gradle 에서는 json만 추가 (*refresh해주기)

 

컨트롤러에서 항상 @ResponseBody 붙여주기 번거로우니 @RestController로 변경

db에서 받아오는 값들은 Map타입의 List로 받을거다.

json객체 생성하고 jsonArr에 담아서 리턴한다.

(오류가 났었는데 db 포트번호 변경 후 비밀번호가 초기화 됐었는데 그 이후 다시 안바꿔줬나보다ㅎㅎㅎㅎ)

db연결 잘되니 아래처럼 잘 뜬다.

jsp나 타임리프를 이용하는것이 아닌 json객체로 뷰단에 보내주는 것이기때문에 컨트롤러의 어노테이션을 RestController로한다. (그 페이지 안에 있는 것들을 다 @ResponseBody한다는 뜻)

 


vs code로 가서 터미널 실행하고 

axios 설치 : npm install -save axios


시작하기 | Axios Docs (axios-http.com)

 

시작하기 | Axios Docs

시작하기 브라우저와 node.js에서 사용할 수 있는 Promise 기반 HTTP 클라이언트 라이브러리 Axios란? Axios는 node.js와 브라우저를 위한 Promise 기반 HTTP 클라이언트 입니다. 그것은 동형 입니다(동일한 코

axios-http.com


 

구동시켜준다.

npm run serve

components 폴더 내부에 BoardList.vue 파일생성

 

[ App.vue ]

<template>
  <BoardList/>
</template>

<script>
import BoardList from './components/BoardList.vue'

export default {
  name: 'App',
  components: {
    BoardList
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

vue.js의 created와 mounted의 차이 + Vue의 라이프 사이클 :: 꾸밍 (tistory.com)

 

vue.js의 created와 mounted의 차이 + Vue의 라이프 사이클

created인스턴스가 작성된 후 동기적으로 호출됩니다. 이 단계에서 인스턴스는 데이터 처리, 계산된 속성, 메서드, 감시/이벤트 콜백 등과 같은 옵션 처리를 완료합니다. 그러나 마운트가 시작되

aomee0880.tistory.com


mounted 역할은 구동될때 읽어들이는 역할

<template>
    <div>
        <h1>board</h1>
        <table>
            <thead>
            <tr>
                <th>번호</th>
                <th>제목</th>
                <th>글쓴이</th>
                <th>날짜</th>
                <th>조회수</th>
            </tr>
            </thead>
            <tbody>
            <tr v-for="n in list" v-bind:key="n.bno">
                <td>{{ n.bno }}</td>
                <td>{{ n.btitle }}</td>
                <td>{{ n.m_name }}</td>
                <td>{{ n.bdate }}</td>
                <td>{{ n.blike }}</td>
            </tr>
            </tbody>
        </table>

    </div>
</template>

<script>
import axios from 'axios';

export default {

    name: 'BoardList',
    data(){
        return{
            list: [
                {bno:10, btitle:'10번글', m_name: '홍길동', bdate:'2023-09-09', blike:2},
                {bno:9, btitle:'9번글', m_name: '김길동', bdate:'2023-09-09', blike:3},
                {bno:8, btitle:'8번글', m_name: '최길동', bdate:'2023-09-09', blike:4},
                {bno:7, btitle:'7번글', m_name: '박길동', bdate:'2023-09-09', blike:5},
                {bno:6, btitle:'6번글', m_name: '이길동', bdate:'2023-09-09', blike:6}
            ],
        }
    },
    mounted(){
        axios.get('http://localhost:3000/board')
        .then()
        .catch();
    }
}
</script>

<style>

</style>

 

여기서  화살표는 함수의? 축약형 문법이다.

    mounted(){
        axios.get('http://localhost:3000/board')
        .then((res) => {}
           
        )
        .catch();
    }

 

    mounted(){
        axios.get('http://localhost:3000/board')
        .then((res) => {
            alert(res.data);
        })
        .catch((err) => {
            alert('문제가 발생했습니다.' + err);
        });
    }

아래처럼 에러가 난다.

 

다시 이클립스 보드컨트롤러로 가자.

아래처럼 @CrossOrigin * 추가선언 해주고

위에서 보낸 list의 0번지의 btitle을 뽑아내보자 (list는 배열*)

    mounted(){
        axios.get('http://localhost:3000/board')
        .then((res) => {
            alert(res.data.list[0].btitle);
        })
        .catch((err) => {
            alert('문제가 발생했습니다.' + err);
        });
    }

그러면 아래처럼 btitle을 잘 가져와 팝업이 뜬다.

이제 위의 넣어준 더미데이터는 필요가 없고

아래처럼 list로 내보낸걸 받아와서 vue가 알아서 형식에 맞춰서 브라우저에 띄워준다.

더미데이터를 지운다. list가 배열이라는것 까지만 선언해주면 된다.

이런 방식으로 이제 스프링에서는 데이터를 보내주기만 하고

그 데이터를 받아서 보여주는건 vue가 알아서 해준다. 

 

제목부분에 class이름 지정해주고

    <table>
      <thead>
        <tr>
          <th>번호</th>
          <th>제목</th>
          <th>글쓴이</th>
          <th>날짜</th>
          <th>조회수</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="n in list" v-bind:key="n.bno">
          <td>{{ n.bno }}</td>
          <td class="title">{{ n.btitle }}</td>
          <td>{{ n.m_name }}</td>
          <td>{{ n.bdate }}</td>
          <td>{{ n.blike }}</td>
        </tr>
      </tbody>
    </table>

스타일 지정해준다.

<style scoped>
table {
  width: 800px;
  height: 300px;
  border-collapse: collapse;
  margin: 0 auto;
}
th {
  background-color: rgb(169, 109, 109);
}
td {
  border-bottom: 1px gray solid;
}
tr:hover {
  background-color: gray;
  color: white;
}
.title {
  text-align: left;
}
#td1 {
  width: 10%;
}
#td2 {
  width: 30%;
}
td a {
  color: black;
}
</style>

[ BoardList.vue ]

<template>
  <div>
    <h1>board</h1>
    <table>
      <thead>
        <tr>
          <th>번호</th>
          <th>제목</th>
          <th>글쓴이</th>
          <th>날짜</th>
          <th>조회수</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="n in list" v-bind:key="n.bno">
          <td>{{ n.bno }}</td>
          <td class="title">{{ n.btitle }}</td>
          <td>{{ n.m_name }}</td>
          <td>{{ n.bdate }}</td>
          <td>{{ n.blike }}</td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
import axios from "axios";

export default {
  name: "BoardList",
  data() {
    return {
      list: [],
    };
  },
  mounted() {
    axios
      .get("http://localhost:3000/board")
      .then((res) => {
        //alert(res.data.list[0].btitle);
        this.list = res.data.list;
      })
      .catch((err) => {
        alert("문제가 발생했습니다." + err);
      });
  },
};
</script>

<style scoped>
table {
  width: 800px;
  height: 300px;
  border-collapse: collapse;
  margin: 0 auto;
}
th {
  background-color: rgb(169, 109, 109);
}
td {
  border-bottom: 1px gray solid;
}
tr:hover {
  background-color: gray;
  color: white;
}
.title {
  text-align: left;
}
#td1 {
  width: 10%;
}
#td2 {
  width: 30%;
}
td a {
  color: black;
}
</style>

이제 제목을 클릭하면 detail로 이동하도록 기능을 넣어줄거다.

          <td class="title">
            <a v-on:click="viewDetail('${n.bno}')">{{ n.btitle }}</a>
          </td>
export default {
  name: "BoardList",
  data() {
    return {
      list: [],
    };
  },
  mounted() {
    axios
      .get("http://localhost:3000/board")
      .then((res) => {
        //alert(res.data.list[0].btitle);
        this.list = res.data.list;
      })
      .catch((err) => {
        alert("문제가 발생했습니다." + err);
      });
  },
  methods:{
    viewDetail(bno){
        alert(bno+'번을 눌렀습니다');
    }
  }
};

 

그런데 ${n.bno}를 감싸고 있는건 홑따옴표가 아니라 백틱(`)을 사용해줘야 한다.

 <td class="title">
            <a v-on:click="viewDetail(`${n.bno}`)">{{ n.btitle }}</a>
          </td>

그래야 아래의 메소드가 작동하여 글제목을 누르면 글번호를 잘 가져온다.

  methods:{
    viewDetail(bno){
        alert(bno+'번을 눌렀습니다');
    }
  }

 

App.vue 에

<template>
  <BoardList/>
</template>

요렇게 설정되어 있으면 계속 이 페이지만 보여주게 된다.

 

터미널에서 일단 구동 멈추고 아래 명령어로 라우터 설치

npm install --save vue-router 

 

package.json가서 확인해보면 잘 설치되어있다.

 

src 내부에 views 폴더 생성 - 내부에 DetailPage.vue 파일 생성

MenuPage.vue 파일 생성하고 

pdf _ 3차에 있는 내용 넣어준다.

 

src 내부에 router 폴더 만들고 내부에 index.js 파일 생성_ pdf _ 3차에 있는 내용 넣어준다.

그리고 detailPage 임포트 해주고 detailPage에 대한 path 설정도 추가해준다.

(오류가 나서 아직 없는 페이지들 import 및 경로는 일단 주석처리 해준다.)

/* 실제로 라우팅을 해주는 파일. 이곳에서 각 파일로 연결시켜 줍니다. */
import { createRouter, createWebHistory } from 'vue-router'
//import indexPage from '@/views/IndexPage.vue'
//import boardList from '@/views/BoardList.vue'
//import mainPage from '@/views/MainPage.vue'
//import loginPage from '@/views/LoginPage.vue'
//import joinPage from '@/views/JoinPage.vue'
import detailPage from '@/views/DetailPage.vue'

const routes = [
 //{path: '/', name: 'index', component: indexPage},
 //{path: '/join', name: 'join', component: joinPage},
 //{path: '/boardList', name: 'boardList', component: boardList},
 //{path: '/main', name: 'main', component: mainPage},
 //{path: '/login', name: 'login', component: loginPage},
 {path: '/detail', <span style="color: #9cd

* 추가정리 *

게시판에서 글제목(title)을 누르면 detailPage로 이동하도록 method에 설정해준다.

BoardList.vue에서 글제목을 눌렀을 때 bno를 매개변수로 가지고 viewDetail 함수가 실행되도록 해줬다.

        <td class="title">
          <a v-on:click="viewDetail(`${n.bno}`)">{{ n.btitle }}</a>
        </td>

 

viewDetail 함수는 <script>태그 내부의 methods 영역에 만들어준다.

  methods: {
    viewDetail(bno) {
      alert(bno);
    },
  },

bno를 잘 잡아온다면

 

data부분에 아래처럼 사용할 변수들 선언해준다.

$route.query : 현재 URL의 쿼리 매개변수를 가진 객체  => requestBody 객체에 담아 서버로 전송예정.

  data() {
    return {
      list: [],
      requestBody: this.$route.query,
      bno: this.$route.query.bno,
    };
  },

this.$router.push 를 통해 ./boardDetail 페이지로 이동.

이때 query값으로 위에서 선언해준 requestBody 객체를 가져가는데 여기에는 bno가 담겨있다.

methods: {
    viewDetail(bno) {
      //alert(bno);
      this.requestBody.bno = bno;
      this.$router.push({
        path: "./boardDetail",
        query: this.requestBody,
      });
    },
  },

 

이제 boardList 페이지에서 727번 글을 클릭하면 

해당 글의 bno를 가지고 boardDetail 페이지로 잘 이동한다.

 


 

@CrossOrigin 어노테이션

웹 애플리케이션에서 CORS (Cross-Origin Resource Sharing) 정책을 적용하기 위해 사용.

즉, 다른 출처에서의 리소스 요청이나 데이터 교환이 발생할 경우 CORS 정책을 고려해야 하며, 이를 위해 서버 측에서 @CrossOrigin 어노테이션 또는 다른 CORS 관련 설정을 적용한다.

 

* CORS (Cross-Origin Resource Sharing) 정책

브라우저의 동일 출처 정책(Same-Origin Policy)에 따라 다른 도메인 또는 포트에서 온 요청을 허용하거나 거부하는 보안 메커니즘

 

 


 

$route : $route 객체는 Vue Router에서 제공하는 객체로, 현재 라우팅 상태와 URL 정보를 제공하는데 사용 ( 페이지 전환을 관리하는 데 도움을 주는 중요한 객체 중 하나 )

 

$route.query: URL에서 쿼리 매개변수를 포함한 객체입니다. 쿼리 매개변수는 ? 뒤에 나열되며, 이러한 값을 $route.query를 통해 접근할 수 있습니다.