2차 프로젝트(6) - 게시판 기능 구현(Vue.js)

이애옹·2022년 9월 23일
0

2차 프로젝트

목록 보기
6/12
post-thumbnail

💬 이번에는 vue를 활용한 게시판 기능 구현!!
지금까지는 JQuery와 Ajax만 사용했었는데, 이번에는 새롭게 만들어보기로 했다!!

Vue는 렌더링이 빠르고 배우기 쉽다는 장점이 있다고 한다👀

📝 BoardVO 생성하기

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도 추가했다 ㅎㅎ

📝 BoardMapper 생성하기

@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>

=> 게시판 삭제하기 구문

📝 BoardDAO 생성하기

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를 생성했다!

📝 BoardController 생성하기

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";
	   }
	   
	}

📝 BoardRestController 생성하기

@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를 사용해주면 된다 ㅎㅎ

📝 list.jsp 생성하기

<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>

&nbsp;&nbsp;<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);

이거는 값이 제대로 넘어오는지 확인할수있는 코드다!
그냥 확인용으로 작성한거니까 안써도 상관없당

📝 insert.jsp 생성하기

<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로 보낼 데이터를 넣어준다.
이름,제목,상세내용,비밀번호를 보내줄거다 ㅎㅎ

📝 detail.jsp 생성하기

<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>
&nbsp;&nbsp;<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>

게시글 상세내용을 확인하기 위한 코드!

📝 delete.jsp 생성하기

<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>
&nbsp;&nbsp;<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를 맞춰준다.

💻 실행결과 확인하기

✔️ 게시판 목록 출력 기능 구현


ㅎㅎ 게시판 깔끔해서 좋댜😉

✔️ 글 상세보기 기능 구현

✔️ 글 작성하기 기능 구현



✔️ 글 수정하기 기능 구현

✔️ 글 수정하기 기능 구현 - 비밀번호가 틀릴 경우

✔️ 글 수정하기 기능 구현 - 비밀번호가 맞을 경우

✔️ 글 삭제하기 기능 구현

✔️ 글 삭제하기 기능 구현 - 비밀번호가 틀릴 경우

✔️ 글 삭제하기 기능 구현 - 비밀번호가 맞을 경우

profile
안녕하세요

0개의 댓글