22.11.17 THU (DAY 8)
사용하는 이유
6 원칙
Uniform Interface
🔗 velog.io/@zirryo/series
🪄 velog, zirryo의 series 를 나타내는 것을 바로 알 수 있음.
Client Server
Stateless
Cacheable
Layered System
Code On Demand
URL 네이밍 규칙
⭕️ : velog.io/@zirryo/reviews
❌ : velog.io/@zirryo/reviewed
⭕️ : velog.io/@zirryo/series
❌ : velog.io/@zirryo/Series
⭕️ : velog.io/@zirryo/users
❌ : velog.io/@zirryo/user
⭕️ : velog.io/@zirryo/best-sellers
❌ : velog.io/@zirryo/bestSellers
/
로 끝내지 않음⭕️ : velog.io/@zirryo/carts
❌ : velog.io/@zirryo/cart/
⭕️ : velog.io/@zirryo/item
❌ : velog.io/@zirryo/item.jpg
Controller
여러 형태의 클라이언트로부터 요청을 받아 이를 비즈니스 로직으로 전달하고,
비즈니스 로직의 결과를 클라이언트에게 응답해주는 역할을 하는 클래스
Structure
// CartController.java @Slf4j @RestController @RequiredArgsConstructor @RequestMapping("/carts") public class CartController { private final CartService cartService; private final ItemCartService itemCartService; private final CartMapper cartMapper; private final ItemMapper itemMapper; private final ItemCartMapper itemCartMapper; ... ... }
@Slf4j
: Simple Logging Facade for Java, 로깅 프레임워크 라이브러리@RestController
: 컨트롤러 클래스를 Spring Bean으로 등록함.@RequiredArgsConstructor
: final, @NotNull 이 붙은 필드의 생성자를 만드는 애너테이션 (순환 참조 방지, 객체 변이 방지)@RequestMapping
: 요청에 대한 Methods, URL 매핑.getCart
// CartController.java @GetMapping public ResponseEntity getCart(@RequestParam(value="subscription", defaultValue="false") boolean subscription) { Cart cart = cartService.findMyCart(); return new ResponseEntity<>(new SingleResponseDto<>(cartMapper.cartToCartResponseDto( cart, cartService, subscription, itemCartService, itemMapper, itemCartMapper)), HttpStatus.OK); }
// 서버의 응답 예시 { "data": { "cartId": 1, // 카트 고유 식별자 "subscription": false, // 일반 혹은 정기 카트 구분 (false == 일반 카트) "itemCarts": { // 현재 카트에 포함된 목록 "data": [ // 각각의 항목이 data 리스트에 들어있음 { "itemCartId": 1, // 아이템 카트 고유 식별자 "quantity": 3, // 해당 아이템을 담은 수량 "period": 0, // 정기 구독 일경우 구독 주기 정보 표기 "buyNow": true, // 선택된 항목일경우 true, 선택되지 않았다면 false "subscription": false, // 일반 혹은 정기 항목 구분 "item": { // 해당 아이템의 간략한 정보 "itemId": 1, // 아이템 고유 식별자 "brand": "BRAND1", // 제약회사명 "thumbnail": "썸네일_이미지의_주소.jpg", // 썸네일 이미지를 불러올 주소 "title": "메가타민 비타민B", // 아이템의 이름 "capacity": 30, // 영양제 한 통에 포함된 수량(30 -> 30정) "price": 12000, // 정가 "discountRate": 20, // 현재 할인율 "disCountPrice": 9600 // 실제 구매 금액 (할인가 적용) }, "createdAt": "2022-11-30T13:05:41.934396+09:00", // 처음 카트에 해당 아이템을 담은 시간 "updatedAt": "2022-11-30T13:05:41.93441+09:00" } ], "pageInfo": null // 카트 목록은 무한 스크롤 기능을 적용하므로 페이지 정보 필요없음 }, "totalItems": 1, // 현재 카트에 포함된 아이템 중 buyNow==true 인 항목의 수 "totalPrice": 36000, // 현재 카트에 포함된 아이템 중 buyNow==true 인 정가 총합 "totalDiscountPrice": 7200, // 현재 카트에 포함된 아이템 중 buyNow==true 인 할인 금액 총합 "expectPrice": 28800 // 현재 카트에 담긴 물건을 주문할 경우 결제 예상 금액 } }
@RequestParam
으로 받아, 조건에 맞는 카트의 정보를 리턴하는 장바구니 조회 컨트롤러Structure
// ItemCartController.java @Slf4j @RestController @RequiredArgsConstructor @RequestMapping("/carts") public class ItemCartController { private final ItemCartService itemCartService; private final ItemCartMapper itemCartMapper; private final ItemMapper itemMapper; private final ItemService itemService; private final CartService cartService; private final UserService userService; ... ... }
카트의 개별 항목인 🔗 ItemCart를 다루는 컨트롤러 클래스
@Slf4j
: Simple Logging Facade for Java, 로깅 프레임워크 라이브러리@RestController
: 컨트롤러 클래스를 Spring Bean으로 등록함.@RequiredArgsConstructor
: final, @NotNull 이 붙은 필드의 생성자를 만드는 애너테이션 (순환 참조 방지, 객체 변이 방지)@RequestMapping
: 요청에 대한 Methods, URL 매핑.postItemCart
// ItemCartController.java @PostMapping("/{item-id}") public ResponseEntity postItemCart(@Valid @RequestBody ItemCartDto.Post itemCartPostDto, @PathVariable("item-id") @Positive long itemId) { ItemCart itemCart = itemCartService.addItemCart(itemCartMapper. itemCartPostDtoToItemCart(itemId, userService, itemService, itemCartPostDto)); cartService.refreshCart(itemCart.getCart().getCartId(), itemCart.isSubscription()); return new ResponseEntity<>( new SingleResponseDto<>(itemCartMapper.itemCartToItemCartResponseDto( itemMapper, itemCart)), HttpStatus.CREATED); }
// ItemCartDto.java @Getter public static class Post { @Min(value = 1, message = "수량은 1개 이상 선택해주세요.") private Integer quantity; // 아이템의 수량 private Integer period; // (정기 구독 항목일 경우) 구독 주기 private boolean subscription; // 구독 여부 }
@PostMapping
: HTTP(S)의 POST request를 처리하는 애너테이션@PathVariable
: itemId 를 이용해서 장바구니에 담을 아이템의 정보를 얻음.@RequestBody
: dto 클래스를 이용해 필요한 정보를 입력 받음.refreshCart
: 카트의 정보를 갱신하는 메서드upDownItemCart
// ItemCartController.java @PatchMapping("/itemcarts/{itemcart-id}") public ResponseEntity upDownItemCart(@PathVariable("itemcart-id") @Positive long itemCartId, @RequestParam(value="upDown") int upDown) { ItemCart upDownItemCart = itemCartService.updownItemCart(itemCartId, upDown); cartService.refreshCart(upDownItemCart.getCart().getCartId(), upDownItemCart.isSubscription()); return new ResponseEntity<>(new SingleResponseDto<>( itemCartMapper.itemCartToItemCartResponseDto(itemMapper, upDownItemCart)), HttpStatus.OK); }
@PatchMapping
: HTTP(S)의 PATCH request를 처리하는 애너테이션@PathVariable
: itemcartId 를 이용해서 수량 변경할 대상을 찾기 위함.@RequestParam
: updown == 1 수량 증가, -1 수량 감소refreshCart
: 카트의 정보를 갱신하는 메서드periodItemCart
// ItemCartController.java @PatchMapping("/itemcarts/period/{itemcart-id}") public ResponseEntity periodItemCart(@PathVariable("itemcart-id") @Positive long itemCartId, @RequestParam(value="period") int period) { ItemCart itemCart = itemCartService.periodItemCart(itemCartId, period); return new ResponseEntity<>(new SingleResponseDto<>( itemCartMapper.itemCartToItemCartResponseDto(itemMapper, itemCart)), HttpStatus.OK); }
@PatchMapping
: HTTP(S)의 PATCH request를 처리하는 애너테이션@PathVariable
: itemcartId 를 이용해서 구독 주기를 변경할 대상을 찾기 위함.@RequestParam
: period 값에 구독 주기를 정수로 입력함. (30, 60, 90, 120) refreshCart
: 카트의 정보를 갱신하는 메서드excludeItemCart
// ItemCartController.java @PatchMapping("/itemcarts/exclude/{itemcart-id}") public ResponseEntity excludeItemCart(@PathVariable("itemcart-id") @Positive long itemCartId, @RequestParam(value="buyNow", defaultValue = "false") boolean buyNow) { ItemCart itemCart = itemCartService.excludeItemCart(itemCartId, buyNow); cartService.refreshCart(itemCart.getCart().getCartId(), itemCart.isSubscription()); return new ResponseEntity(HttpStatus.OK); }
@PatchMapping
: HTTP(S)의 PATCH request를 처리하는 애너테이션@PathVariable
: itemcartId 를 이용해서 선택 여부를 변경할 대상를 찾기 위함.@RequestParam
: buyNow == true 선택, false 선택 해제refreshCart
: 카트의 정보를 갱신하는 메서드deleteItemCart
// ItemCartController.java @DeleteMapping("/itemcarts/{itemcart-id}") public ResponseEntity deleteItemCart(@PathVariable("itemcart-id") @Positive long itemCartId, @RequestParam(value = "subscription") boolean subscription) { long cartId = itemCartService.deleteItemCart(itemCartId); cartService.refreshCart(cartId, subscription); return new ResponseEntity(HttpStatus.NO_CONTENT); }
@DeleteMapping
: HTTP(S)의 DELETE request를 처리하는 애너테이션@PathVariable
: itemcartId 를 이용해서 삭제하고자 하는 대상을 찾기 위함.@RequestParam
: subscription == true 정기 구독 항목, false 일반 항목refreshCart
: 카트의 정보를 갱신하는 메서드