카카오 쇼핑하기 클론프로젝트 #3

2023. 8. 9. 01:30·프로젝트/카카오 쇼핑하기 web
728x90
728x90

매주 프로젝트 진행상황을 블로그에 업데이트 하려고 했지만...

4주차 까지는 스프링부트에 적응하면서 과제 하느라 바빴고 쏟아지는 새로운 개념들을 익히기 바빴다..ㅎ ㅎ

 

저번 주에 드디어 6주간의 프로젝트가 끝나고 뭘 해야 좋을 지 고민하다가...

새로운 프로젝트를 하는 것 보단 기존 프로젝트의 부족한 기능을 추가하거나 코드를 리팩토링 하는 시간을 갖는 게 좋을것 같다고 생각했다.

 

기존 프로젝트엔 "장바구니 조회" , "장바구니 추가" , "장바구니 수량 수정 기능"만 구현했다.

따라서 "장바구니 옵션 삭제" 기능을 추가해봤다.

제대로 잘 구현했는지 확신할 수 없지만ㅜ..ㅜ 일단 스스로 구현한 내용을 바탕으로 적어보겠다.

사진은 프론트 UI인데 각 옵션마다 삭제할 수 있는 x 버튼을 추가했다고 가정하자.


x 버튼을 눌렀을 때, /carts/update 경로로 DELETE 요청이 들어오고

Request Header에는 Bearer Token이 들어가고

 

Request Body (예시)

{
	"cartId" : 3
}

 

Response Body (예시)

