💬 이번에는 vue를 활용한 게시판 기능 구현!!
지금까지는 JQuery와 Ajax만 사용했었는데, 이번에는 새롭게 만들어보기로 했다!!
Vue는 렌더링이 빠르고 배우기 쉽다는 장점이 있다고 한다👀
package com.sist.vo;
import java.util.Date;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class BoardVO {
private int no,hit;
private String name,subject,content,dbday,pwd;
private Date regdate;
}
이전에 제작한 테이블을 바탕으로 BoardVO를 생성했다!
원하는 형식으로 날짜데이터를 출력하기 위해 Dbday도 추가했다 ㅎㅎ
@Select("SELECT no,subject,name,TO_CHAR(regdate,'YYYY-MM-DD') as dbday,hit,num "
+"FROM (SELECT no,subject,name,regdate,hit,rownum as num "
+"FROM (SELECT no,subject,name,regdate,hit "
+"FROM replyboard_3 ORDER BY no DESC)) "
+"WHERE num BETWEEN #{start} AND #{end}")
public List<BoardVO> boardListData(Map map);
@Select("SELECT CEIL(COUNT(*)/10.0) FROM replyboard_3")
public int boardTotalPage();
@SelectKey(keyProperty = "no", resultType = int.class, before = true,
statement = "SELECT NVL(MAX(no)+1,1) as no FROM replyboard_3")
@Insert("INSERT INTO replyboard_3(no,name,subject,content,pwd,regdate,hit) VALUES(#{no},#{name},#{subject},#{content},#{pwd},SYSDATE,0)")
public void boardInsert(BoardVO vo);
//조회수 증가
@Update("UPDATE replyboard_3 SET "
+ "hit=hit+1 "
+ "WHERE no=#{no}")
public void hitIncrement(int no);
//detail
@Select("SELECT no,name,subject,content,hit,TO_CHAR(regdate,'YYYY-MM-DD') as dbday FROM replyboard_3 "
+ "WHERE no=#{no}")
public BoardVO boardDetailData(int no);
//비밀번호 값 받아오기
@Select("SELECT pwd FROM replyboard_3 "
+"WHERE no=#{no}")
public String boardGetPassword(int no);
//게시글 수정하기
@Update("UPDATE replyboard_3 SET "
+"name=#{name},subject=#{subject},content=#{content} "
+"WHERE no=#{no}")
public void boardUpdate(BoardVO vo);
@Delete("DELETE FROM replyboard_3 "
+"WHERE no=#{no}")
public void boardDelete(int no);
public List boardListData(Map map)
=> 게시판 목록 출력을 위한 구문
public Listpublic int boardTotalPage();
=> 페이지네이션을 위한 구문 (10개씩 페이지를 나눈다)
public Listpublic void boardInsert(BoardVO vo);
=> 게시판 데이터 삽입을 위한 구문
public Listpublic void hitIncrement(int no);
=> 조회수 증가를 위한 구문
public Listpublic BoardVO boardDetailData(int no);
=> 게시판 데이터 상세보기를 위한 구문
public Listpublic BoardVO boardDetailData(int no);
=> 게시판 데이터 상세보기를 위한 구문
public Listpublic String boardGetPassword(int no);
=> 비밀번호를 받아오는 구문 => 게시글 수정/삭제 시 비밀번호 확인
public void boardUpdate(BoardVO vo);
=> 게시글 수정하기 구문
<span style="background-color: rgba(242,179,188,0.5)"> public void boardDelete(int no);</span>
=> 게시판 삭제하기 구문
public List<BoardVO> boardListData(Map map)
{
return mapper.boardListData(map);
}
public int boardTotalPage()
{
return mapper.boardTotalPage();
}
public void boardInsert(BoardVO vo)
{
mapper.boardInsert(vo);
}
public BoardVO boardDetailData(int no)
{
mapper.hitIncrement(no);
return mapper.boardDetailData(no);
}
public BoardVO boardUpdateData(int no)
{
return mapper.boardDetailData(no);
}
public String boardUpdate(BoardVO vo)
{
String result="no";
String db_pwd=mapper.boardGetPassword(vo.getNo());
if(db_pwd.equals(vo.getPwd()))
{
result="yes";
mapper.boardUpdate(vo);
}
return result;
}
public String boardDelete(int no,String pwd)
{
String result="no";
String db_pwd=mapper.boardGetPassword(no);
if(db_pwd.equals(pwd))
{
result="yes";
mapper.boardDelete(no);
}
return result;
}
작성된 mapper를 바탕으로 DAO를 생성했다!
ackage com.sist.web;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import com.sist.dao.BoardDAO;
import com.sist.vo.BoardVO;
@Controller
public class BoardController {
@Autowired
private BoardDAO dao;
//사용자 요청 받기 => URI를 통하여 사용자의 요청을 받음
@GetMapping("board/list.do")
//사용자에게 받은 주소가 board/list.do 라면 어떤 역할을 수행할건지??
public String board_list()
{
return "board/list";
}
@GetMapping("board/insert.do")
public String board_insert()
{
return "board/insert";
}
@GetMapping("board/detail.do")
public String board_detail(int no,Model model)
{
//model을 이용해서 no값 넘기기
model.addAttribute("no",no);
return "board/detail";
}
@GetMapping("board/update.do")
public String board_update(int no,Model model)
{
model.addAttribute("no", no);
return "board/update";
}
@GetMapping("board/delete.do")
public String board_delete(int no,Model model)
{
model.addAttribute("no", no);
return "board/delete";
}
}
@Autowired
private BoardDAO dao;
//VueJS에서 페이지 전송
@GetMapping(value = "board/list_vue.do",produces = "text/plain;charset=utf-8")
public String board_list_vue(String page)
{
if(page==null)
page="1";
int curpage=Integer.parseInt(page);
Map map=new HashMap();
int rowSize=10;
int start=(rowSize*curpage)-(rowSize-1);
int end=rowSize*curpage;
map.put("start", start);
map.put("end", end);
List<BoardVO> list=dao.boardListData(map);
int totalpage=dao.boardTotalPage();
//JavaScript에 데이터를 전송
String result="";
try
{
JSONArray arr=new JSONArray();
int k=0;
for(BoardVO vo:list)
{
JSONObject obj=new JSONObject();
obj.put("no", vo.getNo());
obj.put("subject", vo.getSubject());
obj.put("name", vo.getName());
obj.put("dbday", vo.getDbday());
obj.put("hit", vo.getHit());
if(k==0)
{
obj.put("curpage", curpage);
obj.put("totalpage",totalpage);
}
arr.add(obj);
k++;
}
result=arr.toJSONString();
}catch(Exception ex) {}
return result;
}
@GetMapping(value = "board/insert_vue.do",produces = "text/plain;charset=utf-8")
public String board_insert_vue(BoardVO vo)
{
dao.boardInsert(vo);
return "OK";
}
@GetMapping(value = "board/detail_vue.do",produces = "text/plain;charset=utf-8")
public String board_detail_vue(int no)
{
String result="";
BoardVO vo=dao.boardDetailData(no);
JSONObject obj=new JSONObject();
obj.put("no", vo.getNo());
obj.put("name", vo.getName());
obj.put("subject", vo.getSubject());
obj.put("content", vo.getContent());
obj.put("dbday", vo.getDbday());
obj.put("hit", vo.getHit());
result=obj.toJSONString();
return result;
}
@GetMapping(value="board/update_vue.do",produces = "text/plain;charset=utf-8")
public String board_update_vue(int no)
{
String result="";
BoardVO vo=dao.boardUpdateData(no);
JSONObject obj=new JSONObject();
obj.put("no", vo.getNo());
obj.put("name", vo.getName());
obj.put("subject", vo.getSubject());
obj.put("content", vo.getContent());
result=obj.toJSONString();
return result;
}
@GetMapping(value="board/update_vue_ok.do",produces = "text/plain;charset=utf-8")
public String board_update_nue_ok(BoardVO vo)
{
String result=dao.boardUpdate(vo);
return result;
}
@GetMapping(value="board/delete_vue.do",produces = "text/plain;charset=utf-8")
public String board_delete_vue_ok(int no,String pwd)
{
String result=dao.boardDelete(no,pwd);
return result;
}
여러개의 값을 넘기기위해 JSONArray를 사용해줬다!
하나의 데이터만 넘길때는 JSONObject를 사용해주면 된다 ㅎㅎ
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<h2><b>자유게시판</b></h2>
<hr>
<div class="container">
<div class="row">
<div class="col-xs-12">
<a href="../board/insert.do" class="btn btn-sm btn-primary">새글</a>
<br>
<br>
<table summary="This table shows how to create responsive tables using Datatables' extended functionality" class="table table-bordered table-hover dt-responsive">
<thead>
<tr style="background-color: #b4b4b4;">
<th width=10% class="text-center">번호</th>
<th width=45% class="text-center">제목</th>
<th width=15% class="text-center">아이디</th>
<th width=20% class="text-center">작성일</th>
<th width=10% class="text-center">조회수</th>
</tr>
</thead>
<tbody>
<tr v-for="vo in board_list">
<th width=10% class="text-center">{{vo.no}}</th>
<th width=45%><a :href="'../board/detail.do?no='+vo.no">{{vo.subject}}</a></th>
<th width=15% class="text-center">{{vo.name}}</th>
<th width=20% class="text-center">{{vo.dbday}}</th>
<th width=10% class="txt_org text-center">{{vo.hit}}</th>
</tr>
</tbody>
<tr>
</table>
<table class="text-center">
<tr>
<td class="text-center">
<input type=button value="이전" class="btn btn-sm btn-danger">
{{curpage}} page / {{totalpage}} pages
<input type=button value="다음" class="btn btn-sm btn-danger">
</td>
</tr>
</table>
</div>
</div>
</div>
<script>
new Vue({
//el : 관리 영역 지정 => container
el:'.container',
data:{
board_list:[],
curpage:1,
totalpage:0
},
mounted:function(){
let _this=this;
axios.get("http://localhost:8080/web/board/list_vue.do",{
params:{
page:_this.curpage
}
}).then(function(result){
//개발자도구창에서 넘어온값 확인가능
console.log(result.data);
_this.board_list=result.data;
_this.curpage=result.data[0].curpage;
_this.totalpage=result.date[0].totalpage;
})
}
})
</script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
이 구문은 Vue사용을 위한 셋팅!
{{vo.no}}
vue는 기존 방식과 다르게 이렇게 데이터 선언을 해준다. ${} => {{}} 이렇게 바뀌었다
<a :href="'../board/detail.do?no='+vo.no">
이건 페이지 뒤에 no를 붙여서 넘기는 구문인데, 이것도 기존과 다르다!
꼭 앞에 ":"를 붙여줘야 값을 보낼 수 있다고 한다.
그리고 뒤에 붙이는 no 변수에는 꼭 ' ' 를 붙여줘야한다! 이거 안붙이면 그냥 문자열로
인식한다고 하니 조심!!
el:'.container',
el을 이용해서 제어 할 구문을 선택해준다!
나는 전체구문을 제어할거니까 container를 지정 해 줬지만,
파트별 데이터 제어도 가능하다고 하니 상황에 맞게 선택해주면 될것같다.
mounted:function(){
화면이 뜨자마자 실행되는 구문을 지정해준다
console.log(result.data);
이거는 값이 제대로 넘어오는지 확인할수있는 코드다!
그냥 확인용으로 작성한거니까 안써도 상관없당
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div class="container">
<h1>글쓰기</h1>
<div class="row row1">
<table class="table">
<tr>
<th width=20% class="text-right">이름</th>
<td width=80%>
<input type=text v-model="name" size=20 class="input-sm" ref="name">
</td>
</tr>
<tr>
<th width=20% class="text-right">제목</th>
<td width=80%>
<input type=text v-model="subject" size=50 class="input-sm" ref="subject">
</td>
</tr>
<tr>
<th width=20% class="text-right">내용</th>
<td width=80%>
<textarea rows="10" cols="50" v-model="content" ref="content"></textarea>
</td>
</tr>
<tr>
<th width=20% class="text-right">비밀번호</th>
<td width=80%>
<input type=password v-model="pwd" size=15 class="input-sm" ref="pwd">
</td>
</tr>
<tr>
<td colspan="2" class="text-center">
<input type=button value="글쓰기" class="btn btn-sm btn-warning" v-on:click="boardWrite()">
<%--
v-on:click="aaa()"
@click="aaa()"
--%>
<input type=button value="취소" class="btn btn-sm btn-info"
onclick="javascript:history.back()">
</td>
</tr>
</table>
</div>
</div>
<script>
//입력하면 멤버변수 안에 값을 채워줌(name,subject...)
new Vue({
el:'.container',
data:{
name:'',
subject:'',
content:'',
pwd:''
},
methods:{
boardWrite:function(){
if(this.name.trim()=="")
{
this.$refs.name.focus();
return;
}
if(this.subject.trim()=="")
{
this.$refs.subject.focus();
return;
}
if(this.content.trim()=="")
{
this.$refs.content.focus();
return;
}
if(this.pwd.trim()=="")
{
this.$refs.pwd.focus();
return;
}
//전송
axios.get("http://localhost:8080/web/board/insert_vue.do",{
//값 채워주기
params:{
name:this.name,
subject:this.subject,
content:this.content,
pwd:this.pwd
}
}).then(function(result){
location.href="../board/list.do";
})
}
}
})
</script>
똑같이 Vue를 이용하여 게시글을 작성하는 코드를 생성했다.
boardWrite:function(){
if(this.name.trim()=="")
{
this.$refs.name.focus();
return;
}
if(this.subject.trim()=="")
{
this.$refs.subject.focus();
return;
}
if(this.content.trim()=="")
{
this.$refs.content.focus();
return;
}
if(this.pwd.trim()=="")
{
this.$refs.pwd.focus();
return;
}
name, subject, content, pwd 중 작성되지 않은 부분이 있다면 화면이 넘어가지 않는다.
=> 작성되지 않은 부분에 focus를 놔준다.
axios.get("http://localhost:8080/web/board/insert_vue.do",{
//값 채워주기
params:{
name:this.name,
subject:this.subject,
content:this.content,
pwd:this.pwd
}
insert_vue.do로 보낼 데이터를 넣어준다.
이름,제목,상세내용,비밀번호를 보내줄거다 ㅎㅎ
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<h2><b>상세보기 페이지</b></h2>
<hr>
<div class="container">
<div class="row">
<div class="col-xs-12">
<table summary="This table shows how to create responsive tables using Datatables' extended functionality" class="table table-bordered table-hover dt-responsive">
<tr>
<th width=20% class="text-center warning">번호</th>
<td width=30% class="text-center">{{vo.no }}</td>
<th width=20% class="text-center warning">작성일</th>
<td width=30% class="text-center">{{vo.dbday }}</td>
</tr>
<tr>
<th width=20% class="text-center warning">이름</th>
<td width=30% class="text-center">{{vo.name }}</td>
<th width=20% class="text-center warning">조회수</th>
<td width=30% class="text-center">{{vo.hit }}</td>
</tr>
<tr>
<th width=20% class="text-center warning">제목</th>
<td colspan="3">{{vo.subject }}</td>
</tr>
<tr>
<td colspan="4" class="text-left" valign="top" height="200">
<pre style="white-space: pre-wrap;border:none;background-color: white;">{{vo.content }}</pre>
</td>
</tr>
<tr>
<td colspan="4" class="text-right">
<a :href="'../board/update.do?no='+no" class="btn btn-xs btn-info">수정</a>
<a :href="'../board/delete.do?no='+no" class="btn btn-xs btn-warning">삭제</a>
<a href="../board/list.do" class="btn btn-xs btn-success">목록</a>
</td>
</tr>
</table>
</div>
</div>
</div>
<script>
new Vue({
el:'.container',
data:{
//array
vo:{},
no:${no}
},
mounted:function(){
let _this=this;
axios.get("http://localhost:8080/web/board/detail_vue.do",{
params:{
no:_this.no
}
//요청 처리 결과값 읽기 => 데이터값만 변경(상태변경)
}).then(function(result){
_this.vo=result.data;
})
}
})
</script>
게시글 상세내용을 확인하기 위한 코드!
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<h2><b>삭제하기 페이지</b></h2>
<hr>
<div class="container">
<div class="row">
<div class="col-xs-12">
<table summary="This table shows how to create responsive tables using Datatables' extended functionality" class="table table-bordered table-hover dt-responsive">
<tr>
<th width=20% class="text-right">비밀번호</th>
<td width=80%>
<input type=password v-model="pwd" ref="pwd" size=15 class="input-sm">
</td>
</tr>
<tr>
<td colspan="2" class="text-center">
<input type=submit value="삭제" class="btn btn-sm btn-warning" v-on:click="boardDelete()">
<input type=button value="취소" class="btn btn-sm btn-info"
onclick="javascript:history.back()">
</td>
</tr>
</table>
</div>
</div>
</div>
<script>
new Vue({
el:'.container',
data:{
no:${no},
pwd:'',
res:''
},
methods:{
boardDelete:function(){
let _this=this;
axios.get("http://localhost:8080/web/board/delete_vue.do",{
params:{
no:_this.no,
pwd:_this.pwd
}
}).then(function(result){
_this.res=result.data
if(_this.res=='yes')
{
location.href="../board/list.do"
}
else
{
alert("비밀번호가 틀립니다!!")
_this.pwd="";
_this.$refs.pwd.focus()
}
})
}
}
})
</script>
마지막으로 게시글 삭제를 위한 구문이다!
게시글 삭제를 진행 할 경우, 비밀번호를 한번 확인 한 후 값을 넘겨주기 때문에
if(_this.res=='yes')
{
location.href="../board/list.do"
}
else
{
alert("비밀번호가 틀립니다!!")
_this.pwd="";
_this.$refs.pwd.focus()
}
요론 구문을 넣어줬다!
비밀번호가 맞을 경우 list.do로 페이지를 이동하고,
틀릴 경우 "비밀번호가 틀립니다!!"라는 구문을 출력해준다.
그리고 기입되어있던 비밀번호를 삭제 한 후에,
비밀번호 칸에 focus를 맞춰준다.
ㅎㅎ 게시판 깔끔해서 좋댜😉