Study Web Development

2월 4일

이전으로

Servlet & JSP

9. MVC

mvcPage

프로젝트 생성 및 설정
  1. WEB-INF 폴더의 board.properties 하단에 다음의 내용을 추가
/board/detail.do=kr.board.action.DetailAction
/board/updateForm.do=kr.board.action.UpdateFormAction
/board/update.do=kr.board.action.UpdateAction
/board/deleteFile.do=kr.board.action.DeleteFileAction
  1. css 폴더의 layout.css 하단에 다음의 내용을 추가
/* 글 상세 */
.detail-img {
	max-width: 500px;
}
Model
  1. 새 클래스 DetailAction 생성
package kr.board.action;

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

import kr.board.dao.BoardDAO;
import kr.board.vo.BoardVO;
import kr.controller.Action;
import kr.util.StringUtil;

public class DetailAction implements Action {

	@Override
	public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 글 번호 반환
		int board_num = Integer.parseInt(request.getParameter("board_num"));
		
		BoardDAO dao = BoardDAO.getInstance();
		
		// 조회수 증가
		dao.updateReadcount(board_num);
		
		BoardVO board = dao.getBoard(board_num);
		
		// HTML 태그를 허용하지 않음
		board.setTitle(StringUtil.useNoHtml(board.getTitle()));
		// HTML 태그를 허용하지 않으면서 줄바꿈 처리
		board.setContent(StringUtil.useBrNoHtml(board.getContent()));
		
		request.setAttribute("board", board);
		
		// JSP 경로 반환
		return "/WEB-INF/views/board/detail.jsp";
	}

}
  1. 새 클래스 UpdateFormAction 생성
package kr.board.action;

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

import kr.board.dao.BoardDAO;
import kr.board.vo.BoardVO;
import kr.controller.Action;

public class UpdateFormAction implements Action {

	@Override
	public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpSession session = request.getSession();
		Integer user_num = (Integer)session.getAttribute("user_num");
		if(user_num==null) { // 로그인되어 있지 않은 경우
			return "redirect:/member/loginForm.do";
		}
		
		int board_num = Integer.parseInt(request.getParameter("board_num"));
		BoardDAO dao = BoardDAO.getInstance();
		BoardVO db_board = dao.getBoard(board_num);
		if(user_num!=db_board.getMem_num()) { // 로그인한 회원 번호와 작성자 회원 번호가 일치하지 않는 경우
			return "/WEB-INF/views/common/notice.jsp";
		}
		
		// 로그인한 회원 번호와 작성자 회원 번호가 일치하는 경우
		request.setAttribute("board", db_board);
		
		// JSP 경로 반환
		return "/WEB-INF/views/board/updateForm.jsp";
	}

}
  1. 새 클래스 UpdateAction 생성
package kr.board.action;

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

import com.oreilly.servlet.MultipartRequest;

import kr.board.dao.BoardDAO;
import kr.board.vo.BoardVO;
import kr.controller.Action;
import kr.util.FileUtil;

public class UpdateAction implements Action {

	@Override
	public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpSession session = request.getSession();
		Integer user_num = (Integer)session.getAttribute("user_num");
		if(user_num==null) { // 로그인되어 있지 않은 경우
			return "redirect:/member/loginForm.do";
		}
		
		MultipartRequest multi = FileUtil.createFile(request);
		int board_num = Integer.parseInt(multi.getParameter("board_num"));
		String filename = multi.getFilesystemName("filename");
		
		BoardDAO dao = BoardDAO.getInstance();
		// 수정 전 데이터
		BoardVO db_board = dao.getBoard(board_num);
		if(user_num!=db_board.getMem_num()) { // 로그인한 회원 번호와 작성자 회원 번호가 일치하지 않는 경우
			FileUtil.removeFile(request, filename); // 업로드된 파일이 있으면 파일 삭제
			return "/WEB-INF/views/common/notice.jsp";
		}
		
		// 로그인한 회원 번호와 작성자 회원 번호가 일치하는 경우
		BoardVO board = new BoardVO();
		board.setBoard_num(board_num);
		board.setTitle(multi.getParameter("title"));
		board.setContent(multi.getParameter("content"));
		board.setFilename(filename);
		board.setIp(request.getRemoteAddr());
		