{
    "success": true,
    "response": {
        "carts": [
            {
                "cartId": 4,
                "optionId": 1,
                "optionName": "01. 슬라이딩 지퍼백 크리스마스에디션 4종",
                "quantity": 10,
                "price": 100000
            },
            {
                "cartId": 5,
                "optionId": 2,
                "optionName": "02. 슬라이딩 지퍼백 플라워에디션 5종",
                "quantity": 10,
                "price": 109000
            }
        ],
        "totalPrice": 209000
    },

라고 가정하자.

예시 응답 Body에는 cartId가 3인 옵션이 삭제된 후의 장바구니를 나타내었다.


장바구니 옵션 삭제 로직을 구현할 때 고민했던 점 

1. 요청Body에 (장바구니 수량 수정 로직과 같이) 리스트 형태로 넘겨줄지 말지 고민했다.

{
    "cartId": 1
}
{
    "cartId": 2
} //or 하나만?
  • 장바구니 수정 로직, 장바구니 추가 로직을 구현할 때도 리스트 형태로 넘겨줬었다..,, 
  • 화면 설계서를 보면 동시에 두 옵션의 수량을 수정하는 것이 아닌 하나하나 수량을 수정하는데 굳이 리스트로 넘겨줘야하나? 라는 생각이 들었다.
  • 프론트가 요청Body를 백에게 넘겨주는 작동원리를 몰라서 이런 의문이 드는 것 같다.
  • 마찬가지로 삭제 요청도 사용자가 하나하나 처리하므로 굳이 리스트 형태로 넘겨주지 않아도 될 것 같다고 생각했다.
  • 물론 리스트 형태로 넘기는 게 정답일 수도 있지만! 그냥 단일 객체로 넘기는 형태로 작성하기로 했다.

2. 화면 UI내에서 발생할 수 없는 요청Body에 대한 예외처리도 하는 게 좋다. (추후에 공부)

  • 처음엔 클라이언트는 보통 화면 UI 내에서 조작하기 때문에 들어올 수 없는 예외처리는 필요 없다고 생각했다.
  • 하지만 잘못된 경로로 이상한 요청이 들어올 수 있기 때문에 최대한 모든 예외를 잡아야 한다.
  • 명백히 잘못 들어온 경우라면 다양한 사이드 이팩트 방지를 위해 예외처리해 주는게 맞다고 한다!
  • 보안문제, 데이터 무결성 유지, 서버 자원 보호 등의 이유도 있다.

3. 마지막에 남은 하나의 레코드를 삭제하려고 할 때 삭제가 되지 않는 에러 발생 

처음에 작성한 서비스 계층 로직

@Transactional
    public CartResponse.DeleteDTO delete(CartRequest.DeleteDTO requestDTO, User sessionUser) {
        List<Cart> cartList = cartJPARepository.findAllByUserId(sessionUser.getId());

        if(cartList.isEmpty()){
            throw new Exception404("장바구니가 비어있습니다.");
        }

        HashSet<Integer> set = new HashSet<>();
        for(Cart cart : cartList){
            set.add(cart.getId());
        }
        if(!set.contains(requestDTO.getCartId())){
            throw new Exception400("장바구니에 없는 상품은 삭제할 수 없습니다.");
        }


        for(Cart cart : cartList){
            if(requestDTO.getCartId() == cart.getId()){
                cartJPARepository.deleteById(requestDTO.getCartId());
                cartList.remove(cart);
            }
        }
		return new CartResponse.DeleteDTO(cartList);
    }
  • user의 장바구니 정보를 가져오고, 장바구니가 비었다면 예외처리를 한다.
  • 만약에 요청Body로 존재하지 않는 cartId 가 들어온다면 예외처리를 한다.
  • for 루프를 돌면서 요청Body로 들어온 cartId와 DB에 존재하는 cartId가 같다면 delete하고 cartList에도 삭제한다.
    • But 문제 발생….!!
    • 삭제를 하나하나 하다가 장바구니에 마지막 하나의 상품이 남고 삭제 시도를 했을 때 500 에러가 발생했다.
    • cartJPARepository.deleteById(requestDTO.getCartId())를 호출한 후에 cartList 에서 해당 요소를 제거하는 것은 데이터베이스와의 동기화 문제를 야기할 수 있다고 한다.
    • 삭제 작업은 데이터베이스에서 먼저 수행되도록 보장해야 한다.
    • 따라서 삭제가 완료된 후 리스트에서 제거하는 로직으로 변경해야 한다.

변경 후

for (Cart cart : cartList) {
            if (requestDTO.getCartId() == cart.getId()) {
                cartJPARepository.deleteById(requestDTO.getCartId());
                break;  // 삭제 후 반복문 종료
            }
        }
        
cartList.removeIf(cart -> cart.getId() == requestDTO.getCartId());

테이블 확인

만약에 user_id가 2인 사용자로 로그인 한 상태라고 가정했을 때 모든 경우의 응답을 유도해보겠다.


장바구니에 없는 상품을 삭제하려고 시도했을 때


cartId가 4인 상품은 본인 장바구니에 있는 상품이 아니므로 삭제 불가능


cartId가 1인 상품 삭제


cartId가 3인 상품 삭제


cartId가 2인 상품 삭제 -> 장바구니에 담겨있던 상품들을 모두 삭제해서 장바구니가 비어있다.


장바구니가 비어있는 상태에서 상품을 삭제하려고 시도했을 경우


삭제 시 실행된 쿼리문

@Query("select c from Cart c join fetch c.option o join fetch o.product where c.user.id =:userId")
    List<Cart> findAllByUserId(int userId);

불필요한 select쿼리를 방지하기 위해 product 테이블과 option 테이블을 fetch join 하여 한번에 값을 가져왔다.

Hibernate: 
    select
        cart0_.id as id1_0_0_,
        option1_.id as id1_4_1_,
        product2_.id as id1_6_2_,
        cart0_.option_id as option_i4_0_0_,
        cart0_.price as price2_0_0_,
        cart0_.quantity as quantity3_0_0_,
        cart0_.user_id as user_id5_0_0_,
        option1_.option_name as option_n2_4_1_,
        option1_.price as price3_4_1_,
        option1_.product_id as product_4_4_1_,
        product2_.description as descript2_6_2_,
        product2_.image as image3_6_2_,
        product2_.price as price4_6_2_,
        product2_.product_name as product_5_6_2_ 
    from
        cart_tb cart0_ 
    inner join
        option_tb option1_ 
            on cart0_.option_id=option1_.id 
    inner join
        product_tb product2_ 
            on option1_.product_id=product2_.id 
    where
        cart0_.user_id=?
Hibernate: 
    delete 
    from
        cart_tb 
    where
        id=?

 

끗

728x90
728x90
저작자표시 (새창열림)

'프로젝트 > 카카오 쇼핑하기 web' 카테고리의 다른 글

카카오 쇼핑하기 클론프로젝트 #4  (0) 2023.08.22
카카오 쇼핑하기 클론프로젝트 #2  (0) 2023.07.07
카카오 쇼핑하기 클론프로젝트 #1  (0) 2023.07.02
'프로젝트/카카오 쇼핑하기 web' 카테고리의 다른 글
  • 카카오 쇼핑하기 클론프로젝트 #4
  • 카카오 쇼핑하기 클론프로젝트 #2
  • 카카오 쇼핑하기 클론프로젝트 #1
성장하고픈개발자
성장하고픈개발자
방학 기념 개발블로그 작성하기
    반응형
  • 성장하고픈개발자
    꾸준히하자아자
    성장하고픈개발자
  • 전체
    오늘
    어제
    • 분류 전체보기 (65)
      • 프로젝트 (5)
        • 카카오 쇼핑하기 web (4)
        • 요약쏙 (0)
      • Algorithm (46)
        • 백준 & 프로그래머스 (40)
        • 알고리즘 (5)
      • Web (5)
        • 네트워크 (1)
        • Spring (4)
        • JPA (0)
        • HTTP (1)
      • 후기 (3)
      • SSAFY 일상 (6)
      • 취준 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 깃허브
  • 링크

    • github
  • 공지사항

  • 인기 글

  • 태그

    C++
    스택
    백엔드
    그리디
    SpringBoot
    쉬운딥러닝
    BOJ
    FNN
    네이버데이터센터각
    신경망기초
    합격수기
    withmockuser
    싸피 13기
    알고리즘
    책리뷰
    싸피
    백준
    일상
    코딩
    회고
    PS
    Spring Data JPA
    SSAFY
    testing
    DP
    Andrew Ng
    web
    정렬
    딥러닝
    Spring
  • 최근 댓글

  • 최근 글

  • 250x250
  • hELLO· Designed By정상우.v4.10.1
성장하고픈개발자
카카오 쇼핑하기 클론프로젝트 #3
상단으로

티스토리툴바