본문 바로가기

개발 공부/Spring

코드로 배우는 스프링 웹 프로젝트-11.화면처리

코드로 배우는 스프링 웹 프로젝트-11.화면처리

코드로 배우는 스프링 웹 프로젝트 - 개정판

2019년 7월 10일 인쇄판

 

 

Part3. 기본적인 웹 게시물 관리

Chapter11. 화면처리

 

각 영역에 대한 모든 처리와 테스트가 완료되었다. 만일 에러가 발생한다면 모든 문제는 화면 쪽에서 발생한다고 할 수 있다. 화면을 개발하기 전엔 반드시 전체 레이아웃과 디자인이 반영된 상태에서 개발하는 것을 추천한다.

 

디자이너가 같이 참여하지 못한다면 BootStrap을 이용해 무료 디자인을 찾아보는 것도 좋다.

 

https://startbootstrap.com/template-categories/all/

 

Redirecting…

 

startbootstrap.com

 

예제에서 사용할 디자인은 SB Admin2이다.

 

 

startbootstrap-sb-admin-2-gh-pages.zip
1.48MB

11. 1 목록 페이지 작업과 includes

 

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

스프링 MVC의 JSP 설정은 위와 같다.

화면 설정은 ViewResolver 객체를 통해 이루어지며 /WEB-INF/views 폴더를 이용한다. 브라우저에서 직접 접근할 수 없는 경로이므로 반드시 Controller을 이용하는 모델2방식에서 기본적으로 사용하는 방식이다.

 

게시물 리스트의 URL은 /board/list이므로 최종적인 WEB-iNF/views/board/list.jsp가 된다. 

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1> List Page </h1>
</body>
</html>

우선 정상적으로 URL 처리가 되는지를 확인해야 하므로 Tomcat 실행으로 확인해야 한다.

Run on server

 

Tomcat 설정에서 Web Settings를 이용해 경로를 /로 조정한다.

 

 

11.1.1 SB Admin2 페이지 적용하기.

 

SB Admin2의 pages 폴더의 tables.html 내용을 그대로 복사해 수정하고 실행한다.

이 때 맨 상단의 <%@ page ~~ 부분은 지우지 않고 그 밑으로 붙여넣기 한다.

 

 

CSS 등이 완전히 깨진 상태로 출력된다.

 

SB Admin2의 CSS 경로는 /vendor/bootstrap/css/bootstrap.min.css 경로이므로 제대로 서비스될 수 없다.

org.zerock.config.WebConfig 클래스에는 CSS나 JS 파일과 같이 정적인 자원들의 경로를 resource로 지정하고 있다.

 

아래는 servlet-context.xml 일부

 

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

 

SB Admin2의 압축을 풀어둔 모든 폴더를 프로젝트 내 webapp 밑의 resources 폴더로 복사해 넣는다.

 

 

list.jsp에서 ../를 /resources/로 치환한다.

 

 

11.1.2 includes 적용

 

JSP를 작성할 때마다 HTML 코드 양이 많아지는 걸 피하기 위해 JSP의 include 지시자로 페이지 제작 시에 필요한 내용만을 작성할 수 있게 사전에 작업을 해야 한다.

 

header.jsp

 

list.jsp의 약 380라인 근처까지의 내용을 복사해 header.jsp에 복붙

 

footer.jsp

 

약 737라인 이하의 모든 내용을 footer.jsp에 복붙

 

 

list.jsp의 상단과 하단에 다음 내용 추가

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@include file="../includes/header.jsp" %>
            <div class="row">
                <div class="col-lg-12">
                    <h1 class="page-header">Tables</h1>
                </div>
                <!-- /.col-lg-12 -->
            </div>
                   <!-- /.panel-body -->
                    </div>
                    <!-- /.panel -->
                </div>
                <!-- /.col-lg-6 -->
            </div>
            <!-- /.row -->
            
<%@include file="../includes/footer.jsp" %>
 

 

 

11.1.3 jQuery 라이브러리 변경

 

Javascript로 브라우저 내에서 조작이 필요할 경우가 많다.

jQuery를 header.jsp에 선언해두면 JSP에서 자유롭게 사용할 수 있다.

 

footer.jsp 상단에 있는 jquery.min.js 파일의 <script> 태그를 제거한다.

 

https://developers.google.com/speed/libraries

 

Hosted Libraries  |  Google Developers

A stable, reliable, high-speed, globally available content distribution network for the most popular open-source JavaScript libraries.

developers.google.com

 

위 페이지에서 jquery.min.js 복사, header.jsp에 끼워넣는다.

 

 

반응형 웹 처리

 

SB Admin2는 반응형으로 설계되어 있어 브라우저 크기에 맞게 자동 변경되지만 jQuery 최신 버전에선 모바일 크기에서 새로고침 시 모든 메뉴가 펼쳐지는 문제가 발생한다.

 

jQuery 3.5.1에선 그렇지 않지만 혹시라도 그럴 경우 footer.jsp에 아래 내용 추가.

 

 

