웹 서비스를 개발할 때 사용자가 검색창이나 입력 필드에 타이핑을 하면 실시간으로 연관 키워드나 데이터를 하단에 리스트로 보여주고 선택할 수 있게 만드는 자동완성(Autocomplete) 기능은 UX(사용자 경험) 설계의 필수 요소입니다.
이러한 고급 폼 기능을 순수 스크립트로 직접 구현하려면 키보드 이벤트 리스너, 포커스 제어, 다국어 처리 등 코드가 매우 복잡해지는데요. jQuery UI에서 제공하는 Autocomplete 위젯을 활용하면 단 몇 줄의 설정만으로 부드럽고 완성도 높은 자동완성을 빌드할 수 있습니다.
이번 포스팅에서는 기본 로컬 데이터 매핑부터 서버 통신(PHP) 연동, 다중 값 선택, 기존 셀렉트 박스(Combo Box) 변환까지 실무 프로젝트에서 자주 쓰이는 정석 패턴 6가지를 완벽 정리해 드립니다. 아래 본문을 마우스로 드래그하여 가독성 높은 반전 인터랙션을 직접 체험해 보세요!
jQuery UI Autocomplete 위젯을 한국어(IME 환경)와 결합할 때, 방향키나 마우스 커서로 리스트 아이템을 탐색하거나 선택하면 입력창의 텍스트가 정상적으로 유지되지 않고 자동완성 목록이 공중분해 되는 치명적인 한글 깨짐 오류가 존재합니다. 본문에 수록된
focus: function() { return false; } 옵션을 바인딩해 주면 브라우저의 기본 포커스 이벤트를 안전하게 우회하여 이 고질적인 버그를 한 줄로 해결할 수 있습니다.
1. 필수 코어 파일 인클루드 (CDN 연동)
기능을 활성화하려면 jQuery 기본 코어 파일 외에 jQuery UI 전용 스타일시트(CSS)와 스크립트 라이브러리가 함께 서버에 로드되어야 합니다. 로컬 서버에 다운로드하여 절대경로로 삽입해도 되지만, 구글 인덱싱 크롤러 속도 향상을 위해 아래와 같이 공인된 CDN 주소를 사용할 것을 적극 권장합니다.
<!-- jQuery UI 및 Core CDN 라이브러리 로드 -->
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
2. 로컬 배열 데이터를 이용한 기본 자동완성 구현
서버 통신 없이 자바스크립트 내부에 정적 배열로 보관된 데이터를 인풋 박스에 매핑하는 가장 간결하고 직관적인 선언 방식입니다.
<script type="text/javascript">
$(function() {
// 1. 자동완성에 띄워줄 로컬 원본 배열 정의
var availableCity = ["서울", "부산", "대구", "광주", "울산"];
// 2. 대상 인풋 필드에 autocomplete 플러그인 바인딩
$("#city").autocomplete({
source: availableCity, // 자동완성 리스트의 데이터 소스
select: function(event, ui) {
// 아이템 선택 시 실행될 콜백 핸들러
console.log(ui.item);
},
focus: function(event, ui) {
// [우회 필수] 한글 검색어 입력 시 커서 이동 오류 방지
return false;
}
});
});
</script>
<!-- HTML 입력 폼 요소 -->
<div class="ui-widget">
<label for="city">도시: </label>
<input id="city">
</div>
3. Ajax 원격 데이터 조회 연동 (search.php)
실무 환경에서는 데이터가 계속해서 추가되므로 로컬 배열이 아닌 데이터베이스(DB)에서 동적으로 결과를 읽어와야 합니다. source 옵션에 문자열로 서버측 백엔드 파일 경로를 지정해 주면 사용자가 타이핑할 때마다 Ajax 통신으로 백엔드 쿼리를 전송합니다.
<script type="text/javascript">
$(function() {
$("#city").autocomplete({
source: "search.php", // 실시간 검색어를 받아 처리할 백엔드 파일 경로
minLength: 2, // 최소 2글자 이상 타이핑해야 서버로 요청 (트래픽 과부하 방지)
response: function(event, ui) {
// 서버에서 결과 배열 데이터가 수신된 직후 실행
console.log(ui);
},
select: function(event, ui) {
console.log("Selected: " + ui.item.value);
},
focus: function(event, ui) {
return false;
}
});
});
</script>
4. 원격 조회를 위한 PHP 서버측 처리 예제
jQuery UI 위젯이 서버로 데이터를 전송할 때 검색어 파라미터명은 자동으로 term($_GET['term'])이라는 이름으로 바인딩됩니다. 서버는 들어온 단어를 검색하여 반드시 JSON 규격으로 응답(Echo)해 주어야 브라우저가 정상적으로 인덱싱을 처리할 수 있습니다.
<?php
// 1. HTTP 응답 헤더 컨텐츠 타입을 표준 JSON 포맷으로 선언
header("Content-Type: application/json");
// 2. DB를 대체할 예제 샘플 배열
$cities = array("서울", "부산", "대구", "광주", "울산");
// 3. jQuery UI가 자동으로 보내온 검색 파라미터 수신
$term = $_GET['term'];
$result = array();
foreach($cities as $city) {
// 문자열 내부에서 검색어 단어가 매칭되는지 전방위 검사
if(strpos($city, $term) !== false) {
// 폼의 레이블과 내부 밸류값 구조에 맞춰 배열 생성
$result[] = array("label" => $city, "value" => $city);
}
}
// 4. 최종 검색 결과 배열을 고속 변환하여 출력
echo json_encode($result);
?>
5. 하나의 입력 필드에 콤마(,) 기준 다중 선택 구현
이메일 수신자 지정이나 해시태그 입력창처럼, 콤마 분리자(Delimiter)를 이용해 단일 인풋 필드 박스 안에 여러 개의 자동완성 요소를 연속적으로 넣는 확장 팁입니다.
<script type="text/javascript">
$(function() {
function split(val) { return val.split(/,\s*/); }
function extractLast(term) { return split(term).pop(); }
$("#city")
.on("keydown", function(event) {
// 자동완성 메뉴가 활성화된 상태에서 탭(TAB)키를 누르면 포커스 이탈 방지
if(event.keyCode === $.ui.keyCode.TAB && $(this).autocomplete("instance").menu.active) {
event.preventDefault();
}
})
.autocomplete({
source: function(request, response) {
// 가장 마지막 콤마 뒤에 있는 단어 조각만 추출해 Ajax 요청 전송
$.getJSON("search.php", { term: extractLast(request.term) }, response);
},
search: function() {
var term = extractLast(this.value);
if(term.length < 2) { return false; } // 최소 입력 길이 제한
},
focus: function() { return false; },
select: function(event, ui) {
var terms = split(this.value);
terms.pop(); // 현재 미완성 상태의 입력값 제거
terms.push(ui.item.value); // 선택된 밸류 요소 보충
terms.push(""); // 끝부분에 다음 입력을 유도할 여백 콤마 추가
this.value = terms.join(", ");
return false;
}
});
});
</script>
6. 기존 HTML 콤보박스(Select) 자동완성 커스텀 위젯화
이미 마크업이 완료된 고정형 <select> 드롭다운 메뉴를 완전히 숨기고, 사용자가 타이핑하여 필터링할 수 있는 완전 커스텀 콤보박스로 객체를 치환·생성하는 하이엔드 테크닉 코드입니다.
<script type="text/javascript">
$(function() {
// $.widget 모듈을 사용해 상속형 객체 custom.combobox를 신규 선언
$.widget("custom.combobox", {
_create: function() {
this.wrapper = $("<span>").addClass("custom-combobox").insertAfter(this.element);
this.element.hide(); // 기존의 select 태그는 숨김
this._createAutocomplete();
this._createShowAllButton();
},
_createAutocomplete: function() {
var selected = this.element.children(":selected"), value = selected.val() ? selected.text() : "";
this.input = $("<input>")
.appendTo(this.wrapper)
.val(value)
.addClass("custom-combobox-input ui-widget ui-widget-content ui-state-default ui-corner-left")
.autocomplete({
delay: 0, minLength: 0,
source: $.proxy(this, "_source"),
focus: function(event, ui) { return false; }
});
this._on(this.input, {
autocompleteselect: function(event, ui) {
ui.item.option.selected = true;
this._trigger("select", event, { item: ui.item.option });
},
autocompletechange: "_removeIfInvalid"
});
},
_createShowAllButton: function() {
var input = this.input, wasOpen = false;
$("<a>")
.attr("tabIndex", -1).attr("title", "모두보기").tooltip()
.appendTo(this.wrapper)
.button({ icons: { primary: "ui-icon-triangle-1-s" }, text: false })
.removeClass("ui-corner-all").addClass("custom-combobox-toggle ui-corner-right")
.on("mousedown", function() { wasOpen = input.autocomplete("widget").is(":visible"); })
.on("click", function() {
input.trigger("focus");
if(wasOpen) { return; }
input.autocomplete("search", ""); // 전체 항목 탐색을 위해 빈 문자열 전송
});
},
_source: function(request, response) {
var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i");
response(this.element.children("option").map(function() {
var text = $(this).text();
if(this.value && (!request.term || matcher.test(text)))
return { label: text, value: text, option: this };
}));
},
_removeIfInvalid: function(event, ui) {
if(ui.item) { return; }
var value = this.input.val(), valueLowerCase = value.toLowerCase(), valid = false;
this.element.children("option").each(function() {
if($(this).text().toLowerCase() === valueLowerCase) {
this.selected = valid = true; return false;
}
});
if(valid) { return; }
// 일치 품목이 없을 경우 데이터 초기화 및 툴팁 가이드 바인딩
this.input.val("").attr("title", value + " 일치하는 항목이 없습니다.").tooltip("open");
this.element.val("");
this._delay(function() { this.input.tooltip("close").attr("title", ""); }, 2500);
this.input.autocomplete("instance").term = "";
},
_destroy: function() { this.wrapper.remove(); this.element.show(); }
});
// 완성된 빌더 메서드를 타겟 ID에 바인딩 실행
$("#city").combobox();
});
</script>
7. 공식 데모 웹사이트 및 API 문서 저장소
본 가이드라인에 수록된 속성 외에 추가적인 옵션 스펙(가장자리 애니메이션 속도, CSS 테마 레이어 커스텀 등)에 대한 정밀한 작동 규칙 정보는 아래 공식 저장소 버튼 링크를 통해 직접 점검해 보시기 바랍니다.
8. 맺음말
자동완성 스크립트를 올바르게 구현하면 사이트 사용자의 불필요한 타이핑 오타와 페이지 이탈률을 획기적으로 낮출 수 있습니다. 오늘 소개해 드린 초경량 모듈 라이브러리와 예제 스크립트를 응용하여 본인이 구상 중인 웹 시스템 폼(Form) 환경에 최적화된 고기능성 자동완성 인터페이스를 탑재해 보시기 바랍니다!