피곤핑
코딩일탈
피곤핑
전체 방문자
오늘
어제
  • 분류 전체보기
    • Kotlin & Java
    • Spring
      • Spring Security
      • Spring
    • 네트워크
    • JavaScript & Node js
    • Docker
    • Python3
    • Unity
    • 딥러닝
    • 객체지향프로그래밍
    • Error 보고서
    • 나의 이야기 & 회고
    • HTML & CSS
    • Archive
    • 독서

블로그 메뉴

  • 홈
  • 방명록

공지사항

인기 글

태그

  • 99클럽
  • 오블완
  • TiL
  • 코딩테스트준비
  • JavaScript
  • 항해99
  • 티스토리챌린지
  • nodejs
  • 개발자취업
  • Client

최근 댓글

hELLO · Designed By 정상우.
피곤핑

코딩일탈

[15] 기본적인 웹 게시물 관리 - 화면 처리 2
Spring/Spring

[15] 기본적인 웹 게시물 관리 - 화면 처리 2

2019. 12. 20. 12:56

 

 

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 prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/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>
			<!-- end panel-body -->
		</div>
		<!-- end panel-body -->
	</div>
	<!-- end panel -->
</div>
<!-- /.row -->
<%@include file="../includes/footer.jsp"%>

 

register 페이지에서는 <form> 태그를 이용해서 필요한 데이터를 전송한다. <input>이나 <textarea> 태그의 name 속성은 BoardVO 클래스의 변수와 일치시켜 준다.

 

브라우저를 통해 'board/register' 화면이 제대로 출력되는지 확인한다.

 

화면이 정상적으로 보인다면 입력 항목을 넣어서 새로운 게시물이 등록되는지를 확인한다. BoardController의 POST 방식으로 동작하는 register()는 redirect 시키는 방식을 이용하므로, 게시물의 등록 후에는 다시 '/board/list' 로 이동하게 된다. 

 

게시물의 등록은 정상적으로 이루어지지만 한글이 깨지는 문제가 발생한다. 

 

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

 

새로운 게시물을 등록했을 때 만일 한글 입력에 문제가 있다는 것을 발견했다면

1) 브라우저에서 한글이 깨져서 전송되는지 확인

2) 문제가 없다면, 스프링 MVC 쪽에서 한글을 처리하는 필터를 등록

 

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

 

 

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

 

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

 

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

 

이 문제를 해결하기 위해 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>

한글에 대한 처리가 끝난 후 다시 게시물을 작성해 보면 한글에 문제가 없이 입력되는 것을 확인할 수 있다.

 

3-2. 재전송 처리

 

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

 

BoardController 에서 register() 메서드는 'redirect:/board/list'를 전송하는데 브라우저는 이를 통보받은 후 '/board/list'로 이동하게 된다. 만일 위와 같이 재전송을 하지 않는다면 사용자는 브라우저의 '새로고침'을 통해서 동일한 내용을 계속 서버에 등록할 수 있기 때문에 (흔히 도배라고 표현하는) 문제가 발생하게 된다. 브라우저에서는 이런 경우 경고창을 보여주기는 하지만 근본적으로 차단하지는 않는다. 

 

따라서 등록,수정,삭제 작업은 처리가 완료된 후 다시 동일한 내용을 전송할 수 없도록 아예 브라우저의 URL을 이동하는 방식을 이용한다. 이러한 과정에서 하나 더 신경써야 하는 것은 브라우저에 등록, 수정, 삭제의 결과를 바로 알 수 있게 피드백을 줘야 한다는 점이다. 경고창이나 <div>를 이용하는 모달창을 이용해서 이러한 작업을 처리한다. 

 

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

 

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

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

 

새로운 게시물의 번호는 addFlashAttribute()로 저장되었기 때문에 한 번도 사용된 적이 없다면 값을 만들어 내지만 사용자가 '/board/list'를 호출하거나, '새로고침'을 통해서 호출하는 경우는 아무런 내용이 없게 된다.

 

addFlashAttribute()를 이용해서 일회성으로만 데이터를 사용할 수 있으므로 이를 이용해서 경고창이나 모달창 등을 보여주는 방식으로 처리할 수 있다.

 

3-3. 모달창 보여주기

 

최근에는 브라우저에서 경고창을 띄우는 방식보다 모달창을 보여주는 방식을 많이 사용합니다. BootStrap은 모달창을 간단하게 사용할 수 있으므로 목록 화면에서 필요한 메시지를 보여주는 방법을 사용해 보자.

 