<script>
	$(document).ready(function() {
		$('#dataTables-example').DataTable( {
			responsive: true
		});
	$(".sidebar-nav")
	.attr("class", "sidebar-nav navbar-collapse collapse")
	.attr("aria-expanded", 'false')
	.attr("style", "height:1px");
	});
</script>

 

 

11.2 목록 화면 처리

 

list.jsp에는 JSTL의 출력과 포맷을 적용할 수 있ㅇ는 태그 라이브러리를 추가한다.

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>

 

11.2.1 Model에 담긴 데이터 출력

 

/board/list를 실행했을 때 이미 BoardController는 Model을 이용해 게시물의 목록을 list라는 이름에 담아 전달했으므로 list.jsp에서 이를 출력한다. 출력은 JSTL을 이용해 처리한다.

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@include file="../includes/header.jsp" %>
            <div class="row">
                <div class="col-lg-12">
                    <h1 class="page-header">Tables</h1>
                </div>
                <!-- /.col-lg-12 -->
            </div>
            <!-- /.row -->
            <div class="row">
                <div class="col-lg-12">
                    <div class="panel panel-default">
                        <div class="panel-heading">
                           Board List Page
                        </div>
                        <!-- /.panel-heading -->
                        <div class="panel-body">
                            <table class="table table-striped table-bordered table-hover">
                                <thead>
                                    <tr>
                                        <th>#번호</th>
                                        <th>제목</th>
                                        <th>작성자</th>
                                        <th>작성일</th>
                                        <th>수정일</th>
                                    </tr>
                                </thead>
                                
                                <tbody>
                                	<c:forEach items="${list}" var="board">
                                	    <tr class="odd gradeX">
                                 	       <td><c:out value="${board.bno }"/></td>
                                 	       <td><c:out value="${board.title }"/></td>
                                 	       <td><c:out value="${board.writer }"/></td>
                                 	       <td><fmt:formatDate pattern="yyyy-MM-dd" value="${board.regdate }"/></td>
                                 	       <td><fmt:formatDate pattern="yyyy-MM-dd" value="${board.updateDate }"/></td>
                                 	   </tr>
                                   </c:forEach>
                                </tbody>
                            </table>
                            
                        </div>
                        <!-- /.panel-body -->
                    </div>
                    <!-- /.panel -->
                </div>
                <!-- /.col-lg-12 -->
            </div>
           
            
<%@include file="../includes/footer.jsp" %>
 

 

 

11.3 등록 입력 페이지와 등록 처리

 

게시물의 등록 작업은 POST로 처리하지만 화면에서 입력을 받아야 하므로 GET 방식으로 입력 페이지를 볼 수 있도록 BoardController에 메서드 추가

 

	@GetMapping("/register")
	public void register() {
		
	}

 

register는 별도의 처리가 필요하지 않는다.

 

views 폴더에 includes를 적용한 입력 페이지를 작성.

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@include file="../includes/header.jsp" %>
            <div class="row">
                <div class="col-lg-12">
                    <h1 class="page-header">Board Register</h1>
                </div>
                <!-- /.col-lg-12 -->
            </div>
            <!-- /.row -->
            
            
            <div class="row">
                <div class="col-lg-12">
                    <div class="panel panel-default">
                        <div class="panel-heading">
                           Board Register
                        </div>
                        <!-- /.panel-heading -->
                        <div class="panel-body">
                        	<form role="form" action="/board/register" method="post">
                        		<div class="form-group">
                        			<label>Title</label>
                        			<input class="form-control" name='title'>
                        		</div>
                        		<div class="form-group">
                        			<label>Text area</label>
                        			<textarea class="form-control" rows="3" name='content'></textarea>
                        		</div>
                        		<div class="form-group">
                        			<label>Writer</label>
                        			<input class="form-control" name='writer'>
                        		</div>
                        		<button type="submit" class="btn btn-default">Submit Button</button>
                        		<button type="reset" class="btn btn-default">Reset Button</button>
                        	</form>
                            
                        </div>
                        <!-- /.panel-body -->
                    </div>
                    <!-- /.panel -->
                </div>
                <!-- /.col-lg-12 -->
            </div>
           
            
<%@include file="../includes/footer.jsp" %>
 

 

등록은 되지만 한글이 깨진다.

 

(BoardController의 POST 방식으로 동작하는 register()는 redirect 시키는 방식을 이용하므로 등록 후에 /board/list로 이동한다)

 

 

11.3.1 한글 문제와 UTF-8 필터 처리

 

한글이 깨지는 경우 1) 브라우저에서 한글이 깨져서 전송되는지 확인하고 2) 문제가 없다면 스프링 MVC 쪽에 한글 처리 필터를 등록해야 한다.

 

개발자 도구 > Network에서 전송 상태를 확인할 수 있다.

 

 

	<filter>
		<filter-name>encoding</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
	</filter>
	
	<filter-mapping>
		<filter-name>encoding</filter-name>
		<servlet-name>appServlet</servlet-name>
	</filter-mapping>Bo

web.xml에 위 내용 추가

 

 

JAVA 설정의 경우

 

WebConfig 클래스에서 getServletFilters() 재정의로 처리 가능.

 

package org.zerock.config;



