자동 완성에 이미지 넣기

Jaejun Kim·2022년 12월 18일

실전다운 실전 프로젝트에서 나는 백엔드였다. 사실 팀원 3명 있었는데 모두 백엔드였다. 왜냐면 그런 팀이었기 때문이다. 프론트는 개차반으로 만들어도 괜찮고 백엔드 기술만 보는..

그중에 가장 프론트다운걸 만들 수 있던 사람이 나였기 때문에 자연스럽게 프론트엔드를 도맡게 되었다. 내가 맡은건 검색도 있었는데, 검색을 만들다보니 자동완성이 그렇게 땡기는 것 아닌가.

그래서 어떻게 간단하게라도 구현을 해보기 위해 jQurey를 뒤지고 다녔다.

간단한 자동 완성을 만드는건 어려운 일이 아니었다. jQuery의 autocomplete를 사용하면 간단하게 자동완성을 구현할 수 있다.

$('#inputBox').autocomplete({ // 자동완성 시작
  source: function (request, response) { // 자동완성에 띄울 데이터를 정하는 함수
    $.ajax({
      type: 'post',
      url: `/search/autocomplete`,
      dataType: 'json',
      data: { value: request.term }, // post를 사용, #inputBox 키워드 === request.term
      success: function (data) {
        response(
          $.map(data, function (item) { // 받아온 데이터는 select 등 다른 프로퍼티에서 사용가능
            return {
              label: item._source.name,
              appid: item._source.appid,
              img_url: item._source.img_url,
            }
          })
        );
      }
    });
  },
  minLength: 2, // 자동완성이 실행되는 최소 글자수
  delay: 350, // 자동완성 딜레이. 없으면 서버터짐
  select: function (event, ui) { // 자동완성 리스트 중 하나를 선택했을 경우 생기는 이벤트
    movePage(ui.item.appid, ui.item.label) 
  }
})

생각보다 금방 만들어버린 자동완성.

근데 아쉬운 점은 autocomplete 기본 기능만을 사용하다보니 확장성이 떨어진다는 것이다.

스팀은 검색결과에 가격과 이미지 또한 넣어주는데, 가격은 모르겠지만 이미지는 꼭 들어가야 한다고 생각하게 되었고, 곧장 코드를 수정하기 시작했다.

express.js에서는 변경할 것이 별로 없었다. 기존 레포지토리 계층 메서드에서 img_url 하나를 더 가져오게 하면 끝이다.

// ElasticSearch JS client.search() option
let option_keywords = {
                "from": 0, "size": 10,
                "_source": ['appid', 'name', 'img_url'],
                "index": "games_data",
                "body": {
                    "query": {
                        "bool": {
                            "must": [
                                {
                                    "match": {
                                        "name.autocomplete": {
                                            "query": value,
                                            "fuzziness": 2 
                                        }
                                    }
                                },
                                { "exists": { "field": "img_url" } }, // 이미지가 있어야함
                                { "exists": { "field": "review_score_desc" } }, // 리뷰가 존재해야함
                            ],
                            "should": [
                                { "prefix": { "name.standard": { "value": value } } },
                                { "match": { "name.standard": value } }, // 노말 검색 up
                                { "match": { "type": 'game' } }, // type이 game이면 + 
                            ]
                        }
                    }
                }
            }

그리고 기존 코드에서 받을 수 있게 끔 해주는 것까지는 완료했다. 근데 여기서 어떻게 무엇을 더 해야할까? 리서치를 해보았다.

검색 결과로는 $(autocomplete) 에 autocomplete를 한번 더 걸어서 temp_html을 붙여넣는 방식이 많았다. 리서치를 한 끝에 블로그 하나를 잡고 시도를 해보기로 했다.

출처: https://wyseburn.tistory.com/entry/jQuery-%EB%B2%84%EC%A0%84-%EB%B3%84-autocomplete-renderItem-%EC%B0%A8%EC%9D%B4

JQuery-ui 버전에 따라 autocomplete 사용법이 다름.

[1.12.0]

jquery 1.7.x 버전 이상을 써야 하고 최신버전 3.2.1 에서도 동작한다.

.autocomplete( "instance" )._renderItem 을 사용할 수 있고

.data("ui-autocomplete")._renderItem 을 사용할 수도 있지만 old style

(대충 아래 방식을 추천한다는 글)

.autocomplete( "instance" )._renderItem = function( ul, item ) {
return $( "<li>" ).append( "<div>" + gubun + S9HL.highlight(label, searched) + "</div>" ).appendTo( ul );
)};

여기서 첫번째 트러블 슈팅이 발생한다.

바로 jQuery의 버전 문제였는데, autocomplete를 사용한 대부분의 자동완성 포스팅은 jQuery, jQuery-ui.js, jQuery-ui.css 모두 1.12.0 버전으로 통일해서 사용하고 있었다.

해당 문법도 적용되면서 지금까지 작성한 코드 중 못쓰는 코드가 발생하지 않도록 jQuery 버전을 조절해야 했는데, 여기서 시간을 상당 시간 소비하였다. 결국 글쓴이가 글을 쓴 시점의 최신 버전인 3.2.1 버전을 적용하니 문제가 생기지 않아 적용하였다.

이후

.data("ui-autocomplete")._renderItem 

.autocomplete( "instance" )._renderItem = function( ul, item ) {
return $( "<li>" ).append( "<div>" + gubun + S9HL.highlight(label, searched) + "</div>" ).appendTo( ul );
)};

둘을 적용해 보았고 두번째 트러블 슈팅이 발생하였다. css를 잘 모르는 나로서는 이것을 어떻게 조합해야 괜찮게 나올지 모르겠던 것. 혼자서 몇 번 시도하다 결국 steam에 들어갔다.

해당 부분의 Element 를 베껴서 일단 그대로 적용해보았다.

.autocomplete("instance")._renderItem = function (ul, item) {
          return $("<li>").append(`
            <a class="match ds_collapse_flag  app_impression_tracked">
              <div class="match_name ">${item.label}</div>
              <div class="match_img">
              <img src="${item.img_url}"></div>
              <div class="match_price">??</div>
            </a>`).appendTo(ul);
        };

결과

처참하게 실패. 쓰지 않는 class를 떼어내고, 필요한 속성들을 집어넣어 사투를 벌인 결과, 어느정도 만족스러운 결과를 낼 수 있었다.

그럼에도 마음에 들지 않는 부분은 여전히 존재했는데, autocomplement로 인해 기본적으로 가지고 있는 기능 중 하나가 계속해서 사라지지 않고 버티던 것. 바로 호버hover 기능이다.

커서를 가져다 댈 때마다 이미지 아래에 살짝 튀어나오는 파란 녀석을 잡는 것을 목표로 해야 했고, 때문에 금방 끝날 줄 알았던 작업이 훨씬 지체되었다.

하다 못해 리액트 수강생 에게도 도움을 청하며 머리를 맞댄 결과, img 크기를 키우고, 이미지가 붙여져있는 방식인 absolute 속성을 이용해서 사진을 움직여 감추기로 했다.

//style
  .match_img {
    position: absolute;
    top: -2px;
    left: -6px;
    border-radius: 2px;
  }

//script
.autocomplete("instance")._renderItem = function (ul, item) {
            return $("<li class='match_li'>").append(
              `<a class="match">
          <div class="match_name">
              ${item.label}
          </div>
          <div class="match_img">
            <img src="${item.img_url}" style="width: 120px; height: 52px;">
          </div>
          <div></div>
        </a>`).appendTo(ul);
          };

결과물

0개의 댓글