감 잃지말고 개발하기

[JSP][MVC][Session] 세션 및 로그인 도서 장바구니 구현하기 #7. 장바구니 수량 변경하기 (2탄) 본문

JSP/MVC

[JSP][MVC][Session] 세션 및 로그인 도서 장바구니 구현하기 #7. 장바구니 수량 변경하기 (2탄)

persii 2023. 5. 24. 02:18

지난 포스팅에서는 장바구니 수량을 변경하는 로직의 전체적인 흐름을 살펴본 후, 클라이언트 딴의 로직을 정리해 보았다. 

이번 포스팅에서는 클라이언트 딴을 이어 서버 딴에서의 로직을 정리해 보도록 하겠다.

 


♠ 해당 구현의 로직 흐름이 궁금하면 아래 포스팅을 참고하세요 ♠

 

2023.05.24 - [JSP/MVC] - [JSP][MVC][Session] 세션 및 로그인 도서 장바구니 구현하기 #6. 장바구니 수량 변경하기 (1탄)

 

[JSP][MVC][Session] 세션 및 로그인 도서 장바구니 구현하기 #6. 장바구니 수량 변경하기 (1탄)

세션 및 로그인 도서 장바구니 구현 여섯 번째 포스팅이다. 저번 포스팅을 끝으로 기본적인 장바구니 구현이 완성되었다. 이제 추가적인 구현으로, 장바구니 페이지에서 도서의 수량을 변경하

persimmon-ary-stepbystep.tistory.com


 

목표

JSP 서버 딴에서 MVC 패턴을 지키면서 AJAX방식을 처리할 수 있다.

 

 

서버 딴 로직 코드 및 실행화면

1. 컨트롤러(AjaxFrontController.java)

AJAX 방식을 처리하는 컨트롤러에서 클라이언트에서 요청하는 URL에 대한 매핑을 처리한다.

/bookCartFormQty.ax로 요청되면 BookCartFormQtyAction 클래스를 호출한다.

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.AjaxAction;
import action.BookCartFormQtyAction;

/** AJAX로 데이터 통신 */
@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;
		
		// 장바구니 수량 증가/감소 요청
		if (command.equals("/bookCartFormQty.ax")) {
			action = new BookCartFormQtyAction();
			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);
	}
}

 

2. Action 클래스(BookCartFormQtyAction.java)

☞ Action 클래스 처리 흐름

  1. 세션 확인
    • 로그인의 경우, DB의 장바구니 테이블에서 해당 도서 수량 +1 / -1
    • 비로그인의 경우, 세션의 장바구니 속성에서 해당 도서 수량 +1 / -1
  2. 처리 결과를 브라우저에 응답
    • 응답할 결과 형태는 int형 타입으로 할 것이다.
      • DB 처리에 성공할 경우 1
      • DB 처리에 실패한 경우 -2
      • 세션 장바구니 속성을 처리한 경우 0
      • 세션이 존재하지 않는 경우 -1을 브라우저에 건네줄 것이다.
    • 이에 따라 HttpServletResponse 객체에 컨텐츠 유형을 "text/plain"으로 설정한다.

 

☞ SessionUtil.isLogined(req, resp);

해당 클래스의 isLogined() 메서드는 세션의 로그인 상태를 확인하는 메서드이다. 

로그인된 상태면 1을, 비로그인 상태면 0을, 세션이 존재하지 않는다면 -1을 리턴한다.


♠ SessionUtil 클래스 꼬라지는 아래 포스팅을 참고하세요(공통 클래스 참고) ♠

 

2023.05.20 - [JSP/MVC] - [JSP][MVC][Session] 세션 및 로그인 도서 장바구니 구현하기 #2. DB 테이블 및 공통 클래스 설정

 

[JSP][MVC][Session] 세션 및 로그인 도서 장바구니 구현하기 #2. DB 테이블 및 공통 클래스 설정

세션 및 로그인 도서 장바구니 구현 두 번째 포스팅이다. 저번 포스팅에서는 교보문고 페이지를 살펴보면서 구현 방향을 정리해 보았다. 이번 포스팅에서는 본격적인 로직 구현에 앞서 이번 과

persimmon-ary-stepbystep.tistory.com


 

☞ service.upCartQty(b_id, (String) req.getSession().getAttribute("userId"));

service 객체의 upCartQty 메서드에 첫 번째 인자로 도서 아이디를, 두 번째 인자로 세션의 로그인 유저 아이디를 건네준다.

 

handleDBResult(dbResult);

해당 메서드는 DB 처리 결과에 따른 리턴값을 고정시키기 위해 만든 메서드이다.

파라미터 dbResult에는 1 또는 -1의 값이 저장되는데(아래의 Service 클래스 참고), 

값이 1인 경우에는 1을, -1인 경우에는 -2를 리턴한다.

 

