ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [3-ch11 Spring view] 게시글 조회, 수정/삭제, 뒤로가기 처리
    Back-End/Spring Legacy 2022. 8. 26. 16:21

     

    조회 페이지 작성

     

    조회 페이지는 입력 페이지와 거의 유사하지만 게시물의 번호(bno)가 출력된다는 점과 모든 데이터가 읽기 전용으로 처리된다는 점이 가장 큰 차이점이다. 목록 페이지에서 링크를 통해 GET 방식으로 특정 번호의 게시물을 조회할 수 있다. 

     

    <BoardController 에서 get 매핑>

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

     

     

    get.jsp는 게시물의 번호를 보여줄 수 있는 필드를 추가한다. 모든 데이터는 readonly를 지정해서 작성한다. 특정 게시글 조회 페이지에선 데이터를 전송할 일이 없기에 form 태그를 사용하지 않아도 된다. 

     

     

    <%@ 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 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"
    				onclick="location.href='/board/modify?bno=<c:out value="${board.bno }"/>'">Modify</button>
    		
    		<button data-oper='list' class="btn btn-info"
    				onclick="location.href='/board/list'">List</button> --%>
    				
    		<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=${board.bno } />
    		</form>
    
    
    
          </div>
          <!--  end panel-body -->
    
        </div>
        <!--  end panel-body -->
      </div>
      <!-- end panel -->
    </div>
    <!-- /.row -->
    
    
    <script type="text/javascript">
    $(document).ready(function(){
    	
    	var operForm = $("#operForm"); //id가 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>
    
    <%@include file="../includes/footer.jsp"%>

     

     

     

    하단의 버튼을 누르면 목록과 수정하기로 이동 가능한데 주석 처리 되어 있는 것 처럼 onclick으로 location.href 값을 줘도 되지만 여기선  jQuery를 이용해서 선택한 요소에 속성을 추가하는 방식을 이용하였다. 브라우저에서는 <form> 태그의 내용은 보이지 않는다. (hidden 으로 처리했기에) 사용자가 버튼을 클릭하면 operForm이라는  id 값을 가진 <form> 태그를 전송한다. 즉) 사용자가 수정 버튼을 누르는 경우 bno 값을 같이 전달하고 <form> 태그를 submit 시켜서 처리한다. 만일 사용자가 list로 이동하는 경우 아무런 데이터가 필요하지 않으므로 <form> 태그의 bno 값을 지우고 submit을 통해 리스트 페이지로 이동한다. 

     

     

     

     

     

     

     

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

     

    목록 페이지에서 각 게시물의 제목에 <a> 태그를 적용해 조회 페이지로 이동하게 처리한다. 웹 페이지들을 이용하다 보면 의외로 이러한 처리가 제대로 되지 않은 경우를 볼 수 있다. 예를 들어) '뒤로 가기'를 하면 다시 다운로드를 시도하거나 경고창이 뜨는 것 말이다. 

     

    일단 list.jsp에서 게시물 목록을 조회해 출력할 때 게시글 제목인 title에 링크를 걸어 상세보기 페이지로 이동하게 한다. 

     

    <c:forEach items="${list}" var="board">
        <tr>
            <td>${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>

     

     

    브라우저를 통해 화면을 확인해보면 제목에 링크가 걸린 것을 볼 수 있다. 만약 조회 페이지로 이동하는 방식이 아니라 '새창'을 통해 보고 싶다면 <a> 태그의 속성에 target='_blank'를 지정하면 된다. 

     

     

     


     

     

    만약 게시글을 등록하고 나서 뒤로가기 버튼을 눌렀을 때 문제가 발생하는데, 아래 그림을 살펴보자

     

     

     

     

     

    이러한 문제가 생기는 원인은 브라우저에서 '뒤로 가기'나 '앞으로 가기'를 하면 서버를 다시 호출하는 게 아니라 과거에 자신이 가진 모든 데이터를 활용하기 때문이다. 브라우저에서 조회 페이지와 목록 페이지를 여러 번 앞으로 혹은 뒤로 이동해도 서버는 처음에 호출을 제외하고 별다른 변화가 없는 것을 확인할 수 있다. 이 문제를 해결하기 위해 window의 history 객체를 이용해서 현재 페이지는 모달창을 띄울 필요가 없다는 표시를 해 두는 방식을 이용한다. 

     

     

    <script type="text/javascript">
    	$(document).ready(
    		function() {
    			//컨트롤러에서 bno 값을 result에 저장해뒀다. 
    			//c:out을 쓰는 이유는 크로스 사이트 스크립팅 방지하기 위해서
    			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 = window.location
    				self.location = "/board/register";
    
    			});
    		});
    
    </script>

     

     

    기존과 달라지는 거는 2줄 뿐인데 history.replaceState({}, null, null); 이 줄 추가한 것과 checkModal()에서 if 문에서 결과값이 없을 때에 history.state 값이 있을 때도 추가하는 것이다.  

     

    만약 게시글 등록 후 checkModal()이 실행되는 것이라면 모달창이 보이게 된다. 그 후 javascript의 모든 처리가 끝나면 history에 쌓이는 상태는 모달창을 보여줄 필요가 없는 상태가 되므로 처음 생성 그 이후에는 모달 창이 뜨지 않게 되는 것.

     

     

     

     

    게시물의 수정/삭제

     

     

    게시물의 수정 작업은 일반적으로 2가지 방식이있다.

    1) 조회 페이지에서 직접 처리하는 방식
    2) 별도의 수정/삭제 페이지를 만들어서 해당 페이지에서 수정과 삭제를 처리하는 방식

     

    근래 게시물의 조회 페이지에서 댓글 등에 대한 처리가 많아지면서 수정과 삭제는 별개의 페이지에서 하는 것이 일반적이다. 조회 페이지에는 GET 방식으로 처리되는 URL을 통해 수정/삭제 버튼이 존재하는 화면을 볼 수 있게 제작해야 한다. 

     

    보통 조회는 GET방식 / 수정, 등록, 삭제는 POST 방식이라고 생각하고 있는데 앞에서 포스팅했던 등록(register) 페이지와 수정(modify) 페이지는 왜 GetMapping 어노테이션으로 url을 작성하지? 의문점이 들었다. GET/POST 두 방식의 차이점은 아래 링크에 걸어두겠다. 

     

    GET방식에는 Select , POST 방식에는 Create/Update/Delete 을 사용하는 것이다. 

    + 그러나 사용자의 입력이 필요한 페이지는 GET 방식!! 예를들어) 게시판에서 게시글을 등록하거나 수정하기 위해 어떤 버튼을 누른다! 그랬을 때 이동하는 첫 페이지는 GET 방식이고 게시물 등록 페이지에서 submit같은 버튼을 클릭했을 때 전달되는 방식이 POST이다. 

     

    따라서 post 방식으로 url 매핑을 해뒀지만 BoardController에 추가로 get 방식으로도 매핑해둬야한다.

     

     

    https://wonisdaily.tistory.com/55

     

    [Spring] GET/POST 두 방식의 특징과 차이점 알아보기

    서버 API를 구현하다보면 GET/POST 방식을 볼 수 있는데 주로 @RequestMapping(value = "/basic",  method= {RequestMethod.GET, RequestMethod.POST}) 이런 방식으로 RequestMapping이나 아예 각자 하나씩만 다..

    wonisdaily.tistory.com

     

     

    따라서 BoardController에 @GetMapping으로 modify url도 추가해준다. 

     

    @GetMapping({"/get","/modify"})
    public void get(@RequestParam("bno") Long bno, Model model) {
      log.info("get" );
      model.addAttribute("board", service.get(bno));
    }
    
    
    @PostMapping("/modify")
    public String modify(BoardVO board,RedirectAttributes rttr ) {
      log.info("modify.....");
      if(service.modify(board)) {
         rttr.addFlashAttribute("result", "success");
      }
      return "redirect:/board/list";
    }

     

     

    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 Modify</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 Modify</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>Update Date</label> 
    	  <input class="form-control" name='updateDate'
    	    value='<fmt:formatDate pattern = "yyyy/MM/dd" value = "${board.updateDate}" />'  readonly="readonly">            
    	</div>
    	
    	          
    
    	<!-- data- 는 custom으로 속성을 만들어서 사용할 수 있는 것. -->
    	<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>
    
          </div>
          <!--  end panel-body -->
    
        </div>
        <!--  end panel-body -->
      </div>
      <!-- end panel -->
    </div>
    <!-- /.row -->	
    
    <script type="text/javascript">
    	$(document).ready(function(){
    		
    		var formObj = $("form");
    		
    		$('button').on("click",function(e){
    			//기본동작을 막는다. 버튼을 누르면 해야하는 일 (ex. form 태그 안에서 버튼을 누르면 submit)을 막아준다.
    			e.preventDefault();
    			//$(this) = button을 의미한다.
    			var operation =$(this).data("oper");
    			
    			console.log(operation);
    			
    			if(operation=='remove'){
    				formObj.attr("action", "/board/remove");
    			}else if(operation =='list'){
    				/* self.location ="/board/list";
    				return; */
    				formObj.attr("action","/board/list").attr("method","get");
    				formObj.empty();
    			}
    			formObj.submit();
    		});
    	});
    </script>
    
    
    <%@include file="../includes/footer.jsp"%>

     

     

    <form> 태그는 action 속성을 '/board/modify'로 지정했지만, 삭제를 하면 '/board/remove'와 같이 action의 속성의 내용을 수정해서 사용하게 된다. 

     

    이에 대한 처리는 script를 사용해서 했는데 form의 값을 가져와서 변수 var = formObj로 지정해둔 다음, 이번엔 버튼의 data-oper의 속성 값을 가져와 var operation에 저장한다. 그 값이 remove라면 action에 요소 값을 /board/remove로 지정해 삭제하게 만들고 그 값이 list라면 get 방식으로 action에 /board/list값을 준다. list의 이용을 아무런 파라미터가 없기 때문에 <form> 태그의 모든 내용은 삭제한 상태에서 submit을 진행한다. 

     

     

     

     

    게시물의 '제목', '내용'은 수정이 가능한 형태로 사용해서 사용자가 편집할 수 있게 한다. 등록일과 수정일은 나중에 BoardVO로 수집되어야 하므로 날짜 포맷을 맞춰야 한다. modify 버튼을 누르면 form 태그의 내용이 /board/modify url에 post 방식으로 전송되기에 Modal 창에 처리가 완료되었다는 문구가 뜨게 된다. 

     

     

    반응형

    댓글

Designed by Tistory.