// jQuery 라이브러리를 로드 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script> // jQuery UI 라이브러리를 로드, 다양한 위젯, 애니메이션 효과, 인터랙션 등을 제공 <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> // jQuery UI 위젯과 효과에 대한 기본 스타일을 정의하는 CSS 파일. 이 파일을 포함하면 jQuery UI 위젯들이 기본적으로 제공하는 스타일을 사용할 수 있음 <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
이거 3개 필요
RestAPI/RestController
package org.example.projectspringfalling.RestAPI; import lombok.RequiredArgsConstructor; import org.example.projectspringfalling._core.utils.ApiUtil; import org.example.projectspringfalling.song.SongService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import java.util.List; @RequiredArgsConstructor @org.springframework.web.bind.annotation.RestController public class RestController { private final SongService songService; // 검색 자동완성 기능 @GetMapping("/search/auto") public ApiUtil<List<RestResponse.SearchAutoCompleteDTO>> searchAuto(@RequestParam("keyword") String keyword) { List<RestResponse.SearchAutoCompleteDTO> responseDTO = songService.searchKeywordAuto(keyword); return new ApiUtil<>(responseDTO); } }
ajax 요청을 위해 restAPI 컨트롤러가 필요하다.
SongService
public List<RestResponse.SearchAutoCompleteDTO> searchKeywordAuto(String keyword) { List<Song> songList = songRepository.findByAutoComplete(keyword).orElseThrow(() -> new RuntimeException("조회된 데이터가 없습니다.")); return songList.stream().map(song -> new RestResponse.SearchAutoCompleteDTO(song)).toList(); }
SongRepository
@Query("select s,ar,al from Song s join fetch s.album al join fetch s.artist ar where s.title like %:keyword% or al.title like %:keyword% or ar.name like %:keyword%") Optional<List<Song>> findByAutoComplete(@Param("keyword") String keyword);
키워드로 원하는 값을 조회한다.
RestResponse
package org.example.projectspringfalling.RestAPI; import lombok.Data; import org.example.projectspringfalling.song.Song; public class RestResponse { @Data public static class SearchAutoCompleteDTO { private String songTitle; private String albumTitle; private String artistName; public SearchAutoCompleteDTO(Song song) { this.songTitle = song.getTitle(); this.albumTitle = song.getAlbum().getTitle(); this.artistName = song.getArtist().getName(); } } }
header.mustache 전체 코드
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="/css/style.css"> <title>Falling</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script> <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"> </head> <body> <header class="header-main" style="margin-top: 10px"> <nav class="nav-header"> <div class="logo"> <a href="/"> <svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="110.000000pt" height="20.800000pt" viewBox="0 0 275.000000 52.000000" preserveAspectRatio="xMidYMid meet"> <g transform="translate(0.000000,52.000000) scale(0.100000,-0.100000)" fill="#004AAD" stroke="none"> <path d="M52 418 c-8 -8 -12 -59 -12 -164 0 -165 5 -182 55 -170 23 6 25 11 25 66 l0 60 60 0 c49 0 61 3 70 20 25 46 -17 74 -100 67 -21 -2 -25 2 -25 23 0 24 2 25 90 30 85 5 90 6 93 28 7 45 -12 52 -132 52 -74 0 -116 -4 -124 -12z"/> <path d="M598 418 c-9 -7 -47 -78 -84 -156 -72 -153 -75 -174 -22 -180 20 -2 28 4 43 38 l18 41 66 -3 c65 -3 66 -4 86 -41 16 -30 25 -37 44 -35 52 6 49 28 -22 175 -85 175 -95 187 -129 161z m40 -163 c16 -35 16 -35 -18 -35 -16 0 -30 2 -30 5 0 11 24 55 30 55 4 0 12 -11 18 -25z"/> <path d="M953 423 c-10 -3 -13 -47 -13 -169 0 -192 -12 -177 140 -172 l95 3 0 30 0 30 -77 3 -78 3 0 127 c0 147 -10 169 -67 145z"/> <path d="M1342 418 c-8 -8 -12 -59 -12 -163 0 -104 4 -155 12 -163 8 -8 48 -12 115 -12 93 0 103 2 113 21 20 36 -3 49 -86 49 l-74 0 0 134 c0 113 -2 135 -16 140 -24 9 -39 7 -52 -6z"/> <path d="M1743 423 c-10 -3 -13 -47 -13 -169 l0 -164 25 -6 c50 -12 55 4 55 172 0 141 -2 154 -19 164 -21 11 -29 11 -48 3z"/> <path d="M1976 414 c-13 -13 -16 -42 -16 -163 0 -160 5 -176 52 -169 22 3 23 7 26 96 1 51 7 92 12 90 4 -2 39 -43 77 -93 66 -87 92 -106 120 -87 19 12 20 322 1 333 -46 30 -68 -5 -68 -109 l0 -86 -65 90 c-88 118 -106 132 -139 98z"/> <path d="M2515 416 c-66 -29 -111 -111 -99 -180 9 -60 29 -92 76 -126 54 -39 137 -42 189 -7 32 22 34 26 37 91 4 89 -4 98 -82 94 -60 -3 -61 -3 -61 -33 0 -27 4 -30 33 -33 29 -3 33 -6 30 -30 -3 -23 -8 -27 -40 -30 -29 -2 -43 3 -67 25 -66 62 -31 163 58 163 22 0 43 -4 46 -10 17 -28 84 12 72 43 -15 40 -131 60 -192 33z"/> </g> </svg> </a> </div> <div class="nav-links" style="font-size: 14px"> <a href="/song-chart" class="text-body">차트별</a> <a href="/song-genre" class="text-body">장르별</a> <a href="/storage" class="text-body">보관함</a> <a href="/subscription-list" class="text-body">이용권</a> </div> <div class="search-bar"> <form action="/search" method="get" class="form-inline"> <i class="fa fa-search"></i> <input type="text" placeholder="검색어를 입력하세요." name="keyword" id="search-keyword"> </form> </div> <div class="auth-links"> <a href="/login-form" class="text-body">로그인</a> <a href="/join-section" class="text-body">회원가입</a> <!-- <a href="/logout" class="text-body">로그아웃</a>--> <!-- <a href="/profile" class="text-body">마이페이지</a>--> </div> </nav> </header> <!-- header end --> <script> $(document).ready(function () { $('#search-keyword').autocomplete({ source: function (request, response) { $.ajax({ url: '/search/auto', type: 'GET', data: { keyword: request.term }, dataType: 'json', success: function (data) { console.log('통신 확인:', data); response($.map(data.body, function (item) { return { label: item.songTitle, value: item.songTitle, songTitle: item.songTitle, albumTitle: item.albumTitle, artistName: item.artistName, }; })); }, error: function (xhr, status, error) { console.error('Error:', error); console.error('Status:', status); console.error('Response:', xhr.responseText); } }); }, minLength: 1, select: function (event, ui) { console.log('Selected ID:', ui.item.id); } }); }); </script> </body> </html>
인풋 태그에 id 를 설정한다.
id 값을 찾아 autocomplete 함수를 실행한다.
적절한 값을 넣는다.
label : 화면에 표시되는 값
value : label 값을 선택하면 value에 해당 값이 검색 태그에 표시된다.
그 밑에 값 : 왜 있는지 잘 모르겠음. 없어도 DTO 에 담겨져 있는 값으로 검색 잘 되는거같음
부족한 점
현재 화면에 뭘 검색하든 노래 제목만 자동 완성됨.
어떻게 서버에서 가져온 값을 자동 검색으로 띄워야 될지 고민 필요
Share article