Spring Projcect/[갠플] Online-mall

[7일차] 관리자 API - 상품 조회

계란💕 2022. 9. 29. 20:03

 

1. Product 관리 - 등록, 수정, 삭제

 

  • ProductDto (상품 리스트 반환할 때 쓰인다.)
    • 엔티티인 Product를 직접 사용해서 어떤 코드를 작성하면 참조형 데이터의 특성상 데이터베이스의 내용이 바로 변경될 우려가 있다.
    • 그래서 Dto라는 클래스를 이용한다.
<hide/>
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ProductDto {

    private Long id;    // 상품 테이블의 PK

    // 상품 리스트를 반환할 때 필요한 정보
    private String productName;
    private Integer price;      // 상품 가격
    private Boolean soldOutYn;  // 품절 여부
    private String productImg;  // 상품 이미지 사진?

    @Enumerated(EnumType.STRING)
    private Category category;  // 딱 한개

//    @ManyToOne(fetch = LAZY)
//    @JoinColumn(name = "CART_ID")   // 외래 키 매핑 - CART 테이블의 PK id와 JOIN
//    private Cart cart;

    // 재고 관련 사항
    private Integer productQuantity;    // 재고 수량
    private LocalDateTime stockedDt;  // 입고 예정일 => 한 번에 1000개씩 들어온다 가정?


    // 추가 컬럼
    private Integer cumulativeSales;    // 상품별 누적 판매 개수

    // 페이징 위한 컬럼
    long totalCount;
    long seq;


    // of() 오버로딩
    public static List<ProductDto> of(List<Product> productList) {
        if (productList != null) {
            List<ProductDto> productDtoList = new ArrayList<>();
            for (Product product : productList) {
                productDtoList.add(of(product));
            }
            return productDtoList;
        }
        return null;
    }

    static ProductDto of(Product parameter) {
        return ProductDto.builder()
            .category(parameter.getCategory())
            .cumulativeSales(parameter.getCumulativeSales())
            .price(parameter.getPrice())
            .productImg(parameter.getProductImg())
            .productName(parameter.getProductName())
            .productQuantity(parameter.getProductQuantity())
            .soldOutYn(parameter.getSoldOutYn())
            .stockedDt(parameter.getStockedDt())
            .build();
    }
}

 

  • ProductInput (상품 수정)
<hide/>
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class ProductInput {

    // 상품 수정하기 위한 매개 변수, 입력값
    private Long id;    // 상품 테이블의 PK
    private String productName;
    private Integer price;
    //    private Boolean soldOutYn;
    private String productImg;

    @Enumerated(EnumType.STRING)
    private Category category;

// 카트를 입력할 일은 없음
//    @ManyToOne(fetch = LAZY)
//    @JoinColumn(name = "CART_ID")   // 외래 키 매핑 - CART 테이블의 PK id와 JOIN
//    private Cart cart;

    // 재고 관련 사항
    private Integer productQuantity;    // 재고 수량
    private LocalDateTime stockedDt;  // 입고 예정일 => 한 번에 1000개씩 들어온다 가정?

}

 

  • ProductParam - id만 가지고 상품을 찾을 때 쓸 수 있다.
<hide/>
@Data
public class ProductParam extends CommonParam {
    long id; // 상품 id
}

 

  •  AdminProductController
    • 상품 update는 바뀔 수도 있음
<hide/>
package com.example.mall.admin.controller;

import com.example.mall.admin.model.ProductDto;
import com.example.mall.admin.model.ProductInput;
import com.example.mall.admin.model.ProductParam;
import com.example.mall.admin.service.ProductService;
import com.example.mall.controller.BaseController;
import com.example.mall.member.service.MemberService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
@RequiredArgsConstructor
public class AdminProductController extends BaseController {


    private final ProductService productService;
    private final MemberService memberService;