import javax.servlet.Filter;
import javax.servlet.ServletRegistration;

import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebConfig extends AbstractAnnotationConfigDispatcherServletInitializer{

	@Override
	protected Class<?>[] getRootConfigClasses() {
		// TODO Auto-generated method stub
		return new Class[] {RootConfig.class};
	}

	@Override
	protected Class<?>[] getServletConfigClasses() {
		// TODO Auto-generated method stub
		return new Class[] {ServletConfig.class};
	}

	@Override
	protected String[] getServletMappings() {
		// TODO Auto-generated method stub
		return new String[] {"/"};
	}
	
	@Override
	protected void customizeRegistration(ServletRegistration.Dynamic registration) {
		registration.setInitParameter("throwExceptionIfNoHandlerFound",  "true");
	}
	
	@Override
	protected Filter[] getServletFilters() {
		CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
		characterEncodingFilter.setEncoding("UTF-8");
		characterEncodingFilter.setForceEncoding(true);
		return new Filter[] { characterEncodingFilter };
	}
}

* 아마도 서버를 재시작해야 깨짐 현상이 사라진다.

 

 

 

11.3.2 재전송(redirect) 처리

 

등록 과정에서 POST 방식으로 데이터가 처리되는 과정을 표현한 그림.

 

BoardController에서 register 메서드는 redirect:/board/list를 전송하는데 브라우저는 이를 통보받은 후 /board/list로 이동하게 된다. 만일 위와 같이 재전송을 하지 않으면 도배 문제가 발생할 수 있다.

따라서 등록, 수정, 삭제 작업은 처리가 완료된 후 다시 동일 내용 전송이 없도록 아예 브라우저의 URL을 이동하는 방식을 이용한다.

이러한 과정에서 브라우저가 등록, 수정, 삭제 결과를 바로 알 수 있게 피드백을 해야 한다.

경고창이나 div를 이용하는 모달 창을 이용해 처리한다.

 

BoardController에서 redirect 처리를 할 떄 RedirectAttributes라는 객체를 이용했다. addFlashAttribute()의 경우 이러한 처리에 적합한데 일회성으로만 데이터를 전달하기 때문. addFlashAttribute로 보관된 데이터는 단 한번만 사용할 수 있게 보관된다(내부적으로 HttpSession을 이용해 처리)

 

           <script type="text/javascript">
           	$(document).ready(function() {
           		var result = '<c:out value="${result}"/>';
           	});
           </script>

list.jsp에 script 태그를 사용하면 상황에 따른 메시지를 확인할 수 있다.

 

새로운 게시물 번호는 addFlashAttribute로 저장되므로 한번도 사용된 적이 없다면 위와 같은 값을 만들어내지만

사용자가 직접 board/list를 호출하거나 새로고침을 하면 아무런 내용이 담기지 않는다.

 

 

11.3.3 모달(Modal) 창 보여주기

 

div를 화면에 특정 위치에 보여주고 배경이 되는 div에 배경색을 입혀 처리한다.

모달창은 활성화된 div를 선택하지 않고선 다시 원래 화면을 볼 수 없도록 막기 때문에 메시지를 보여주는데 효과적이다.

 

다운받은 SB Admin2 pages 폴더 내 notifications.html을 참고하자.

                                <tbody>
                                	<c:forEach items="${list}" var="board">
                                	    <tr class="odd gradeX">
                                 	       <td><c:out value="${board.bno }"/></td>
                                 	       <td><c:out value="${board.title }"/></td>
                                 	       <td><c:out value="${board.writer }"/></td>
                                 	       <td><fmt:formatDate pattern="yyyy-MM-dd" value="${board.regdate }"/></td>
                                 	       <td><fmt:formatDate pattern="yyyy-MM-dd" value="${board.updateDate }"/></td>
                                 	   </tr>
                                   </c:forEach>
                                </tbody>
                            </table>
                            
                            <!-- Modal -->
                            <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
                                <div class="modal-dialog">
                                    <div class="modal-content">
                                        <div class="modal-header">
                                            <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                                            <h4 class="modal-title" id="myModalLabel">Modal title</h4>
                                        </div>
                                        <div class="modal-body">
                                        	처리가 완료되었습니다.
                                        </div>
                                        <div class="modal-footer">
                                            <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                                            <button type="button" class="btn btn-primary">Save changes</button>
                                        </div>
                                    </div>
                                    <!-- /.modal-content -->
                                </div>
                                <!-- /.modal-dialog -->
                            </div>
                            <!-- /.modal -->
                            
                        </div>
                        <!-- /.panel-body -->
                    </div>
                    <!-- /.panel -->
                </div>
                <!-- /.col-lg-12 -->
            </div>
           <script type="text/javascript">
           	$(document).ready(function() {
           		var result = '<c:out value="${result}"/>';
           	});
           </script>
            
<%@include file="../includes/footer.jsp" %>
            <script type="text/javascript">
            	$(document).ready(function() {
            		var result='<c:out value="${result}"/>';
            		checkModal(result);
            		
            		function checkModal(result) {
            			if(result==='') {
            				return;
            			}
            			
            			if(parseInt(result) > 0) {
            				$(".modal-body").html("게시글 " + parseInt(result) + "번이 등록되었습니다.");
            			}
            			
            			$("#myModal").modal("show");
            		}
            	});
            </script>

