일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- Spring MVC
- jakarta.mail
- Level 1
- jquery
- Sts
- SESSION
- 인증코드로 비밀번호 변경 구현
- 스프링
- 오라클
- java
- 교보문고 따라하기
- 대분류/중분류/소분류
- Spring
- jsp 프로젝트
- js
- Oracle
- 자바
- ajax
- 고객센터 구현
- 세션
- 로그인과 장바구니 구현
- 이메일로 인증코드 전송 구현
- MVC
- 일단_해보는거야
- 프로그래머스
- json
- 코딩
- jsp
- MySQL
- 다중 카테고리 구현
감 잃지말고 개발하기
[JSP] [MVC] [AJAX] [JS] [JSON] AJAX를 이용해 고객센터 페이지 구현하기 #5. FAQ 페이지 구현 본문
[JSP] [MVC] [AJAX] [JS] [JSON] AJAX를 이용해 고객센터 페이지 구현하기 #5. FAQ 페이지 구현
persii 2023. 4. 20. 04:07교보문고 고객센터 페이지를 모델로 하여 MVC패턴을 지키면서 ajax를 이용한 비동기 통신으로 DB에 저장된 데이터를 브라우저에 출력하는 로직을 기록하고자 한다.
지난 포스팅에서는 FAQ 페이지가 요청되는 두 가지 방식 중 첫 번째 방식의 로직을 정리해 보았다.
이번 포스팅에서는 두 번째 방식의 로직을 기록해 보겠다. 두 번째 방식의 로직은 AJAX를 이용한 데이터 통신 방식이다.
목표
♠ JSP에서 MVC 패턴을 지키면서 AJAX를 통해 데이터를 출력할 수 있다.
♠ 교보문고 고객센터 페이지를 구현할 수 있다.
로직 흐름
- 이미 한번 요청된 FAQ 페이지에서 다시 대분류 카테고리를 클릭하면 ajax로 해당 대분류 코드(PK)가 서버에 넘어간다(faq.jsp).
- 해당 컨트롤러에서 URL을 받고, 해당 요청을 처리하는 클래스를 호출한다(AjaxFrontController.java).
- Action 클래스에서 비즈니스 로직을 처리하는 클래스를 호출하여 DB로부터 데이터를 가져오고, 가져온 데이터를 JSON 형태의 문자열로 만들어 response 객체에 담아 보낸다(CsCenterFaqAction.java).
- Service 클래스에서 DAO클래스를 호출한다(BookCscenterFaqService.java).
- DAO 클래스에서 MySQL8.0 DB에 접근해 데이터를 가져온다(FaqDAO. FaqKeyDAO).
- 다시 FAQ 페이지로 돌아와 적절한 응답을 한다.
로직 흐름 코드 및 실행화면
1. FAQ 페이지(faq.jsp)
1-1. ▼ FAQ 페이지 대분류 영역
FAQ 페이지에서 대분류를 하나 클릭하면 스크립트 함수가 실행된다.
1-2. ▼ FAQ 페이지 대분류 영역 HTML
<a href="#" onclick="return false" class="fcode" id="f0"><h5>Best 10</h5></a>
- href="#" onclick="return false" : 대분류를 클릭했을 때 AJAX로 통신할 것이므로 <a> 태그의 링크 기능(href로 이동)을 무시해 준다.
- class="fcode" : 대분류 링크를 클릭했을 때 스크립트 함수를 편리하게 호출하기 위한 설정이다.
- id="f0" : 대분류 링크를 클릭했을 때 대분류 코드(PK)를 서버로 넘기기 위해 PK값을 id="f" 뒤에 붙인다.
<section class="features-area section_gap">
<div class="container">
<div class="container-header">
<h3><b>자주 묻는 질문</b></h3>
</div>
<div class="features-inner text-center">
<div class="row row-cols-4 mx-2 faqRow">
<div class="col-3 p-2 border">
<li class="mt-2">
<a href="#" onclick="return false" class="fcode" id="f0"><h5>Best 10</h5></a>
</li>
</div>
<div class="col-3 p-2 border">
<li class="mt-2">
<a href="#" onclick="return false" class="fcode" id="f1"><h5>회원</h5></a>
</li>
</div>
<div class="col-3 p-2 border">
<li class="mt-2">
<a href="#" onclick="return false" class="fcode" id="f2"><h5>도서/상품정보/교과서</h5></a>
</li>
</div>
<div class="col-3 p-2 border">
<li class="mt-2">
<a href="#" onclick="return false" class="fcode" id="f3"><h5>주문/결제</h5></a>
</li>
</div>
<div class="col-3 p-2 border">
<li class="mt-2">
<a href="#" onclick="return false" class="fcode" id="f4"><h5>배송/수령일 안내</h5></a>
</li>
</div>
<div class="col-3 p-2 border">
<li class="mt-2">
<a href="#" onclick="return false" class="fcode" id="f5"><h5>반품/교환/환불</h5></a>
</li>
</div>
<div class="col-3 p-2 border">
<li class="mt-2">
<a href="#" onclick="return false" class="fcode" id="f6"><h5>세금계산서/증빙</h5></a>
</li>
</div>
<div class="col-3 p-2 border">
<li class="mt-2">
<a href="#" onclick="return false" class="fcode" id="f7"><h5>서비스</h5></a>
</li>
</div>
<div class="col-3 p-2 border">
<li class="mt-2">
<a href="#" onclick="return false" class="fcode" id="f8"><h5>eBook</h5></a>
</li>
</div>
<div class="col-3 p-2 border">
<li class="mt-2">
<a href="#" onclick="return false" class="fcode" id="f9"><h5>중고장터</h5></a>
</li>
</div>
<div class="col-3 p-2 border">
<li class="mt-2">
<a href="#" onclick="return false" class="fcode" id="f10"><h5>POD 주문형출판</h5></a>
</li>
</div>
<div class="col-3 p-2 border">
<li class="mt-2">
<a href="#" onclick="return false" class="fcode" id="f11"><h5>PubPle(퍼플)</h5></a>
</li>
</div>
<div class="col-3 p-2 border">
<li class="mt-2">
<a href="#" onclick="return false" class="fcode" id="f12"><h5>sam</h5></a>
</li>
</div>
<div class="col-3 p-2 border">
<li class="mt-2">
<a href="#" onclick="return false" class="fcode" id="f13"><h5>북모닝</h5></a>
</li>
</div>
<div class="col-3 p-2 border">
<li class="mt-2">
<a href="#" onclick="return false"><h5></h5></a>
</li>
</div>
<div class="col-3 p-2 border">
<li class="mt-2">
<a href="#" onclick="return false"><h5></h5></a>
</li>
</div>
</div>
</div>
</div>
</section>
1-3. ▼ FAQ 페이지 대분류 링크 클릭 JS
대분류 링크를 클릭했을 때 호출되는 스크립트 함수이다.클릭하면 해당 태그의 id값에서 f를 뺀 숫자가 fcode 변수에 저장되어 서버에 넘어간다.
- ajax로 호출할 URL : "/cscenter/faq.ax"
- ajax로 보낼 데이터 통신타입 : POST
- ajax로 서버에 요청 시 보낼 파라미터: 해당 대분류의 코드(PK)
- 응답받을 데이터 타입 : TEXT
- 서버에서 DB 조회에 대한 결과물을 문자열로 만들어 보낼 것이므로 dataType을 TEXT로 설정한다.
- 정상적으로 요청/응답된 경우(success) : 서버 딴 코드를 거친 후 설명하겠다.
<script type="text/javascript">
$(document).ready(function(){
$(".fcode").click(function() {
var fcode = $(this).attr('id');
fcode = fcode.substring(1);
// ajax로 보내서 출력
$.ajax({
url: '/cscenter/faq.ax',
type: "POST",
data: {"fcode": fcode},
dataType: "TEXT",
success: function() {
// 추후에 코드 추가
},
error: function(request, status, error) {
console.log("code: "+request.status+"\n"+"message : "+request.responseText+"\n"+"error: "+error);
}
});
});
});
</script>
2. 컨트롤러(AjaxFrontController.java)
웹 브라우저에서 요청한 URL에 해당하는 if문으로 들어오고 CsCenterFaqAction클래스의 execute() 메서드가 호출된다.
package controller;
import java.io.IOException;
import java.rmi.ServerException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import action.CsCenterFaqAction;
import action.CsCenterFkcodeFaqAction;
import svc.AjaxAction;
/** AJAX로 데이터 통신
* ActionForward 클래스 필요 X */
@WebServlet("*.ax")
public class AjaxFrontController extends HttpServlet {
protected void doProcess(HttpServletRequest req, HttpServletResponse resp)
throws ServerException, IOException, ServletException {
req.setCharacterEncoding("UTF-8");
/* 1. 요청 주소 파악 */
String requestURI = req.getRequestURI();
String contextPath = req.getContextPath();
String command = requestURI.substring(contextPath.length());
/* 2. 각 요청 주소의 매핑 처리 */
AjaxAction action = null;
// 전체 Faq 처리 요청
if (command.equals("/cscenter/faq.ax")) {
action = new CsCenterFaqAction();
try {
action.execute(req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServerException, IOException, ServletException {
doProcess(req, resp);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServerException, IOException, ServletException {
doProcess(req, resp);
}
}
3. Action 클래스(CsCenterFaqAction.java)
Action 클래스에서 해야 할 중요한 부분은 DB로부터 데이터를 들고 와 JSON 형태로 만드는 것이다.
- ajax로 건너온 대분류 코드(fcode)를 저장한다.
- 이 fcode에는 0이 저장될 수 있으므로 모든 과정에서 "Best10"의 경우도 생각해야 한다.
- DB에서 fcode에 따른 중분류 리스트와 FAQ 리스트를 가져온다.
- "Best10"의 경우 별다른 중분류가 필요 없으므로 중분류 리스트는 fcode가 0이 아닌 경우에만 가져오도록 한다.
- 가져온 데이터를 JSON 형태의 문자열로 저장한다.
- 만들 JSON 형태는 #2. 구현 틀 잡기 포스팅을 참고하길 바란다.
- 데이터 유무에 따라 저장되는 정보가 다르니 아래 코드의 주석을 참고하길 바란다.
- 보낼 문자열을 response에 담아 보낸다.
2023.04.18 - [JSP/MVC] - [JSP] [MVC] [AJAX] [JS] [JSON] AJAX를 이용해 고객센터 페이지 구현하기 #2. 구현 틀 잡기
[JSP] [MVC] [AJAX] [JS] [JSON] AJAX를 이용해 고객센터 페이지 구현하기 #2. 구현 틀 잡기
교보문고 고객센터 페이지를 모델로 하여 MVC패턴을 지키면서 ajax를 이용한 비동기 통신으로 DB에 저장된 데이터를 브라우저에 출력하는 로직을 기록하고자 한다. 지난 포스팅에서는 교보문고
persimmon-ary-stepbystep.tistory.com
package action;
import java.io.PrintWriter;
import java.util.ArrayList;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import svc.AjaxAction;
import svc.BookCscenterFaqService;
import vo.Faq;
import vo.FaqKeyword;
public class CsCenterFaqAction implements AjaxAction {
@SuppressWarnings("unchecked")
@Override
public void execute(HttpServletRequest req, HttpServletResponse resp) throws Exception {
/* 파라미터 저장 */
int fcode = Integer.parseInt(req.getParameter("fcode"));
/* DB에서 fcode에 따른 데이터 가져오기 */
BookCscenterFaqService service = new BookCscenterFaqService();
ArrayList<FaqKeyword> fkList = new ArrayList<>();
if(fcode != 0) {
fkList = service.selectFks(fcode);
}
ArrayList<Faq> faqList = service.selectFaq(fcode);
String faqStr = null; // 최종적인 JSONObject를 문자열로 만들어 저장할 변수
JSONArray faqsarr = new JSONArray(); // FAQ 리스트를 저장할 JSONArray
JSONArray fkssarr = new JSONArray(); // 중분류 리스트를 저장할 JSONArray
JSONObject faqs = new JSONObject(); // 최종적으로 저장할 JSONObject
if(fkList.size() > 0) {
// Best10 링크 외의 링크를 클릭한 경우
for(FaqKeyword fk : fkList) {
// 하나씩 접근해 JSONObject에 저장
JSONObject fkObj = new JSONObject();
fkObj.put("fk_code", fk.getFk_code());
fkObj.put("fk_value", fk.getFk_value());
// JSONArray에 저장
fkssarr.add(fkObj);
}
// 최종적인 faqs 오브젝트에 중분류 리스트, 대분류 정보 저장
faqs.put("fks", fkssarr);
faqs.put("fc_code", fkList.get(0).getFk_fc_code());
faqs.put("fc_value", fkList.get(0).getFc_value());
} else {
// Best10 링크를 클릭한 경우
faqs.put("fks", "no-fks");
faqs.put("fc_code", fcode);
faqs.put("fc_value", "Best10");
}
if(faqList != null) {
// FAQ 리스트
for(Faq faq : faqList) {
JSONObject faqObj = new JSONObject();
faqObj.put("fk_code", faq.getF_fk_code());
faqObj.put("fk_value", faq.getFk_value());
faqObj.put("f_id", faq.getF_id());
faqObj.put("f_title", faq.getF_title());
faqObj.put("f_text", faq.getF_text());
// 오브젝트 데이터를 JSONArray에 순서대로 저장
faqsarr.add(faqObj);
}
// 최종적인 faqs 오브젝트에 저장
faqs.put("faqs", faqsarr);
} else {
// FAQ 데이터가 없는 경우
faqs.put("faqs", "no-faqs");
}
// 파싱할 데이터 저장
faqStr = faqs.toJSONString();
/* 파싱할 최종 데이터 보내기 */
resp.setCharacterEncoding("utf-8");
PrintWriter out = resp.getWriter();
out.print(faqStr);
out.close();
}
}
4. Service 클래스(BookCscenterFaqService.java)
5. DAO 클래스(FaqKeyDAO.java & FaqDAO.java)
Service 클래스와 DAO 클래스의 코드는 바로 이전 포스팅(#4. FAQ 페이지 구현)에서 다루었으므로 코드 설명은 생략하겠다.
2023.04.19 - [JSP/MVC] - [JSP] [MVC] [AJAX] [JS] [JSON] AJAX를 이용해 고객센터 페이지 구현하기 #4. FAQ 페이지 구현
[JSP] [MVC] [AJAX] [JS] [JSON] AJAX를 이용해 고객센터 페이지 구현하기 #4. FAQ 페이지 구현
교보문고 고객센터 페이지를 모델로 하여 MVC패턴을 지키면서 ajax를 이용한 비동기 통신으로 DB에 저장된 데이터를 브라우저에 출력하는 로직을 기록하고자 한다. 지난 포스팅에서는 고객센터
persimmon-ary-stepbystep.tistory.com
6. FAQ 페이지(faq.jsp)
ajax로 보낸 요청이 정상적으로 처리되고, 서버로부터 정상적인 응답이 이루어진 경우, 화면에 그에 따른 응답이 출력되도록 할 것이다.
이제 ajax가 success 된 상태의 JS 코드를 마저 작성해 보자.
6-1. ▼ FAQ 페이지 대분류 링크 클릭 JS
이 부분에서의 중요한 점은 서버로부터 넘어온 데이터 구조를 정확하게 파악하여 원하는 화면 영역에 출력되도록 작성하는 것에 있다.
살펴보면,
중분류 데이터가 있다면, 그 데이터는 <div class="faqkeywordUI"> 태그 안에 루프를 돌면서 출력되어야 한다.
반대로 중분류 데이터가 없다면, 즉 Best 10의 경우, <div class="faqkeywordUl"> 태그는 없다.
중분류 데이터가 있던 없던, 중분류 데이터와 FAQ 데이터를 구분하는 실선 <hr class="my-3"> 태그는 출력되어야 한다.
FAQ 데이터는 <div class="faqCardList"> 태그 안에 출력되어야 한다.
만약 FAQ 데이터가 있다면, <div class="card"> 태그 안에 루프를 돌면서 출력되어야 한다.
반대로 FAQ 데이터가 없다면, <h3> 등록된 문의가 없습니다. </h3>가 출력되어야 한다.
모든 if-else문을 경유하여 완성된 HTML 코드는 <section class="faqSection"> 태그 안에 출력되어야 한다.
아래의 이미지를 보면서 코드를 이해하면 출력 부분을 이해할 수 있을 것이다.
<script type="text/javascript">
$(document).ready(function(){
$(".fcode").click(function() {
var fcode = $(this).attr('id');
fcode = fcode.substring(1);
// ajax로 보내서 출력
$.ajax({
url: '/cscenter/faq.ax',
type: "POST",
data: {"fcode": fcode},
dataType: "TEXT",
success: function(text) {
// String타입의 text를 자바스크립트 객체로 변환
var toJsObj = JSON.parse(text);
var html = "";
html = '<div class="container-header faqTabArea">';
html += '<h3><b>'+toJsObj["fc_value"]+'</b></h3>';
html += '<input type="hidden" id="fc_code" value="'+toJsObj["fc_code"]+'">';
// 중분류 출력부분
if(toJsObj["fks"] != "no-fks") {
html += '<div>';
html += '<ul class="nav faqkeywordUl">';
html += '<li class="nav-item">';
html += '<button type="button" class="fkbtn" id="tab00">';
html += '<span class="text-center">전체</span>';
html += '</button>';
html += '</li>';
$.each(toJsObj["fks"], function(index, value) {
html += '<li class="nav-item">';
html += '<button type="button" class="fkbtn" id="tab0'+value.fk_code+'">';
html += '<span class="text-center">'+value.fk_value+'</span>';
html += '</button>';
html += '</li>';
});
html += '</ul>';
html += '</div>';
}
html += '<hr class="my-3">';
html += '</div>';
html += '<div class="accordion faqCardList" id="accordionExample">';
// FAQ 출력부분
if(toJsObj["faqs"] === "no-faqs"){
html += '<div>';
html += '<h3><b>등록된 문의가 없습니다.</b></h3>';
html += '</div>';
} else {
// FAQ(질문/답변이 있는 경우)
$.each(toJsObj["faqs"], function(index, value) {
html += '<div class="card">';
html += '<div class="card-header" id="heading"'+index+'">';
html += '<h2>';
html += '<button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse"';
html += ' data-target="#collapse' + index + '" aria-expanded="true"';
html += ' aria-controls="collapse' + index + '">';
html += '['+value.fk_value+']'+ value.f_title;
html += '</button>';
html += '</h2>';
html += '</div>';
html += '<div id="collapse'+index+'" class="collapse" aria-labelledby="heading'+index+'" data-parent="#accordionExample">';
html += '<div class="card-body" style="background-color:rgba(0,0,0,.03);">';
html += value.f_text;
html += '</div>';
html += '</div>';
html += '</div>';
});
html += '</div>';
}
// class="faqSection" 아래에 붙이기
$(".faqSection").html(html);
},
error: function(request, status, error) {
console.log("code: "+request.status+"\n"+"message : "+request.responseText+"\n"+"error: "+error);
}
});
});
});
</script>
6-2. 경우에 따른 FAQ 페이지 화면
경우 1. ▼ 중분류 데이터와 FAQ 데이터가 모두 있는 경우
경우 2. ▼ 중분류 데이터는 있지만 FAQ 데이터는 없는 경우
경우 3. ▼ Best 10 링크를 클릭한 경우
중분류 버튼을 클릭하면 해당 중분류에 따른 FAQ 데이터만 출력되는 로직은 다음 포스팅에서 이어 정리해 보겠다.
♣ 추가 설명 ♣
1. <a> 태그에서 링크 이동 무시하기
<a href="#" onclick="return false">
<a> 태그의 기본 속성은 링크 기능이다. 즉, href 속성이 활성화되어 해당 웹페이지의 주소로 이동하는 것이다.
하지만 만약 <a> 태그에서 onclick 이벤트 핸들러를 걸어주면, <a> 태그가 클릭되었을 때 onclick 이벤트가 먼저 액션을 취한 후, href로 이동하게 된다.
여기다 onclick="return false"를 붙여 준다면 원래 가지고 있는 기본 속성은 무시되어 <a> 태그를 클릭해도 링크가 활성화되지 않는다.