Part 15. 검색 처리

15.4 화면에서 검색 조건 처리

  • 화면에서 검색은 다음과 같은 사항들을 주의해서 개발해야 한다.
    • 페이지 번호가 파라미터로 유지되었던 것처럼 검색 조건과 키워드 역시 항상 화면 이동 시 같이 전송되어야 한다.
    • 화면에서 검색 버튼을 클릭하면 새로 검색을 한다는 의미이므로 1페이지로 이동한다.
    • 한글의 경우 GET 방식으로 이동하는 경우 문제가 생길 수 있으므로 주의해야 한다.

15.4.1 목록 화면에서의 검색 처리

  • 목록 화면인 list.jsp에서는 검색 조건과 키워드가 들어갈 수 있게 HTML을 수정해야 한다.
  • views 폴더 내의 list.jspp를 수정해 페이지 처리 바로 위쪽에 아래의 내용들을 추가한다.
< list.jsp에 검색 조건 처리 >
</c:forEach>
				</table><!-- table 태그의 끝 -->
							<div class='row'>
								<div class="col-lg-12">
									<form id='searchForm' action="/board/list" method='get'>
										<select name='type'>
											<option value="">--</option>
											<option value="T"> 제목</option>
											<option value="C"> 내용</option>
											<option value="W"> 작성자</option>
											<option value="TC"> 재목 or 내용</option>
											<option value="TW"> 제목 or 작성자</option>
											<option value="TWC"> 제목 or 내용 or 작성자</option>
										</select>
										<input type='text' name='keyword' />
										<input type='hidden' name='pageNum' value='${pageMaker.cri.pageNum }'>
										<input type='hidden' name='amount' value='${pageMaker.cri.amount }'>
										<button class='btn btn-default'>Search</button>
									</form>
								</div>
							</div>
  • 화면에서는 다음과 같은 모습으로 보여지게 된다.
  • 수정된 HTML을 보면 페이징 처리를 위해 만들어둔 < form > 태그에 < select> 와 < input > 태그가 추가된 것을 볼 수 있다.
  • < form > 내 < button >의 기본 동작은 submit이므로 별도의 처리 없이 검색이 되는지 확인한다.
  • 항상 테스트는 영문과 한글을 모두 테스트해야 한다.
  • Chrome 브라우저는 한글로 검색하는 경우에 주소창에는 한글이 깨지지 않고 나오지만 실제로는 그림에 있는 박스의 내용물처럼 전송된다.
  • IE에서는 박스의 내용과 동일하게 출력되는 것을 볼 수 있다.
  • 검색이 처리된 후에는 몇 가지 문제가 있다는 사실을 알게 되는데 1) 예를들어, 3페이지를 보다가 검색을 하면 3페이지로 이동하는 문제, 2) 검색 후 페이지를 이동하면 검색 조건이 사라지는 문제, 3) 검색 후 화면에서는 어떤 검색 조건과 키워드를 이용했는지 알 수 없는 문제들이 남이있는 것을 볼 수 있다.

검색 버튼의 이벤트 처리

  • 여러 문제들 중에서 검색 버튼을 클릭하면 검색은 1페이지를 하도록 수정하고, 화면에 검색 조건과 키워드가 보이게 처리하는 작업을 우선으로 진행한다.
< list.jsp의 검색 버튼의 이벤트 처리 >
var searchForm = $("#searchForm");
$("#searchForm button").on("click", function(e){
	if(!searchForm.find("option:selected").val()){
	alert("검색종류를 선택하세요");
	return false;
	}
	if(!searchForm.find("input[name='keyword']").val()){
	alert("키워드를 입력하세요");
	return false;
	}
	searchForm.find("input[name='pageNum']").val("1");
	e.preventDefault();
	searchForm.submit();
	});
  • 브라우저에서 검색 버튼을 클릭하면 < form > 태그의 전송은 막고, 페이지의 번호는 1이 되도록 처리한다.
  • 화면에서 키워드가 없다면 검색을 하지 않도록 제어한다.
  • 검색 후에는 주소창에 검색 조건과 검색 키워드가 같이 GET 방식으로 처리되므로 이를 이용해 < select > 태그나 < input > 태그의 내용을 수정한다.