checkModal 함수는 파라미터에 따라 모달창을 보여주거나 내용을 수정한 뒤 보이도록 작성한다.

checkModal에선 새로운 게시글이 작성되는 경우 RedirectAttributes로 게시물의 번호가 전송되므로 이를 이용해 모달창 내용을 수정한다.

 

 

 

11.3.4 목록에서 버튼으로 이동하기

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@include file="../includes/header.jsp" %>
            <div class="row">
                <div class="col-lg-12">
                    <h1 class="page-header">Tables</h1>
                </div>
                <!-- /.col-lg-12 -->
            </div>
            <!-- /.row -->
            <div class="row">
                <div class="col-lg-12">
                    <div class="panel panel-default">
                        <div class="panel-heading">
                           Board List Page
                           <button id='regBtn' type="button" class="btn btn-xs pull-right" >
                           		Register New Board
                           </button>
                        </div>
                        <!-- /.panel-heading -->
                        <div class="panel-body">
                            <table class="table table-striped table-bordered table-hover">
                                <thead>
                                    <tr>
                                        <th>#번호</th>
                                        <th>제목</th>
                                        <th>작성자</th>
                                        <th>작성일</th>
                                        <th>수정일</th>
                                    </tr>
                                </thead>
                                
                                <tbody>
                                	<c:forEach items="${list}" var="board">
                                	    <tr class="odd gradeX">
                                 	       <td><c:out value="${board.bno }"/></td>
                                 	       <td><c:out value="${board.title }"/></td>
                                 	       <td><c:out value="${board.writer }"/></td>
                                 	       <td><fmt:formatDate pattern="yyyy-MM-dd" value="${board.regdate }"/></td>
                                 	       <td><fmt:formatDate pattern="yyyy-MM-dd" value="${board.updateDate }"/></td>
                                 	   </tr>
                                   </c:forEach>
                                </tbody>
                            </table>
                            
                            <!-- Modal -->
                            <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
                                <div class="modal-dialog">
                                    <div class="modal-content">
                                        <div class="modal-header">
                                            <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                                            <h4 class="modal-title" id="myModalLabel">Modal title</h4>
                                        </div>
                                        <div class="modal-body">
                                        	처리가 완료되었습니다.
                                        </div>
                                        <div class="modal-footer">
                                            <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                                            <button type="button" class="btn btn-primary">Save changes</button>
                                        </div>
                                    </div>
                                    <!-- /.modal-content -->
                                </div>
                                <!-- /.modal-dialog -->
                            </div>
                            <!-- /.modal -->
                            
                        </div>
                        <!-- /.panel-body -->
                    </div>
                    <!-- /.panel -->
                </div>
                <!-- /.col-lg-12 -->
            </div>
           <script type="text/javascript">
           	$(document).ready(function() {
           		var result = '<c:out value="${result}"/>';
           	});
           </script>
            
            <script type="text/javascript">
            	$(document).ready(function() {
            		var result='<c:out value="${result}"/>';
            		checkModal(result);
            		
            		function checkModal(result) {
            			if(result==='') {
            				return;
            			}
            			
            			if(parseInt(result) > 0) {
            				$(".modal-body").html("게시글 " + parseInt(result) + "번이 등록되었습니다.");
            			}
            			
            			$("#myModal").modal("show");
            		}
            		
            		$("#regBtn").on("click", function() {
            			self.location="/board/register";
            		});
            	});
            </script>

<%@include file="../includes/footer.jsp" %>
 

상단에 버튼 추가,

하단 script 부분에 소스 추가

 

 

11.4 조회 페이지와 이동

 

 

11.4.1 조회 페이지 작성

 

register.jsp를 복사해 get.jsp를 만든다.

게시물 번호를 보여줄 수 있는 필드를 추가하고 모든 데이터를 readonly로 지정한다.

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<%@include file="../includes/header.jsp"%>
<div class="row">
	<div class="col-lg-12">
		<h1 class="page-header">Board Register</h1>
	</div>
	<!-- /.col-lg-12 -->
</div>
<!-- /.row -->


<div class="row">
	<div class="col-lg-12">
		<div class="panel panel-default">
			<div class="panel-heading">Board Read Page</div>
			<!-- /.panel-heading -->
			<div class="panel-body">
				<div class="form-group">
					<label>Bno</label> 
					<input class="form-control" name='bno'
					value='<c:out value="${board.bno}"/>' readonly="readonly">
				</div>
				<div class="form-group">
					<label>Title</label> 
					<input class="form-control" name='title'
					value='<c:out value="${board.title}"/>' readonly="readonly">
				</div>
				<div class="form-group">
					<label>Text area</label> 
					<textarea class="form-control" rows="3" name='content'
					readonly="readonly">
						<c:out value="${board.content }"/>
					</textarea>
				</div>
				<div class="form-group">
					<label>Writer</label> 
					<input class="form-control" name='writer'
					value='<c:out value="${board.writer}"/>' readonly="readonly">
				</div>
				<button data-oper='modify' class="btn btn-default">Modify</button>
				<button data-oper='list' class="btn btn-default">List</button>

			</div>
			<!-- /.panel-body -->
		</div>
		<!-- /.panel -->
	</div>
	<!-- /.col-lg-12 -->
