감 잃지말고 개발하기

[JSP] [MVC] [AJAX] [JS] [JSON] AJAX를 이용해 고객센터 페이지 구현하기 #6. FAQ 페이지 구현(마지막) 본문

JSP/MVC

[JSP] [MVC] [AJAX] [JS] [JSON] AJAX를 이용해 고객센터 페이지 구현하기 #6. FAQ 페이지 구현(마지막)

persii 2023. 4. 20. 05:25

교보문고 고객센터 페이지를 모델로 하여 MVC패턴을 지키면서 ajax를 이용한 비동기 통신으로 DB에 저장된 데이터를 브라우저에 출력하는 로직을 기록하고자 한다.

 

지난 포스팅에서는 FAQ 페이지가 요청되는 두 가지 방식 두 번째 방식의 로직을 구현해 보았다. 

이번 포스팅에서는 중분류 버튼을 클릭했을 때 해당 중분류에 따른 FAQ 데이터만 출력되는 로직을 구현해 보도록 하겠다. 전체적인 흐름은 이전 포스팅과 동일하다.

 

목표

 JSP에서 MVC 패턴을 지키면서 AJAX를 통해 데이터를 출력할 수 있다.

 교보문고 고객센터 페이지를 구현할 수 있다.

 

 

로직 흐름

  1. FAQ 페이지에서 중분류 버튼을 클릭하면 ajax로 해당 대분류 코드(PK)와 중분류 코드(PK)가 서버에 넘어간다(faq.jsp).
  2. 해당 컨트롤러에서 URL을 받고, 해당 요청을 처리하는 클래스를 호출한다(AjaxFrontController.java).
  3. Action 클래스에서 비즈니스 로직을 처리하는 클래스를 호출하여 DB로부터 데이터를 가져오고, 가져온 데이터를 JSON 형태의 문자열로 만들어 response 객체에 담아 보낸다(CsCenterFkcodeFaqAction.java).
  4. Service 클래스에서 DAO클래스를 호출한다(BookCscenterFaqService.java).
  5. DAO 클래스에서 MySQL8.0 DB에 접근해 데이터를 가져온다(FaqDAO.java).
  6. 다시 FAQ 페이지로 돌아와 적절한 응답을 한다.

 

 

로직 흐름 코드 및 실행화면

1. FAQ 페이지(faq.jsp)

1-1.  FAQ 페이지 중분류 영역

FAQ 페이지에서 중분류 버튼을 하나 클릭하면 스크립트 함수가 실행된다.

중분류 버튼 클릭

 

1-2.  FAQ 페이지 중분류 영역 HTML 

<button type="button" class="fkbtn" id="tab012"></button>

  • class="fkbtn" : 중분류 버튼을 클릭했을 때 스크립트 함수를 편리하게 호출하기 위한 설정이다.
  • id="tab012" : 중분류 링크를 클릭했을 때 중분류 코드(PK)를 서버로 넘기기 위해 PK값을 id="tab0" 뒤에 붙인다. 이 중분류 코드(PK) 값은 해당 대분류 링크를 클릭했을 때 AJAX로 가져오게끔 만들었다(이전 포스팅 참고).
    • '전체' 버튼을 클릭할 경우, 0이 서버에 전달되도록 할 것이다. 
<!-- faq.ok?fcode=n에 따른 데이터 출력되는 영역 -->
<section class="container mb-5 faqSection">
  <div class="container-header faqTabArea" style="padding:10px 0;">
    <h3><b>도서/상품정보/교과서</b></h3>	
    <div style="height: 50px;">
      <ul class="nav h-100 faqkeywordUl">
        <li class="nav-item my-2 mr-2">
          <button type="button" class="fkbtn" id="tab00">
            <span class="text-center">전체</span>
          </button>
        </li>
        <li class="nav-item my-2 mr-2">
          <button type="button" class="fkbtn" id="tab07">
            <span class="text-center">정보/검색/알림기능</span>
          </button>
        </li>
        <li class="nav-item my-2 mr-2">
          <button type="button" class="fkbtn" id="tab08">
            <span class="text-center">서양도서</span>
          </button>
        </li>
        <li class="nav-item my-2 mr-2">
          <button type="button" class="fkbtn" id="tab09">
            <span class="text-center">일본도서</span>
          </button>
        </li>
        <li class="nav-item my-2 mr-2">
          <button type="button" class="fkbtn" id="tab010">
            <span class="text-center">교과서/방송대교재</span>
          </button>
        </li>
        <li class="nav-item my-2 mr-2">
          <button type="button" class="fkbtn" id="tab011">
            <span class="text-center">일본잡지정기구독</span>
          </button>
        </li>
        <li class="nav-item my-2 mr-2">
          <button type="button" class="fkbtn" id="tab012">
            <span class="text-center">기프트</span>
          </button>
        </li>
      </ul>
    </div>
    <hr class="my-3">
  </div>

  <div class="accordion faqCardList" id="accordionExample">
    <!-- FAQ 데이터 출력(생략) -->
  </div>
