감 잃지말고 개발하기

[JSP] [MVC] [AJAX] [JS] [JSON] AJAX를 이용한 비동기 통신으로 도서 구매 리뷰(후기) 출력하기 본문

JSP

[JSP] [MVC] [AJAX] [JS] [JSON] AJAX를 이용한 비동기 통신으로 도서 구매 리뷰(후기) 출력하기

persii 2023. 4. 10. 14:25

도서 상세 페이지에서 도서 구매 리뷰를 보여주는 내비게이션 버튼을 클릭하면 ajax를 이용한 비동기 통신으로 디비에 저장되어 있는 해당 도서의 모든 리뷰 데이터가 웹 브라우저로 넘어와서 출력되는 로직을 기록하고자 한다.

이 로직 하나 하려고 얼마나 여기저기를 뒤적거렸는지... 원하는 대로 해결되어 뿌듯하다.

 

목표

 JSP에서 MVC 패턴을 지키면서 AJAX를 통해 리뷰 데이터를 웹 브라우저로 가져올 수 있다. 

♠ JSON.parse()을 사용할 수 있다.

 

 

로직 흐름

  1. 도서 상세 페이지에서 도서 구매 리뷰를 보여주는 리스트를 클릭하면 ajax 함수로 도서 아이디와 페이지번호를 서버로 넘긴다(bookView.jsp).
  2. 리뷰를 담당하는 컨트롤러(ajax 처리)에서 해당 URL을 받아 리뷰 데이터를 조회하는 클래스를 호출한다(BookReviewListAction.java).
  3. Action 클래스에서 비즈니스 로직을 처리하는 클래스를 호출하여 해당 도서 아이디에 따른 모든 리뷰 데이터를 가져온다(BookReviewListService.java).
  4. Service 클래스에서 DAO클래스를 호출한다(BookReviewListService.java).
  5. DAO 클래스에서 MySQL8.0 DB에 접근해 데이터를 가져온다(ReviewDAO).
  6. 도서 상세 페이지에서 정상적인 요청과 응답을 받았을 경우(ajax 함수의 success), 리뷰 데이터를 화면에 출력한다.

 

 

로직 흐름 코드 및 실행화면

1. 도서 상세 페이지(bookView.jsp)

1-1. 도서 상세 페이지 웹 화면

Reviews를 클릭하면 ajax로 URL과 데이터가 서버로 넘어간다.

도서 상세 화면

1-2. 도서 상세 설명 리스트 부분 코드

Reviews 리스트를 클릭하면 getReviewList() 함수가 호출된다.

리스트 코드

1-3. getReviewList() JS 코드

DB에서 도서 아이디로 해당 리뷰 데이터를 가져올 것이므로 도서 아이디와 해당 페이지 번호를 서버로 보내기로 한다.

 

jQuery 문법으로 ajax를 사용한다.

  • ajax로 호출할 URL : "/bookReviewList.re"
  • ajax로 보낼 데이터 통신타입 : POST
  • ajax로 서버에 요청 시 보낼 매개변수 : data
  • 응답받을 데이터 타입 : TEXT

서버에서 DB로부터 가져온 리뷰 데이터(문자열)를 response에 담아 보낼 것이므로 dataType을 TEXT로 설정해 주었다.

<script type="text/javascript">
	
    // b_id에 해당하는 모든 리뷰를 불러오는 script
    function getReviewList() {
    
        // 건네줄 데이터를 js 오브젝트로 만든다.  
        var data = {
            'b_id' : ${book.b_id},
            'page' : ${page}
        };
       
        $.ajax({
            url: "/bookReviewList.re",
            type: "POST",
            data: data,
            dataType: "TEXT",
            success: {
            	// 정상적인 요청과 응답이 온 경우의 액션
            },
            error: function(request, status, error) {
            	// 오류 발생 시 액션
                console.log("code: "+request.status+"\n"+"message : "+request.responseText+"\n"+"error: "+error);
            }
        }); 
    }
 </script>

 