updateCartQuantity(ArrayList<Cart> cartList, int b_id, int quantityChange);

해당 메서드는 세션의 장바구니 속성 도서 수량을 처리하는 메서드이다. 

인자 quantityChange가 1인 경우에는 수량을 1 증가시키고,

-1인 경우에는 현재 해당 도서의 수량이 1보다 큰 경우에만 수량을 -1 감소하도록 처리한 후 장바구니를 리턴한다.

 

package action;

import java.io.PrintWriter;
import java.util.ArrayList;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import svc.BookCartFormQtyService;
import util.SessionUtil;
import vo.Cart;

/** 장바구니 도서의 수량을 1 증가/감소하는 요청을 처리하는 Action 클래스 */
public class BookCartFormQtyAction implements AjaxAction {

	@Override
	public void execute(HttpServletRequest req, HttpServletResponse resp) throws Exception {

       /* 요청 URL에서 전달된 파라미터 값 저장 */
       int b_id = Integer.parseInt(req.getParameter("b_id"));
       String how = req.getParameter("how");

       int returnResult = -1;

       switch (SessionUtil.isLogined(req, resp)) {
          case 1:
            // 로그인
            // DB 처리
            BookCartFormQtyService service = new BookCartFormQtyService();
            int dbResult = 0;

            if(how.equals("up")) {
               // 장바구니 수량 +1
               dbResult = service.upCartQty(b_id, (String) req.getSession().getAttribute("userId"));
            } else {
               // 장바구니 수량 -1
               dbResult = service.downCartQty(b_id, (String) req.getSession().getAttribute("userId"));
            }

            returnResult = handleDBResult(dbResult);
            break;
				
          case 0:
            // 비로그인
            ArrayList<Cart> cartList = (ArrayList<Cart>) req.getSession().getAttribute("cartList");

            if (how.equals("up")) {
               cartList = updateCartQuantity(cartList, b_id, 1);
            } else if (how.equals("down")) {
               cartList = updateCartQuantity(cartList, b_id, -1);
            }

            // 확인용
            System.out.println("수정된 장바구니 ------");
            for(Cart cart:cartList) {
               System.out.println(cart.toString());
            }

            returnResult = 0;
            break;
	
          default:
            break;
       }
		
       /* 결과 응답 처리 
        * 응답할 결과 형태 : int형 데이터  -> dataType: 'TEXT'
        * resp.setContentType("text/plain"); 응답의 컨텐츠 유형을 설정
        * */
       resp.setContentType("text/plain");
       PrintWriter out = resp.getWriter();
       out.print(returnResult);
    }

    /** DB 결과 처리 */
    private int handleDBResult(int dbResult) {

      if(dbResult == 1) {
        // DB에 성공적으로 저장된 경우
        return 1;
      } else {
        // DB에 저장 X (DB 에러)
        return -2;
      }
    }
	
    /** 세션 장바구니 속성 처리 
     * @return */
    private ArrayList<Cart> updateCartQuantity(ArrayList<Cart> cartList, int b_id, int quantityChange) {

      for (Cart cart : cartList) {
        if (cart.getC_b_id() == b_id) {
          switch (quantityChange) {
            case 1:
              // 수량 증가
              cart.setC_b_qty(cart.getC_b_qty() + quantityChange);
              break;

            default:
              // 수량 감소
              // 수량이 1 보다 크고 감소하는 경우에만 처리되도록 설정
              if(cart.getC_b_qty() > 1) cart.setC_b_qty(cart.getC_b_qty() + quantityChange);
              break;
          }
        }
      }	
      return cartList;
    }
}

 

3. Service 클래스(BookCartFormQtyService.java)

☞ upCartQty(int b_id, String m_id);

해당 메서드는 DB cart 테이블에서 도서 아이디와 로그인 아이디에 따른 도서 항목의 수량을 1 증가시킨다. 

DB 처리에 성공하면 1을, 실패하면 -1을 리턴한다.

 

downCartQty(int b_id, String m_id)

해당 메서드는 DB cart 테이블에서 도서 아이디와 로그인 아이디에 따른 도서 항목의 수량을 1 감소시킨다. 

마찬가지로 DB 처리에 성공하면 1을, 실패하면 -1을 리턴한다.

 

package svc;

import java.sql.Connection;
import java.sql.SQLException;

import dao.CartDAO;
import db.JdbcUtil;

/** 장바구니 항목 수량 증가 요청을 처리하는 비즈니스 로직을 구현하는 Service 클래스 */
public class BookCartFormQtyService {

