[v3] springboot 블로그 만들기-7

제목 검색 구현 게시글 목록 조회 시 주로 사용되는 쿼리는 select * from board이다. 제목을 기준으로 검색을 하고 싶다면 select * from board where title like %?% 와 같은 쿼리를 사용할 수 있다.
HootJem's avatar
Sep 10, 2024
[v3] springboot 블로그 만들기-7

제목 검색 및 게시글 목록 조회 구현

게시글 목록 조회 시 주로 사용되는 쿼리는 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 요청 처리

이제 프론트 부분 검색 기능 구현을 한다.
notion image
get 요청으로 날아갈땐 queryString 이고 where 에 걸린다 구체적으로 질문할때 사용된다.
form 요청으로 보내기 때문에 button 은 타입을 제거하고 기본 속성인 submit 을 갖게한다.

결과

notion image
notion image
 

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 이벤트를 감지하여 입력되는 값을 감지한다.
notion image

2. 기존 코드에 keyup 이벤트 추가

notion image
 
조회된 데이터를 위의 양식과 동일하게 추가해야 하므로 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);
 

결과

notion image
검색할 제목을 입력하면 성공적으로 결과를 반영하지만, 의미없는 글자까지 모두 감지되어서 패치 요청이 과하게 많이 가는 문제가 있다. (감자 검색하고 싶을 때 ㄱ, 감ㅈ 같은걸 패치 보낼 이유는 없음)
이럴때 디바운스, 스로틀을 사용한다. 프로그래밍 기법중 하나로 둘 다 최적화를 위해 사용된다. (이 포스팅에선 다루지 않을것임) 다음 포스팅에서는 페이지네이션을 구현할 것이다.

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

[HootJem] 개발 기록 블로그