For Programmer

PART3 - 스프링 MVC 프로젝트의 기본 구성(4) : 화면처리(JSP) 본문

Spring/스프링 프로젝트

PART3 - 스프링 MVC 프로젝트의 기본 구성(4) : 화면처리(JSP)

유지광이 2020. 9. 3. 20:43
728x90

화면 처리

화면에는 JSP와 JavaScript(JQuery), CSS, HTML 을 이용해서 작성하니다. 예제에서 사용할 디자인은 'SB Admin2'를 이용합니다.

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

-목록 페이지 작업과 includes

게시물 리스트의 URL은 '/board/list'이므로 최종적인 '/WEB-INF/views/board/list.jsp'가 됩니다. 해당 경로에 list.jsp 파일을 추가합니다.

BoardController 에서 void 형태로 list 함수를 만들어 놨으니 /board/list 로 URL에 접속이 되는지 톰캣서버를 킨 후에 확인 합니다.

그 후 SB Admin2 JSP 페이지를 적용합니다.(JSP에 대한 설명은 생략하겠습니다.)

 

또한 header 부분과 footer부분은 모든 jsp 파일에서 사용할 것이기 때문에 views/include폴더를 따로 생성하여 include해준다.

list.jsp 코드

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

<!DOCTYPE html>
<html lang="en">

<head>

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">

<title>SB Admin 2 - Bootstrap Admin Theme</title>

<!-- Bootstrap Core CSS -->
<link href="/resources/vendor/bootstrap/css/bootstrap.min.css"
	rel="stylesheet">

<!-- MetisMenu CSS -->
<link href="/resources/vendor/metisMenu/metisMenu.min.css"
	rel="stylesheet">

<!-- DataTables CSS -->
<link
	href="/resources/vendor/datatables-plugins/dataTables.bootstrap.css"
	rel="stylesheet">

<!-- DataTables Responsive CSS -->
<link
	href="/resources/vendor/datatables-responsive/dataTables.responsive.css"
	rel="stylesheet">

<!-- Custom CSS -->
<link href="/resources/dist/css/sb-admin-2.css" rel="stylesheet">

<!-- Custom Fonts -->
<link href="/resources/vendor/font-awesome/css/font-awesome.min.css"
	rel="stylesheet" type="text/css">

<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
        <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
        <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->

</head>

<body>


	<%@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 width="100%" class="table table-striped table-bordered table-hover">
						<thead>
							<tr>
								<th>#번호</th>
								<th>제목</th>
								<th>작성자</th>
								<th>작성일</th>
								<th>수정일</th>
							</tr>
						</thead>

					</table>
				</div>
				<!-- /.panel-body -->
			</div>
			<!-- /.panel -->
		</div>
		<!-- /.col-lg-6 -->
	</div>
	<!-- /.row -->


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

</body>

</html>

 

실행결과

 

-목록 화면 처리

-Model에 담긴 데이터 출력

 

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

 

list.jsp 일부

		<table width="100%" class="table table-striped table-bordered table-hover">
						<thead>
							<tr>
								<th>#번호</th>
								<th>제목</th>
								<th>작성자</th>
								<th>작성일</th>
								<th>수정일</th>
							</tr>
						</thead>
						<c:forEach items="${list}" var="board">
							<tr>
								<td><c:out value="${board.bno }" /></td>
								<td><c:out value="${board.title }" /></td>
								<td><c:out value="${board.writer }" /></td>
								<td><fmt:formatDate value="${board.regdate }" pattern="yyyy-MM-dd"/></td>
								<td><fmt:formatDate value="${board.updateDate }" pattern="yyyy-MM-dd"/></td>
							</tr>
						</c:forEach>
					</table>

실행결과

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

BoardController의 일부

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

	}

*register( )는 입력 페이지를 보여주는 역할만을 하기 때문에 별도의 처리가 필요하지 않습니다. views 폴더에는 includes를 적용한 입력페이지를 작성합니다.

 

views/board/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"%>

<!DOCTYPE html>
<html lang="en">

<head>

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">

<title>SB Admin 2 - Bootstrap Admin Theme</title>

<!-- Bootstrap Core CSS -->
<link href="/resources/vendor/bootstrap/css/bootstrap.min.css"
	rel="stylesheet">

<!-- MetisMenu CSS -->
<link href="/resources/vendor/metisMenu/metisMenu.min.css"
	rel="stylesheet">

<!-- DataTables CSS -->
<link
	href="/resources/vendor/datatables-plugins/dataTables.bootstrap.css"
	rel="stylesheet">

