본문 바로가기

카테고리 없음

카트에 대한 설명

package com.sparta.dianomi.domain.member.model

import com.fasterxml.jackson.annotation.JsonIdentityInfo
import com.fasterxml.jackson.annotation.ObjectIdGenerators
import com.sparta.dianomi.domain.store.model.Menu
import com.sparta.dianomi.domain.store.model.Store
import jakarta.persistence.*

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator::class, property = "id")

@Entity
@Table(name = "cart")
class Cart(
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long? = null,

    @ManyToOne
    @JoinColumn(name = "member_id")
    var member: Member,

    @ManyToOne
    @JoinColumn(name = "menu_id")
    var menu: Menu,

    @ManyToOne
    @JoinColumn(name = "store_id")
    var store: Store,

    @Column(name = "count")
    var count: Int
)

 

여기선 카트는 한 멤버(Member), 메뉴(Menu), 스토어(Store)가 여러 카트(Cart)를 가질 수 있기 때문에 manytoone을 넣었고 무한참조를 피하기 위해서 

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator::class, property = "id")를 넣었습니다.

data class CartCreateDto(
    val storeId: Long,
    val menuId: Long,
    val count: Int
)

 

이걸로 카트를 만들수 있고

data class CartDto(
    val id: Long?,
    val storeId: Long?,
    val menuId: Long?,
    val memberId: Long?,
    val count: Int?
) {
    companion object {
        fun from(cart: Cart): CartDto {
            return CartDto(
                id = cart.id,
                storeId = cart.store.id,
                menuId = cart.menu.id,
                memberId = cart.member.id,
                count = cart.count
            )
        }
    }
}

 

 

여기서 컴패니언 오브젝트는 제가 알기론 컨트롤러에서 애드투카트가 크레이트 디티오를 쓰고있는데 addToCart 함수에서 새로운 아이템을 카트에 추가한 후, 그 결과를 CartDto로 변환하여 클라이언트에게 반환하고  데이터베이스와의 의존성을 줄여준다고 합니다.

 

@RestController
@RequestMapping("/carts")
class CartController(private val cartService: CartService) {

    @PostMapping
    fun addToCart(@AuthenticationPrincipal user: UserPrincipal, @RequestBody cartCreateDto: CartCreateDto): ResponseEntity<CartDto> {
        val memberId = user.id
        val cart = cartService.addToCart(cartCreateDto.menuId, cartCreateDto.storeId, memberId, cartCreateDto.count)
        return ResponseEntity.status(HttpStatus.CREATED).body(CartDto.from(cart))
    }

    @DeleteMapping("/{cartId}")
    fun removeFromCart(@AuthenticationPrincipal user: UserPrincipal, @PathVariable cartId: Long): ResponseEntity<String> {
        val memberId = user.id
        return try {
            cartService.removeFromCart(memberId, cartId)
            ResponseEntity.ok("성공적으로 제거하였습니다.")
        } catch (e: Exception) {
            ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("제거에 실패했습니다.")
        }
    }

    @DeleteMapping("/member/{memberId}")
    fun clearCart(@AuthenticationPrincipal user: UserPrincipal, @PathVariable memberId: String): ResponseEntity<String> {
        val memberId = user.id
        return try {
            cartService.clearCart(memberId)
            ResponseEntity.ok("성공적으로 제거하였습니다.")
        } catch (e: Exception) {
            ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("제거에 실패했습니다.")
        }
    }
    @GetMapping("/{cartId}")
    fun getCart(@AuthenticationPrincipal user: UserPrincipal, @PathVariable cartId: Long): ResponseEntity<out Any> {
        val memberId = user.id
        return try {
            val cart = cartService.getCart(memberId, cartId)
            if (cart.member.id != memberId) {
                ResponseEntity.status(HttpStatus.FORBIDDEN).body("You do not have permission to view this cart.")
            } else {
                ResponseEntity.ok(CartDto.from(cart))
            }
        } catch (e: Exception) {
            ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("조회할 수 없습니다.")
        }
    }

}

 

  1. addToCart: 새로운 아이템을 카트에 추가합니다. @PostMapping 어노테이션이 붙어 있으므로, POST 요청을 처리합니다. @RequestBody를 통해 요청 본문으로부터 CartCreateDto 객체를 생성하고, @AuthenticationPrincipal을 통해 현재 인증된 사용자의 정보를 가져옵니다.
  2. removeFromCart: 카트에서 특정 아이템을 제거합니다. @DeleteMapping 어노테이션을 사용하여 DELETE 요청을 처리하며, @PathVariable을 통해 요청 URL의 일부인 'cartId'를 파라미터로 받습니다.
  3. clearCart: 특정 사용자의 카트를 모두 비웁니다. @DeleteMapping 어노테이션을 사용하여 DELETE 요청을 처리하며, @PathVariable을 통해 요청 URL의 일부인 'memberId'를 파라미터로 받습니다.
  4. getCart: 특정 사용자의 특정 카트 정보를 가져옵니다. @GetMapping 어노테이션을 사용하여 GET 요청을 처리하며, @PathVariable을 통해 요청 URL의 일부인 'cartId'를 파라미터로 받습니다.

 

유저프린시플로 카트를 생성한 아이디만 삭제, 조회가 가능하게 만들었고 이중 전체삭제는 멤버아이디까지 입력해야만 사용가능하도록 해서 보안을 추구하긴 했는데 생각해보면 그냥 귀찮기만 한거 아닌가 싶긴 하네요

 

