본문으로 바로가기

검색 처리와 동적 SQL

category 스프링 2020. 7. 19. 18:11
  • 지난 페이징 처리에 이어서 페이징처리와 함께 쓰이는 검색처리를 작성해봤습니다.

  • 페이징 처리의 마지막 단계는 검색 조건과 검색 키워드를 입력하고, 이에 맞는 검색 데이터를 추출하여 화면에 출력해주는 작업입니다.

  • 검색 처리에서 가장 어려운 부분은 JSP단이 아닌 SQL문의 처리부분 입니다. 이를 위해서 MyBatis의 동적 SQL을 이용하여 처리할 것입니다.

검색에 필요한 데이터, SearchCriteria

  • 사용자가 게시물을 검색 후, 목록 페이지에서 필요한 데이터는 다음과 같습니다.

    1. 현재 페이지의 번호(page)
    2. 페이지당 보여지는 데이터의 수(perPageNum)
    3. 검색의 종류(searchType)
    4. 검색의 키워드(keyword)
  • 이 4개의 데이터를 전달하기 위한 객체를 만들어 줍니다.

    SearchCriteria.java

    public class SearchCriteria extends Criteria{
    
        private String searchType;
        private String keyword;
    
        public String getSearchType() {
            return searchType;
        }
        public void setSearchType(String searchType) {
            this.searchType = searchType;
        }
        public String getKeyword() {
            return keyword;
        }
        public void setKeyword(String keyword) {
            this.keyword = keyword;
        }
        @Override
        public String toString() {
            return super.toString() + " SearchCriteria "
                    + "[searchType=" + searchType + ", keyword="
                    + keyword + "]";
        }
    }

검색조건과 검색키워드를 전달하는 객체입니다.

Criteria를 상속받습니다.

검색조건(searchType)과 검색키워드(keyword)에 대한 링크를 처리하기 위한 PageMaker.java

  • 페이징 처리를 하기위한 PageMaker.java 에서의 검색조건과 키워드를 처리하는 메서드입니다.

    public String makeSearch(int page) {
    
        UriComponents uriComponents = 
        UriComponentsBuilder.newInstance()
        .queryParam("page", page)
        .queryParam("perPageNum", cri.getPerPageNum())
        .queryParam("searchType", ((SearchCriteria) cri).getSearchType())
        .queryParam("keyword", ((SearchCriteria) cri).getKeyword())
        .build();
    
        return uriComponents.toUriString();
    }

UriComponentBuilder 객체는 url 생성을 도와주어 페이징 처리에 도움을 줍니다.

?page=1&perPageNum=10&searchType=t&keyword=테스트 이런식으로 URL이 생성됩니다.

JSP에서의 페이징 처리

  • 화면단에서 페이징을 담당하는 부분을 pageMaker를 이용하여 처리합니다.

     <ul class="pagination">
     <c:if test="${pageMaker.prev}">
         <li>
             <a href="list${pageMaker.makeSearch(pageMaker.startPage - 1) }">&laquo;</a>
         </li>
     </c:if>
    
     <c:forEach begin="${pageMaker.startPage }" end="${pageMaker.endPage }" var="idx">
         <li <c:out value="${pageMaker.cri.page == idx?'class =active':''}"/>>
             <a href="list${pageMaker.makeSearch(idx)}">${idx}</a>
         </li>
     </c:forEach>
    
     <c:if test="${pageMaker.next && pageMaker.endPage > 0}">
         <li>
             <a href="list${pageMaker.makeSearch(pageMaker.endPage +1) }">&raquo;</a>
         </li>    
     </c:if>
     </ul>
    • 검색처리를 담당하는 부분은 자바스크립트로 작성해줍니다.

    <div class='box'>
       <div class="box-header with-border">
           <h3 class="box-title">Board List</h3>
       </div>
    <div class='box-body'>
    
       <select name="searchType">
           <option value="n"
       <c:out value="${cri.searchType == null?'selected':''}"/>>
       ---</option>
       <option value="t"
       <c:out value="${cri.searchType eq 't'?'selected':''}"/>>
       제목</option>
       <option value="c"
       <c:out value="${cri.searchType eq 'c'?'selected':''}"/>>
       내용</option>
       <option value="w"
       <c:out value="${cri.searchType eq 'w'?'selected':''}"/>>
       작성자</option>
       <option value="tc"
       <c:out value="${cri.searchType eq 'tc'?'selected':''}"/>>
       제목 OR 내용</option>
       <option value="cw"
       <c:out value="${cri.searchType eq 'cw'?'selected':''}"/>>
       내용 OR 작성자</option>
       <option value="tcw"
       <c:out value="${cri.searchType eq 'tcw'?'selected':''}"/>>
       제목 OR 내용 OR 작성자</option>
       </select> <input type="text" name='keyword' id="keywordInput"
       value='${cri.keyword }'>
       <button id='searchBtn'>Search</button>
       <button id='newBtn'>New Board</button>
       </div>
    </div>
    
    <script>
    $(document).ready(function() {
       $('#searchBtn').on("click",function(event) {
           str = "list"
                   + '${pageMaker.makeQuery(1)}'
                   + "&searchType="
                   + $("select option:selected").val()
                   + "&keyword="
                   + encodeURIComponent($('#keywordInput').val());
           alert(str);
           self.location = str;
       });
       $('#newBtn').on("click", function(evt) {
           self.location = "register";
       });
    });
    </script>