< list.jsp에서 검색 조건과 키워드 보여주는 부분 >
<form id='searchForm' action="/board/list" method='get'>
	<select name='type'>
		<option value=""
			<c:out value="${pageMaker.cri.type == null? 'selected':''}"/>>--</option>									<option value="T"
			<c:out value="${pageMaker.cri.type eq 'T'?'selected':''}"/>>제목</option>									<option value="C"
			<c:out value="${pageMaker.cri.type eq 'C'?' 'selected':''}"/>>내용</option>									<option value="W"
			<c:out value="${pageMaker.cri.type eq 'W'?' 'selected':''}"/>>작성자</option>									<option value="TC"
			<c:out value="${pageMaker.cri.type eq 'TC'?' 'selected':''}"/>>제목 or 내용</option>							<option value="TW"
			<c:out value="${pageMaker.cri.type eq 'TW'?' 'selected':''}"/>>제목 or 작성자</option>							<option value="TWC"
			<c:out value="${pageMaker.cri.type eq 'TWC'?' 'selected':''}"/>>제목 or 내용 or 작성자</option>				</select> 
			<input type='text' name='keyword' value='<c:out value="${pageMaker.cri.keyword }" />' /> 
			<input type='hidden' name='pageNum' value='<c:out value="${pageMaker.cri.pageNum }" /> ' />
			<input type='hidden' name='amount' value='<c:out value="${pageMaker.cri.amount }" /> ' />
	<button class='btn btn-default'>Search</button>
</form>
  • < select > 태그 내부는 삼항 연산자를 이용해 해당 조건으로 검색되었다면 'selected'라는 문자열을 출력하게 해서 화면에서 선택된 항목으로 보이도록 한다.
  • 페이지 번호를 클릭해서 이동할 때에도 검색 조건과 키워드는 같이 전달되어야 하므로 페이지 이동에 사용한 < form > 태그를 아래와 같이 수정한다.
< list.jsp >
		<form id='actionForm' action="/board/list" method='get'>
			<input type='hidden' name='pageNum' value='${pageMaker.cri.pageNum}'>
			<input type='hidden' name='amount' value='${pageMaker.cri.amount}'>
			<input type='hidden' name='type' value='<c:out value="${ pageMaker.cri.type }" />'>
			<input type='hidden' name='keyword' value='<c:out value="${ pageMaker.cri.keyword }" />'>
		</form>
  • 검색 조건과 키워드에 대한 처리가 되면 검색 후 페이지를 이동해서 동일한 검색 사항들이 계속 유지되는 것을 볼 수 있다.

15.4.2 조회 페이지에서 검색 처리

  • 목록 페이지에서 조회 페이지로의 이동은 이미 < form > 태그를 이용해 처리했기 때문에 별도의 처리가 필요하지 않다.
  • 다만 조회 페이지는 아직 Criteria의 type과 keyword에 대한 처리가 없기 때문에 이 부분을 수정해 줘야 한다.
< views/board/get.jsp >
<form id='operForm' action="/boad/modify" method="get">
	<input type='hidden' id='bno' name='bno' value='<c:out value="${board.bno}"/>'>
	<input type='hidden' name='pageNum' value='<c:out value="${cri.pageNum}"/>'>
	<input type='hidden' name='amount' value='<c:out value="${cri.amount}"/>'>
	<input type='hidden' name='keyword' value='<c:out value="${cri.keyword}"/>'>
	<input type='hidden' name='type' value='<c:out value="${cri.type}"/>'>
</form>

15.4.3 수정/삭제 페이지에서 검색 처리

  • 조회 페이지에서 수정/삭제 페이지로의 이동은 GET 방식을 통해 이동하고, 이동 방식 역시 < form > 태그를 이용하는 방식이므로 기존의 < form > 태그에 추가적인 type과 keyword 조건만을 추가한다.
< views/board/modify.jsp >
<form role="form" action="/board/modify" method="post">
	<input type='hidden' name='pageNum' value='<c:out value="${cri.pageNum }"/>'>
	<input type='hidden' name='amount' value='<c:out value="${cri.amount }"/>'>
	<input type='hidden' name='type' value='<c:out value="${cri.type }"/>'>
	<input type='hidden' name='keyword' value='<c:out value="${cri.keyword }"/>'>
  • 수정/삭제 처리는 BoardController에서 redirect 방식으로 동작하므로 type과 keyword 조건을 같이 리다이렉트 시에 포함시켜야만 한다.
< org.zerock.controller.BoardController >
// 게시글 수정
	@PostMapping("/modify")
	public String modify(BoardVO board, @ModelAttribute("cri") Criteria cri, RedirectAttributes rttr) {
		log.info("modify: " + board);
		if(service.modify(board)) {
			rttr.addFlashAttribute("result", "success");
		}
		rttr.addAttribute("pageNum", cri.getPageNum());
		rttr.addAttribute("amount", cri.getAmount());
		rttr.addAttribute("type", cri.getType());
		rttr.addAttribute("keyword", cri.getKeyword());
		return "redirect:/board/list";
	}
	// 게시글 삭제
	@PostMapping("/remove")
	public String remove(@RequestParam("bno") Long bno, @ModelAttribute("cri") Criteria cri, RedirectAttributes rttr) {
		log.info("remove..." + bno);
		if(service.remove(bno)) {
			rttr.addFlashAttribute("result", "success");
		}
		rttr.addAttribute("pageNum", cri.getPageNum());
		rttr.addAttribute("amount", cri.getAmount());
		rttr.addAttribute("type", cri.getType());
		rttr.addAttribute("keyword", cri.getKeyword());
		return "redirect:/board/list";
	}
  • 리다이렉트는 GET 방식으로 이루어지기 때문에 추가적인 파라미터를 처리해야 한다.
  • modify.jsp에서는 다시 목록으로 이동하는 경우에 필요한 파라미터만 전송하기 위해서 < form > 태그의 모든 내용을 지우고 다시 추가하는 방식을 이용했으므로 keyword와 type 역시 추가하도록 아래와 같이 관련된 JavaScript 코드를 수정해야 한다.