모달창은 기본적으로 <div>를 화면에 특정 위치에 보여주고, 배경이 되는 <div>에 배경색을 입혀서 처리한다. 모달창은 활성화된 <div>를 선택하지 않고는 다시 원래의 화면을 볼 수 없도록 막기 때문에 메시지를 보여주는데 효과적인 방식이다. 모달창에 대한 코드는 다운로드한 SBAdmin2의 폴더 내 notifications.html 파일을 참고하면 된다. 

 

모달창을 처리하기 위해서는 우선 <div>를 이용해서 페이지의 코드에 추가해야 한다. list.jsp 내에 <table> 태그의 아래쪽에 모달창의 <div>를 추가한다. 

          	<!-- 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를 이용해서 처리할 수 있다.

            <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로 게시물의 번호가 전송되므로 이를 이용해서 모달창의 내용을 수정한다. $("#modal").modal('show')를 호출하면 모달창이 보이게 된다.

 

이제 'board/register'를 이용해서 새로운 게시물을 작성하고 나면 자동으로 'board/list'로 이동하며 모달창이 보이게 된다. 

 

띠용..

 

해결! (-> 방법은 error 보고서 카테고리에!)

 

 

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

 

게시물의 작성과 목록 페이지로 이동이 정상적으로 동작했다면, 마지막으로 목록 페이지 상단에 버튼을 추가해서 등록 작업을 시작할 수 있게 처리하는 것이다. 

 

우선은 list.jsp 의 HTML 구조를 아래와 같이 수정한다.

(panel-heading의 div 태그를 닫기전 버튼을 추가!)

 

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

            <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){
            				console.log("들어옴?");
            				$(".modal-body").html("게시글" + parseInt(result) + " 번이 등록되었습니다.");
            			}
            	
            			$("#myModal").modal("show");
            		}
        		    
        		    $("#regBtn").on("click", function(){
        		    	self.location = "/board/register";
        		    });
            	});
            </script>

 

화면에서 'Register New Board' 버튼을 클릭하면 게시물의 등록 페이지로 이동할 수 있다.

(그런데 나는 왜 버튼이 진하게 표시되지않지???..)

 

4. 조회 페이지와 이동

 

게시물의 등록과 리스트 처리가 끝났다면 가장 중요한 틀은 완성되었다고 볼 수 있다. 다음으로는 목록 페이지에서 링크를 통해서 GET 방식으로 특정한 번호의 게시물을 조회할 수 있는 기능을 작성한다. 

 

4-1. 조회 페이지 작성

 

조회 페이지는 입력 페이지와 거의 유사하지만 게시물 번호가 출력된다는 점과 모든 데이터가 읽기 전용으로 처리된다는 점이 가장 큰 차이다.

 

게시물의 조회는 BoardController에서 get() 메서드로 구성되어 있다.

	@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> 태그는 조회 페이지에서는 그다지 필요하지 않으므로 제거하는 대신 마지막에는 수정/삭제 페이지로 이동하거나 원래의 목록 페이지로 이동할 수 있는 버튼을 추가한다. 

 

브라우저에서는 '/board/get?bno=1' 과 같이 게시물의 번호를 반드시 파라미터로 전달해야 한다. 파라미터로 전달하는 bno 값이 존재한다면 아래와 같은 페이지를 보게된다. 

 

화면 하단의 버튼은 '/board/list'와 '/board/modify?bno=xx'와 같이 이동하는 링크를 추가한다.

					<button data-oper='modiyfy'
						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>

 

'Spring > Spring' 카테고리의 다른 글

[SpringDataJPA] @Modifying 과 @Query 의 관계와 동작방식 (@Query 없이 @Modifying 만 사용한다면 어떻게 될까?)  (1) 2023.12.28
[SpringCache] 쪼금의 개념 설명과 Caffeine maximumSize 옵션 테스트  (2) 2023.11.01
[14] 기본적인 웹 게시물 관리 - 화면 처리  (0) 2019.12.19
[13] 기본적인 웹 게시물 관리 - 프레젠테이션 계층의 CRUD 구현  (1) 2019.12.19
[12] 기본적인 웹 게시물 관리 - 비즈니스 계층  (0) 2019.12.19
    'Spring/Spring' 카테고리의 다른 글
    • [SpringDataJPA] @Modifying 과 @Query 의 관계와 동작방식 (@Query 없이 @Modifying 만 사용한다면 어떻게 될까?)
    • [SpringCache] 쪼금의 개념 설명과 Caffeine maximumSize 옵션 테스트
    • [14] 기본적인 웹 게시물 관리 - 화면 처리
    • [13] 기본적인 웹 게시물 관리 - 프레젠테이션 계층의 CRUD 구현
    피곤핑
    피곤핑

    티스토리툴바