웹_프론트_백엔드/JAVA프레임윅기반_풀스택

2020.07.06

shine94 2020. 7. 6. 09:07

1. STS19_REST, REST(AJAX + SPA) 게시판 만들기

1) 사전 세팅

** [src/main/java] com.lec.sts19_rest.board.controller > BCommand.java

package com.lec.sts19_rest.board.controller;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.lec.sts19_rest.board.C;
import com.lec.sts19_rest.board.beans.BWriteDTO;
import com.lec.sts19_rest.board.command.BCommand;
import com.lec.sts19_rest.board.command.BDeleteCommand;
import com.lec.sts19_rest.board.command.BListCommand;
import com.lec.sts19_rest.board.command.BSelectCommand;
import com.lec.sts19_rest.board.command.BUpdateCommand;
import com.lec.sts19_rest.board.command.BViewCommand;
import com.lec.sts19_rest.board.command.BWriteCommand;

@Controller
@RequestMapping("/board")
public class BoardController {

private BCommand command;
	
	// MyBabatis
	private SqlSession sqlSession;
	
	public BoardController() {
		super();
		System.out.println("BoardController() 생성");
	}
	
	@Autowired
	public void setSqlSession(SqlSession sqlSession) {
		this.sqlSession = sqlSession;
		C.sqlSession = sqlSession;
	}

	@RequestMapping("/list.do")
	public String list(Model model) {
		command = new BListCommand();
		command.execute(model);
		return "board/list";
	}
	
	@RequestMapping("/write.do")
	public String write(Model model) {
		return "board/write";
	}
	
	@RequestMapping(value = "/writeOk.do", method = RequestMethod.POST)
	public String writeOk(BWriteDTO dto, Model model) {
		model.addAttribute("dto", dto);
		new BWriteCommand().execute(model);
		return "board/writeOk";
	}
	
	@RequestMapping("/view.do")
	public String view(int uid, Model model) {
		model.addAttribute("uid", uid);
		new BViewCommand().execute(model);
		return "board/view";
	}
	
	@RequestMapping("/update.do")
	public String update(int uid, Model model) {
		model.addAttribute("uid", uid);
		new BSelectCommand().execute(model);
		return "board/update";
	}
	
	@RequestMapping(value = "/updateOk.do", method = RequestMethod.POST)
	public String updateOk(BWriteDTO dto, Model model) {
		model.addAttribute("dto", dto);
		new BUpdateCommand().execute(model);
		return "board/updateOk";
	}
	
	@RequestMapping("/deleteOk.do")
	public String deleteOk(int uid, Model model) {
		model.addAttribute("uid", uid);
		new BDeleteCommand().execute(model);
		return "board/deleteOk";
	}
	
	// REST 게시판
	@RequestMapping(value="/rest")
	public String rest() {
		return "board/rest";
	}
	
}

 

** src > main > webapp > WEB-INF > views > board > rest.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath }/CSS/common.css"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://kit.fontawesome.com/a076d05399.js"></script>
<script src="${pageContext.request.contextPath }/JS/board.js"></script>

<title>SPA 게시판</title>
</head>
<body>
<h2>게시판 - SPA</h2>
<%-- 글목록 --%>
<div id="list">
	<div class="d01">
		<div class="left" id="pageinfo"></div>
		<div class="right" id="pageRows"></div>
	</div>
	
	<div class="clear"></div>
	
	<form id="frmList" name="frmList">
	
	<table>
		<thead>
			<th>#</th>
			<th>UID</th>
			<th>제목</th>
			<th>작성자</th>
			<th>조회수</th>
			<th>작성일</th>
		</thead>
		
		<tbody>
		
		</tbody>
	</table>
	
	</form>
	
	<%--버튼 --%>
	<div class="d01">
		<div class="left">
			<button type="button" id="btnDel" class="btn danger">글삭제</button>
		</div>
		<div class="right">
			<button type="button" id="btnWrite" class="btn success">글작성</button>
		</div>	
	</div>
	

</div>

<br>
<%-- 페이징 --%>
<div class="center">
	<ul class="pagination" id="pagination">
	
	</ul>
</div>


<%-- 글작성/보기/수정 대화상자 --%>
<div id="dlg_write" class="modal">
  
  <form class="modal-content animate" id="frmWrite" name="frmWrite" method="post">
    <div class="container">      
      <h3 class="title">새글 작성</h3>
      
      <span class="close" title="Close Modal">&times;</span>
      
      <input type="hidden" name="uid">  <%-- 삭제나 수정 위해 필요 --%>      
      
      <div class="d01 btn_group_header">
      	<div class="left">
      		<p id="viewcnt"></p>
      	</div>
      	<div class="right">
      		<p id="regdate"></p>
      	</div>
      	<div class="clear"></div>
      </div>
      
      <label for="subject"><b>글제목</b></label>
      <input type="text" placeholder="글제목(필수)" name="subject" required>

      <label for="name"><b>작성자</b></label>
      <input type="text" placeholder="작성자(필수)" name="name" required>
      
      <label for="content"><b>내용</b></label>
      <textarea placeholder="글내용" name="content"></textarea>
      
      <div class="d01 btn_group_write">
	      <button type="submit" class="btn success">작성</button>
      </div>  
      
      <div class="d01 btn_group_view">
			<div class="left">
				<button type='button' class="btn danger" id="viewDelete">삭제</button>
			</div>
			<div class="right">
				<button type='button' class="btn info" id="viewUpdate">수정</button>
			</div>
			<div class="clear"></div>
      </div>
      
      <div class="d01 btn_group_update">
      		<div>
      			<button type="button" class="btn info fullbtn" id="updateOk">수정완료</button>
      		</div>
      </div>
      
    </div>
  </form>
</div>


</body>
</html>

 

** src > main > webapp > CSS > common.css

@charset "UTF-8";