	/** 장바구니 항목의 수량을 증가시키는 메서드 
	 * @throws SQLException */
	public int upCartQty(int b_id, String m_id) throws SQLException {
		
		/* DB 작업 */
		Connection conn = JdbcUtil.getConnection();
		CartDAO cartDAO = CartDAO.getInstance();
		cartDAO.setConnection(conn);
		int updateCount = cartDAO.updateQty(b_id, m_id, "up");
		int isUpdateSuccess = -1;
		
		if((updateCount) > 0) {
			conn.commit();
			isUpdateSuccess = 1;
		}
		else conn.rollback();
		
		conn.close();
		return isUpdateSuccess;
	}

	/** 장바구니 항목 수량을 -1하는 메서드 
	 * @throws SQLException */
	public int downCartQty(int b_id, String m_id) throws SQLException {
				
		/* DB 작업 */
		Connection conn = JdbcUtil.getConnection();
		CartDAO cartDAO = CartDAO.getInstance();
		cartDAO.setConnection(conn);
		
		int isUpdateSuccess = -1;

		int updateCount = cartDAO.updateQty(b_id, m_id, "down");
		
		if((updateCount) > 0) {
			// DB 수정 성공
			conn.commit();
			isUpdateSuccess = 1;
		}
		else conn.rollback();
		
		conn.close();
		return isUpdateSuccess;
	}

}

 

4. DAO 클래스(CartDAO.java)

기존 CartDAO 클래스에 아래 메서드를 추가해 준다.

 

인자 status의 값이 "up" 이면 증가시키는 쿼리문을, "down"이면 감소시키는 쿼리문을 sql 변수에 저장한다.

 

/** 도서 수량을 1 증가시키는 메서드 
 * @param status */
public int updateQty(int b_id, String m_id, String status) {

    PreparedStatement pstmt = null;
    int updateCount = 0;
    String sql = "";
    if(status.equals("up")) {
        sql = "UPDATE jspbookshop.cart "
                + "SET ca_b_qty = ca_b_qty + 1 "
                + "WHERE ca_b_id = ? "
                + "AND ca_m_id = ?";
    } else {
        sql = "UPDATE jspbookshop.cart "
                + "SET ca_b_qty = ca_b_qty - 1 "
                + "WHERE ca_b_id = ? "
                + "AND ca_m_id = ?";
    }

    try {
        pstmt = conn.prepareStatement(sql);
        pstmt.setInt(1, b_id);
        pstmt.setString(2, m_id);
        updateCount = pstmt.executeUpdate();
    } catch (Exception e) {
        System.out.println(" Ca.DAO : updateQty() ERROR : "+e);
    } finally {
        close(pstmt);
    }

    return updateCount;
}

 

5. 브라우저 결과

AJAX에 대한 처리가 정상적으로 수행되면, ajax 함수의 success에서 응답 결과에 따른 처리를 수행한다.

경우 1. 비로그인 상태

1.  수량 증가

<장하준의 경제학 레시피> 도서의 수량을 1 증가시켜 보았다.

수량이 2로 변하고, 금액이 바뀌었으며, 최종 총액 또한 알맞게 변경되었음이 확인된다.

수량 증가(비로그인 상태)

 

▼ 콘솔 출력 확인

변경하고자 하는 도서의 아이디 및 가격, 현재 수량, 총액 정보가 잘 출력되었다.

콘솔 창 확인

 

▼ 서버 딴 출력 확인

서버 딴에서 세션의 장바구니 속성의 도서 정보가 제대로 변경되었다.

서버 딴 세션 확인

 

2.  수량 증가

다시 <장하준의 경제학 레시피> 도서의 수량을 1 감소시켜 보았다.

장바구니 수량 감소 화면

 

▼ 콘솔 출력 확인

마찬가지로 잘 출력되었다.

콘솔 창 확인

 

▼ 서버 딴 출력 확인

서버 딴 역시 마찬가지로 세션의 장바구니 속성의 도서 정보가 제대로 변경되었다.

서버 딴 세션 확인

 

경우 2. 로그인 상태

1.  기존 장바구니 확인

먼저 사용자 yejii의 장바구니를 확인해 보자.

여기서 <스즈메의 문단속> 도서 수량을 변경해 볼 것이다. 현재 <스즈메의 문단속> 도서 수량은 3이다.

로그인 유저의 장바구니 상태

 

2.  수량 감소

<스즈메의 문단속> 도서 수량이 2로 변경되었고 금액 역시 변경되었다.

수량 감소(로그인 후)

 

▼ 콘솔 출력 확인

콘솔 창 확인

 

▼ DB 확인

실제로 값이 변경되었는지 DB에 확인해 보니 잘 변경되어 저장되었음을 확인할 수 있다.

DB 테이블 확인

 

 


 

 

이렇게 세션의 로그인 속성과 장바구니 속성 및 DB를 이용해 장바구니 수량을 변경하는 로직이 모두 구현되었다.

 

 

장바구니 수량 변경 구현 끝.