    @GetMapping("/admin/product/list.do")
    public String list(Model model,
        ProductParam param) {

        param.init();   // 페이지 초기화
        List<ProductDto> list = productService.list();

        long totalCount = 0;
        if (list != null && list.size() > 0) {
            totalCount = productService.list().size();
        }
        String queryString = param.getQueryString();
        String pagerHtml = getPaperHtml(totalCount,
            param.getPageSize(),
            param.getPageIndex(),
            queryString);
        model.addAttribute("list", list);
        model.addAttribute("totalCount", totalCount);
        model.addAttribute("pager", pagerHtml);

        model.addAttribute("list", list);
        return "admin/product/list";
    }

    @PostMapping("/admin/product/add.do")
    public String add(Model model, ProductInput parameter) {

        boolean result = productService.add(parameter);
        return "redirect:/admin/product/list.do";
    }

    @PostMapping("/admin/product/delete.do")
    public String del(Model model, ProductInput parameter) {
        boolean result = productService.delete(parameter.getId());
        return "redirect:/admin/product/list.do";
    }


    @PostMapping("/admin/product/update.do")
    public String update(Model model, ProductInput parameter) {

        boolean result = productService.update(parameter);
        return "redirect:/admin/product/list.do";
    }

}

 

  • ProductService 
<hide/>
public interface ProductService {

    /**
     * 상품 신규 추가
     */
    boolean add(ProductInput product);   // 인풋으로 바꿔야할 수도 있다.

    /**
     * 상품 수정
     */
    boolean update(ProductInput parameter);

    /**
     * 상품 삭제
     */
    boolean delete(long id); // 기본키

    /**
     * 상품 리스트
     */
    List<ProductDto> list();

}

 

========================오류 - update() 가 작동하지 않는다. ========================

  • 오류: update()를 실행해도 데이터베이스에 들어가지 않는다.
  • 원인: product를 save()하지 않았다.
<hide/>
package com.example.mall.admin.service.impl;

import com.example.mall.admin.model.ProductDto;
import com.example.mall.admin.model.ProductInput;
import com.example.mall.admin.repository.ProductRepository;
import com.example.mall.admin.service.ProductService;
import com.example.mall.entity.Product;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class ProductServiceImpl implements ProductService {

    private final ProductRepository productRepository;

    // 등록
    @Override
    public boolean add(ProductInput parameter) {

        Product product = Product.builder().productName(parameter.getProductName())
            .productImg(parameter.getProductImg()).productQuantity(parameter.getProductQuantity())
            .price(parameter.getPrice()).category(parameter.getCategory())
            .stockedDt(parameter.getStockedDt()).build();

        productRepository.save(product);
        return true;
    }
/*
    // 수정
    @Override
    public boolean update(ProductInput parameter) {  // id로 찾아서 다른 값들을 바꾼다.

        Optional<Product> OptionalProduct = productRepository.findById(parameter.getId());

        if (OptionalProduct.isPresent()) {
            Product product = OptionalProduct.get();
            product.setProductImg(parameter.getProductImg());
            product.setProductName(parameter.getProductName());
            product.setProductQuantity(parameter.getProductQuantity());
            product.setCategory(parameter.getCategory());
            product.setPrice(parameter.getPrice());
            product.setStockedDt(parameter.getStockedDt());
        }
        return true;
    }
*/
    // 삭제
    @Override
    public boolean delete(long id) {
        Optional<Product> OptionalProduct = productRepository.findById(id);
        productRepository.delete(OptionalProduct.get());
        return true;
    }


    @Override
    public List<ProductDto> list() {
        return ProductDto.of(productRepository.findAll());
    }
}

 

  • list.html
<hide/>
<!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>관리자</title>

  <style>
    .list table {
      width: 100%;
      border-collapse: collapse;
    }

    .list table th, .list table td {
      border: solid 1px #000;
    }

    .search-form {
      position: relative;
      padding: 5px 0 5px 0;
      text-align: right;
    }

    .search-form .total-count {
      position-position: absolute;
      left: 0;
      top: 0;
      height: 20px;
      float: left;
    }

    .pager {
      margin-top: 10px;
      text-align: center;
    }

    .pager a.on {
      font-weight: bold;
      color: red;
    }
  </style>
