[Springboot] 23 검색 기능 ( 자바스크립트 키업 이벤트, RestAPI, JSON, 디바운싱과 쓰로틀링 )

내용업데이트필요
김호정's avatar
Sep 13, 2024
[Springboot] 23 검색 기능 ( 자바스크립트 키업 이벤트, RestAPI, JSON, 디바운싱과 쓰로틀링 )
 
notion image
<div class="d-flex justify-content-end mb-2"> <form class="d-flex col-md-3"> <input class="form-control me-2" type="text" placeholder="Search"> <button class="btn btn-primary" type="button">Search</button> </form> </div>
검색 디자인 추가
 
- 부트스트랩 d-flex 는 해당 태그 내부를 flex로 만들때 사용
<form class = “d-flex> 의 내부에 있는 태그에는 flex가 적용된다.
 
<form>을 div로 감싸고 d-flex를 적으면 form과 input, button 까지 다 flex가 적용된다. 그리고 justify-content-end 를 적으면 자식 요소들이
다 오른쪽 끝으로 정렬됨. mb-2는 margin bottom 이다.
 
 
아래 form 태그에 col-md-3은 “그리드”
→ 부트스트랩은 12개의 그리드로 화면을 나눈다.
→ 3은 12 그리드 중 3개를 차지한다는 의미
→ 전체 화면의 3/12를 차지하게 됨
 
notion image
 
 
notion image
(기존에 만들었는데 사용안하는 mFindAll매서드는 지우고)
검색 쿼리 생성 : )
// 검색 시 사용할 findAll 만들어주기 ( 검색 쿼리 ) @Query("select b from Board b where b.title like %:title% order by b.id desc") List<Board> mFindAll(@Param("title") String title);
 
쿼리가 그리 복잡한게 아니니 Repository에 만든다!
복잡한 쿼리는 xxxQueryRepository 에 만든다.
 
++
notion image
 
네이티브 쿼리로 써봤을 때!
notion image
 
 
네이티브 쿼리로 정렬까지 해봄. → 이렇게 하면 위의 JPQL 로 쓴거랑 똑같은 결과가 나옴
 
 
notion image
BoardRepository에서 만든 매서드 테스트하기 : )
String title = “제”로 바꾸면 제목1~제목5까지 5개가 출력될 것이다!
notion image
 
notion image
Get 요청은 쿼리스트링 밖에 없는데
 
 
이렇게 들어온다. ? 해서 들어오는 데이터는 @requestParam을 앞에 써준다.
원래 해당 어노테이션은 생략할 수 있다. 근데 버그 때문에 써줘야함
 
localhost:8080/?title=1 로 쳐보니까
notion image
쿼리스트링 값(1)이 제대로 전달 된 것을 확인할 수 있다.
 
notion image
localhost:8080/?title= 이렇게 값 없이 전달해도
defaultValue 가 “”이기 때문에 “” 이 title의 값으로 들어가서 실행된다.
 
notion image
required = false 걸어주면 쿼리스트링이 반드시 필요한게 아니니 title을 전달하지 않아도 에러가 안난다. → 대신 쿼리스트링으로 파라미터가 전달되지 않았으니 null 값이 들어간다.
 
 
notion image
게시물목록보기 매서드에 파라미터를 매개변수로 넣어주고
 
 
notion image
서비스 레이어에서 위에서 만든 검색 쿼리 mFindAll 를 사용하도록 코드를 수정한다.
 
지금 list 화면은 “메인/목록보기 화면”인데
검색어를 입력했을 때 게시글 목록보기 부분이 리로드되면서 바뀌는 형태이다.
  1. 검색을 안했을 때, 모든 게시글이 보이는 경우 와
  1. 검색을 했을 때, 목록보기 부분이 바뀌는 경우
 
2가지를 고려하면 이 게시글목록보기 매서드는 2가지 경우에 따라 다른 쿼리를 사용해야한다.
쿼리스트링으로 파라미터가 넘어오지 않은 경우 ( = 메인/목록보기 화면으로 이동하는 경우 )
에는 findAll 매서드를 사용 (정렬만 하는 매서드)
쿼리스트링 파라미터가 넘어오는 경우 → mFindAll ( → 검색어로 제목을 찿는 매서드 )
 
그리고 각자 return 해주기
아 그리고 영속객체로 return 하면 안되니 DTO 만들어서 return 해야하는데 이건 뒤에서.
 