</div>


<%@include file="../includes/footer.jsp"%>

				<button data-oper='modify' class="btn btn-default"
				onclick="location.href='/board/modify?bno=<c:out value="${board.bno }"/>'">
					Modify
				</button>
				<button data-oper='list' class="btn btn-default"
				onclick="location.href='/board/list'">
					List
				</button>

 

버튼 처리는 이렇게.

 

 

11.4.2 목록 페이지와 뒤로 가기 무넺

 

목록 페이지에서 각 게시물 제목에 <a> 태그를 적용해 조회 페이지로 가도록 적용

최근에 웹페이지들은 사용자의 트래픽을 고려해 목록 페이지에서 새창을 띄워 조회 페이지로 이동하는 걸 선호하지만 전통적인 방식에선 현재창 내에서 이동하는 방식을 사용한다

 

목록에서 조회 페이지로의 이동

 

list.jsp 수정

   <tbody>
                                	<c:forEach items="${list}" var="board">
                                	    <tr class="odd gradeX">
                                 	       <td>
                                 	       		<a href='/board/get?bno=<c:out value="${board.bno}"/>'>
                                 	       		<c:out value="${board.bno }"/>
                                 	       		</a>
                                 	       </td>
                                 	       <td><c:out value="${board.title }"/></td>
                                 	       <td><c:out value="${board.writer }"/></td>
                                 	       <td><fmt:formatDate pattern="yyyy-MM-dd" value="${board.regdate }"/></td>
                                 	       <td><fmt:formatDate pattern="yyyy-MM-dd" value="${board.updateDate }"/></td>
                                 	   </tr>
                                   </c:forEach>
                                </tbody>

브라우저를 통해 확인해보면 각 게시물 제목에 링크가 걸리는 것을 확인할 수 있다.

 

만일 조회 페이지를 이동하는 방식이 아니라 새창을 통해 보고 싶다면 <a> 태그 속성으로 target='_blank'를 지정하면 된다.

 

 

뒤로 가기의 문제

 

한 가지 문제가 남아 있다. 등록->목록->조회까지는 순조롭지만 뒤로 가기를 선택하면 게시물의 등록 결과를 확인하는 방식으로 동작한다는 것이다.

 

등록 페이지-> 모달창->목록 페이지 -> 조회 페이지에서 뒤로 가기를 누르면 다시 모달창이 뜨는 것.

뒤로 가기나 앞으로 가기를 하면 서버를 다시 호출하는 게 아닌 과거 자신이 가진 모든 데이터를 활용하기 때문이다.

 

이 문제를 해결하려면 window의 history 객체를 이용해 현재 페이지는 모달창을 띄울 필요가 없다고 표시를 해 두는 방식을 사용해야 한다. window의 history 객체는 스택 구조로 동작한다.

 

그림1에서 사용자가 /board/list를 최초로 호출한다. history에 쌓으면서 현재 페이지는 모달창을 보여줄 필요가 없다는 표시를 해 준다.

/board/register를 호출한 그림2에서 뒤로 가기를 실행하면 /board/list가 보여지는데 이 때 모달창을 띄울 필요가 없다는 표시를 확인할 수 있다.

그림3은 등록을 완료하고 /board/list가 호출되는 상황에서, 앞으로가기나 뒤로 가기로 한 것이 아니므로 스택의 상단에 추가된다. 등록 직후에 board/list로 이동한 경우 모달창이 동작한다.

그림3에서 모달창을 보여준 후엔 스택의 상단에 모달창이 필요하지 않다는 표시를 해 주어야 한다.

 

            <script type="text/javascript">
            	$(document).ready(function() {
            		var result='<c:out value="${result}"/>';
            		checkModal(result);
            		
            		history.replaceState({}, null, null);
            		
            		function checkModal(result) {
            			if(result==='' || history.state) {
            				return;
            			}
            			
            			if(parseInt(result) > 0) {
            				$(".modal-body").html("게시글 " + parseInt(result) + "번이 등록되었습니다.");
            			}
            			
            			$("#myModal").modal("show");
            		}
            		
            		$("#regBtn").on("click", function() {
            			self.location="/board/register";
            		});
            	});
            </script>

javascript 처리는 우선 checkModal을 실행하는데 만일 등록된 후에 이동한 것이라면 그림3처럼 되므로 모달창이 보이게 된다.

 

모달창이 보이는 여부와 관계없이 Javascript의 모든 처리가 끝나면 history에 쌓이는 상태는 모달창을 보여줄 필요가 없는 상태가 된다.

 

 

11.5 게시물의 수정/삭제 처리

 

수정 작업은 일반적으로 1) 조회 페이지에서 직접 처리 2) 별도의 페이지를 만들어서 처리하는 방식을 사용한다.

최근에는 별개 페이지에서 처리하는 게 일반적

 

