1.  검색 조건을 위한 화면 처리

검색 기능은 /WEB-INF/view/todo/list.jsp에서 이루어지므로 list.jsp에 검색 관련된 화면을 작성하기 위해서 <div class="card">를 하나 추가하고 검색에 필요한 내용들을 담을 수 있도록 구성.

 <div class="row content">
    <div class="col">
        <div class="card">
            <div class="card-body">
                <h5 class="card-title">Search </h5>
                <form action="/todo/list" method="get">
                    <input type="hidden" name="size" value="${pageRequestDTO.size}">
                    <div class="mb-3">
                        <input type="checkbox" name="finished">완료여부
                    </div>
                    <div class="mb-3">
                        <input type="checkbox" name="types" value="t">제목
                        <input type="checkbox" name="types" value="w">작성자
                        <input type="text"  name="keyword" class="form-control">
                    </div>
                    <div class="input-group mb-3 dueDateDiv">
                        <input type="date" name="from" class="form-control">
                        <input type="date" name="to" class="form-control">
                    </div>
                    <div class="input-group mb-3">
                        <div class="float-end">
                             <button class="btn btn-primary" type="submit">Search</button>
                             <button class="btn btn-info clearBtn" type="reset">Clear</button>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

 

 

  ✓  화면에는 <form> 태그를 이용해서 검색 조건을 전송할 수 있도록 구성
  ✓  검색을 하는 경우 무조건 페이지는 1페이지가 되므로 별도의 파라미터를 지정하지 않음
 

TodoController에서는 log.info()를 이용해서 파라미터가 정상적으로 수집되는지 확인

 

  ✓  브라우저의 주소창에는 모든 검색 조건이 GET 방식의 쿼리 스트링으로 만들어짐


 

1) 화면에 검색 조건 표시하기


검색이 처리되기는 하지만 PageRequestDTO의 정보를 EL로 처리하지 않았기 때문에 검색 후에는 검색 부분이 초기화되는 문제가 있음
  ➡️  다른 페이지로 이동을 하면 검색값이 유지가 안됨
작성된 <div>에 EL을 적용할 때 가장 문제가 되는 부분은 제목 title, 작성자 writer를 배열로 처리하고 있으므로 화면에서 처리 할 때 좀 더 편하게 사용하기 위하여 PageRequestDTO에 별도의 메서드를 구성하도록 함

 public boolean checkType(String type) {
        if(this.types == null || this.types.length == 0) {
            return false;
        }
        return Arrays.asList(this.types).contains(type);
    }

 

 

list.jsp 코드 수정
 <form action="/todo/list" method="get">
    <input type="hidden" name="size" value="${pageRequestDTO.size}">
        <div class="mb-3">
            <input type="checkbox" name="finished"${pageRequestDTO.finished ? " checked" : ""}>완료여부
        </div>
        <div class="mb-3">
            <input type="checkbox" name="types" value="t"${pageRequestDTO.checkType("t") ? " checked" : ""}>제목
            <input type="checkbox" name="types" value="w"${pageRequestDTO.checkType("w") ? " checked" : ""}>작성자
            <input type="text"  name="keyword" class="form-control" value="${pageRequestDTO.keyword}">
        </div>
        <div class="input-group mb-3 dueDateDiv">
            <input type="date" name="from" class="form-control" value="${pageRequestDTO.from}">
            <input type="date" name="to" class="form-control" value="${pageRequestDTO.to}">
        </div>
        <div class="input-group mb-3">
            <div class="float-end">
                <button class="btn btn-primary" type="submit">Search</button>
                <button class="btn btn-info clearBtn" type="reset">Clear</button>
            </div>
        </div>    
</form>


 

2) 검색 조건 초기화 시키기


검색 영역에 Clear 버튼을 누르면 모든 검색 조건을 무효화 시키도록 '/todo/list'를 호출하도록 수정

 

화면의 버튼에 'clearBtn'이라는 class 속성을 추가
 <div class="input-group mb-3">
    <div class="float-end">
        <button class="btn btn-primary" type="submit">Search</button>
        <button class="btn btn-info clearBtn" type="reset">Clear</button>
    </div>
</div>
<script>
document.querySelector('.clearBtn').addEventListener('click', function (e) {
    self.location = '/todo/list';
});
</script>

 


 

3) 조회를 위한 링크 처리


검색 기능이 추가되면 문제 되는 것은 조회나 수정 화면에 있는 'List' 버튼. 기존과 달리 검색 조건들을 그대로 유지해야 하므로 상당히 복잡한 처리가 필요. 하지만 PageRequestDTO의 getLink()를 이용하면 상대적으로 간단히 처리 가능

 