/* 기본 버튼 */
.btn {
  border: none;
  color: white;
  padding: 14px 28px;
  font-size: 16px;
  cursor: pointer;
}

.success {background-color: #4CAF50;} /* Green */
.success:hover {background-color: #46a049;}

.info {background-color: #2196F3;} /* Blue */
.info:hover {background: #0b7dda;}

.warning {background-color: #ff9800;} /* Orange */
.warning:hover {background: #e68a00;}

.danger {background-color: #f44336;} /* Red */ 
.danger:hover {background: #da190b;}

.default {background-color: #e7e7e7; color: black;} /* Gray */ 
.default:hover {background: #ddd;}

/* 글 목록 */
#list table {width: 100%;}

#list table, #list th, #list td {
	border: 1px solid black;
	border-collapse: collapse;
}

#list th, #list td {
	padding: 10px;
}

#list .subject:hover {	/* 글 제목 위에 커서를 올린 경우*/
	text-decoration: underline;
	color: orange;
	cursor: pointer;
}

.clear { clear: both; }

.left {
	float: left;
}

.right {
	float: right;
}

/* 페이징 */
.center {
    text-align: center;
}

ul.pagination{
	list-style-type:none
}

ul.pagination li{
    display: inline-block;
}

ul.pagination a {
    color: black;
    float: left;
    padding: 4px 8px;
    text-decoration: none;
    transition: background-color .3s;
    /* border: 1px solid #ddd; */
    /* margin: 0 4px; */
    margin: 0px;
}

ul.pagination a.active {
    background-color: #4CAF50;
    color: white;
    border: 1px solid #4CAF50;
}

ul.pagination a:hover:not(.active) {background-color: #ddd;}

/* 버튼 그룹 */
.d01 {
	margin: 5px 0;
}

/* 모달 팝업 */
/* 모달 팝업 */
.modal {   /* 모달 전체 적용 */	
	background-color: rgba(0, 0, 0, 0.4);
	width: 100%;
	height: 100%;
	position : fixed;
	top: 0;
	left: 0;
	z-index: 1;
	padding-top: 40px;  /* 내부여백  */
	overflow: auto;
	
	display : none;  /* 기본적으로 안보이기 */  
}

.modal .modal-content {
	background-color: #fefefe;  /* 배경은 흰색 */
	width: 80%;   /* 화면대비 80% */
	margin: 5% auto 15% auto;  /* 위에서 5%,  아래에서 15%, 좌우 중앙정렬 */
	border: 1px solid #888;  /* 테두리 */
}

.modal .container {
	padding: 16px;
	position: relative;  /* 이래야 안에 있는 absolute 들이 동작 */
} 

.modal .close {  /* close 버튼 */
	font-size: 35px;	
	font-weight: bold;
	color: #000;
	position: absolute;
	right: 25px;
	top: 0px;
}

.modal .close:hover,
.modal .cloas:focus {
	color: red;
	cursor: pointer;
}


.modal input[type=text] {
	width: 100%;
	border: 1px solid #ccc;
	margin: 8px 0;
	padding: 12px 20px;
	display: inline-block;
	border: 1px solid #ccc;
	box-sizing: border-box;
}

.modal textarea {
	width: 100%;
	margin: 8px 0;
}

.modal .fullbtn {
	width: 100%;
	cursor: pointer;
}

 

** src > main > webapp > JS > board.js

var page = 1  // 현재 페이지 
var pageRows = 10   // 한 페이지에 보여지는 게시글 개수

$(document).ready(function(){
	// 게시판 목록 1페이지 로딩
	loadPage(page);
	
	// 글작성 버튼 누르면 팝업
	$("#btnWrite").click(function() {
		setPopup("write");
		$("#dlg_write").show();
	});
	
	// 모달 대화상자에서 close 버튼 누르면 닫기
	$(".modal .close").click(function(){
		$(this).parents(".modal").hide();
	});
	
	// 글 작성 submit 되면
	$("#frmWrite").submit(function(){
		$(this).parents(".modal").hide();
		return chkWrite();
	});
	
	// 글 삭제 버튼 누르면
	$("#btnDel").click(function(){
		chkDelete();
	});
	
	// 글 읽기(view) 대화상자에서 삭제 버튼 누르면 해당 글(uid) 삭제 진행
	$("#viewDelete").click(function(){
		var uid = viewItem.uid;
		if(deleteUid(uid)){	// 해당 글 삭제
			$(this).parents(".modal").hide();	// 삭제 성공하면 대화상자 닫기
		}
	});
	
	// 글 읽기(view) 대화상자에서 수정버튼 누르면 
	$("#viewUpdate").click(function() {
		setPopup("update");
	});
	
	// 글 수정 완료 버튼 누르면
	$("#updateOk").click(function(){
		chkUpdate();
	});
	
});

// page 번째 페이지 로딩
function loadPage(page){
	
	$.ajax({
		url : "list.ajax?page=" + page + "&pageRows=" + pageRows
		, type : "GET"
		, cache : false
		, success : function(data, status){
			if(status == "success"){
				//alert("AJAX 성공: 받아쮸~");
				if(updateList(data)){
					
					// 업데이트된 list에 필요한 이벤트 가동
					addViewEvent();
					// ★ 만약 이 코드를 $(document).ready()에 두면 동작 안할 것이다!! ★
				}
			}			
		}
	});
} // end loadPage()

//
function updateList(jsonObj){
	result = ""; 
	
	if(jsonObj.status == "OK"){
		
		var count = jsonObj.count;
		
		// 전역변수 업데이트!
		window.page = jsonObj.page;
		window.pageRows = jsonObj.pagerows;
		
		var i;
		var items = jsonObj.data;   // 배열
		for(i = 0; i < count; i++){
			result += "<tr>\n";
			result += "<td><input type='checkbox' name='uid' value='" + items[i].uid + "'></td>\n";
			result += "<td>" + items[i].uid + "</td>\n";
			result += "<td><span class='subject' data-uid='" + items[i].uid + "'>" + items[i].subject + "</span></td>\n";
			result += "<td>" + items[i].name + "</td>\n";
			result += "<td><span data-viewCnt='" + items[i].uid + "'>" + items[i].viewcnt + "</span></td>\n";
			result += "<td>" + items[i].regdate + "</td>\n";			
			result += "</tr>\n";
		} // end for
		$("#list tbody").html(result);  // 테이블 업데이트!
		
		// 페이지 정보 업데이트
		$("#pageinfo").text(jsonObj.page + "/" + jsonObj.totalpage + "페이지, " + jsonObj.totalcnt + "개의 글");
		
		// pageRows
		var txt = "<select id='rows' onchange='changePageRows()'>\n";
		txt += "<option " + ((window.pageRows == 10)?"selected":"") + " value='10'>10개씩</option>\n";
		txt += "<option " + ((window.pageRows == 20)?"selected":"") + " value='20'>20개씩</option>\n";
		txt += "<option " + ((window.pageRows == 50)?"selected":"") + " value='50'>50개씩</option>\n";
		txt += "<option " + ((window.pageRows == 100)?"selected":"") + " value='100'>100개씩</option>\n";		
		txt += "</select>\n";
		$("#pageRows").html(txt);
		
		
		// 페이징 업데이트
		var pagination = buildPagination(jsonObj.writepages, jsonObj.totalpage, jsonObj.page, jsonObj.pagerows);
		$("#pagination").html(pagination);
		
		return true;
	} else {
		alert(jsonObj.message);
		return false;
	}
	return false;
} // end updateList()


function buildPagination(writePages, totalPage, curPage, pageRows){
	
	var str = "";   // 최종적으로 페이징에 나타날 HTML 문자열 <li> 태그로 구성

	// 페이징에 보여질 숫자들 (시작숫자 start_page ~ 끝숫자 end_page)
    var start_page = ( (parseInt( (curPage - 1 ) / writePages ) ) * writePages ) + 1;
    var end_page = start_page + writePages - 1;

    if (end_page >= totalPage){
    	end_page = totalPage;
    }
    
    //■ << 표시 여부
	if(curPage > 1){
		str += "<li><a onclick='loadPage(1)' class='tooltip-top' title='처음'><i class='fas fa-angle-double-left'></i></a></li>\n";
	}
	
  	//■  < 표시 여부
    if (start_page > 1) 
    	str += "<li><a onclick='loadPage(" + (start_page -1) + ")' class='tooltip-top' title='이전'><i class='fas fa-angle-left'></i></a></li>\n";
    
    //■  페이징 안의 '숫자' 표시	
	if (totalPage > 1) {
	    for (var k = start_page; k <= end_page; k++) {
	        if (curPage != k)
	            str += "<li><a onclick='loadPage(" + k + ")'>" + k + "</a></li>\n";
	        else
	            str += "<li><a class='active tooltip-top' title='현재페이지'>" + k + "</a></li>\n";
	    }
	}
	
	//■ > 표시
    if (totalPage > end_page){
    	str += "<li><a onclick='loadPage("+ (end_page + 1 ) + ")' class='tooltip-top' title='다음'><i class='fas fa-angle-right'></i></a></li>\n";
    }

	//■ >> 표시
    if (curPage < totalPage) {
        str += "<li><a onclick='(" + totalPage + ")' class='tooltip-top' title='맨끝'><i class='fas fa-angle-double-right'></i></a></li>\n";
    }

    return str;

	
	
	
} // end buildPagination()

function changePageRows(){
	window.pageRows = $("#rows").val();
	loadPage(window.page);
}

// 새글 등록 처리
function chkWrite() {
	
	var data = $("#frmWrite").serialize();	// 해당 폼 안의 name이 있는 것들을 끌어 들어옴
											// 리턴값은 Object
	//alert(data + "--" + typeof data);
	//subject=aaa&name=bbb&content=ccc--string
	
	
	// ajax request
	$.ajax({
		url : "writeOk.ajax",
		type : "POST",
		cache : false,
		data : data,  // POST 로 ajax request 하는 경우 parameter 담기
		success : function(data, status){
			if(status == "success"){
				if(data.status = "OK") {
					alert("INSERT 성공" + data.count + "개 : " + data.status);
					loadPage(1);	// 첫 페이지 리로딩
				} else {
					alert("INSERT 실패" + data.status + " : " + data.message);
				}
			}
		}
	});
	
	// request 후, form 에 입력된것 reset()
	$("#frmWrite")[0].reset();
	
	// type이 submit일때 비록 action이 없더라도
	// 자기 페이지로 리로딩된다...!!
	// 페이지 리로딩을 원치 않는다면 리턴 값을 false 값을 주면 된다!
	return false;	// 페이지 리로딩은 안 할 것이다
} // end chkWrite();

// check된 uid의 게시글들만 삭제하기
function chkDelete() {
	var uids = [];	// 빈 배열 준비
	
	// .each는 $("#list tbody input[name=uid") 각각에
	// each function()을 실행시키라는 의미!
	$("#list tbody input[name=uid]").each(function() {
		//$(this)는 checkbox
		if($(this).is(":checked")) {	// jQuery에서 check 여부 확인 방법
			uids.push($(this).val());	// 배열에 uid 값 추가
		}
	});
	
	//alert(uids);
	
	if(uids.length == 0) {
		alert("삭제할 글을 체크해 주세요");
	} else {
		
		if(!confirm(uids.length + "개의 글을 삭제하시겠습니까?")) return false;
		
		var data = $("#frmList").serialize();
			//   uid=1010&uid=1011&uid=1012
		
		$.ajax({
			url : "deleteOk.ajax",
			type : "POST",
			data : data,
			cache : false,
			success : function(data, status){
				if(status == "success"){
					if(data.status == "OK"){
						alert("DELETE 성공 " + data.count + "개");
						// 현재 페이지 리로딩
						loadPage(window.page);
					} else {
						alert("DELETE 실패 " + data.message);
					}
				}
			}
		});
		
	}
	
} // end chkDelete()

// 현재 글 목록 list에 대해 이벤트 등록
// - 제목(subject) 클릭하면 view 팝업 화면 뜰 수 있게 하기
function addViewEvent() {
	
	$("#list .subject").click(function(){
		// data-uid 는 내가 만튼 커스텀 애트리뷰트 데이터임
		//alert($(this).text() + " : " + $(this).attr('data-uid'));
		
		// 읽어오기
		$.ajax({
			url : "view.ajax?uid=" + $(this).attr('data-uid'),
			type : "GET",
			cache : false,
			success : function(data, status) {
				if(status = "successs") {
					if(data.status == "OK") {
						
						// 읽어온 view 데이터를 전역변수에 세팅
						viewItem = data.data[0];
						
						// 팝업에 보여주기
						setPopup("view");
						$("#dlg_write").show();
						
						// 리스트상의 조회수 증가시키기
						$("#list [data-viewcnt='" + viewItem.uid + "']").text(viewItem.viewcnt);
						
					} else {
						alert("VIEW 실패" + data.message);
					}
				}
			}
		});
	});
} // end addViewEvent()

// 대화상자 셋업
function setPopup(mode) {
	
	// 글작성
	if(mode == 'write'){
		$('#frmWrite')[0].reset();  // form 안의 기존 내용 reset
		$("#dlg_write .title").text("새글 작성");
		$("#dlg_write .btn_group_header").hide();
		$("#dlg_write .btn_group_write").show();
		$("#dlg_write .btn_group_view").hide();
		$("#dlg_write .btn_group_update").hide();
		
		
		$("#dlg_write input[name='subject']").attr("readonly", false);
		$("#dlg_write input[name='subject']").css("border", "1px solid #ccc");

		$("#dlg_write input[name='name']").attr("readonly", false);
		$("#dlg_write input[name='name']").css("border", "1px solid #ccc");

		$("#dlg_write textarea[name='content']").attr("readonly", false);
		$("#dlg_write textarea[name='content']").css("border", "1px solid #ccc");
		
	}
	
	// 글읽기
	if(mode == 'view'){
		$("#dlg_write .title").text("글 읽기");
		$("#dlg_write .btn_group_header").show();
		$("#dlg_write .btn_group_write").hide();
		$("#dlg_write .btn_group_view").show();
		$("#dlg_write .btn_group_update").hide();
		
		$("#dlg_write #viewcnt").text("#" + viewItem.uid + " - 조회수: " + viewItem.viewcnt);
		$("#dlg_write #regdate").text(viewItem.regdate);
		
		$("#dlg_write input[name='uid']").val(viewItem.uid);  // 나중에 삭제/수정을 위해 필요
		
		$("#dlg_write input[name='subject']").val(viewItem.subject);
		$("#dlg_write input[name='subject']").attr("readonly", true);
		$("#dlg_write input[name='subject']").css("border", "none");

		$("#dlg_write input[name='name']").val(viewItem.name);
		$("#dlg_write input[name='name']").attr("readonly", true);
		$("#dlg_write input[name='name']").css("border", "none");

		$("#dlg_write textarea[name='content']").val(viewItem.content);
		$("#dlg_write textarea[name='content']").attr("readonly", true);
		$("#dlg_write textarea[name='content']").css("border", "none");
		
	}
	
	// 글수정
	if(mode == "update"){
		$("#dlg_write .title").text("글 수정");
		$("#dlg_write .btn_group_header").show();
		$("#dlg_write .btn_group_write").hide();
		$("#dlg_write .btn_group_view").hide();
		$("#dlg_write .btn_group_update").show();
		
		$("#dlg_write input[name='subject']").attr("readonly", false);
		$("#dlg_write input[name='subject']").css("border", "1px solid #ccc");

		$("#dlg_write input[name='name']").attr("readonly", true);

		$("#dlg_write textarea[name='content']").attr("readonly", false);
		$("#dlg_write textarea[name='content']").css("border", "1px solid #ccc");
	}
		
} // end setPopup()

// 특정 uid 의 글 삭제하기
function deleteUid(uid) {
	
	if(!confirm(uid + "글을 삭제하시겠습니까?")) {return false;}
	
	// POST 방식
	$.ajax({
		url : "deleteOk.ajax",
		type : "POST",
		data : "uid=" + uid,
		cache : false,
		success : function(data, status) {
			if(status = "success") {
				if(data.status = "OK") {
					alert('DELETE 성공' + data.count + "개");
					loadPage(window.page);	// 현재 페이지 리로딩
											// 콕 찝어서 전역변수임을 알려주고 싶을때는 window.를 앞에 명시해주면 된다
				} else {
					alert("DELETE 실패 " + data.message);
					return false;
				}
			}
		}
	});
	
	return true;
	
} // end deleteUid(uid)

// 글 수정
function chkUpdate() {
	
	// serialize의 리턴은 String -> ex) name=aaa&subject=bbb&content=ccc&uid=1230
	var data = $("#frmWrite").serialize();
	
	$.ajax({
		url : "updateOk.ajax",
		type : "POST",
		cache : false,
		data : data,
		success : function(data, status){
			if(status == "success"){
				if(data.status == "OK"){
					alert("UPDATE 성공 " + data.count + "개:" + data.status);
					loadPage(window.page);  // 현재 페이지 리로딩
				} else {
					alert("UPDATE 실패 " + data.status + " : " + data.message);
				}
				$("#dlg_write").hide();  // 현재 팝업 닫기
			}
		}
	});
	
} // end chkUpdate()

 

** src > main > webapp > WEB-INF > spring > appServlet > servlet-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
	
	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />

	
	<!-- CSS와 JS의 경로 추가 필요!! -->
	<resources mapping="/CSS/**" location="/CSS/" />
	<resources mapping="/JS/**" location="/JS/" />


	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
	<context:component-scan base-package="com.lec.sts19_rest" />
	
	
	<!-- spring-jdbc 빈 객체 생성 -->
	<!-- DataSource 객체 -->
	<beans:bean name="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<beans:property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
		<beans:property name="url" value="jdbc:oracle:thin:@localhost:1521:XE"/>
		<beans:property name="username" value="scott0316"/>
		<beans:property name="password" value="tiger0316"/>
	</beans:bean>
	
	<!-- MyBatis 설정 -->
	<!-- SqlSessionFactoryBean 생성  -->
	<beans:bean name="SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<beans:property name="dataSource" ref="dataSource"/>
		<beans:property name="mapperLocations" value="classpath:com/lec/sts19_rest/mapper/*.xml"/>
	</beans:bean>
	<!-- SqlSessionTemplate 생성 -->
	<beans:bean name="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
		<beans:constructor-arg index="0" ref="SqlSessionFactory"/>
	</beans:bean>
	
	<!-- Transaction -->
	<beans:bean name="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<beans:property name="dataSource" ref="dataSource"/>
	</beans:bean>
	
	<beans:bean name="transactionTemplate"
		class="org.springframework.transaction.support.TransactionTemplate">
		<beans:property name="transactionManager" ref="transactionManager"/>
	</beans:bean>
	
	<beans:bean name="ticketService"
		class="com.lec.sts19_rest.ticket.TicketService">
	</beans:bean>
	
</beans:beans>

 

** [src/main/java] com.lec.sts19_rest.board.beans > AjaxWriteList.java

package com.lec.sts19_rest.board.beans;

import java.util.List;

import com.fasterxml.jackson.annotation.JsonProperty;

public class AjaxWriteList extends AjaxWriteResult {
	@JsonProperty("data")
	private List<BWriteDTO> list;  // 데이터 목록
	
	private int page;   // 현재 페이지 #
	
	@JsonProperty("totalpage")
	private int totalPage;   // 총 몇 '페이지' 분량?
	
	@JsonProperty("totalcnt")
	private int totalCnt;   // 데이터(글)은 총 몇개?
	
	@JsonProperty("writepages")
	private int writePages;   // 한[페이징]에 몇개의 '페이지'를 표현할 것인가
	
	@JsonProperty("pagerows")
	private int pageRows;    // 한 '페이지' 에 몇개의 글을 리스트 할것인가

	public List<BWriteDTO> getList() {
		return list;
	}

	public void setList(List<BWriteDTO> list) {
		this.list = list;
	}

	public int getPage() {
		return page;
	}

	public void setPage(int page) {
		this.page = page;
	}

	public int getTotalPage() {
		return totalPage;
	}

	public void setTotalPage(int totalPage) {
		this.totalPage = totalPage;
	}

	public int getTotalCnt() {
		return totalCnt;
	}

	public void setTotalCnt(int totalCnt) {
		this.totalCnt = totalCnt;
	}

	public int getWritePages() {
		return writePages;
	}

	public void setWritePages(int writePages) {
		this.writePages = writePages;
	}

	public int getPageRows() {
		return pageRows;
	}

	public void setPageRows(int pageRows) {
		this.pageRows = pageRows;
	}
	
}

 

** [src/main/java] com.lec.sts19_rest.board.beans > AjaxWriteResult.java

package com.lec.sts19_rest.board.beans;

public class AjaxWriteResult {
	private int count;   // 데이터 개수
	private String status;  // 처리 결과
	private String message;  // 결과 메세지
	
	/*
	{
		count : ○○○,
		status : ○○○,
		message : ○○○
	}
	 */
	
	public AjaxWriteResult() {}
	
	public int getCount() {
		return count;
	}
	public void setCount(int count) {
		this.count = count;
	}
	public String getStatus() {
		return status;
	}
	public void setStatus(String status) {
		this.status = status;
	}
	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}
	
}

 

2) 본격적으로 REST 게시판 만들기

① Ajax 용 DAO + Mapper 생성

** [src/main/java] com.lec.sts19_rest.board.beans > IAjaxDAO.java

package com.lec.sts19_rest.board.beans;

import java.util.List;

import org.apache.ibatis.annotations.Param;

public interface IAjaxDAO {
	
	// 페이징용 SELECT
	// from : 몇번재 row 부터
	// pageRows : 몇개의 데이터(게시글)
	public List<BWriteDTO> selectFromRow(
			@Param("from") int from,
			@Param("pageRows") int pageRows
			);
	
	// 전체 글의 개수
	public int countAll();
	
	// 글 읽기
	public BWriteDTO selectByUid(int uid);
	
	// 조회수 증가
	public int incViewCnt(int uid);
	
	// 글작성
	public int insert(
			String subject,
			String content,
			String name
			);
	
	// 글수정
	public int update(
			@Param("uid") int uid,
			@Param("subject") String subject,
			@Param("content") String context
			);
	
	// 특정 uid 글(들)을 삭제하기
	public int deleteByUid(int[] uids);
	
	/*
	 * DELETE FROM test_write
	 * WHERE uid in (10, 20, 30)
	 * 
	 * WHERE uid in (1, 2)
	 * 
	 * WHERE uid in (11)
	 * 
	 */

}

 

** [src/main/java] com.lec.sts19_rest.mapper > IAjaxDAO.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lec.sts19_rest.board.beans.IAjaxDAO">

	<select id="selectFromRow" 
		resultType="com.lec.sts19_rest.board.beans.BWriteDTO">
		
		<!-- 비교연산자 같은 경우에는 xml에서 사용할 수 없기 때문에 CDATA를 이용하여 사용한다 -->
		<![CDATA[
		SELECT 
			wr_uid "uid",
			wr_subject subject,
			wr_content content,
			wr_name name,
			wr_viewcnt viewcnt,
			wr_regdate regdate 
		FROM 
			(SELECT ROWNUM AS RNUM, T.* FROM 
				(SELECT * FROM test_write ORDER BY wr_uid DESC) T) 
		WHERE 
			RNUM >= #{from} AND RNUM < (#{from} + #{pageRows})
		]]>
	</select>
	
	<!-- 
		SELECT 쿼리문인데, 단일값 int 값 받고 싶다면
		resultType="java.lang.Integer" 혹은
		resultType="int" 
	-->
	<select id="countAll" resultType="int">
		SELECT count(*) FROM test_write
	</select>
	
	<!-- 글 읽기 -->
	<select id="selectByUid"
		resultType="com.lec.sts19_rest.board.beans.BWriteDTO">
		SELECT 
			wr_uid "uid",
			wr_subject subject,
			wr_content content,
			wr_name name,
			wr_viewcnt viewcnt,
			wr_regdate regdate 
		FROM 
			test_write
		WHERE
			wr_uid = #{uid}
	</select>
	
	<!-- 조회수 증가 -->
	<update id="incViewCnt">
		UPDATE test_write
		SET wr_viewcnt = wr_viewcnt + 1
		WHERE wr_uid = #{param1}
	</update>

	<!-- 글작성 -->
	<insert id="insert" flushCache="true">
		INSERT INTO test_write
			(wr_uid, wr_subject, wr_content, wr_name)
		VALUES
			(test_write_seq.nextval,
			#{param1}, #{param2}, #{param3})
	</insert>
	
	<!-- 글수정 -->
	<update id="updqte" flushCache="true">
		UPDATE test_write
		set
			wr_subject = #{subject},
			wr_context = #{context}
		WHERE
			wr_uid = #{uid}
	</update>
	
	<!-- 글삭제 -->
	<!-- 
		배열을 파라미터로 받는 경우
		collection에 꼭 array로!
		
		item : foreach 문 안에서 사용할 매개변수 이름
		separator : foreach 문이 한 번 돌고 다음 번 돌때의 구분자
		open : 시작 문자열
		close : 닫는 (종료) 문자열 
	-->
	<delete id="deleteByUid" flushCache="true"
		parameterType="java.util.ArrayList">
		DELETE FROM test_write
	 	WHERE wr_uid in (
	 		<foreach collection="array" item="item" index="index" separator=",">
	 			${item}
	 		</foreach>
	 	)
	</delete>
	
</mapper>


② Command 객체 수정

** [src/main/src] com.lec.sts19_rest.board.ajaxcommand > ListCommand.java

package com.lec.sts19_rest.board.ajaxcommand;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.lec.sts19_rest.board.C;
import com.lec.sts19_rest.board.beans.BWriteDTO;
import com.lec.sts19_rest.board.beans.IAjaxDAO;

public class ListCommand implements Command {

	@Override
	public void execute(HttpServletRequest request, HttpServletResponse response) {
		
		IAjaxDAO dao = C.sqlSession.getMapper(IAjaxDAO.class);
		List<BWriteDTO> arr = null;
		
		// ajax response 에 필요한 값들
		StringBuffer message = new StringBuffer();
		String status = "FAIL";   // 기본 FAIL
		
		// 페이징 관련 세팅값들
		int page = 1;  // 현재 페이지 (디폴트는 1 page)
		int pageRows = 8;   // 한 '페이지' 에 몇개의 글을 리스트? (디폴트 8개)
		int writePages = 10;  // 한 [페이징] 에 몇개의 '페이지' 를 표시? (디폴트 10)
		int totalCnt = 0;    // 글은 총 몇개인지?
		int totalPage = 0;   // 총 몇 '페이지' 분량인지?
		
		String param;
		
		// page 값 : 현재 몇 페이지?
		param = request.getParameter("page");
		if(param != null && param.trim().length() != 0) {
			try {				
				page = Integer.parseInt(param);
			} catch(NumberFormatException e) {
				// 예외 처리 안함
			}
		}
		
		// pageRows 값 :  '한 페이지' 에 몇개의 글?
		param = request.getParameter("pageRows");
		if(param != null && param.trim().length() != 0) {
			try {				
				pageRows = Integer.parseInt(param);
			} catch(NumberFormatException e) {
				// 예외 처리 안함
			}
		}
		
		
		try {
			// 글 전체 개수 구하기
			totalCnt = dao.countAll();
			
			// 총 몇 페이지 분량인가?
			totalPage = (int)Math.ceil(totalCnt / (double)pageRows);
			
			// 몇번재 row 부터 ?
			int fromRow = (page - 1) * pageRows + 1;  // ORACLE 은 1부터 ROWNUM시작
			//int fromRow = (page - 1) * pageRows;  // MySQL 은 0부터 시작
			
			arr = dao.selectFromRow(fromRow, pageRows);
			
			if(arr == null) {
				message.append("[리스트할 데이터가 없습니다]");
			} else {
				status = "OK";
			}
			
		} catch(Exception e) {
			//e.printStackTrace();
			message.append("[트랜잭션 에러:" + e.getMessage()+ "]");
		} // end try
		
		request.setAttribute("status", status);
		request.setAttribute("message", message.toString());
		request.setAttribute("list", arr);
		
		request.setAttribute("page", page);
		request.setAttribute("totalPage", totalPage);
		request.setAttribute("writePages", writePages);
		request.setAttribute("pageRows", pageRows);
		request.setAttribute("totalCnt", totalCnt);
		
	} // end execute()
	
} // end Command


** [src/main/src] com.lec.sts19_rest.board.ajaxcommand > WriteCommand.java

package com.lec.sts19_rest.board.ajaxcommand;

// Ctrl + Shift + O : 필요한 inport만 남기기
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.lec.sts19_rest.board.C;
import com.lec.sts19_rest.board.beans.IAjaxDAO;

public class WriteCommand implements Command {

	@Override
	public void execute(HttpServletRequest request, HttpServletResponse response) {
		
		int cnt = 0;
		IAjaxDAO dao = C.sqlSession.getMapper(IAjaxDAO.class);
		
		// ajax response 에 필요한 값들
		StringBuffer message = new StringBuffer();
		String status = "FAIL";   // 기본 FAIL
		
		
		// 매개변수 받아오기
		String name = request.getParameter("name");
		String subject = request.getParameter("subject");
		String content = request.getParameter("content");
		
		// 유효성 체크
		if(name == null || name.trim().length() == 0) {
			message.append("[유효하지 않은 parameter : 작성자 필수]");
		} else if (subject == null || subject.trim().length() == 0) {
			message.append("[유효하지 않은 parameter : 글제목 필수]");
		} else {
			try {
				cnt = dao.insert(subject, content, name);
				if(cnt == 0) {
					message.append("[트랙잰셕 실패: 0 insert");
				} else {
					status = "OK";
				}
				
			} catch(Exception e) {
				//e.printStackTrace();
				message.append("[트랜잭션 에러:" + e.getMessage() + "]");
			}
			
		} // end if
					
			
		request.setAttribute("result", cnt);
		request.setAttribute("status", status);
		request.setAttribute("message", message.toString());
		
		
	} // end execute()

} // end Command


** [src/main/src] com.lec.sts19_rest.board.ajaxcommand > ViewCommand.java

package com.lec.sts19_rest.board.ajaxcommand;

import java.util.Arrays;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.lec.sts19_rest.board.C;
import com.lec.sts19_rest.board.beans.BWriteDTO;
import com.lec.sts19_rest.board.beans.IAjaxDAO;

public class ViewCommand implements Command {

	@Override
	public void execute(HttpServletRequest request, HttpServletResponse response) {
		IAjaxDAO dao = C.sqlSession.getMapper(IAjaxDAO.class);
		BWriteDTO dto = null;
		
		// ajax response 에 필요한 값들
		StringBuffer message = new StringBuffer();
		String status = "FAIL";   // 기본 FAIL
		
		String param = request.getParameter("uid");
		
		// 유효성 검사
		if(param == null) {
			message.append("[유효하지 않은 parameter 0 or null]");
			
		} else {	
			
			try {
				int uid = Integer.parseInt(param);
				
				dao.incViewCnt(uid);  // 조회수 증가
				dto = dao.selectByUid(uid);  // 읽기
				
				if(dto == null) {
					message.append("[해당 데이터가 없습니다]");
				} else {
					status = "OK";
				}
				
			} catch (NumberFormatException e) {
				message.append("[유효하지 않은 parameter:" + e.getMessage() + "]");
			} catch (Exception e) {  
				//e.printStackTrace();
				message.append("[트랜잭션 에러:" + e.getMessage() + "]");
			} // end try
		} // end if

		request.setAttribute("list", Arrays.asList(dto));
		request.setAttribute("status", status);
		request.setAttribute("message", message.toString());

	}

}


** [src/main/src] com.lec.sts19_rest.board.ajaxCommand > UpdateCommand.java

package com.lec.sts19_rest.board.ajaxcommand;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.lec.sts19_rest.board.C;
import com.lec.sts19_rest.board.beans.IAjaxDAO;

public class UpdateCommand implements Command {

	@Override
	public void execute(HttpServletRequest request, HttpServletResponse response) {
		int cnt = 0;

		IAjaxDAO dao = C.sqlSession.getMapper(IAjaxDAO.class);
		
		// ajax response 에 필요한 값들
		StringBuffer message = new StringBuffer();
		String status = "FAIL";   // 기본 FAIL

		//입력한 값을 받아오기
		String param = request.getParameter("uid");
		String subject = request.getParameter("subject");
		String content = request.getParameter("content");

		// 유효성 체크
		if(param == null) {
			message.append("[유효하지 않은 parameter 0 or null]");
			
		} else if (subject == null || subject.trim().length() == 0) {
			message.append("[유효하지 않은 parameter : 글제목 필수]");
			
		} else {
			
			try {		
				int uid = Integer.parseInt(param);
				
				cnt = dao.update(uid, subject, content);
				status = "OK";
				
				if(cnt == 0) {
					message.append("[0 update]");
				}				
				
			} catch (NumberFormatException e) {
				message.append("[유효하지 않은 parameter] " + param);
			} catch (Exception e) {
				//e.printStackTrace();
				message.append("[트랜잭션 에러:" + e.getMessage() + "]");
			}

		} // end if

		request.setAttribute("result", cnt);
		request.setAttribute("status", status);
		request.setAttribute("message", message.toString());

	} // end execute()

} // end Command


** [src/main/src] com.lec.sts19_rest.board.ajaxCommand > DeleteCommand.java

package com.lec.sts19_rest.board.ajaxcommand;

import java.util.Arrays;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.lec.sts19_rest.board.C;
import com.lec.sts19_rest.board.beans.IAjaxDAO;

public class DeleteCommand implements Command {

	@Override
	public void execute(HttpServletRequest request, HttpServletResponse response) {
		int cnt = 0;

		IAjaxDAO dao = C.sqlSession.getMapper(IAjaxDAO.class);
		
		// ajax response 에 필요한 값들
		StringBuffer message = new StringBuffer();
		String status = "FAIL";   // 기본 FAIL

		// 유효성 검증
		String[] params = request.getParameterValues("uid");
		int[] uids = null;
		
		if(params == null || params.length == 0) {
			message.append("[유효하지 않은 parameter 0 or null]");
			
		} else {
			uids = new int[params.length];
			
			try {	
				for(int i = 0; i < params.length; i++) {
					uids[i] = Integer.parseInt(params[i]);
				}
				cnt = dao.deleteByUid(uids);
				status = "OK";
			} catch (NumberFormatException e) {
				message.append("[유효하지 않은 parameter]" + Arrays.toString(params));
			} catch (Exception e) {
				//e.printStackTrace();
				message.append("[트랜잭션 에러 : " + e.getMessage() + "]");
			}
		} // end if
		

		request.setAttribute("result", cnt);
		request.setAttribute("status", status);
		request.setAttribute("message", message.toString());
	
	} // end execute()

} // end Command

 

③ AjaxController 생성

** [src/main/java] com.lec.sts19_rest.board.controller > AjaxController.java

package com.lec.sts19_rest.board.controller;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.lec.sts19_rest.board.ajaxcommand.*;
import com.lec.sts19_rest.board.beans.AjaxWriteList;
import com.lec.sts19_rest.board.beans.AjaxWriteResult;
import com.lec.sts19_rest.board.beans.BWriteDTO;

@RestController
@RequestMapping("/board/*.ajax")
public class AjaxController {
	
	@RequestMapping("/board/list.ajax")
	public AjaxWriteList list(HttpServletRequest request, HttpServletResponse response) {
		new ListCommand().execute(request, response);
		return buildList(request);
	} // end list()
	
	@RequestMapping("/board/view.ajax")
	public AjaxWriteList view(HttpServletRequest request, HttpServletResponse response) {
		new ViewCommand().execute(request, response);
		return buildList(request);
	} // end view()
	
	@RequestMapping(value = "/board/writeOk.ajax", method = RequestMethod.POST)
	public AjaxWriteResult writeOk(HttpServletRequest request, HttpServletResponse response) {
		new WriteCommand().execute(request, response);
		return buildResult(request);
	} // end writeOk()
	
	@RequestMapping("/board/updateOk.ajax")
	public AjaxWriteResult updateOk(HttpServletRequest request, HttpServletResponse response) {
		new UpdateCommand().execute(request, response);
		return buildResult(request);
	} // end updateOk()
	
	@RequestMapping(value = "/board/deleteOk.ajax", method = RequestMethod.POST)
	public AjaxWriteResult deleteOk(HttpServletRequest request, HttpServletResponse response) {
		new DeleteCommand().execute(request, response);
		return buildResult(request);
	} // end deleteOk()
	
	public AjaxWriteResult buildResult(HttpServletRequest request) {
		AjaxWriteResult result = new AjaxWriteResult();
		
		result.setStatus((String)request.getAttribute("status"));
		result.setMessage((String)request.getAttribute("message"));
		result.setCount((Integer)request.getAttribute("result"));
		
		return result;
	} // end buildResult()
	
	public AjaxWriteList buildList(HttpServletRequest request) {
		List<BWriteDTO> list = (List<BWriteDTO>)request.getAttribute("list");
		
		AjaxWriteList result = new AjaxWriteList();
		result.setStatus((String)request.getAttribute("status"));
		result.setMessage((String)request.getAttribute("message"));
		
		if(list != null) {			
			result.setCount(list.size());
			result.setList(list);
		}

		// 페이징 할때만 필요한 것들.
		try {
			result.setPage((Integer)request.getAttribute("page"));
			result.setTotalPage((Integer)request.getAttribute("totalPage"));
			result.setWritePages((Integer)request.getAttribute("writePages"));
			result.setPageRows((Integer)request.getAttribute("pageRows"));
			result.setTotalCnt((Integer)request.getAttribute("totalCnt"));
		} catch(Exception e) {
			//e.printStackTrace();
		}
		
		return result;
	} // end buildList()

}


④ DTO 수정

** [src/main/java] com.lec.sts19_rest.board.beans > BWriteDTO.java

package com.lec.sts19_rest.board.beans;

import java.sql.Timestamp;
import java.text.SimpleDateFormat;

import com.fasterxml.jackson.annotation.JsonProperty;

public class BWriteDTO {
	private int uid;     	// wr_uid
	private String subject;	//wr_subject
	private String content;	//wr_content
	private String name;	// wr_name
	@JsonProperty("viewcnt")
	private int viewCnt;	// wr_viewcnt
	@JsonProperty("regdate")
	private Timestamp regDate;	// wr_regdate
	
	public BWriteDTO() {
		super();
	}
	public BWriteDTO(int uid, String subject, String content, String name, int viewCnt, Timestamp regDate) {
		super();
		this.uid = uid;
		this.subject = subject;
		this.content = content;
		this.name = name;
		this.viewCnt = viewCnt;
		this.regDate = regDate;
	}
	
	public int getUid() {
		return uid;
	}
	public void setUid(int uid) {
		this.uid = uid;
	}
	public String getSubject() {
		return subject;
	}
	public void setSubject(String subject) {
		this.subject = subject;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getViewCnt() {
		return viewCnt;
	}
	public void setViewCnt(int viewCnt) {
		this.viewCnt = viewCnt;
	}
	
//	public Timestamp getRegDate() {
//		return regDate;
//	}
	public String getRegDate() {
		return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(regDate);
	}
	
	public void setRegDate(Timestamp regDate) {
		this.regDate = regDate;
	}

}

 

'웹_프론트_백엔드 > JAVA프레임윅기반_풀스택' 카테고리의 다른 글

2020.07.07  (0) 2020.07.07
2020.07.06  (0) 2020.07.06
2020.07.03  (0) 2020.07.03
2020.07.02  (0) 2020.07.02
2020.07.01  (0) 2020.07.01