조회 페이지에서 GET 방식으로 처리되는 URL을 통해 수정/삭제 버튼이 존재하는 화면을 볼 수 있게 제작해야 한다.

수정/삭제 작업은 POST 방식으로, 결과는 다시 목록 화면에서 확인할 수 있는 형태로 제작

 

11.5.1 수정/삭제 페이지로 이동

	@GetMapping({"/get", "/modify"})
	public void get(@RequestParam("bno") Long bno, Model model)
	{
		log.info("/get or modify");
		model.addAttribute("board", service.get(bno));
	}

기존의 BoardController 클래스 약간 수정.

 

modify.jsp 파일

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<%@include file="../includes/header.jsp"%>
<div class="row">
	<div class="col-lg-12">
		<h1 class="page-header">Board Register</h1>
	</div>
	<!-- /.col-lg-12 -->
</div>
<!-- /.row -->


<div class="row">
	<div class="col-lg-12">
		<div class="panel panel-default">
			<div class="panel-heading">Board Register</div>
			<!-- /.panel-heading -->
			<div class="panel-body">
				<form role="form" action="/board/modify" method="post">
					<div class="form-group">
						<label>Bno</label> <input class="form-control" name='bno'
							value='<c:out value="${board.bno}"/>' readonly="readonly">
					</div>
					<div class="form-group">
						<label>Title</label> <input class="form-control" name='title'
							value='<c:out value="${board.title}"/>' >
					</div>
					<div class="form-group">
						<label>Text area</label>
						<textarea class="form-control" rows="3" name='content'>
							<c:out value="${board.content }" />
						</textarea>
					</div>
					<div class="form-group">
						<label>Writer</label> <input class="form-control" name='writer'
							value='<c:out value="${board.writer}"/>' readonly="readonly">
					</div>
					<div class="form-group">
						<label>RegDate</label> <input class="form-control" name='regDate'
							value='<fmt:formatDate pattern="yyyy/MM/dd" value="${board.regdate}"/>'
							readonly="readonly">
					</div>
					<div class="form-group">
						<label>Writer</label> <input class="form-control"
							name='updateDate'
							value='<fmt:formatDate pattern="yyyy/MM/dd" value="${board.updateDate}"/>'
							readonly="readonly">
					</div>
					<button type="submit" data-oper='modify' class="btn btn-dafult">Modify</button>
					<button type="submit" data-oper='remove' class="btn btn-danger">Remove</button>
					<button type="submit" data-oper='list' class="btn btn-info">List</button>
				</form>

			</div>
			<!-- /.panel-body -->
		</div>
		<!-- /.panel -->
	</div>
	<!-- /.col-lg-12 -->
</div>


<%@include file="../includes/footer.jsp"%>

form 태그는 action 속성을 /board/modify로 지정했지만 삭제를 하면 /board/remove와 같이 액션 속성 내용을 수정해 사용하게 된다. 게시물의 제목과 내용은 사용자가 편집할 수 있게 되며 등록일과 수정일은 BoardVO로 나중에 수집되어야 하므로 특정 날짜 포맷으로 지정해야 한다.

 

<script type="text/javascript">
	$(document).ready(function() {
		var formObj = $("form");
		$('button').on("click", function(e) {
			e.preventDefault();
			var operation = $(this).data("oper");
		
			console.log(operation);
		
			if(operation === 'remove') {
				formObj.attr("action", "/board/remove");
			} else if(operation==='list') {
				//move to list
				self.location="/board/list";
				return;
			}
			formObj.submit();
		});
	});
</script>

 

script 추가.

data-oper 속성으로 원하는 기능을 하도록 처리.

 

form 태그의 모든 버튼은 기본적으로 submit을 처리하기 때문에, e.preventDefault()로 기본 동작을 막고 마지막에 직접 submit을 수행하도록 함

 

 

 

11.5.2 게시물 수정/삭제 확인

 

Modify 버튼을 통해 BoardController에 수정을 요청한다.

Modify 버튼을 클릭하면 BoardController에서 주어진 파라미터들을 BoardVO로 처리하게 된다.

 

Remove를 클릭하면 form 태그의 action 값이 /board/remove가 되고 데이터들을 전송한다.

 

 

11.5.3 조회 페이지에서 <form>처리

 

게시물의 조회 페이지에서 수정과 삭제가 필요한 페이지로 링크를 처리해야 한다.

직접 버튼에 링크를 처리하는 방식을 사용해 처리했지만 나중에 다양한 상황을 처리하기 위해 form 태그를 이용해 수정해보자.

 

				<form id="openForm" action="/board/modify" method="get">
					<input type='hidden' id='bno' name='bno' value='<c:out value="${board.bno}"/>' >
				</form>

get.jsp 일부 내용 수정

 

	
	<script type="text/javascript">
		$(document).ready(function() {
			var openForm = $("#openForm");
			$("button[data-oper='modify']").on("click", function(e) {
				operForm.attr("action", "/board/modify").submit();
			});
			
			$("button[data-oper='list']").on("click", function(e) {
				operForm.find("#bno").remove();
				operForm.attr("action", "/board/list");
				operForm.submit();
			});
		});
	</script>