public List<Board> 게시글목록보기(String title) { if(title == null){ //Pageable pg = PageRequest.of(0, 3, Sort.Direction.DESC, "id"); 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; } }
위에 if else로 적은건 동적쿼리가 아니다.
 
동적쿼리(Dynamic Query)는 런타임 중에 조건에 따라 쿼리를 생성하고 실행하는것을 의미
→ 쿼리의 일부 또는 전체가 런타임 데이터나 사용자의 입력에 따라 변경됨
 
if(title == null) {
em. createQuery (””)
} else {
em.createQuery(””)
}
 
이게 동적 쿼리임. 띠ㅐ렸을 때 쿼리를 만들어서 실행함. 입력에 따라서 다른 쿼리가 실행됨
 
notion image
list에 추가 ( → Get 요청할 거니까 form에 action 및 input에 name 추가 )
 
쿼리스트링은 날아가서 쿼리의 where절에 걸린다! (중요!!!)
( → 그래서 id같은 pk는 input type hidden 써서 body에 담아서 보내거나 그러지 말고
쿼리스트링으로 전달하기 )
 
왜 AJAX로 요청하지 않고 Form 태그를 사용하는지 궁금할 것이다.
 
AJAX를 무조건 써야하는 경우가 있고(ex : 아이디 중복체크. 아이디 부분만 리로드해서 패스워드랑 이메일에 입력한 값은 유지되도록 지킴)
전체 리로드해도 괜찮은 경우(→ 지금 같은 경우. AJAX 쓰는거보다 트래픽은 더 걸리지만)가 있다.
 
→ 무조건 다 AJAX로 처리한다고 좋은게 아니다
→ 검색은 검색하는 경우 전체 리로드를 해서 결과를 보여줄 수 있으니 지금 FORM 태그를 사용
 
notion image
form 태그안에 있는거니까 submit으로 바꿔준다.
폼태그안에 button은 type=submit 이라고 해줘도 되지만 없어도 자동으로 submit됨
 
→ type=”submit”은 지워줘도 된다.
 

키업 이벤트 샘플링 하기

 
키보드 이벤트
-> 내가 할 수 없는 기술을 프로젝트에 바로 적용하지 말고 샘플링 해봐야 한다.
통신만 안하면
-> vscode켜서 샘플링 하기
자바스크립트 이벤트가 아니라 통신에서 터질수도 있으니 만약 이걸 바로 프로젝트에 바로 작성하면 디버깅이 어려워질 수 있따.
실제 필드에 나갈 필요는 없다.
통신없이 연결안하고 기술을 한번 적용해봐야한다.
뭐가 잘못됐는지 알기 위해서는 샘플링을 해야한다.
 
notion image
 
<!DOCTYPE html> <html lang="ko"> <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> </script> </body> </html>
 
notion image
 
공식문서를 봐도 되지만 최근의 블로그 포스팅 보는게 지금 상태에서는 더 쉽다.
 
 
 
 
notion image
keyup이벤트 적어주고 테스트삼아 console.log에 아무거나 찍어보면
 
notion image
잘 찍힌다.
 
 
 
 
notion image
이벤트를 보면
 
notion image
notion image
 
 
notion image
target안에
 
notion image
value 가 값을 가지고 잇음
 
 
notion image
 
notion image
 
notion image
이 형태로 하면 된다.
 
실험해봤으니 스프링에 해보자
 
notion image
 
일단 지금 검색 키업 부분은 추가된 기능이기 때문에 따로 브랜치를 만들어서 작업하기!
→ git checkout -b ajax/topic 브랜치 만들고 작업하고
다하고 나서 add. commit 하고 해당 브랜치로 push 하면 된다 !
→ 지금 만든 ajax/topic 브랜치는 master에 merge만 안하면 됨. 실제 프로젝트에 사용할 게 아니니까 브랜치 만들어서 기능 실험만 해보고 나중에 완성되면 깃허브에서는 해당 브랜치 삭제하거나 함.
 
 
notion image
AJAX로 요청할 거니 검색부분 위처럼 수정
→ name → id
 
 
notion image
스크립트 작성하고 여기까지 해서 되는지 먼저 돌려서 확인!
 
키보드 이벤트 처리 기능을 작성하고 되는지 확인 : )
 
개발할 때는 한꺼번에 작성해서 다 되는지 마지막에 돌려보고 확인하는게 아니라
중간중간 기능1을 다 작성했으면 그때 되는지 확인해야한다.
앞에 기능1이 되면 그 다음 기능2로 넘어가서 작업한다!
 
→ 아니면 나중에 어디서 안되는지 찾을때 디버깅이 어려워짐
( 자바스크립트에서 안되는지, 데이터가 안넘어오는건지, 통신에서 문제인지 모르기 때문에 디버깅 시간이 길어짐)
 
 
notion image
keyup해서 데이터 가져와지는걸 확인했다.
 
이제 통신으로 날리자.
 
컨트롤러로 이동
notion image
아래 list 복사해서 데이터만 날릴 boardList 매서드를 만들어준다.
 
 
notion image
 
2가지 방법 중 하나를 사용하면 된다.
 
 
  • 게시글목록보기는 Board가 아닌 DTO를 return 하도록 BoardResponse안에 DTO를 만들어준다.
 
notion image
기존에 있는 DTO는 content도 주니까 이거 쓰지말고 새로 만들어주기
 
근데 귀찮으니까 이번에는 이거 쓰자. content 만 안쓰면 되니까
 
notion image
서비스 수정
 
notion image
컨트롤러 수정
 
notion image
잘 나온다
이렇게 하면 엔티티가 아니니까 No session 이 뜰 수가 없다 : )
 
 
 