</section>
<!-- faq.ok?fcode=n에 따른 데이터 출력되는 영역 -->

 

1-3.  FAQ 페이지 중분류 버튼 클릭 JS

중분류 버튼을 클릭했을 때 호출되는 스크립트 함수이다.

중분류 버튼은 AJAX로 생성된 HTML이기 때문에 $(document).on('click', '식별자', function() { });을 사용한다.

클릭하면 해당 중분류의 PK값과 대분류의 PK값이 서버에 넘어간다.

 

올바른 요청 및 응답이 이루어진 후의 액션은 이전 포스팅에서의 FAQ 데이터 출력 과정과 동일하다.

단, 여기서는 FAQ 데이터만 새로 붙일 것이기 때문에 <div class="faqCardList"> 아래에 붙인다.

<script type="text/javascript">
  
  // 중분류 버튼 클릭 시
  $(document).on('click', '.fkbtn', function() {
    var fkbtnID = $(this).attr('id').substring(4);
    var fc_code = $('#fc_code').val();
    

    $.ajax({
      url: '/cscenter/fkcode/faq.ax',
      method: 'POST',
      data: { "fk_code" : fkbtnID, "fc_code" : fc_code},
      success: function(text) {
        var toJsObj = JSON.parse(text);

        var html = "";

        // FAQ 출력
        if(toJsObj["faqs"] === "no-faqs"){
          // FAQ 데이터가 없는 경우
          html += '<div>';
            html += '<h4><b>등록된 문의가 없습니다.</b></h4>';
          html += '</div>';
        } else {
          $.each(toJsObj["faqs"], function(index, value) {
            html += '<div class="card">';
              html += '<div class="card-header" id="heading"'+index+'" style="background-color:white !important;">'; 
                html += '<h2 class="mb-0">';
                  html += '<button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse"';
                  html += ' data-target="#collapse' + index + '" aria-expanded="true" aria-controls="collapse' + index + '"';
                  html += 'style="color:black !important;">';
                    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>';
          });
        }
        
        // class="faqCardList" 아래에 붙이기
        $(".faqCardList").html(html);
      },
      error: function(jqXHR, textStatus, errorThrown) {
        console.error(errorThrown);
      }
    });
  });
</script>

 

2. 컨트롤러(AjaxFrontController.java)

아래 코드만 기존 코드에 추가해준다.