</div>


 

위 내용도 추가.

사용자가 수정 버튼을 누르면 bno 값을 전달하고 form 태그를 submit 시켜 처리.

 

만일 사용자가 list로 이동하면 아직 아무런 데이터도 필요치 않으므로 form 태그의 bno 태그를 지우고 submit을 통해 리스트 페이지로 이동.

 

11.5.4 수정 페이지에서 링크 처리

 

modify.jsp 수정

 

<script type="text/javascript">
	$(document).ready(function() {
		var formObj = $("form");
		$('button').on("click", function(e) {
			e.preventDefault();
			var operation = $(this).data("oper");
		
			console.log(operation);
		
			if(operation === 'remove') {
				formObj.attr("action", "/board/remove");
			} else if(operation==='list') {
				//move to list
				formObj.attr("action", "/board/list").attr("method".get");
				formObj.empth();
			}
			formObj.submit();
		});
	});
</script>

수정된 내용은 클릭 버튼이 list일 경우 action 속성과 method 속성을 변경함.

 

 

 

<get.jsp>

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<%@include file="../includes/header.jsp"%>
<div class="row">
	<div class="col-lg-12">
		<h1 class="page-header">Board Register</h1>
	</div>
	<!-- /.col-lg-12 -->
</div>
<!-- /.row -->


<div class="row">
	<div class="col-lg-12">
		<div class="panel panel-default">
			<div class="panel-heading">Board Read Page</div>
			<!-- /.panel-heading -->
			<div class="panel-body">
				<div class="form-group">
					<label>Bno</label> <input class="form-control" name='bno'
						value='<c:out value="${board.bno}"/>' readonly="readonly">
				</div>
				<div class="form-group">
					<label>Title</label> <input class="form-control" name='title'
						value='<c:out value="${board.title}"/>' readonly="readonly">
				</div>
				<div class="form-group">
					<label>Text area</label>
					<textarea class="form-control" rows="3" name='content'
						readonly="readonly">
						<c:out value="${board.content }" />
					</textarea>
				</div>
				<div class="form-group">
					<label>Writer</label> <input class="form-control" name='writer'
						value='<c:out value="${board.writer}"/>' readonly="readonly">
				</div>
				<button data-oper='modify' class="btn btn-default">Modify</button>
				<button data-oper='list' class="btn btn-info">List</button>
				<form id="operForm" action="/board/modify" method="get">
					<input type='hidden' id='bno' name='bno' value='<c:out value="${board.bno}"/>' >
				</form>
			</div>
			<!-- /.panel-body -->
		</div>
		<!-- /.panel -->
	</div>
	<!-- /.col-lg-12 -->
	
	<script type="text/javascript">
		$(document).ready(function() {
			var operForm = $("#operForm");
			$("button[data-oper='modify']").on("click", function(e) {
				operForm.attr("action", "/board/modify").submit();
			});
			
			$("button[data-oper='list']").on("click", function(e) {
				operForm.find("#bno").remove();
				operForm.attr("action", "/board/list");
				operForm.submit();
			});
		});
	</script>
</div>


<%@include file="../includes/footer.jsp"%>

list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@include file="../includes/header.jsp" %>
            <div class="row">
                <div class="col-lg-12">
                    <h1 class="page-header">Tables</h1>
                </div>
                <!-- /.col-lg-12 -->
            </div>
            <!-- /.row -->
            <div class="row">
                <div class="col-lg-12">
                    <div class="panel panel-default">
                        <div class="panel-heading">
                           Board List Page
                           <button id='regBtn' type="button" class="btn btn-xs pull-right" >
                           		Register New Board
                           </button>
                        </div>
                        <!-- /.panel-heading -->
                        <div class="panel-body">
                            <table class="table table-striped table-bordered table-hover">
                                <thead>
                                    <tr>
                                        <th>#번호</th>
                                        <th>제목</th>
                                        <th>작성자</th>
                                        <th>작성일</th>
                                        <th>수정일</th>
                                    </tr>
                                </thead>
                                
                                <tbody>
                                	<c:forEach items="${list}" var="board">
                                	    <tr class="odd gradeX">
                                 	       <td>
                                 	       		<c:out value="${board.bno}"/>
                                 	       </td>
                                 	       <td>
                                 	       		<a href='/board/get?bno=<c:out value="${board.bno}"/>'>
                                 	       		  <c:out value="${board.title }"/>
                                 	       		</a>
                                 	       	</td>
                                 	       <td><c:out value="${board.writer }"/></td>
                                 	       <td><fmt:formatDate pattern="yyyy-MM-dd" value="${board.regdate }"/></td>
                                 	       <td><fmt:formatDate pattern="yyyy-MM-dd" value="${board.updateDate }"/></td>
                                 	   </tr>
                                   </c:forEach>
                                </tbody>
                            </table>
                            
                            <!-- Modal -->
                            <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
                                <div class="modal-dialog">
                                    <div class="modal-content">
                                        <div class="modal-header">
                                            <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                                            <h4 class="modal-title" id="myModalLabel">Modal title</h4>
                                        </div>
                                        <div class="modal-body">
                                        	처리가 완료되었습니다.
                                        </div>
                                        <div class="modal-footer">
                                            <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                                            <button type="button" class="btn btn-primary">Save changes</button>
                                        </div>
                                    </div>
                                    <!-- /.modal-content -->
                                </div>
                                <!-- /.modal-dialog -->
                            </div>
                            <!-- /.modal -->
                            
                        </div>
                        <!-- /.panel-body -->
                    </div>
                    <!-- /.panel -->
                </div>
                <!-- /.col-lg-12 -->
            </div>
         
            
            <script type="text/javascript">
            	$(document).ready(function() {
            		var result='<c:out value="${result}"/>';
            		checkModal(result);
            		
            		history.replaceState({}, null, null);
            		
            		function checkModal(result) {
            			if(result==='' || history.state) {
            				return;
            			}
            			
            			if(parseInt(result) > 0) {
            				$(".modal-body").html("게시글 " + parseInt(result) + "번이 등록되었습니다.");
            			}
            			
            			$("#myModal").modal("show");
            		}
            		
            		$("#regBtn").on("click", function() {
            			self.location="/board/register";
            		});
            	});
            </script>

<%@include file="../includes/footer.jsp" %>
 

modify.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<%@include file="../includes/header.jsp"%>
<div class="row">
	<div class="col-lg-12">
		<h1 class="page-header">Board Register</h1>
	</div>
	<!-- /.col-lg-12 -->
</div>
<!-- /.row -->


<div class="row">
	<div class="col-lg-12">
		<div class="panel panel-default">
			<div class="panel-heading">Board Register</div>
			<!-- /.panel-heading -->
			<div class="panel-body">
				<form role="form" action="/board/modify" method="post">
					<div class="form-group">
						<label>Bno</label> <input class="form-control" name='bno'
							value='<c:out value="${board.bno}"/>' readonly="readonly">
					</div>
					<div class="form-group">
						<label>Title</label> <input class="form-control" name='title'
							value='<c:out value="${board.title}"/>'>
					</div>
					<div class="form-group">
						<label>Text area</label>
						<textarea class="form-control" rows="3" name='content'>
							<c:out value="${board.content }" />
						</textarea>
					</div>
					<div class="form-group">
						<label>Writer</label> <input class="form-control" name='writer'
							value='<c:out value="${board.writer}"/>' readonly="readonly">
					</div>
					<div class="form-group">
						<label>RegDate</label> <input class="form-control" name='regDate'
							value='<fmt:formatDate pattern="yyyy/MM/dd" value="${board.regdate}"/>'
							readonly="readonly">
					</div>
					<div class="form-group">
						<label>Writer</label> <input class="form-control"
							name='updateDate'
							value='<fmt:formatDate pattern="yyyy/MM/dd" value="${board.updateDate}"/>'
							readonly="readonly">
					</div>
					<button type="submit" data-oper='modify' class="btn btn-dafult">Modify</button>
					<button type="submit" data-oper='remove' class="btn btn-danger">Remove</button>
					<button type="submit" data-oper='list' class="btn btn-info">List</button>
				</form>

			</div>
			<!-- /.panel-body -->
		</div>
		<!-- /.panel -->
	</div>
	<!-- /.col-lg-12 -->
</div>


<script type="text/javascript">
	$(document).ready(function() {
		var formObj = $("form");
		$('button').on("click", function(e) {
			e.preventDefault();
			var operation = $(this).data("oper");
		
			console.log(operation);
		
			if(operation === 'remove') {
				formObj.attr("action", "/board/remove");
			} else if(operation==='list') {
				//move to list
				formObj.attr("action", "/board/list").attr("method","get");
				formObj.empty();
			}
			formObj.submit();
		});
	});
</script>
<%@include file="../includes/footer.jsp"%>

register.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@include file="../includes/header.jsp" %>
            <div class="row">
                <div class="col-lg-12">
                    <h1 class="page-header">Board Register</h1>
                </div>
                <!-- /.col-lg-12 -->
            </div>
            <!-- /.row -->
            
            
            <div class="row">
                <div class="col-lg-12">
                    <div class="panel panel-default">
                        <div class="panel-heading">
                           Board Register
                        </div>
                        <!-- /.panel-heading -->
                        <div class="panel-body">
                        	<form role="form" action="/board/register" method="post">
                        		<div class="form-group">
                        			<label>Title</label>
                        			<input class="form-control" name='title'>
                        		</div>
                        		<div class="form-group">
                        			<label>Text area</label>
                        			<textarea class="form-control" rows="3" name='content'></textarea>
                        		</div>
                        		<div class="form-group">
                        			<label>Writer</label>
                        			<input class="form-control" name='writer'>
                        		</div>
                        		<button type="submit" class="btn btn-default">Submit Button</button>
                        		<button type="reset" class="btn btn-default">Reset Button</button>
                        	</form>
                            
                        </div>
                        <!-- /.panel-body -->
                    </div>
                    <!-- /.panel -->
                </div>
                <!-- /.col-lg-12 -->
            </div>
           
            
<%@include file="../includes/footer.jsp" %>