이제 FETCH 때리자
notion image
notion image
 
상세보기에 repplyItem 만든거처럼 여기도 해당 부분 item으로 빼서 만들어 주기!
그전에 <div>로 감싸야 append할 수 있으니, div id=”board-box” 하나 만들어준다.
 
notion image
boardItem 작성 : ) return 부분에 models 안의 div 부분 복사해서 붙여넣어준다.
 
책임(기능)에 따라 3가지로 나누었다.
 
  1. 데이터 렌더링 → boardItem
 
 
 
notion image
→ 지금까지 AJAX 활용해서 부분리로딩 하는건 SRR 하기로 한 master 브랜치의 기능과 분리되는
것이니 ajax/topic 브랜치에 push 해준다!
 
그리고 master 에 merge 하면 안되고 브랜치로 두고 코드 확인하면 된다!
(→ master의 코드에 추가할 것이 아니기 때문에 머지하면 안됨 )
 
notion image
RestApi (/board) 로 요청해보면 검색 쿼리가 잘 동작해 필요한 데이터만 전달해 주는것을 확인할 수 있다.
 
 
notion image
파라미터로 아무것도 안들어가면 “”공백이 들어가니 전체 가 나온다!
 
 
notion image
타이틀에 1이 들어간 것도 잘 조회된다 : )
 
화면에서 해보면
notion image
“제”가 들어간 것도 잘 검색된다. ( 검색창 아래부분이 부분 리로드 되는 것도 확인하자 ? )
 
 
notion image
제목5 라고 검색해도 잘 나온다.
 
 

바운싱

 
1초로 키보드 속도를 “바운싱”을 잡으면 사용자가 키보드로 적기 시작하고 1초 지나면 요청이 간다.
그럼 통신이 1번만 일어난다.
 
바운싱을 “키보드에서 손을 떼는 순간 + 0.5.초”를 줬으면
0.5초에 1번 , 키보드 손떼는 순간 1번 - > 2번 일어난다.
 
검색버튼을 안눌러도 입력만하면 검색되니까 UX는 좋아지는데
요청을 엄청 많이해서
 

디바운싱과 쓰로틀링

 
notion image
 
스크롤 내릴때 수많은 이벤트가 발생하니까 그걸 다 보려면 부하가 걸린다.
→ 아무리 이벤트가 들어와도 “일정시간마다 이벤트를 처리”하도록 해서 과도한 이벤트 처리가 발생하지 않도록 할 수 있다.
 
디바운싱과 쓰로틀링 이해하기
 
즉,
  • 디바운스 : 특정시간이 지난 후 하나의 이벤트만 발생하도록 하는 기술
→ 10번의 패치가 일어나면 앞에 9개는 무시하고 마지막꺼면 때림
  • 쓰로틀 : 특정 시간이 지났을 때 일정한 주기마다 실행되도록 하는 기술
→ 해주는데 마지막에는 강제로 1번 실행되도록 해야함
 
제일 처음에 연습해볼때 Throttle 로 해보고 Debounce 연습해보기
 
연습해보고
패치가 너무 많이 일어나서 줄이기 위해서 인터넷 찾아보니까
쓰로틀링과 디바운싱이 있어서 이걸 적용해봤다.
 
고 대답하기
 
Share article

keepgoing