<!-- DataTables Responsive CSS -->
<link
	href="/resources/vendor/datatables-responsive/dataTables.responsive.css"
	rel="stylesheet">

<!-- Custom CSS -->
<link href="/resources/dist/css/sb-admin-2.css" rel="stylesheet">

<!-- Custom Fonts -->
<link href="/resources/vendor/font-awesome/css/font-awesome.min.css"
	rel="stylesheet" type="text/css">

<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
        <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
        <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->

</head>

<body>

	<%@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>
				<!--  end panel-body -->

			</div>
			<!--  end panel-body -->
		</div>
		<!-- end panel -->
	</div>
	<!-- /.row -->
	<%@include file="../includes/footer.jsp"%>

</body>

</html>

실행결과

*그러나 입력하여 Submit Button을 눌리면 실제로 한글이 깨지는 문제가 발생한다.

 

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

새로운 게시물을 등록했을 때 만일 한글 입력에 문제가 있다는 것을 발견했다면 1. 브라우저에서 한글이 깨져서 전송이되는지를 확인하고 2.문제가 없다면 스프링MVC 쪽에서 한글을 처리하는 필터를 등록해야합니다.

브라우저에서 전송되는 데이터는 개발자 도구를 이용해서 확인할 수 있습니다. 개발자 도구에서 'Network'탭을 열어둔 상태에서 데이터를 보내면 해당 내용을 볼 수 있으므로 이때 POST방식으로 제대로 전송되었는지, 한글이 깨진 상태로 전송된 것인지를 확인할 수 있습니다.

 

위의 화면을 보면 브라우저가 한글을 문제없이 보냈음을 알 수 있는데, 문제는 Controller 혹은 데이터베이스 쪽이라는 것을 알 수 있습니다.

BoardController와 BoardServiceImpl을 개발할 때는 이미 Lombok의 로그를 이용해서 필요한 기능들을 기록해 두었으므로, 이를 확인해야합니다.

위의 로그를 보면 BoardController에 전달될 때 이미 한글이 깨진 상태로 처리된 것을 볼 수 있습니다.

이 문제를 해결하기 위해서 web.xml에 아래와 같이 필터를 추가합니다.

 

web.xml 일부

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

JAVA 설정의 경우

web.xml을 대신하는 WebConfig 클래스에서는 필터를 getServletFilters( )를 재정의해서 처리할 수 있습니다.

	@Override
	protected Filter[] getServletFilters() {
		CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
		characterEncodingFilter.setEncoding("UTF-8");
		characterEncodingFilter.setForceEncoding(true);

		return new Filter[] { characterEncodingFilter };
	}

 

-재전송(redirect) 처리

BoardController에서 register( ) 메서드는 'redirect:/board/list"를 전송하는데 브라우저는 이를 통보받은 후 '/board/list'로 이동하게 됩니다. 만일 위와 같이 재전송을 하지 않는다면 사용자는 브라우저의 '새로고침'을 통해서 동인한 내용을 계속 서버에 등록할 수 있기 때문에(흔히 도배라고 표현) 문제가 발생합니다. 브라우저는 이런 경우 경고창을 보여주기는 하지만 근본적으로 차단하지 않습니다. 따라서 등록, 수정 , 삭제 작업은 처리가 완료된 후에 다시 동일한 내용을 전송할 수 없도록 아예 브라우저의 URL을 이동하는 방식을 이용합니다. 이러한 과정에서 하나 더 신경 써야 하는 것은 브라우저에 등록,수정,삭제의 결과를 바로 알 수 있게 피드백을 줘야한다는 점입니다. 경고창이나 <div>를 이용하는 모달창을 이용해서 이러한 작업을 처리합니다. 또한 BoardController에서 redirect 처리를 할 때 RedirectAttributes라는 특별한 타입의 객체를 이용했습니다. addFlashAttributes( ) 의 경우 이러한 처리에 적합한데, 그 이유는 일회성으로만 데이터를 전달하기 때문입니다. addFlashAttribute( )로 보관된 데이터는 단 한번만 사용할 수 있게 보관됩니다.(내부적으로는 HttpSession을 이용해서 처리) 

list.jsp페이지의 아래쪽에 <Script>태그를 이용해서 상황에 따른 메시지를 확인할 수 있습니다.

 

views/board/list.jsp의 일부

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

BoardController에서 register메서드

	@PostMapping("/register")
	public String register(BoardVO board, RedirectAttributes rttr) {

		log.info("register: " + board);

		service.register(board);

		rttr.addFlashAttribute("result", board.getBno());

		return "redirect:/board/list";
	}