// 중분류에 따른 Faq 처리 요청
else if (command.equals("/cscenter/fkcode/faq.ax")) {
    action = new CsCenterFkcodeFaqAction();
    try {
        action.execute(req, resp);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

 

3. Action 클래스(CsCenterFkcodeFaqAction.java)

  • ajax로 건너온 대분류 코드(fc_code)와 중분류 코드(fk_code)를 저장한다.
    • Best 10에는 중분류 자체가 출력이 안되므로 fc_code에는 0이 저장될 수 없다.
    • fk_code에는 0이 저장될 수 있다. 0의 경우, 전체 FAQ 데이터를 가져온다('전체' 버튼). 
  • DB에서 fc_code와 fk_code에 따른 FAQ 리스트를 가져온다.
  • 가져온 데이터를 JSON 형태의 문자열로 저장한다.
    • 만들 JSON 형태는 #2. 구현 틀 잡기 포스팅을 참고하길 바란다.
  • 완성된 문자열을 response에 담아 보낸다.

보낼 JSON 형태는 아래와 같다.

{ "faqs" :
   [ 
      {"fk_code" : 1, "fk_value" : "회원가입/탈퇴", "f_id" : 1, "f_title" : "제목", "f_text" : "내용"},
      {"fk_code" : 1, "fk_value" : "회원가입/탈퇴", "f_id" : 2, "f_title" : "제목", "f_text" : "내용"},
      {"fk_code" : 2, "fk_value" : "본인성인인증", "f_id" : 3, "f_title" : "제목", "f_text" : "내용"}
   ]  
}

 

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;

/** fk_code에 따른 FAQ 요청을 처리하는 Action 클래스 */
public class CsCenterFkcodeFaqAction implements AjaxAction {

	@SuppressWarnings("unchecked")
	@Override
	public void execute(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		
		/* 파라미터 저장 */
		int fk_code = Integer.parseInt(req.getParameter("fk_code"));
		int fc_code = Integer.parseInt(req.getParameter("fc_code"));
		
		/* DB에서 fk_code에 따른 데이터 가져오기 */
		BookCscenterFaqService service = new BookCscenterFaqService();
		ArrayList<Faq> faqList = service.selectFaq(fc_code, fk_code);

		String faqStr = null;
		JSONArray faqsarr = new JSONArray();
		JSONObject faqs = new JSONObject();
		
		if(faqList != null) {
			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 오브젝트에 JSON배열 저장
			faqs.put("faqs", faqsarr);
		} else {
			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)

기존 Service 클래스 안에 selectFaq(fc_code, fk_code) 메서드를 추가한다.

/** fc_code, fk_code에 따른 FAQ 데이터를 ArrayList 객체 타입으로 반환하는 메서드 */
public ArrayList<Faq> selectFaq(int fc_code, int fk_code) {
    /* DB 처리 */
    FaqDAO faqDAO = FaqDAO.getInstance();
    Connection conn = getConnection();
    faqDAO.setConnection(conn);
    ArrayList<Faq> faqList = faqDAO.selectFaqList(fc_code, fk_code);
    close(conn);

    return faqList;
}

 

5. DAO 클래스(FaqDAO.java)

기존 FaqDAO 클래스 안에 selectFaqList(fc_code, fk_code) 메서드를 추가한다.

/** fc_code, fk_code에 따른 FAQ 데이터를 ArrayList 객체 타입으로 반환하는 메서드 */
public ArrayList<Faq> selectFaqList(int fc_code, int fk_code) {

	PreparedStatement pstmt = null;
    ResultSet rs = null;
    ArrayList<Faq> faqList = null;

    String sql  = "select f.f_id, f.f_title, f.f_text, fk.fk_code, fk.fk_value "
                + "from faqkeyword fk join faq f "
                + "on f.f_fk_code = fk.fk_code ";		

    if(fk_code == 0) {
        // 전체
        sql += "and fk.fk_fc_code = " + fc_code + " "
             + "and f.f_use = 1 "
             + "order by f.f_id asc";
    } else {
        sql += "and fk.fk_fc_code = " + fc_code + " "
             + "and fk.fk_code = " + fk_code + " "
             + "and f.f_use = 1 "
             + "order by f.f_id asc";
    }

    try {
        pstmt = conn.prepareStatement(sql);
        rs = pstmt.executeQuery();

        if (rs.next()) {
            faqList = new ArrayList<Faq>();

            do {
                faqList.add(new Faq(rs.getInt(1), 
                                    rs.getInt(4), 
                                    rs.getString(2), 
                                    rs.getString(3), 
                                    rs.getString(5)));
            } while (rs.next());
        }
    } catch (Exception e) {
        System.out.println(" F.DAO : selectFaqList(fc_code, fk_code) ERROR : "+e);
    } finally {
        close(rs);
        close(pstmt);
    }
    return faqList;
}

 

6. FAQ 페이지(faq.jsp)

ajax로 보낸 요청이 정상적으로 처리되고, 서버로부터 정상적인 응답이 이루어진 경우, 화면에 그에 따른 응답이 출력된다.

FAQ 중분류 클릭 액션

 

 

이렇게 중분류 버튼을 클릭했을 때 해당 중분류에 따른 FAQ 데이터만 출력되는 로직이 모두 구현되었다. 

이로써 목표한 고객센터 페이지 구현이 완성되었다.

미흡한 부분이 있지만 본 포스팅을 끝으로 고객센터 페이지 구현을 마무리하도록 하겠다.

 

 

고객센터 페이지 구현 끝.