2. 컨트롤러(ReviewFrontController.java)

웹 브라우저에서 요청한 URL인 /bookReviewList.re에 해당하는 if문으로 들어오고 BookReviewListAction 클래스의 execute() 메서드가 호출된다. 

 

AJAX 통신을 처리하는 컨트롤러이기 때문에 페이지 이동 처리 정보를 담는 ActionForward 클래스가 필요 없다.

또한, 페이지 이동 처리 역시 필요 없다.

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.BookReviewListAction;
import action.BookReviewRegistProAction;
import svc.AjaxAction;

/** AJAX로 데이터 통신
 * ActionForward 클래스 필요 X */
@WebServlet("*.re")
public class ReviewFrontController 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());

		System.out.println(" R.Front.C : 1. 요청 주소 계산 완료");

		/* 2. 각 요청 주소의 매핑 처리 */
		AjaxAction action = null;

		// b_id에 해당하는 모든 리뷰 리스트 처리 요청
		if (command.equals("/bookReviewList.re")) {
			System.out.println(" R.Front.C : /bookReviewList.re 주소 호출");
			action = new BookReviewListAction();
			try {
				action.execute(req, resp);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		System.out.println(" R.Front.C : 2. 요청 주소 매칭 끝");	
	}

	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 클래스(BookReviewListAction.java)

  1. ajax로 넘어온 데이터를 저장한다.
  2. DB에 접근하여 도서 아이디에 따른 모든 리뷰 데이터를 가져온다.
  3. 가져온 리뷰를 JSON 형태의 문자열로 만든다. 
    • DB로부터 가져온 리뷰 리스트를 JSONObject에 담은 후, JSONArray에 순서대로 저장한다.
    • 저장된 JSONArray를 새로운 JSONObject에 저장한다.
    • JSONArray가 담긴 새로운 JSONObject를 JSONString으로 만들어 String형 변수에 저장한다.
  4.  리뷰 데이터가 담긴 String형 변수를 response에 담아 보낸다. 
    • 단순 데이터만 보내므로 인코딩만 설정해 준다. 

JSONObject로 만들 형태는 아래와 같다.

{
	"univ":
    		[
       	 		{"r_text":"리뷰 1", "r_buy_opt":1, "r_regdate":"2023-04-09", "r_star":3, "r_m_id":"y1234"},
          		{"r_text":"리뷰 2", "r_buy_opt":1, "r_regdate":"2023-04-06", "r_star":3, "r_m_id":"yejii"}
       		]
}

 

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 org.json.simple.parser.JSONParser;

import svc.AjaxAction;
import svc.BookReviewListService;
import vo.Review;

/** b_id에 해당하는 모든 리뷰 출력 요청을 처리하는 Action 클래스 */
public class BookReviewListAction implements AjaxAction {

	@SuppressWarnings("unchecked")
	@Override
	public void execute(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		System.out.println(" B.ReviewList.A : execute() 호출");
		
		/* AJAX로 넘어온 파라미터 받기 */
		String b_id = req.getParameter("b_id"); 
		String nowPage = req.getParameter("page");
		int r_b_id = Integer.parseInt(b_id);
		
		/* DB에 접근해 b_id에 해당하는 모든 리뷰 가져오기 */
		BookReviewListService service = new BookReviewListService();
		ArrayList<Review> reviewList = service.getReviewList(r_b_id);
		
		/* 가져온 리뷰를 JSON 형태로 보내주기 위한 처리 */
		String json = "";	// 파싱할 최종 데이터 담아주기 위한 문자열
		JSONArray jsArr = new JSONArray();
		
		if(reviewList != null) {
			for(Review review: reviewList) {
				JSONObject jObject = new JSONObject();
				jObject.put("r_text", review.getR_text());
				jObject.put("r_m_id", review.getR_m_id());
                // JSON에 문자열 형태로 넣어주기 위한 처리
				jObject.put("r_regdate", review.getR_regdate().toString());	
				jObject.put("r_star", review.getR_star());
				jObject.put("r_buy_opt", review.getR_buy_opt());
				
				// 오브젝트 데이터를 JSONArray에 순서대로 저장
				jsArr.add(jObject);
			}
		}
        
		// 최종적으로 univ오브젝트에 JSON배열을 저장
		JSONObject univ = new JSONObject();
		univ.put("univ", jsArr);
		
		// 파싱할 데이터 저장
		json = univ.toJSONString();
				
		/* 파싱할 최종 데이터 보내기 */
		// dataType : "TEXT" (bookView.jsp ajax)
		resp.setCharacterEncoding("utf-8");
		PrintWriter out = resp.getWriter();
		out.print(json);
		out.close();

		System.out.println(" B.ReviewList.A : execute() 종료");
		return;
	}

}

 

4. Service 클래스(BookReviewListService.java)

package svc;

import static db.JdbcUtil.*;

import java.sql.Connection;
import java.util.ArrayList;

import dao.ReviewDAO;
import vo.Review;

/** b_id에 해당하는 리뷰를 가져오는 요청을 처리하는 Service 클래스 */
public class BookReviewListService {

	/** b_id에 해당하는 모든 리뷰 수를 구하는 메서드 */
	public int getReviewCount(int b_id) {
		
		int reviewCount = 0;
		
		/* DB 처리 */
		Connection conn = getConnection();
		ReviewDAO reviewDAO = ReviewDAO.getInstance();
		reviewDAO.setConnection(conn);
		reviewCount = reviewDAO.selectReviewCount(b_id);
		close(conn);

		return reviewCount;
	}

	/** b_id에 해당하는 모든 리뷰를 ArrayList객체 타입으로 반환하는 메서드 */
	public ArrayList<Review> getReviewList(int b_id) {

		/* DB 처리 */
		Connection conn = getConnection();
		ReviewDAO reviewDAO = ReviewDAO.getInstance();
		reviewDAO.setConnection(conn);
		ArrayList<Review> reviewList = reviewDAO.selectReviewList(b_id);
		close(conn);

		return reviewList;
	}

	
}

 

4. DAO 클래스(ReviewDAO.java)

생성자 접근 제한자를 private으로 지정해 외부 클래스에서 ReviewDAO 객체를 생성할 수 없도록 설정했다.

package dao;

import static db.JdbcUtil.*;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;

import vo.Review;

public class ReviewDAO {

	Connection conn;
	private static ReviewDAO reviewDAO;
	
	private ReviewDAO() {
	}
	
	/** 싱글톤 패턴으로 ReviewDAO 객체를 생성하여 리턴하는 메서드 */
	public static ReviewDAO getInstance() {
		if(reviewDAO == null) {
			reviewDAO = new ReviewDAO();
		}
		return reviewDAO;
	}
	
	/** ReviewDAO 객체에 Connection 객체를 주입하는 메서드 */
	public void setConnection(Connection conn) {
		this.conn = conn;
	}

	/** b_id에 해당하는 모든 리뷰 개수를 구하는 메서드 */
	public int selectReviewCount(int b_id) {
		
		int listCount = 0;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		
		try {
			pstmt = conn.prepareStatement("select count(*) from jspbookshop.review where r_b_id=?");
			pstmt.setInt(1, b_id);
			rs = pstmt.executeQuery();
			
			if(rs.next()) {
				listCount = rs.getInt(1);
			}
		} catch (Exception e) {
			System.out.println(" R.DAO : selectReviewCount() ERROR : "+e);
		} finally {
			close(rs);
			close(pstmt);
		}

		return listCount;
	}

	/** b_id에 해당하는 모든 리뷰를 구하는 메서드 */
	public ArrayList<Review> selectReviewList(int b_id) {

		PreparedStatement pstmt = null;
		ResultSet rs = null;
		ArrayList<Review> reviewList = null;
		
		String review_sql = "select * from jspbookshop.review where r_b_id="+b_id+" order by r_regdate desc";
		
		try {
			pstmt = conn.prepareStatement(review_sql);
			rs = pstmt.executeQuery();
			
			if(rs.next()) {
				reviewList = new ArrayList<>();
				
				do {
					Review review = new Review();
					review.setR_id(rs.getInt("r_id"));
					review.setR_b_id(b_id);
					review.setR_m_id(rs.getString("r_m_id"));
					review.setR_buy_opt(rs.getInt("r_buy_opt"));
					review.setR_star(rs.getInt("r_star"));
					review.setR_text(rs.getString("r_text"));
					review.setR_regdate(rs.getDate("r_regdate"));
					
					reviewList.add(review);
				} while (rs.next());
			}
		} catch (Exception e) {
			System.out.println(" R.DAO : selectReviewList ERROR : "+e);
		} finally {
			close(rs);
			close(pstmt);
		}
        
		return reviewList;
	}
}

 

5. 도서 상세 페이지(bookView.jsp)

5-1. getReviewList() JS 코드

서버에서 정상적으로 웹 브라우저의 요청을 수행해 DB로부터 리뷰 데이터를 가져와 다시 웹 브라우저로 넘겨주면, ajax의 success가 수행된다.

 

1. 서버에서 String형으로 바꾸어 보냈으니 function의 인자 jsArr의 타입은 String형이다.

2. JSON.parse()를 이용해 JSON으로 인코딩 된 jsArr 객체를 다시 자바스크립트 객체로 디코딩한다. 

JSON.parse()로 파싱된 jsArr 객체

3. jsArr 객체에서 "univ"로 접근해 루프를 돌면서 html의 원하는 위치에 리뷰 데이터가 출력되게 한다.  

<script type="text/javascript">
		
    // b_id에 해당하는 모든 리뷰를 불러오는 script
    function getReviewList() {

        // 건네줄 데이터를 js 오브젝트로 만든다.  
        var data = {
            'b_id' : ${book.b_id},
            'page' : ${page}
        };

        $.ajax({
            url: "/bookReviewList.re",
            type: "POST",
            data: data,
            dataType: "HTML",
            success: function(jsArr) {

                var html = "";

                // String타입의 jsArr(JSON으로 인코딩된 객체)를 자바스크립트 객체로 변환
                var toJsObj = JSON.parse(jsArr);

			// toJsObj에서 key가 univ에 접근하여 데이터를 가져와 화면에 출력한다.
                if(toJsObj.univ != null) {
                    $.each(toJsObj["univ"], function(index, value) {

                        html += "<div class='review_item'>";
                            html += "<div class='media'>";
                                html += "<div class='d-flex'>";
                                    html += "<img src='img/product/review-2.png' alt=''>";
                                html += "</div>";
                                html += "<div class='media-body row'>";
                                    html += "<div class='col-lg-8'>";
                                        html += "<h4>" + value.r_m_id + "</h4>";
                                    html += "</div>";
                                    html += "<div class='col-lg-4'>";
                                        html += "<h5>" + value.r_regdate + "</h5>";
                                    html += "</div>";
                                    html += "<div class='col-lg-12 star'>";
                                        for(let i = 1; i <= value.r_star; i++) {
                                              html += "<i class='fa fa-star'></i>";
                                            }
                                    html += "</div>";
                                html += "</div>";
                            html += "</div> ";
                            html += "<p>" + value.r_text + "</p>";
                        html += "</div>";

                        $(".review_list").html(html);
                    });	
                }
                else { 
                    html += "<div class='review_item'>" + "등록된 리뷰가 없습니다. " + "</div>";
                }
            },
            error: function(request, status, error) {
                console.log("code: "+request.status+"\n"+"message : "+request.responseText+"\n"+"error: "+error);
            }
        }); 
    }
</script>

 

5-2. 도서 상세 페이지 웹 화면

bookView.jsp

 

끝.

'JSP' 카테고리의 다른 글

[JSP] [MySQL] MySQL의 Auto Increment DAO에서의 처리방법  (0) 2023.03.26