*만일 새로운 게시물이 등록된 직후에 위의 코드에서 result는 예를들어 bno번호가 15일 경우 var result = '15'; 가 되게 됩니다. 새로운 게시물의 번호는 addFlashAttribute( ) 로 저장되었기 때문에 한번도 사용된 적이 없다면 위와 같이 값을 만들어 내지만 사용자가 '/board/list'를 호출하거나, '새로고침'을 통해서 호출하는 경우는 아무런 내용이 없게 됩니다. var result = ' ';  따라서 addFlashAttribute( )를 이용해서 일회성으로만 데이터를 사용할 수 있으므로 이를 이용해서 경고창이나 모달창 등을 보여주는 방식으로 처리할 수 있습니다.

 

-모달(Modal)창 보여주기

최근 브라우저에서 경고창을 띄우는 방식보다 모달창을 보여주는 방식을 많이 사용합니다. BootStrap은 모달창을 간단하게 사용할 수 있으므로 목록 화면에서 필요한 메시지를 보여주는 방법을 사용해 봅니다. 모달창은 기본적으로 <div>를 화면에 특정 위치에 보여주고, 배경이 되는 <div>에 배경색을 입혀서 처리합니다. 모달창은 활성화된 <div>를 선택하지 않고는 다시 원래의 화면을 볼 수 없도록 막기 때문에 보여주는데 효과적인 방식입니다.

모달창을 처리하기 위해서는 우선<div>를 이용해서 페이지의 코드에 추가해야 합니다. list.jsp내에 <table> 태그의 아래쪽에 모달창의 <div>를 추가합니다.(다운로드 받은 SBAdmin2의 pages폴더 내의 notifications.html 파일을 참고)

 

list.jsp 페이지 일부

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

모달창을 보여주는 작업은 jQuery를 이용해서 처리할 수 있습니다.

list.jsp 내 jQuery 처리

	<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로 게시물의 번호가 전송되므로 이를 이용해서 모달창의 내용을 수정합니다. $("#myModal").modal("show"); 으로 모달창을 보이게 합니다.

 

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

목록페이지 상단에 버튼을 추가해서 등록 작업을 시작할 수 있게 처리해야합니다. 우선 list.jsp의 HTML구조를 아래와 같이 수정합니다.

 

list.jsp 수정

	<div class="panel-heading">Board List Page
	<button id='regBtn' type="button" class="btn btn-xs pull-right">Register New Board</button>
	</div>

list.jsp 하단의 jQuery를 이용하는 부분에서 해당 버튼을 클릭했을 때의 동작을 정의합니다.

 

list.jsp 하단의 jQuery부분 추가

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

 

-조회 페이지와 이동

조회 페이지 작성

조회페이지는 입력 페이지와 거의 유사하지만 게시물 번호(bno)가 출력된다는 점과 모든 데이터가 읽기 전용으로 처리된다는 점이 가장 큰 차이입니다.게실물 조회는 BoardController에서 get( )메서드로 구성되어 있습니다.

 

BoardController 클래스 일부

	@GetMapping("/get")
	public void get(@RequestParam("bno") Long bno, Model model) {

		log.info("/get");
		model.addAttribute("board", service.get(bno));
	}

views/board 폴더 내 get.jsp를 register.jsp를 복사해서 작성합니다.get.jsp는 게시물 번호를 보여줄 수 있는 필드를 추가하고, 모든 데이터는 readonly를 지정해서 작성합니다. register.jsp에 있던 <form>태그는 조회 페이지에서는 그다지 필요하지 않으므로 제거하는 대신 마지막에는 수정/삭제 페이지로 이동하거나 원래의 목록 페이지로 이동할 수 있는 버튼을 추가합니다.

 

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

<!DOCTYPE html>
<html lang="en">

<head>

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">

<title>SB Admin 2 - Bootstrap Admin Theme</title>

<!-- Bootstrap Core CSS -->
<link href="/resources/vendor/bootstrap/css/bootstrap.min.css"
	rel="stylesheet">

<!-- MetisMenu CSS -->
<link href="/resources/vendor/metisMenu/metisMenu.min.css"
	rel="stylesheet">

<!-- DataTables CSS -->
<link
	href="/resources/vendor/datatables-plugins/dataTables.bootstrap.css"
	rel="stylesheet">

<!-- DataTables Responsive CSS -->
<link
	href="/resources/vendor/datatables-responsive/dataTables.responsive.css"
	rel="stylesheet">

<!-- Custom CSS -->
<link href="/resources/dist/css/sb-admin-2.css" rel="stylesheet">

