[v3] springboot 블로그 만들기-7
제목 검색 구현 게시글 목록 조회 시 주로 사용되는 쿼리는
select * from board이다. 제목을 기준으로 검색을 하고 싶다면
select * from board where title like %?% 와 같은 쿼리를 사용할 수 있다.
Sep 10, 2024
제목 검색 및 게시글 목록 조회 구현
게시글 목록 조회 시 주로 사용되는 쿼리는
select * from board이다
제목을 기준으로 검색을 하고 싶다면
select * from board where title like %?% 와 같은 쿼리를 사용할 수 있다.
즉 title 을 파라미터로 주어 원하는 게시물을 조회할 수 있다.1. JPQL을 통한 검색 쿼리 작성
먼저 title 을 기준으로 한 검색을 위한 쿼리를 작성한다.
@Query("select b from Board b where b.title like %:title% order by b.id desc ")
List<Board> mFindAll(@Param("title") String title); like %:title% 은 매개변수인 title 의 앞이나 뒤에 무엇이 오든 해당 단어가 존재하면 모두 조회한다.
2. 검색 기능 추가를 위한 @RequestParam 사용
현재 코드
@GetMapping("/")
  public String list(HttpServletRequest request) {
      List<Board> boardList = boardService.게시글목록보기();
      request.setAttribute("models", boardList);
      return "board/list";
  } → localhost:8080?title=제목 
@GetMapping("/")
public String list(@RequestParam(name = "title") String title, HttpServletRequest request) {
    List<Board> boardList = boardService.게시글목록보기();
    request.setAttribute("models", boardList);
    return "board/list";
} 이렇게 하면 
title 이라는 파라미터 없이 요청을 하면 에러가 난다 localhost:8080:/ 요청이 불가해짐. 따라서 @GetMapping("/")
  public String list(@RequestParam(name = "title", required = false) String title, HttpServletRequest request) {
      List<Board> boardList = boardService.게시글목록보기();
      request.setAttribute("models", boardList);
      return "board/list";
  }  required = false 를 추가한다. (파라미터가 필수가 아니란의미)3. 검색어 유무에 따른 분기 처리
title 유무에 따라 일반 조회, 검색 조회로 나누어 코드를 작성한다.
public List<Board> 게시글목록보기(String title) { 
  if(title==null){
    Sort sort = Sort.by(Sort.Direction.DESC, "id");
    List<Board> boardList = boardRepository.findAll(sort);
    return boardList;
    }else {
     List<Board> boardList = boardRepository.mFindAll(title);
     return boardList;
    }
}4. 검색 바 구현 및 GET 요청 처리
이제 프론트 부분 검색 기능 구현을 한다.

get 요청으로 날아갈땐 queryString 이고 where 에 걸린다 구체적으로 질문할때 사용된다.
form 요청으로 보내기 때문에 button 은 타입을 제거하고 기본 속성인 
submit 을 갖게한다.결과


keyUp 을 사용 하여 동적으로 검색결과 출력하기
검색을 하면 버튼을 누르지 않아도 자동으로 결과가 출력되게 하는것,
keyUp 이벤트를 사용하여 구현해 볼 것이다.
1. keyup 테스트
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
</head>
<body>
    <input type="text" id="keyword">
    <script>
        let keyword = document.querySelector("#keyword");
        keyword.addEventListener("keyup", function (e) {
            console.log(e.target.value);
        })
    </script>
</body>
</html>addEventListener가 keyup 이벤트를 감지하여 입력되는 값을 감지한다.
2. 기존 코드에 keyup 이벤트 추가

조회된 데이터를 위의 양식과 동일하게 추가해야 하므로 
boardItem 을 만들어
템플릿, 데이터 매핑을 한다.// 1. 디자인에 데이터 랜더링
  function boardItem(board){
      return `     <div class="card mb-3">
          <div class="card-body">
              <h4 class="card-title mb-3">${board.title}</h4>
              <a href="/board/${board.id}" class="btn btn-primary">상세보기</a>
          </div>
      </div>`;
  }통신을 위한 
getBoardList 응답받은 데이터를 사용하여 위의 boardItem 을 호출한다.//2. 통신 + CSR
async function getBoardList(title){
    let response= await fetch(`/board?title=${title}`);
    let responseBody = await response.json();
    if(response.ok){
        $("#board-box").empty(); // remove 는 dom 제거, empty는 안에 있는 내용 제거 두개는 다름!
        let boardList = responseBody.body;
        for(board of boardList){
            let dom = boardItem(board);
            $("#board-box").append(dom);
        }
    }else {
        alert(responseBody.msg);
    }
}$("#title").on("keyup", function (e){
    // 1. 값 가져오기
    let title = e.target.value;
    // 2. fetch 요청
    getBoardList(title);
});3. 백로직
얘는 html 을 리턴 받지 않음 ( 비동기 통신이고 ccr 이기 때문에)
@GetMapping("/board")
public ResponseEntity<?> boardList(@RequestParam(name = "title", required = false) String title) {
    List<BoardResponse.DTO> boardList = boardService.게시글목록보기(title);
    return ResponseEntity.ok(Resp.ok(boardList));
}public List<BoardResponse.DTO> 게시글목록보기(String title) {
    List<BoardResponse.DTO> dtos = new ArrayList<>();
    List<Board> boardList = null;
    if(title == null){
        Sort sort = Sort.by(Sort.Direction.DESC, "id");
        boardList = boardRepository.findAll(sort);
    }else{
        boardList = boardRepository.mFindAll(title);
    }
    for(Board board : boardList){
        BoardResponse.DTO dto = new BoardResponse.DTO(board);
        dtos.add(dto);
    }
    return dtos;
}@Query("select b from Board b where b.title like %:title% order by b.id desc ")
List<Board> mFindAll(@Param("title") String title);결과

검색할 제목을 입력하면 성공적으로 결과를 반영하지만,
의미없는 글자까지 모두 감지되어서 패치 요청이 과하게 많이 가는 문제가 있다. 
(감자 검색하고 싶을 때 ㄱ, 감ㅈ 같은걸 패치 보낼 이유는 없음)
이럴때 
디바운스, 스로틀을 사용한다.
프로그래밍 기법중 하나로 둘 다 최적화를 위해 사용된다. (이 포스팅에선 다루지 않을것임)
다음 포스팅에서는 페이지네이션을 구현할 것이다.SpringBoot 블로그 만들기 - v3 시리즈 1. https://inblog.ai/hj/v3-시작-27809 (개발환경 설정 및 post 맨 이용한 api 테스트) 2. https://inblog.ai/hj/v3-springboot-블로그-만들기-2-28708 (댓글 엔티티 생성 및 양방향 매핑) 3. https://inblog.ai/hj/v3-springboot-블로그-만들기-3-28793 (댓글 조회하기 완료) 4. https://inblog.ai/hj/v3-rest-api-를-위한-exception-설정-28848 (REST API 위한 익셉션 핸들러 구현) 5. https://inblog.ai/hj/v3-springboot-블로그-만들기5-28859 (댓글 삭제 기능 구현) 6. https://inblog.ai/hj/v3-springboot-블로그-만들기6-29071 (ajax 를 사용한 댓글 작성) 7. https://inblog.ai/hj/v3-springboot-블로그-만들기7-29077 (게시물 검색 기능 구현) 8. https://inblog.ai/hj/v3-springboot-블로그-만들기8-29073 (유효성 검사)
Share article