라떼군 이야기
고도몰 자동완성 구현
Problem
고도몰에서는 검색 시 자동 완성 기능을 제공하지 상품이 많은 경우 노출에 한계가 있어서 불편함이 있다.
현재는 최근 검색한 기록만 제공하고 있어서 설정이나 부가 서비스로는 설정이 불가능하다.
그래서 ajax
를 이용해 상품 결과를 확인할 수 있는 기능을 만들어 보고자 한다.
일부 기능은 고도몰과 관련된 설정이지만 수정한다면 일반적인 쇼핑몰에도 적용할 수 있을 것이다. 아래 구글 검색과 비슷한 화면을 구현하고자 한다.
Solution
고도몰에서 직접 php 파일을 만든다면 $_GET
을 바로 사용할 수 없었다.
여기 문서에 보면 $_GET
은 unset
되었다고 나와있다.
따라서 아래와 같이 별도 제공하는 클래스를 사용해야 한다.
코드 내용 중 해당 쇼핑몰을 위한 구현도 많아서 전체 코드는 추가하지 못했다.
데이터베이스 접속 정보는 /config/database.php
파일에서 확인할 수 있었다.
<?php
return [
'host' => 'host.godomall.com',
'username' => 'username',
'password' => 'password',
'database' => 'database',
];
/extensions/search.php
에 아래와 같은 파일을 만들고 쿼리스트링 q
로 원하는 결과의 응답이 오면 해상 상품을 json
형태로 반환하도록 구현하였다.
<?php
use Framework\Http\Request;
$_GET = \Request::get();
$q = trim($_GET->get('q'));
$config = @require('/[PATH_HERE]/config/database.php');
$conn = new mysqli($config['host'], $config['username'], $config['password'], $config['database']);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT * FROM es_goods where goodsNm like '%" . $q ."%' limit 10";
$result = $conn->query($sql);
$data = [];
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
array_push($data, ['id' => $row["goodsNo"], 'name' => $row["goodsNm"]]);
}
}
$conn->close();
header('Content-type: application/json');
echo json_encode($data);
이제 페이지를 호출해보자. query
로 a
를 입력했고 a
가 포함된 결과가 json
포맷으로 응답되는 것을 확인할 수 있다. /extensions/search.php?q=a
[{"id":"1000000138","name":"iHope Zipper Card Wallet Grey"},{"id":"1000000414","name":"Paper Bug Paper Book"},{"id":"1000000892","name":"Double Lovers x Viva Studio Whiskey[Wiss-Key] Black"},{"id":"1000000903","name":"Bon Tote205 Mild Black"},{"id":"1000000954","name":"Oldies Candle Palmarosa & Basil 330ml"},{"id":"1000001017","name":"Neck Point Pola T Black"},{"id":"1000001030","name":"Karmuel Maple Wood Genuine Leather"},{"id":"1000001043","name":"Nick Night L Boots Black"},{"id":"1000001074","name":"Holiday Moon Earrings Sliver"},{"id":"1000001081","name":"Pearl & Crystal Necklace"}]
이제 검색창에 이벤트를 추가해보자. 특이한 것은 아래와 같이 두가지 종류의 이벤트를 구현하였다. keydown
이나 keyup
이벤트 대신 input
을 이용한 것은 ios
safari
에서 타이핑 후 바로 입력을 확인하기 위해서 별도로 분리하였다.
document.getElementById("search_form").addEventListener("input", function(e) {})
이 부분에 아래와 같이 검색 결과를 이용해 내용을 표시하는 부분을 구현하였다. 추가로 빠르게 입력 시 중복해서 요청하기 않도록 하였다.
clearInterval(searchHandler);
searchHandler = setTimeout(function() {
$.get('/extensions/search.php', {
'q': q,
}, function(data, status) {
if (status == 'success' && data) {
recentBox.addClass('search');
container.empty();
if (data.length > 0) {
// recentBox.removeClass('empty');
searchCont.show();
var ul = $('<ul></ul>').addClass('js_recent_list');
for (var i = 0; i < data.length; ++i) {
ul.append('<li><a href="../goods/goods_search.php?keyword=' + data[i] + '">' + data[i] + '</a></li>');
}
container.append(ul);
} else {
// recentBox.addClass('empty');
searchCont.hide();
}
} else {
console.log('request fail. ajax status (' + status + ')');
}
});
}, 100);
$('input#search_form').on('keydown', function(e) {})
이 부분에는 검색 후 화살표(위, 아래)로 검색 결과를 선택할 수 있도록 기능을 구현하였다.
switch (e.keyCode) {
case 13:
case 37:
case 39:
break;
case 38:
var selected = container.find('li.selected');
container.find('li.selected').removeClass('selected');
if (selected.length) {
selected = selected.prev();
selected.addClass('selected');
} else {
container.find('li:last').addClass('selected');
}
var text = container.find('li.selected').text();
if (text) { that.val(text); }
break;
case 40:
var selected = container.find('li.selected');
container.find('li.selected').removeClass('selected');
if (selected.length) {
selected = selected.next();
selected.addClass('selected');
} else {
container.find('li:first').addClass('selected');
}
var text = container.find('li.selected').text();
if (text) { that.val(text); }
break;
default:
break;
}
적용 후 검색창에서 아래와 같은 결과를 확인할 수 있었다. 응용하면 연관 검색어 및 검색어 태그 등 다양한 결과가 노출되도록 할 수도 있을 것이다.