< 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");
 							var pageNumTag = $("input[name='pageNum']").clone();
 							var amountTag = $("input[name='amount']").clone();
 							var keywordTag = $("input[name='keyword']").clone();
 							var typeTag = $("input[name='type']").clone();
 							formObj.empty();
 							formObj.append(pageNumTag);
 							formObj.append(amountTag);
 							formObj.append(keywordTag);
 							formObj.append(typeTag);
 					}
 					formObj.submit();
 				});
 			});
</script>
  • 수정/조회 화면에서 어떤 작업을 하던지 다시 목록 페이지로 검색 조건이 유지되는지 확인해야 한다.
  • 검색한 상태에서 특정 페이지의 게시물을 수정하면 검색 조건은 유지한 채 목록 페이지로 이동하는지를 테스트한다.

UriComponentsBuiler를 이용하는 링크 생성

  • 웹페이지에서 매번 파라미터를 유지하는 일이 번거롭고 힘들다면 한 번쯤 UriComponentsBuilder라는 클래스를 이용해 볼 필요가 있다.
  • org.springframework.web.util.UriComponentsBuilder는 여러 개의 파라미터들을 연결해서 URL의 형태로 만들어 주는 기능을 가지고 있다.
  • URL을 만들어주면 리다이렉트를 하거나, < form > 태그를 사용하는 상황을 많이 줄여줄 수 있다.
  • 검색 조건을 유지하는 org.zerock.domain.Criteria 클래스에 링크를 생성하는 기능을 추가한다.
< Criteria 클래스 >
	// 링크 생성 기능 추가
	public String getListLink() {
		UriComponentsBuilder builder = UriComponentsBuilder.fromPath("")
				.queryParam("pageNum", this.pageNum)
				.queryParam("amount", this.getAmount())
				.queryParam("type", this.getType())
				.queryParam("keyword", this.getKeyword());
		return builder.toUriString();
	}
  • UriComponentsBuilder는 queryParam()이라는 메서드를 이용해 필요한 파라미터들을 손쉽게 추가할 수 있다.
  • 예를 들어, 아래와 같은 조건들로 Criteria가 생성된다고 가정해 보면
Criteria cri = new Criteria();
	cri.setPageNum(3);
    cri.setAmount(20);
    cri.setKeyword("새로");
    cri.setType("TC");
  • 위와 같은 데이터를 가진 Criteria의 getListLink()의 결과는 '?pageNum=3&amount=20&type=TC&keyword=%EC%83%88%EB%A1%9C'와 같이 GET 방식에 적합한 URL 인코딩된 결과로 만들어진다(가장 편리한 점은 한글 처리에 신경 쓰지 않아도 된다는 점이다.).
  • getLisLink()를 이용하면 BoardController의 modify()와 remove()를 다음과 같이 간단하게 정리할 수 있다.
< org.zerock.controller.BoardController >
	// 게시글 수정
	@PostMapping("/modify")
	public String modify(BoardVO board, @ModelAttribute("cri") Criteria cri, RedirectAttributes rttr) {
		log.info("modify: " + board);
		if(service.modify(board)) {
			rttr.addFlashAttribute("result", "success");
		}
		return "redirect:/board/list" + cri.getListLink();
	}
	// 게시글 삭제
	@PostMapping("/remove")
	public String remove(@RequestParam("bno") Long bno, @ModelAttribute("cri") Criteria cri, RedirectAttributes rttr) {
		log.info("remove..." + bno);
		if(service.remove(bno)) {
			rttr.addFlashAttribute("result", "success");
		}
		return "redirect:/board/list" + cri.getListLink();
	}
  • UriComponentsBuilder로 생성된 URL은 화면에서도 유용하게 사용될 수 있는데, 주로 JavaScript를 사용할 수 없는 상황에서 링크를 처리해야 하는 상황에서 사용된다.
profile
한 걸음 한 걸음 나아가는 개발자

0개의 댓글