
스위프트로 앱을 작업 하던 중 검색 기능에서 자동 완성 기능을 구현 해야 하는 상황이 생겼다. 앱의 서버는 FireBase를 사용 했는데, FireBase를 사용 하면 자동 완성 기능을 구현함에 있어서 어려움이 있었다.
우선 자동 완성 기능은 한글이나 영어를 입력 할 때 한 글자를 입력 하면 바로 정보 검색이 실행이 되어야 하는 구조가 완성 되어 있어야 구현 가능한 기능이다. 그러나 FireBase는 그런 내장 기능이 부족할 뿐만 아니라 서버에서 계속 정보를 조회해야 함으로 비용 상으로도 비 효율 적인 결과가 나올 수 있다.
또한 한명이 검색 하는 것도 아니고 여러명의 유저가 검색 했을 때도 서버에 무리가 없어야 한다. 그러나 FireBase는 그런 기능을 아직 지원하지 않는거 같았다. ( 모바일 앱 서버를 쉽게 구현 한다는 취지의 제품인데 자동 완성 기능을 넣지 않았는지 의문이다. ) 필자는 FireBase로 최대한 구현을 해보려고 했다. FireBase Functions로 함수를 구현 해서 뿌려주려고도 했었다. 하지만 그것도 최적화와 효율성, 그리고 내가 아직 접해보지 않은 node언어로 구현 해야 했었다. 그래서 이런 저런 문서를 찾아보던 중. Algolia 라는 자동완성 서비스를 알게 되었고 그걸 앱에 도입 한 순서를 정리 해볼까 한다.
우선 Algolia에 접속 및 가입이 필요하다. Algolia는 일종의 FireBase 같은 외부 라이브러리 인데, 자동 완성 기능을 중점적으로 제공 해주는 제품이라고 생각 하면 되겠다. 한 글자, 한 글자 입력 할 때 마다 쿼리를 요청 하고 반응을 해주는 복잡한 기능을 라이브러리를 사용 하면 쉽게 사용 할 수 있게 해주는 것이다.
Algolia는 Algolia의 index 기능으로 문서를 저장해 놓고, 그걸 라이브러리를 가져와서 구현 하면 바로 검색과 화면에 바인딩 해준다. 나는 정보들을 Algolia에 올리기 위해 FireBase에 있던 정보들을 가져와서 Algolia에 업로드 하는 기능을 넣었고, 이건 코드에서 주석을 해제 하면 업로드 할 수 있게 했다. ( 실시간으로 바로 바로 올리는건 최신의 정보를 요구 하는 검색 기능은 아니였기에 내가 수동으로 해도 충분 하다고 판단 했다. )
func indexFirestoreDataToAlgolia() {
let firestore = Firestore.firestore()
firestore.collection("YOUR_COLLECTION").getDocuments { (snapshot, error) in
guard let documents = snapshot?.documents else {
print("Error fetching documents: \(error!)")
return
}
let records = documents.compactMap { document -> YourDataType? in
// 데이터 모델로 변환
}
// records를 알고리아에 색인화
index.saveObjects(records) { result in
// 처리 결과
}
}
}
앱을 앱스토어에 올릴 땐 주석 처리 해서 비활성화 하고 정보의 갱신이 필요 할때 내가 수동으로 업로드 하게 한 것이다.

이런 식으로 업로드가 되면 Algolia Index에 검색이 되어야 할 정보가 업로드 된다. Algolia에 정보가 업로드 된 것이다. 이제 Algolia에서 정보들을 가져와서 텍스트 필드에 텍스트가 입력 될 때 바인딩을 하고 보여주기만 하면 된다.
나는 유저가 검색 바에 텍스트를 입력할 때마다 알고리아 검색 쿼리를 실행하고, 결과를 받아 자동완성 테이블 뷰에 표시하는 로직을 사용 했다.
extension SearchPage {
func performSearch(with searchText: String) {
var query = Query(searchText)
query.attributesToRetrieve = ["title", "museumName"]
index.search(query: query) { result in
switch result {
case .success(let response):
self.autocompleteResults = try! response.extractHits()
DispatchQueue.main.async {
self.updateAutocompleteTable()
}
case .failure(let error):
print("Error during Algolia search: \(error)")
}
}
}
}
그렇게 되면 검색이 실행 되고 Algolia에서 쿼리의 결과를 받아서 바인딩 하고자 하는 곳에 바인딩 하면 된다.
func updateAutocompleteTable() {
if autocompleteResults.isEmpty {
autocompleteTableView.isHidden = true
} else {
autocompleteTableView.isHidden = false
autocompleteTableView.reloadData()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return autocompleteResults.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "AutocompleteTableViewCell", for: indexPath) as! AutocompleteTableViewCell
let hit = autocompleteResults[indexPath.row]
cell.configure(with: "\(hit.title) - \(hit.museumName)")
return cell
}
나는 텍스트 필드 밑에 테이블 뷰에 셀에 바인딩 처리를 했다. 이런 순서로 바인딩을 한다면, Firebase로는 어렵고 복잡한 자동완성 기능을 보다 손 쉽게 구현 할 수 있을 것이다.
자동완성, 그것도 한글 자동완성은 생각보다 간단한 기능이 아니였다. 자음과 모음이 나누어져 있었고, 글자를 입력 할 때마다 서버에서 쿼리를 요청 하는 것임으로 실제로 구현 할 때 이것 저것 생각을 많이 해야 했다. 일상생활에선 쉽게 쓰는 기능들 이지만 그 뒤로는 얼마나 많은 고심을 했는가를 간접적으로 체험 할 수 있었던거 같다.
Algolia는 편리한 서비스 이지만 유저가 많아지거나 검색 수가 증가 하면 생각보다 요금이 꽤 나오는거 같다. 지금은 그정도를 고려 하지 않아도 되지만 그럴 땐 실제 자체 백엔드 서버의 필요성이 또 다시 대두 될 것으로 예상 된다.
다만 Algolia와 같은 서비스는 대단히 간단하고 편리하며, 빠르다. 그렇지만 이 구현에 만족하지 말고 실제 자동 완성과 같은 기능을 어떻게 처리 되는지 전체적인 로직을 공부 해보는 것도 좋을거 같다는 생각이 들었다. 또한 공부가 깊어질 수록 서버와 백엔드 차원의 고려도 프론트 작업을 함과 동시에 들어야 효율적이고 빠를거라는 생각이 들었다.
현업에서는 RESTful API, HTTP 와 같은 네트워크의 처리도 알아야 하니 조만간 정리를 하고 넘어가야 겠다.