getLink()를 통해서 생성되는 링크에서 검색 조건등을 반영해 주도록 수정
public String getLink() {
            StringBuilder builder = new StringBuilder();
            builder.append("page=" + this.page);
            builder.append("&size=" + this.size);
            if(this.finished){
                builder.append("&finished=on");
            }

            if(this.types != null && this.types.length > 0){
                for (int i = 0; i < this.types.length; i++) {
                    builder.append("&types=" + types[i]);
                }
            }

            if(this.keyword != null){
                try {
                    builder.append("&keyword=" + URLEncoder.encode(keyword, "UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }

            if (this.from != null) {
                builder.append("&from=" + from.toString());
            }

            if (this.to != null) {
                builder.append("&to=" + to.toString());
            }

            return builder.toString();
    }

 

getLink()는 모든 검색 / 필터링 조건을 쿼리 스트링으로 구성해야 한다. 그렇지 않으면 화면에서 모든 링크를 수정해야 하기 때문에 더 복잡하게 되기 때문. 주의할 부분은 한글이 가능한 keyword 부분은 URLEncoder를 이용해서 링크로 처리할 수 있도록 처리해야 됨

 

getLink()가 수정되면 화면에서 링크가 반영되는것 확인
목록 -> 상세 -> 수정 페이지 이동시에 쿼리 스트링이 반영되는 것 확인


 

4)  페이지 이동 링크 처리


페이지 이동에도 검색 / 필터링 조건은 필요하므로 자바스크립트로 동작하는 부분을 수정해야 함
기존에는 자바 스크립트에서 직접 쿼리 스트링을 추가해서 구성했지만, 검색 / 필터링 부분에 name이 page인 부분만 추가해서 <form> 태그를 submit으로 처리해 주면 검색 / 필터링 조건을 유지하면서도 페이지 번호만 변경하는 것이 가능

  document.querySelector(`.pagination`).addEventListener('click', function (e) {
                                    e.preventDefault();
                                    e.stopPropagation(); //이벤트가 상위 엘리먼트에 전달되지 않게 막아 준다

                                    const target = e.target;
                                    if(target.tagName !== 'A') {
                                        return;
                                    }
                                    const num = target.getAttribute('data-num');
                                    const frmPage = document.querySelector('form');
                                    frmPage.innerHTML += `<input type="hidden" name="page" value="\${num}">`;
                                    frmPage.submit();

                                    //self.location = `/todo/list?page=\${num}`;
                                });

 

검색/ 필터링을 유지하면서 페이지 이동하는 것을 확인

 


 

5) 수정 화면에서의 링크 처리


수정 화면인 modify.jsp에는 Remove, Modify, List 버튼이 존재하고 각 버튼에 대한 클릭 이벤트 수정

 

A. List 버튼 처리


PageRequestDTO의 getLink()를 이용해서 처리

document.querySelector('.btn-secondary').addEventListener('click', function (e) {
    self.location = `/todo/list?${pageRequestDTO.link}`;
});

 

B. Remove 버튼 처리


삭제된 후에 1페이지로 이동해도 문제가 되지 않지만 삭제 후에도 기존 페이지와 검색 / 필터링 정보를 유지하고 싶다면 PageRequestDTO를 이용할 수 있음

const frmModify = document.querySelector('form');
document.querySelector('.btn-danger').addEventListener('click', function () {
    frmModify.action = '/todo/remove';
    frmModify.method = 'post';
    frmModify.submit();
});

 

TodoController에서는 remove() 메서드가 이미 PageRequestDTO를 파라미터로 받고 있기 때문에 삭제 처리를 하고나서 리다이렉트하는 경로에 getLink()의 결과를 반영하도록 수정

  @PostMapping("/remove")
    public String remove(Long tno, PageRequestDTO pageRequestDTO , RedirectAttributes redirectAttributes) {
        log.info("-----remove----");
        log.info("tno: " + tno);

        todoService.remove(tno);
        redirectAttributes.addAttribute("page", 1);
        redirectAttributes.addAttribute("size", pageRequestDTO.getSize());
        return "redirect:/todo/list" + pageRequestDTO.getLink();
    }

 

 

C. Modify 버튼 처리


기존과 다른 처리가 필요한데, 검색 / 필터링 기능이 추가되면 Todo의 내용이 수정되면서 검색 / 필터링 조건에 맞지 않게 될 수 있기 때문.
  ➡️  예를 들어 검색 / 필터링에 날짜로 검색했는데 날짜를 수정하면서 검색 / 필터링 조건에 맞지 않아서 목록에 나오지 않을 수 있음

 

따라서 안전하게 하려면 검색 / 필터링의 경우 수정한 후에 조회 페이지로 이동하게 하고, 검색 / 필터링 조건은 없애는 것이 안전
검색 / 필터링 조건을 유지하지 않는다면 modify.jsp 에 선언된 <input type="hidden"> 태그의 내용은 필요하지 않으므로 삭제

 <form action="/todo/modify" method="post">
    <%-- <input type="hidden" name="page" value="${pageRequestDTO.page}">
         <input type="hidden" name="size" value="${pageRequestDTO.size}"> --%>
         <div class="input-group mb-3">
             <span class="input-group-text">Tno</span>
             <input type="text" name="tno" class="form-control" value="${dto.tno}" readonly>
         </div>

 

TodoController에서는 '/todo/list'가 아닌 '/todo/read'로 이동하도록 수정
  @PostMapping("/modify")
    public String midify(PageRequestDTO pageRequestDTO,
                         @Valid TodoDTO todoDTO,
                         BindingResult bindingResult,
                         RedirectAttributes redirectAttributes) {

        // 유효성 검사를 통과하지 못했더라도 값을 전송
        //redirectAttributes.addAttribute("page", pageRequestDTO.getPage());
        //redirectAttributes.addAttribute("size", pageRequestDTO.getSize());

        if (bindingResult.hasErrors()) {
            log.info("has error");
            redirectAttributes.addFlashAttribute("errors", bindingResult.getAllErrors());
            redirectAttributes.addAttribute("tno", todoDTO.getTno());
            return "redirect:/todo/modify";
        }
        redirectAttributes.addAttribute("tno", todoDTO.getTno());
        return "redirect:/todo/read";
    }

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]

+ Recent posts