		// 글 수정
		dao.updateBoard(board);
		
		// 전송된 파일이 있을 경우 이전 파일 삭제
		if(filename!=null) {
			FileUtil.removeFile(request, db_board.getFilename());
		}
		
		// JSP 경로 반환
		return "redirect:/board/detail.do?board_num=" + board_num;
	}

}
  1. 새 클래스 DeleteFileAction 생성
package kr.board.action;

import java.util.HashMap;
import java.util.Map;

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

import org.codehaus.jackson.map.ObjectMapper;

import kr.board.dao.BoardDAO;
import kr.board.vo.BoardVO;
import kr.controller.Action;
import kr.util.FileUtil;

public class DeleteFileAction implements Action {

	@Override
	public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
		Map<String, String> mapAjax = new HashMap<String, String>();
		
		HttpSession session = request.getSession();
		Integer user_num = (Integer)session.getAttribute("user_num");
		if(user_num==null) { // 로그인되어 있지 않은 경우
			mapAjax.put("result", "logout");
		}
		else { // 로그인되어 있는 경우
			int board_num = Integer.parseInt(request.getParameter("board_num"));
			
			BoardDAO dao = BoardDAO.getInstance();
			BoardVO db_board = dao.getBoard(board_num);
			if(user_num!=db_board.getMem_num()) { // 로그인한 회원 번호와 작성자 회원 번호가 일치하지 않는 경우
				mapAjax.put("result", "wrongAccess");
			}
			else { // 로그인한 회원 번호와 작성자 회원 번호가 일치하는 경우
				dao.deleteFile(board_num);
				// 파일 삭제
				FileUtil.removeFile(request, db_board.getFilename());
				
				mapAjax.put("result", "success");
			}
		}
		
		// JSON 데이터 생성
		ObjectMapper mapper = new ObjectMapper();
		String ajaxData = mapper.writeValueAsString(mapAjax);
		
		request.setAttribute("ajaxData", ajaxData);
		
		// JSP 경로 반환
		return "/WEB-INF/views/common/ajax_view.jsp";
	}

}
View
  1. board 폴더에 detail.jsp 생성
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>글 상세</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/layout.css">
<style type="text/css">
	hr {border-style: solid; color: gray;}
</style>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.6.0.min.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/board-reply.js"></script>
</head>
<body>
<div class="page-main">
	<jsp:include page="/WEB-INF/views/common/header.jsp"/>
	<h2>게시판 글 상세</h2>
	<ul>
		<li>
			<label>글 번호</label>
			${board.board_num}
		</li>
		<li>
			<label>글 제목</label>
			${board.title}
		</li>
		<li>
			<label>작성자</label>
			${board.id}
		</li>
		<li>
			<label>조회수</label>
			${board.hit}
		</li>
	</ul>
	<hr>
	<c:if test="${!empty board.filename}">
	<div class="align-center">
		<img src="${pageContext.request.contextPath}/upload/${board.filename}" class="detail-img">
<script type="text/javascript">
	let img = document.getElementsByClassName('detail-img')[0];
	img.src = encodeURI(decodeURI(img.src)); // 파일명에 괄호, 공백 등 특수문자가 사용된 경우 불러오지 못함; encodeURI만 사용하면 %20이 %2520으로 이중 인코딩되므로 이를 해결하기 위해 decodeURI를 먼저 사용; decodeURI만 사용하면 한글 파일명을 불러오지 못함
</script>
	</div>
	</c:if>
	<p>
		${board.content}
	</p>
	<hr>
	<div class="align-right">
		<c:if test="${!empty board.modify_date}">
		최근 수정일 : ${board.modify_date}
		</c:if>
		작성일 : ${board.reg_date}
		<%-- 로그인한 회원 번호와 작성자 회원 번호가 일치해야 수정, 삭제 가능 --%>
		<c:if test="${user_num==board.mem_num}">
		<input type="button" value="수정" onclick="location.href = 'updateForm.do?board_num=${board.board_num}';">
		<input type="button" value="삭제" id="delete_btn">
<script type="text/javascript">
	let delete_btn = document.getElementById('delete_btn');
	delete_btn.onclick = function() {
		let choice = confirm('삭제하시겠습니까?');
		if(choice) {
			location.replace('delete.do?board_num=${board.board_num}'); // 삭제 후 뒤로가기 방지
		}
	};