</head>
<body>
<h1>관리자 상품 리스트</h1>
<div th:replace="/fragments/layout.html :: fragment-admin-body-menu"></div>


<div class="list">
  <div class="search-form">
    <p class="total-count">전체 <span th:text="${totalCount}"></span>개 </p>

    <form method="get">
      <select name="searchType">
        <option value="all">전체</option>

        <!--        상품을 검색하기 위한 수단  상품 이름 / 카테고리 / -->
        <!--        문자열이 아닌 형태는 어떻게 처리하지???-->

        <option th:selected="${#strings.equals(param.searchType, 'productId')}" value="productId">상품
          No
        </option>
        <option th:selected="${#strings.equals(param.searchType, 'productName')}"
                value="productName">이름
        </option>
        <option th:selected="${#strings.equals(param.searchType, 'price')}" value="price">가격
        </option>
      </select>
      <input th:value="${param.searchValue}" type="search" name="searchValue" placeholder="검색어 입력"/>
      <button type="submit">검색</button>
    </form>
  </div>
  <table>
    <thead>
    <tr>
      <th>No</th>
      <th>카테고리</th>
      <th>상품 Id</th>
      <th>상품명</th>
      <th>가격</th>
      <th>품절 여부</th>
      <th>상품 사진</th>
      <th>재고 수량</th>
      <th>입고 예정일</th>
      <th>누적 판매량</th>
    </tr>
    </thead>
    <tbody>
    <tr th:each="x : ${list}">
      <td th:th:text="${x.seq}"></td>
      <td th:th:text="${x.category}"></td>
      <td th:text="${x.id}"></td>
      <td th:text="${x.productName}"></td>
      <td th:text="${x.price}"></td>
      <td th:text="${x.soldOutYn}"></td>
      <td th:text="${x.productImg}"></td>
      <td th:text="${x.productQuantity}"></td>
      <td th:text="${x.stockedDt}"></td>
      <td th:text="${x.cumulativeSales}"></td>
    </tr>
    </tbody>
  </table>
</div>
</body>
</html>

 

  Note) 실행 결과

 

 

 

 

  Ex) delete 테스트

productService.delete(1L);

 

  • delete () 실행 전

 

  • delete () 실행 후

 

 

 

  Ex) update 테스트

  • test
<hide/>
ProductInput productInput = ProductInput.builder()
    .id(3L)
        .productName("목걸이")
            .productQuantity(0)
                .price(542)
                    .category(Category.CATEGORY_ACCESSORY)
                        .build();
productService.update(productInput);

 

  • productServiceImpl
<hide/>
@Override
public boolean update(ProductInput parameter) {  // id로 찾아서 다른 값들을 바꾼다.

    Optional<Product> OptionalProduct = productRepository.findById(parameter.getId());
    if (OptionalProduct.isPresent()) {
        Product product = OptionalProduct.get();
        product.setProductImg(parameter.getProductImg());
        product.setProductName(parameter.getProductName());
        product.setProductQuantity(parameter.getProductQuantity());
        product.setCategory(parameter.getCategory());
        product.setPrice(parameter.getPrice());
        product.setStockedDt(parameter.getStockedDt());
        productRepository.save(product);
        return true;
      }
	return false;
}

 

  • update() 실행 

  • update() 실행 후

 

 

 

점검 사항

  • add(), delete() 는 확인 완료
  • update() 구현부 마지막에 save()를 해줘야한다!
  • 상품 추가, delete는 html에 내용 추가하기
  • cart : product 는 일대다 연관 관계
    • 그러면 cart하나에 여러 개의 product를 매핑 가능하다
    • 장바구니는 곧 멤버를 의미한다.