<!-- Custom Fonts -->
<link href="/resources/vendor/font-awesome/css/font-awesome.min.css"
	rel="stylesheet" type="text/css">

<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
        <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
        <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->

</head>

<body>

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


<div class="row">
	<div class="col-lg-12">
		<h1 class="page-header">Board Read</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">
						<a href="/board/modify?bno=<c:out value="${board.bno}"/>">Modify</a>
					</button>
					<button data-oper='list' class="btn btn-info">
						<a href="/board/list">List</a>
					</button>

			</div>
			<!--  end panel-body -->

		</div>
		<!--  end panel-body -->
	</div>
	<!-- end panel -->
</div>
<!-- /.row -->



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

</body>

</html>

실행결과

 

-목록 페이지와 뒤로 가기 문제

목록 페이지에서 각 게시물 제목에 <a>태그를 적용해서 조회 페이지로 이동하게 처리합니다. 최근에 웹페이지들은 사용자들의 트래픽을 고려해 목록 페이지에서 새창을 띄워서 조회 페이지로 이동하는 방식을 선호하지만 전통적인 방식에서는 현재창 내에서 이동하는 방식을 사용합니다.조금 관심을 가지고 웹페이지들을 이용하다 보면 의외로 이러한 처리가 제대로 되지 않는 경우를 많이 보게 됩니다. 예를 들어, '뒤로가기'를 하면 다시 다운로드 다시 다운로드를 시도하거나 경고창이 뜨는 경험들을 할 수 있습니다. 

 

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

 

list.jsp의 일부(title에 a태그 추가)

		<c:forEach items="${list}" var="board">
				<tr>
					<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 value="${board.regdate }"pattern="yyyy-MM-dd" /></td>
					<td><fmt:formatDate value="${board.updateDate }"pattern="yyyy-MM-dd" /></td>
				</tr>
		</c:forEach>

*만약 새창을 띄우고 싶다면 <a>태그의 속성으로 target='_blank' 를 지정하면 됩니다. <a> 태그와 <form>  태그에는 target속성을 지정할 수 있는데 '_blank'는 새로운 창에서 처리하게 됩니다.

 

실행결과

-뒤로 가기의 문제

동일한 페이지 내에서 목록 페이지와 조회 페이지의 이동은 정상적으로 처리된 것 같아 보이지만 한 가지 문제가 남아 있습니다. '등록-> 목록 -> 조회' 까지는 순조롭지만 브라우저의 '뒤로가기'를 클릭하는 순간 다시 게시물의 등록 결과를 확인하는 방식으로 동작한다는 것입니다. (등록페이지 -> 모달창 -> 목록페이지 -> 조회페이지 -> 여기서 뒤로가기 -> 모달창 )

이러한 문제가 생기는 원인은 브라우저에서 뒤로가기나 앞으로 가기를 하면 서버를 다시 호출하는 게 아니라 과거에 자신이 가진 모든 데이터를 활용하기 때문입니다. 브라우저에서 조회 페이지와 목록 페이지를 여러 번 앞으로 혹은 뒤로 이동해도 서버에서는 처음에 호출을 제외하고 별다른 변화가 없는 것을 확인할 수 있습니다.

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

 

list.jsp에서 javaScript 부분

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

*기존과 달라진 점은 맨 마지막에 추가된 history.replaceState( ) 부분과 checkModal( )에서 history.state를 체크하는 부분입니다. JavaScript의 처리는 우선 checkModal( )을 실행하는데, 만일 등록된 후에 이동한 것이라면 모달창이 보이게 됩니다. 모달창이 보이는 여부와 관계없이 JavaScript의 모든 처리가 끝나게 되면 history에 쌓이는 상태는 모달창을 보여줄 필요가 없는 상태가 됩니다. 

 

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

게시물의 수정 작업은 일반적으로 1) 조회 페이지에서 직접 처리하는 방식이나 2)별도의 수정/삭제 페이지를 만들어서 해당 페이지에서 수정과 삭제를 처리하는 방식을 많이 사용합니다. 최근에는 게시물의 조회 페이지에서 댓글 등에 대한 처리가 많아지면서 수정과 삭제는 별개의 페이지에서 하는것이 일반적입니다.

 

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

BoardController에서 수정/삭제가 가능한 화면으로 이동하는 것은 조회 페이지와 같습니다. 따라서 기존의 get( )메서드를 조금 수정해서 화면을 구성합니다.

 

BoardController 일부

	@GetMapping({"/get","/modify"})
	public void get(@RequestParam("bno") Long bno, Model model) {

		log.info("/get or modify");
		model.addAttribute("board", service.get(bno));
	}