makeSearch() 메서드로 페이징링크에 대한 조작을 할수 있습니다.

MyBatis의 동적 SQL

  • 화면단에서의 검색조건에 따라 SQL문이 달라지기 때문에 이를 처리하기 위해 MyBatis의 동적 SQL을 활용할 것입니다.

  • XML Mapper

    <sql id="search">
     <if test="searchType != null" > 
       <if test="searchType == 't'.toString()">
         and title like CONCAT('%', #{keyword}, '%')
       </if>
       <if test="searchType == 'c'.toString()">
         and content like CONCAT('%', #{keyword}, '%')
       </if>
       <if test="searchType == 'w'.toString()">
         and writer like CONCAT('%', #{keyword}, '%')
       </if>     
       <if test="searchType == 'tc'.toString()">
         and ( title like CONCAT('%', #{keyword}, '%') OR content like CONCAT('%', #{keyword}, '%'))
       </if>        
       <if test="searchType == 'cw'.toString()">
         and ( content like CONCAT('%', #{keyword}, '%') OR writer like CONCAT('%', #{keyword}, '%'))
       </if>        
       <if test="searchType == 'tcw'.toString()">
         and (   title like CONCAT('%', #{keyword}, '%') 
               OR 
                 content like CONCAT('%', #{keyword}, '%') 
               OR 
                 writer like CONCAT('%', #{keyword}, '%'))
       </if>              
     </if>  
    </sql>
    
    <select id="listSearch" resultType="BoardVO">
        <![CDATA[  
           select * 
           from tbl_board 
           where bno > 0 
         ]]>  
     <include refid="search"></include>
        <![CDATA[    
           order by bno desc
           limit #{pageStart}, #{perPageNum}
         ]]>  
    </select>
    
    <select id="listSearchCount" resultType="int">
        <![CDATA[  
           select count(bno) 
           from tbl_board 
           where bno > 0 
         ]]>
        <include refid="search"></include>
    
    </select>

MyBatis의 SQL 문을 동일 처리하고 재사용할 수 있는 태그인 <sql>로 중복된 코드를 줄일 수 있습니다.

사용할 땐 <include>태그로 id값으로 불러오면 됩니다.

<if>태그로 파라미터값인 검색조건(searchType) 에 따라 실행되는 SQL 구문이 달라지게 했습니다.

화면과 DB의 처리가 끝났으니 컨트롤러와 DAO 를 작성해줍니다

  • Controller

    public class SearchBoardController {
    
        private static final Logger logger = LoggerFactory.getLogger(SearchBoardController.class);
    
        @Inject
        private BoardService service;
    
        @RequestMapping(value = "/list", method = RequestMethod.GET)
        public void listPage(@ModelAttribute("cri") SearchCriteria scri, Model model) throws Exception {
    
            logger.info(scri.toString());
    
            model.addAttribute("list", service.listSearchCriteria(scri));
            PageMaker pageMaker = new PageMaker();
            pageMaker.setCri(scri);
            pageMaker.setTotalCount(service.listSearchCount(scri));
    
            logger.info(pageMaker.makeSearch(scri.getPage()));
    
            model.addAttribute("pageMaker", pageMaker);
        }
    }
  • DAO

    @Repository
    public class BoardDAOImpl implements BoardDAO{
        @Inject
        private SqlSession session;
    
        @Override
        public List<BoardVO> listSearch(SearchCriteria cri) throws Exception {
            return session.selectList(namespace + ".listSearch", cri);
        }
    
        @Override
        public int listSearchCount(SearchCriteria cri) throws Exception {
            return session.selectOne(namespace + ".listSearchCount", cri);
        }
    
    }

'스프링' 카테고리의 다른 글

게시글의 페이징 처리  (0) 2020.06.26
스프링의 AOP 란?  (0) 2020.06.23
스프링의 @ModelAttribute 어노테이션  (0) 2020.06.15
AJAX란?  (0) 2020.06.15
인터셉터(Interceptor)란?  (0) 2020.06.13