</script>
		</c:if>
		<input type="button" value="목록" onclick="location.href = 'list.do';">
	</div>
	<!-- 댓글 시작 -->
	<div id="reply_div">
		<span class="re-title">댓글 달기</span>
		<form id="re_form">
			<input type="hidden" name="board_num" value="${board.board_num}" id="board_num">
			<textarea rows="3" cols="50" name="re_content" id="re_content" class="rep-content" <c:if test="${empty user_num}">disabled</c:if>><c:if test="${empty user_num}">로그인해야 작성할 수 있습니다.</c:if></textarea>
			<c:if test="${!empty user_num}">
			<div id="re_first">
				<span class="letter-count">300/300</span>
			</div>
			<div id="re_second" class="align-right">
				<input type="submit" value="전송">
			</div>
			</c:if>
		</form>
	</div>
	<!-- 댓글 목록 출력 시작 -->
	<div id="output"></div>
	<div class="paging-button" style="display: none;">
		<input type="button" value="다음 댓글 보기">
	</div>
	<div class="loading" style="display: none;">
		<img src="${pageContext.request.contextPath}/images/ajax-loader.gif">
	</div>
	<!-- 댓글 목록 출력 끝 -->
	<!-- 댓글 끝 -->
</div>
</body>
</html>
  1. board 폴더에 updateForm.jsp 생성
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>글 수정</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/layout.css">
<style type="text/css">
	hr {border-style: solid; color: gray;}
</style>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
	$(function() {
		$('#update_form').submit(function() {
			if($('#title').val().trim()=='') {
				alert('제목을 입력하세요!');
				$('#title').val('').focus();
				return false;
			}
			if($('#content').val().trim()=='') {
				alert('내용을 입력하세요!');
				$('#content').val('').focus();
				return false;
			}			
		}); // end of submit
		
		$('#filename').change(function() {
			let file = this.files[0];
			if(file.size>5*1024*1024) {
				alert('5MB까지만 업로드 가능합니다!');
				$(this).val('');
			}
		}); // end of change		
	});
</script>
</head>
<body>
<div class="page-main">
	<jsp:include page="/WEB-INF/views/common/header.jsp"/>
	<h2>글 수정</h2>
	<form action="update.do" method="post" enctype="multipart/form-data" id="update_form">
		<input type="hidden" name="board_num" value="${board.board_num}">
		<ul>
			<li>
				<label for="title">제목</label>
				<input type="text" name="title" id="title" value="${board.title}" maxlength="50">
			</li>
			<li>
				<label for="content">내용</label>
				<textarea rows="5" cols="30" name="content" id="content">${board.content}</textarea>
			</li>
			<li>
				<label for="filename">파일</label>
				<input type="file" name="filename" id="filename" accept="image/jpeg, image/png, image/gif">
				<c:if test="${!empty board.filename}">
				<br>
				<span id="file_detail">
					(${board.filename}) 파일이 등록되어 있습니다.
					파일을 새로 업로드하면 기존 파일은 삭제됩니다.
					<input type="button" value="파일 삭제" id="file_del">
				</span>
<script type="text/javascript">
	$('#file_del').click(function() {
		let choice = confirm('삭제하시겠습니까?');
		if(choice) {
			$.ajax({
				url:'deleteFile.do',
				type:'post',
				data:{board_num:${board.board_num}}, // JavaScript를 외부 파일(.js)로 삽입하는 경우 EL 표기법 사용 불가하므로 주의
				dataType:'json',
				cache:false,
				timeout:30000,
				success:function(param) {
					if(param.result=='logout') {
						alert('로그인 후 사용하세요!');
					}
					else if(param.result=='success') {
						$('#file_detail').hide();
					}
					else if(param.result=='worngAccess') {
						alert('잘못된 접근입니다!');
					}
					else {
						alert('파일 삭제 오류 발생!');
					}
				},
				error:function() {
					alert('네트워크 오류 발생!');
				}
			}); // end of ajax
		}
	});
</script>
				</c:if>
			</li>
		</ul>
		<div class="align-center">
			<input type="submit" value="수정">
			<input type="button" value="목록" onclick="location.href = 'list.do';">
		</div>
	</form>
</div>
</body>
</html>

다음으로