*@GetMapping 이나 @PostMapping 등에는 URL을 배열로 처리할 수 있으므로, 위와 같이 하나의 메서드로 여러 URL을 처리할 수 있습니다.

 

브라우저에서는 '/board/modify?bno=30' 와 같은 방식으로 처리하므로, views 폴더 내 board폴더 내 modify.jsp를 작성합니다. modify.jsp는 get.jsp와 같지만 수정이 가능한 제목이나 내용 등이 readonly 속성이 없도록 작성해야하며 POST방식으로 처리하는 부분을 위해서는 <form>태그로 내용들을 감싸게 해야합니다.

 

views/board/modify.jsp 의 일부

				 <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" hidden="true">
						<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" hidden="true">
						<label>Update Date</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-default">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>

 

<form>태그는 action 속성을 '/board/modify'로 지정했지만 삭제를 하면 '/board/remove'와 같이 action속성의 내용을 수정해서 사용하게 됩니다. 게시물의 제목,내용은 수정이 가능한 형태로 사용해서 사용자가 편집할 수 있게 합니다. 등록일과 수정일은 나중에 BoardVO로 수집되어야 하므로 날짜 포맷을 'yyyy/mm/dd'의 포맷으로 해야합니다.(만일 포맷이 맞지않으면 파라미터 수집 부분에 문제가 생기므로 주의가 필요합니다.) 마지막에는 수정/삭제/목록 등의 버튼을 추가합니다.

실행결과

JavaScript에서는 위의 버튼에 따라서 다른 동작을 할 수 있도록 수정해야 합니다.

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

*JavaScript에서는 <button>태그의 'data-oper'속성을 이용해서 원하는 기능을 동작하도록 처리합니다. <form>태그의 모든 버튼은 기본적으로 submit으로 처리하기 때문에 e.preventDefault( )로 기본 동작을 막고 마지막에 직접 submit( )을 수행합니다.

 

-게시물 수정/삭제 확인 

화면에서 게시물을 수정한 후에 'modify'버튼을 통해서 BoardController에 수정을 요청합니다.Modify버튼을 클릭하면 BoardController에서는 주어진 파라미터들을 BoardVO로 처리하게 되고, 다음과 같이 수정된 값이 제대로 수집된 것을 확인 할 수 있습니다.

화면에서 'Remove'버튼을 클리하게 되면 <form> 태그의 action값이 '/board/remove'가 되고 데이터들이 전송됩니다. 물론 BoardController에서는 bno 값 하나만 필요하지만 처리에는 문제가없습니다. 삭제시 BoardController에는 아래와 같은 로그가 기록되게 됩니다.

 

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

게시물의 조회 페이지에서는 수정과 삭제가 필요한 페이지로 링크를 처리해야 합니다. 직접 버튼에 링크를 처리하는 방식을 사용하여 처리하였지만, 나중에 다양한 상화을 처리하기 위해서 <form> 태그를 이용해서 수정합니다.

 

views/board/get.jsp 일부

	...생략	
	<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>	

실행결과

* 브라우저에서는 <form> 태그의 내용은 보이지 않고 버튼만이 보이게 됩니다.

사용자가 버튼을 클릭하면 operForm이라는 id를 가진 <form> 태그를 전송해야 하므로 추가적인 JavaScript 처리가 필요합니다.

 

get.jsp

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

*사용자가 수정 버튼을 누르는 경우에는 bno값을 같이 전달하고 <form> 태그를 submit시켜서 처리합니다. 만일 사용자가 list로 이동하는 경우에는 아직 아무런 데이터도 필요하지 않으므로 <form> 태그 내의 bno 태그를 지우고 submit을 통해서 리스트페이지로 이동합니다.

 

*단 다음과 같이 a태그를 걸 경우 위의 처리를 해줄 필요가 없다.

...생략

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

 

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

수정 페이지에서는 사용자가 다시 목록 페이지로 이동할 수 있도록 하기 위해서 JavaScript의 내용을 조금 수정합니다.

 

views/board/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
							/* self.location = "/board/list";
							return; */
									
							formObj.attr("action","/board/list").attr("method","get");
							formObj.empty();
						}
						formObj.submit();
					});
		});
	</script>

*수정된 내용은 클릭한 버튼이 List인 경우 action속성과 method속성을 변경합니다. '/board/list'로의 이동은 아무런 파라미터가 없기 때문에 <form> 태그의 모든 내용은 삭제한 상태에서 submit()을 진행합니다. 이후에 코드는 실행되지 않도록 return을 통해서 제어합니다.

728x90
Comments