쇼핑 경험을 개선하기 위해 쉽게 검색할 수 있도록 모바일 메뉴에 검색창 추가 방법에 대해서 살펴봤지만 여기서 더 나아가 검색어 입력 시 페이지 이동없이 그 자리에서 검색 결과를 보여주는 라이브 서치 기능에 대해서 살펴 봅니다.
[검색 고도화] 라이브 서치 기능 추가하기
최근 지인이 워드프레스를 이용해 쇼핑몰 구축을 시도하면서 배웠던 배웠던 다양한 경험들을 해당 쇼핑몰 블로그에 연재해 왔는데요.
쇼핑몰이 상품만 파는 것이 아니라 쇼핑몰을 방문하는 고객들에게 열가지 유용한 정보를 제공하는 블로그의 효용성이 높다는 점을 십분 활용하고, 처음 시작하는 쇼핑몰의 신뢰성을 주기 위해 비록 삽질이지만 삽질기를 낱낱히 공개하기로 했다고 하네요.
쇼핑몰 구축 시 도와주었던 인연으로 그 쇼핑몰을 알리고 쇼핑몰 구축 경험담을 보다 널리 알리기 위해서 여기 happist.com에도 같이 공유합니다. 조금 사심이 있기는 합니다.
[검색 고도화] 라이브 서치 기능 추가하기
우커머스 테마를 고를 때 각 테마마다 그들이 주장하는 주요 기능들을 소구할 때, 라이브 서치 기능을 중요한 기능으로 이야기하는 경우를 종종 만납니다.
일반적인 검색 프로세스를 살펴보면 아래와 같은 다소 복잡한 과정을 거칩니다.
그런데 모바일의 경우 별도 페이로 이동했다 다시 되돌아오는 과정이 쉽지는 않습니다. 되돌아가기 버튼이 점점 없어지는 추세죠. 아주 오래전부터 아이폰은 되돌아가ㅣ 하드키를 없앴고, 덩달아 삼성과 같은 다른 스마트폰 회사들도 이러한 하드키를 없앴습니다.
앱의 UI에서 되돌아가기 버튼이 없는 것은 아니지만 아무래도 불편합니다. 그리고 UX를 더 단순화한다고 되돌아가기 기능은 있지만 다시 실행하는 기능을 또 없기도 하죠.
요는 스마트폰일수록 UX가 단순화되면서 이러 저리 이동하는 것이 쉽지 않다는 것이고, 이러한 추세에 사이특 맞추어서 사용 경험을 높일 수 있도록 해야한다는 것입니다.
검색 관련해서는 사이트 내용을 잘 정리해 빠르고 정확하게 검색을 할 수 있게 만드는 검색 엔진과 검색할 수 있도록 검색 양식을 만드는 검색폼 그리고 검색 결과를 어떻게 보여줄 것인가라는 세가지 프로세스가 있습니다.
[검색 고도화] 최고의 검색 엔진을 찾자
아러한 세가지 프로세스중에서 여기에서는 검색 결과를 어떻게 보여줄 것인가에 대한 이야기 중의 하나인 라이브 서치 기능에 대해서 알아보겠습니다.
1. 사용자 경험을 높일 수 있는 라이브 서치 기능
사이트 검색에서 이러한 사용 경험을 높일 수 있는 방법이 바로 라이브 서치 기능입니다.
이 라이브 서치 기능은 검색창에 검색어를 입력하면 바로바로 검색 결과를 창 아래 또는 위에 띄워주죠.
이러한 라이브 서치기능의 장점은 아래와 같이 정리해 볼 수 있습니다.
검색 시 검색 실행 명령인 엔터 단계와 검색 결과 페이지로 이동이라는 단계가 줄어듭니다.
검색 간련 단어를 입력하자마자 첫 글자부터 해당되는 검색 결과를 뿌려주기 때문에 바로 바로 결과를 보면서 검색어를 바꾸어가며 검색할 수 있기 때문에 검색 효율이 높아지비낟.
거기다 바로바로 반응하기 때문에 만족도가 높아질 수 있습니다.
2. 라이브 서치 플러그인 3가지
사이트에 라이브 서치 기능은 테마에서 지원한다면 바로 사용할 수 있겠죠.
그렇지만 대부분의 테마에서 라이브 서치 기능은 기본으로 내장되어 있지는 않습니다.
OceanWP와 같이 커스터마이징을 주요 특징으로하는 테마의 경우 기본 기능하에 여러가지 기능을 추가하는 방식을 취하기 때문에 이러한 기능이 추가되어 있지는 않습니다.
이런 경우 라이브 서치 기능 추가를 위해서는 플러그인의 힘을 빌리는 것도 좋은 방법입니다. 능력이 된다면 직접 프로그램해도 좋지만요.
라이브 서치 기능중에서 가장 많이 추천되는 플러그인이 Dave’s WordPress Live Search인데요.
제가 보기에 다음과 같은 장점이 있는 것 같습니다.
사이트에서 사용하는 검색 명령에 상관없이 기존 검색폼에서 자동으로 라이브 서치 기능을 구현해 줍니다.
사용자에 따라서 라이브 서치 결과 폼을 커스터마이징할 수 있습니다. 그것도 별다른 코딩없이.
리이브 서치 플러그인들이 그렇듯이 성능 좋은 서치 엔진과 같이 사용해 성과를 높일 수 있습니다. 무료로 강력한 검색 기능을 제공하는 Relevanssi – A Better Search 와 활용 시 무료로 좋은 성과를 낼 수 있습니다.
와 활용 시 무료로 좋은 성과를 낼 수 있습니다. 프로그래밍 능력이 된다면 다양한 필터 기능을 적용할수 있습니다.
이 플러그인도 여러가지 단점이 있습니다.
가장 큰 단점은 최근 업데이트가 멈추고, 지원도 안되고 있다는 점입니다. 이 그를 ㅆ는 2019년 6월 기준 근 1년동안 업데이트가 안되고 있습니다.
두번째로 SearchWP Live Ajax Search를 들 수 있습니다.
이 플러그인은 SearchWP라는 굉장히 파워플한 워드프레서 검색 플러근에서 제공하는 라이브 서치 기능인데요.
그렇지만 이 플러그인도 단점이 많이 있기는 합니다.
우선 개발자들이 쉽게 커스터마이징을 할 수 있지만 일반들이 커스터마이징 하기는 쉽지는 않습니다. 어느 정도 기술적인 지식을 가지고 있어야 합니다.
위에서 소개한 Dave’s WordPress Live Search보다는 호환성이 높지는 않습니다. get_search_form()을 명시적으로 사용하는 검색폼은 자동으로 지원하지만 그렇지 않은 경우 별도 커스터마이징을 해야 합니다.
Ajax Search Lite는 Ajax Search Pro라는 검색 엔진과 라이브 서치 기능을 동시에 제공하는 프리미엄 플러그인의 무료 공개판입니다.
기본적인 검색 기능을 라이브 서치로 구현해 보여주는데요.
개인적으로 장점이라면 아래와 같은 점을 들 수 있습니다.
검색폼에서 다양한 필터를 바로 선택할 수 있습니다. 포스팅 제목, 콘텐츠, 글쓴이 등읃
제 개인적으로는 단점이 더 많이 보이기는 합니다.
기존 테마에서 구현해 놓은 검색폼을 그대로 지원하지는 않습니다. Ajax Search Lite에서 제공한ㄴ 검색폼을 별도로 설치해야 하죠.
Ajax Search Lite는 무료 공개판이다보니, 기본적인 검색 기능만 제공합니다.
< 쇼핑몰 웹 사이트 개발 프로젝트 9단계 : 구현 - 상품 나열 및 상품 검색 기능 >
이번 프로젝트는 간단한 쇼핑몰 웹 사이트를 개발하는 것입니다. 프로젝트를 시작하기 전에 필요한 사전 지식으로
Tomcat 서버 운용, HTML, Java, 데이터베이스, JSP과 통합 개발 환경(IDE) 등 프로그래밍 기초 지식에 대한 전반
적인 이해가 필요합니다. 사실 이 프로젝트에 필요한 HTML과 Oracle 데이터베이스 부분은 기초 지식이면 충분하기
때문에 이러한 프로젝트 솔루션을 최대한 빠르게 완주하고 싶으신 분은 Java부터 집중적으로 공부하시길 추천합니다.
Oracle 11g 강좌 : http://blog.naver.com/ndb796/220470405738
JAVA 기초 강좌 : http://blog.naver.com/ndb796/220504477115
JAVA 심화 강좌 : http://blog.naver.com/ndb796/220595701047
프로그래밍 능력은 ‘ 내가 원하는 것을 직접 만들 수 있는 능력 ‘입니다.
자신의 필요에 근거해서 프로젝트의 방향을 바꿀 수 있는 융통성을 가집시다.
—————————————————————- —————————-
지난 시간까지 잘 따라오셨다면 프로젝트 폴더는 아래와 같을 것입니다.
고객 & 관리자 권한으로 로그인 할 수 있는 기능을 제공했고 그와 동시에
회원가입 한 뒤에 만들어진 계정으로 다시 로그인할 수 있게 되었습니다.
이제 모델(Model) 부분을 수정하도록 하겠습니다.
Product.java 라는 자바 클래스를 생성하도록 하겠습니다.
코드 생성하는 방법은 저번 시간에 알려드렸습니다.
< Product.java >
package domain;
public class Product {
int productid;
String producttype;
String productname;
String explanation;
int price;
public int getProductid() {
return productid;
}
public Product(int productid, String producttype, String productname, String explanation, int price, int inventory) {
this.productid = productid;
this.producttype = producttype;
this.productname = productname;
this.explanation = explanation;
this.price = price;
this.inventory = inventory;
}
public void setProductid(int productid) {
this.productid = productid;
}
public String getProducttype() {
return producttype;
}
public void setProducttype(String producttype) {
this.producttype = producttype;
}
public String getProductname() {
return productname;
}
public void setProductname(String productname) {
this.productname = productname;
}
public String getExplanation() {
return explanation;
}
public void setExplanation(String explanation) {
this.explanation = explanation;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getInventory() {
return inventory;
}
public void setInventory(int inventory) {
this.inventory = inventory;
}
int inventory;
}
이쯤에서 데이터베이스의 shoppingproduct 테이블에 예시 데이터를 몇 개 삽입하도록 하겠습니다.
insert into shoppingproduct values(1, ‘Laptop’, ‘Gigabyte P55K V5’,
‘Display Screen 15 inch, Chip set made by Intel, i7’, 1500, 150);
insert into shoppingproduct values(2, ‘Mouse’, ‘Gigabyte GM-M6900’,
‘Interface USB, Maximum sensitivity 32’, 20, 300);
insert into shoppingproduct values(3, ‘Mainboard’, ‘Gigabyte GA-B85M-D3H’,
‘Socket Intel-Socket1150, mATX of Standard’, 100, 50);
참고로 모든 데이터 입력이 끝난 후에 Commit;는 잊지 말아주세요.
이제 데이터에 접근할 수 있는 클래스 인 DAO(Data Access Object)를 생성하겠습니다.
< ProductDAO.java >
package domain;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import util.DBConnectionPool;
public class ProductDAO {
private DBConnectionPool connPool;
private static final String ALLRETRIEVE_STMT
= “SELECT * FROM shoppingproduct”;
private static final String INSERT_STMT = “INSERT INTO shoppingproduct VALUES(?,?,?,?,?,?)”;
private static final String UPDATE_STMT = “UPDATE shoppingproduct SET ProductType = ? ProductName = ? Explanation = ? Price = ? Inventory = ? WHERE ProductID = ?”;
private static final String GETID_STMT = “SELECT COUNT(ProductID) FROM shoppingproduct”;
ArrayList allproductRetrieve() throws SQLException {
ArrayList products = new ArrayList();
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rset = null;
try {
conn = connPool.getPoolConnection();
stmt = conn.prepareStatement(ALLRETRIEVE_STMT);
rset = stmt.executeQuery();
while (rset.next()) {
int ProductID = rset.getInt(1);
String ProductType = rset.getString(2);
String ProductName = rset.getString(3);
String Explanation = rset.getString(4);
int Price = rset.getInt(5);
int Inventory = rset.getInt(6);
products.add(new Product(ProductID, ProductType, ProductName, Explanation, Price, Inventory));
}
return products;
} catch (SQLException se) {
throw new RuntimeException(
“A database error occurred. ” + se.getMessage());
} catch (Exception e) {
throw new RuntimeException(“Exception: ” + e.getMessage());
} finally {
if (rset != null) {
try {
rset.close();
} catch (SQLException se) {
se.printStackTrace(System.err);
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException se) {
se.printStackTrace(System.err);
}
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace(System.err);
}
}
}
}
ArrayList productRetrieve(String productname) throws SQLException {
ArrayList products = new ArrayList();
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rset = null;
try {
conn = connPool.getPoolConnection();
stmt = conn.prepareStatement(“SELECT * FROM shoppingproduct WHERE ProductName like ‘%” + productname + “%'”);
rset = stmt.executeQuery();
while (rset.next()) {
int ProductID = rset.getInt(1);
String ProductType = rset.getString(2);
String ProductName = rset.getString(3);
String Explanation = rset.getString(4);
int Price = rset.getInt(5);
int Inventory = rset.getInt(6);
products.add(new Product(ProductID, ProductType, ProductName, Explanation, Price, Inventory));
}
return products;
} catch (SQLException se) {
throw new RuntimeException(
“A database error occurred. ” + se.getMessage());
} catch (Exception e) {
throw new RuntimeException(“Exception: ” + e.getMessage());
} finally {
if (rset != null) {
try {
rset.close();
} catch (SQLException se) {
se.printStackTrace(System.err);
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException se) {
se.printStackTrace(System.err);
}
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace(System.err);
}
}
}
}
void productInsert(String producttype, String productname, String explanation, int price, int inventory) {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rset = null;
try {
conn = connPool.getPoolConnection();
stmt = conn.prepareStatement(GETID_STMT);
rset = stmt.executeQuery();
int ID = -1;
rset.next();
ID = rset.getInt(“COUNT(ProductID)”);
ID++;
stmt = conn.prepareStatement(INSERT_STMT);
stmt.setInt(1, ID);
stmt.setString(2, producttype);
stmt.setString(3, productname);
stmt.setString(4, explanation);
stmt.setInt(5, price);
stmt.setInt(6, inventory);
stmt.executeQuery();
} catch (SQLException se) {
throw new RuntimeException(
“A database error occurred. ” + se.getMessage());
} finally {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException se) {
se.printStackTrace(System.err);
}
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace(System.err);
}
}
}
}
void productUpdate(int productid, String producttype, String productname, String explanation, int price, int inventory) {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rset = null;
try {
conn = connPool.getPoolConnection();
stmt = conn.prepareStatement(UPDATE_STMT);
stmt.setString(1, producttype);
stmt.setString(2, productname);
stmt.setString(3, explanation);
stmt.setInt(4, price);
stmt.setInt(5, inventory);
stmt.setInt(6, productid);
stmt.executeQuery();
} catch (SQLException se) {
throw new RuntimeException(
“A database error occurred. ” + se.getMessage());
} finally {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException se) {
se.printStackTrace(System.err);
}
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace(System.err);
}
}
}
}
}
위 코드를 잘 보면 총 4개의 SQL 문장이 준비되어있습니다.
하나는 shoppingproduct에서 모든 데이터를 뽑아오는 SQL문이고,
다른 3개는 shoppingproduct에 데이터를 입력하고 수정하는데 쓰이는 SQL문입니다.
함수는 총 4개가 존재합니다.
allproductRetrieve() : 모든 데이터를 뽑아옵니다.
productRetrieve() : 검색된 단어를 포함하는 데이터를 뽑아옵니다.
productInsert() : 새로운 상품 데이터를 입력합니다.
productUpdate() : 상품 데이터를 수정합니다.
이제 ProductService.java를 만들도록 하겠습니다.
< ProductService.java >
package domain;
import java.util.ArrayList;
public class ProductService {
private ProductDAO productDataAccess;
public ProductService() {
productDataAccess = new ProductDAO();
}
public ArrayList getAllProduct() {
ArrayList products = null;
try {
products = productDataAccess.allproductRetrieve();
} catch (Exception e) {
products = null;
}
return products;
}
public ArrayList getProduct(String productname) {
ArrayList products = null;
try {
products = productDataAccess.productRetrieve(productname);
} catch (Exception e) {
products = null;
}
return products;
}
public void insertProduct(String producttype, String productname, String explanation, int price, int inventory) {
productDataAccess.productInsert(producttype, productname, explanation, price, inventory);
}
public void updateProduct(int productid, String producttype, String productname, String explanation, int price, int inventory) {
productDataAccess.productUpdate(productid, producttype, productname, explanation, price, inventory);
}
}
이제 먼저 상품 나열 기능부터 제공하도록 하겠습니다.
지난 시간에 작성했던 LoginServlet.java파일을 다음과 같이 바꿔줍니다.
< LoginServlet.java >
package web;
import domain.Product;
import domain.ProductService;
import domain.UserService;
import domain.User;
import javax.servlet.http.*;
import javax.servlet.*;
import java.io.*;
import java.sql.*;
import java.util.ArrayList;
import oracle.jdbc.pool.OracleDataSource;
import util.Status;
public final class LoginServlet extends HttpServlet {
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
processRequest(request, response);
}
public void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
RequestDispatcher view = null;
UserService UserService = null;
ProductService ProductService = null;
Status status = new Status();
request.setAttribute(“status”, status);
String usertype = request.getParameter(“usertype”);
String username = request.getParameter(“username”);
String password = request.getParameter(“password”);
if (usertype.equals(“unknown”)) {
status.addException(new Exception(
“Please select a login type”));
}
if ((username == null) || (username.length() == 0)) {
status.addException(new Exception(
“Please enter your username”));
}
if ((password == null) || (password.length() == 0)) {
status.addException(new Exception(
“Please enter your password”));
}
User user = null;
ArrayList products = null;
try {
UserService = new UserService();
user = UserService.getUser(usertype, username, password);
ProductService = new ProductService();
products = ProductService.getAllProduct();
if (user == null) {
status.addException(new Exception(
“Please enter your user information in the right way”));
}
if (products == null) {
status.addException(new Exception(
“The product database error”));
}
if (!status.isSuccessful()) {
view = request.getRequestDispatcher(“main.jsp”);
view.forward(request, response);
return;
}
request.setAttribute(“user”, user);
request.setAttribute(“products”, products);
} catch (Exception e) {
status.addException(e);
view = request.getRequestDispatcher(“main.jsp”);
view.forward(request, response);
}
if (usertype.equals(“A”)) {
view = request.getRequestDispatcher(“admin/login.jsp”);
view.forward(request, response);
}
if (usertype.equals(“C”)) {
view = request.getRequestDispatcher(“login.jsp”);
view.forward(request, response);
}
}
}
보시면 아시겠지만 모든 상품의 데이터를 뽑아와서 세션 객체로서 저장했습니다.
이제 기존의 login.jsp 파일의 코드를 수정하도록 하겠습니다.
Product List
<% ArrayList products = (ArrayList) request.getAttribute(“products”);%>
Product ID |
Product Type |
Product Name |
Explanation |
Price |
Inventory |
<%
for (int i = 0; i < products.size(); i++) {
Product product = products.get(i);
%>
<%=product.getProductid()%> |
<%=product.getProducttype()%> |
<%=product.getProductname()%> |
<%=product.getExplanation()%> |
$<%=product.getPrice()%> |
<%=product.getInventory()%> |
<% } %>
이제 상품 나열이 잘 되는지 확인하기 위해서 프로젝트를 실행해보도록 하겠습니다.
보는 것과 같이 잘 데이터베이스와 연동되서 상품의 목록을 나열했습니다.
이제 login.jsp에 다음의 Form을 추가해줍니다.
이제 컨트롤러(Controller)에 SearchProductServlet.java를 만들겠습니다.
< SearchProductServlet.java >
package web;
import domain.Product;
import domain.ProductService;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public final class SearchProductServlet extends HttpServlet {
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
processRequest(request, response);
}
public void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
RequestDispatcher view = null;
ProductService ProductService = null;
String ProductName = request.getParameter(“productname”);
ArrayList products = null;
ProductService = new ProductService();
products = ProductService.getProduct(ProductName);
request.setAttribute(“products”, products);
view = request.getRequestDispatcher(“login.jsp”);
view.forward(request, response);
}
}
또한 web.xml 에서 다음의 내용을 추가해줍니다.
SearchController
web.SearchProductServlet
SearchController
/search
이제 실행해보도록 하겠습니다.
이 기사는 인터넷의 다양한 출처에서 편집되었습니다. 이 기사가 유용했기를 바랍니다. 이 기사가 유용하다고 생각되면 공유하십시오. 매우 감사합니다!