그리고 다른 아이디로 삭제를 시도한다던가 하면 인증실패라는 메시지가 뜨길래 이건 인증 실패라고 부르기엔 애매하지 않나 싶어서 트라이 캐치로 깔끔하고 알기 쉽도록 메시지를 추가했습니다.

 

interface CartRepository : JpaRepository<Cart, Long> {
    fun findAllByMember(member: Member): List<Cart>
}
  1. 본적인 CRUD(Create, Read, Update, Delete) 연산을 수행할 수 있는 메서드를 제공받습니다. 여기서 <Cart, Long>은 각각 엔티티 타입과 ID의 타입을 나타냅니다.
  2. fun findAllByMember(member: Member): List<Cart>: 이 메서드는 회원 객체를 인자로 받아 해당 회원이 가진 모든 카트를 리스트 형태로 반환
interface CartService {
    fun addToCart(menuId: Long, storeId: Long, memberId: Long, count: Int): Cart
    fun getCart(memberId: Long, cartId: Long): Cart
    fun removeFromCart(memberId: Long, cartId: Long)
    fun clearCart(memberId: Long)
}

 

  1. addToCart: 메뉴 ID, 스토어 ID, 회원 ID, 그리고 카트에 추가할 메뉴의 수량을 인자로 받아, 새로운 아이템을 카트에 추가하는 기능을 합니다. 카트를 추가하기에 cart가 붙음
  2. getCart: 회원 ID와 카트 ID를 인자로 받아, 해당 회원이 가진 특정 카트의 정보를 조회하는 기능을 합니다. 이 메소드는 조회된 Cart 를 반환하니까 cart가 붙음
  3. removeFromCart: 회원 ID와 카트 ID를 인자로 받아, 해당 회원의 카트에서 특정 아이템을 제거하는 기능을 합니다. 이 메소드는 반환값이 없습니다.
  4. clearCart: 회원 ID를 인자로 받아, 해당 회원의 카트를 모두 비우는 기능을 합니다. 이 메소드도 반환값이 없습니다.
package com.sparta.dianomi.domain.member.service

import com.sparta.dianomi.domain.member.model.Cart
import com.sparta.dianomi.domain.member.repository.CartRepository
import com.sparta.dianomi.domain.member.repository.MemberRepository
import com.sparta.dianomi.domain.store.repository.MenuRepository
import com.sparta.dianomi.domain.store.repository.StoreRepository
import org.springframework.stereotype.Service

@Service
class CartServiceImpl(
    private val cartRepository: CartRepository,
    private val menuRepository: MenuRepository,
    private val storeRepository: StoreRepository,
    private val memberRepository: MemberRepository
) : CartService {
    override fun addToCart(menuId: Long, storeId: Long, memberId: Long, count: Int): Cart {
        val menu = menuRepository.findById(menuId).orElseThrow { Exception("Menu not found") }
        val store = storeRepository.findById(storeId).orElseThrow { Exception("Store not found") }
        val member = memberRepository.findById(memberId).orElseThrow { Exception("Member not found") }
        val cart = Cart(menu = menu, store = store, member = member, count = count)
        return cartRepository.save(cart)
    }

    override fun removeFromCart(memberId: Long, cartId: Long) {
        val cart = cartRepository.findById(cartId).orElseThrow { Exception("Cart not found") }
        if (cart.member.id != memberId) {
            throw IllegalAccessException("Not authorized to remove this cart.")
        }
        cartRepository.deleteById(cartId)
    }

    override fun clearCart(memberId: Long) {
        val member = memberRepository.findById(memberId).orElseThrow { Exception("Member not found") }
        val carts = cartRepository.findAllByMember(member)
        cartRepository.deleteAll(carts)
    }

    override fun getCart(memberId: Long, cartId: Long): Cart {
        val cart = cartRepository.findById(cartId).orElseThrow { Exception("Cart not found") }
        if (cart.member.id != memberId) {
            throw IllegalAccessException("Not authorized to view this cart.")
        }
        return cart
    }
}

 

  1. addToCart: 메뉴 ID, 스토어 ID, 회원 ID, 그리고 카트에 추가할 메뉴의 수량을 인자로 받아, 새로운 아이템을 카트에 추가하는 기능을 구현합니다. 이 메소드는 새로 생성된 Cart 객체를 반환합니다.
  2. removeFromCart: 회원 ID와 카트 ID를 인자로 받아, 해당 회원의 카트에서 특정 아이템을 제거하는 기능을 구현합니다. 이 메소드는 반환값이 없습니다.
  3. clearCart: 회원 ID를 인자로 받아, 해당 회원의 카트를 모두 비우는 기능을 구현합니다. 이 메소드도 반환값이 없습니다.
  4. getCart: 회원 ID와 카트 ID를 인자로 받아, 해당 회원이 가진 특정 카트의 정보를 조회하는 기능을 구현합니다. 이 메소드는 조회된 Cart 객체를 반환합니다.

각 메소드는 예외 상황에 대비하여 적절한 예외 처리 코드를 포함하고 있습니다. 예를 들어, 찾을 수 없는 메뉴 ID, 스토어 ID, 회원 ID, 또는 카트 ID가 주어졌을 때, 또는 사용자가 자신의 카트가 아닌 다른 카트를 조회하거나 삭제하려고 할 때 예외를 발생시킵니다.