Team project - 쇼핑몰 구현(4)

2024. 4. 29. 15:52개발일지

 

상품 페이지를 빼먹었다.....ㅋㅋ

 

상품은 관리자만 삽입, 수정, 삭제하여 관리할 수 있도록 권한을 설정하였으며,

상품 이미지는 파일업로드를 통해 업로드 된 이미지를 하드디스크에 저장할 수 있도록 Bean을 설정하였다.

파일명은 난수로 설정하여 중복을 방지할 수 있도록 하였고,

이미지 파일 수정 시 기존 파일을 삭제하고 새로 등록할 수 있게 구현하였다.

 

 

 

 

properties에 추가하면 된다.

#파일 업로드
imgUploadLocation = C:/salad/product/
uploadPath = file:///C:/salad/

 

 

 

WebMvcConfig

package com.example.basic.Config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

//
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    //파일업로드 변수 설정
    @Value("${uploadPath}")
    String uploadPath;

    //자원 추가
    //C:/salad/를 resources images폴더로 연결해서 사용
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/images/**")
                .addResourceLocations(uploadPath);
    }
}

 

 

 

ProductDTO

/*
작성자 : 정아름
작성일 : 24.02.19
작성내용 : 상품 구현
확인사항 : 테스트 해야함
 */

package com.example.basic.DTO;

import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ProductDTO {
    //상품 번호
    private Integer productId;

    //상품 이름
    @NotBlank(message = "상품명은 필수입니다.")
    private String productName;

    //상품 상세 내용
    @NotBlank(message = "상세내용은 필수입니다.")
    private String productContent;

    //상품 이미지명
    private String productImage;

    //상품 가격
    private Integer productPrice;

    //상품 수량(출고량)
    private Integer quantity;

    //상품 입고량
    private Integer quantityIn;

    //상품 재고량
    private Integer quantityCount;

    //등록일
    private LocalDateTime regDate;

    //수정일
    private LocalDateTime modDate;

    //장바구니 상품 번호
    private Integer cartItemId;

}

 

 

 

ProductEntity

package com.example.basic.Entity;

/*
    작성자 : 정아름
    작성일 : 24.02.21
    수정사항 :
 */

import jakarta.persistence.*;
import lombok.*;

@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Builder
@Table(name = "product")
@SequenceGenerator(name = "product_SEQ", sequenceName = "product_SEQ", initialValue = 1,
        allocationSize = 1)
public class ProductEntity extends BaseEntity{
    //상품 번호
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "product_SEQ")
    private Integer productId;

    //상품 이름
    @Column(length = 20, nullable = false)
    private String productName;

    //상품 상세 내용
    private String productContent;

    //상품 이미지
    private String productImage;

    //상품 가격
    @Column(nullable = false)
    private Integer productPrice;

    //상품 수량(출고량)
    private Integer quantity;

    //상품 입고량
    private Integer quantityIn;

    //상품 재고량
    private Integer quantityCount;

}

 

 

 

ProductRepository

package com.example.basic.Repository;

import com.example.basic.Entity.ProductEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface ProductRepository extends JpaRepository<ProductEntity, Integer> {
    //상품명 조회
    List<ProductEntity> findByProductNameContaining(String keyword);

    //신상품 조회
    @Query("SELECT w from ProductEntity w order by w.regDate desc")
    List<ProductEntity> findProductEntitiesBy();

    @Query(value = "SELECT ProductEntity FROM CartItemEntity w WHERE w.cartItemId = :cartItemId")
    List<ProductEntity> findAllByCartItemId(Integer cartItemId);
}

 

 

 

ProductService

package com.example.basic.Service;

import com.example.basic.DTO.ProductDTO;
import com.example.basic.Entity.ProductEntity;
import com.example.basic.Repository.ProductRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;

@Service
@RequiredArgsConstructor
@Transactional
public class ProductService {
    @Value("${imgUploadLocation}")
    private String imgUploadLocation;

    private final ProductRepository productRepository;
    private final ModelMapper modelMapper;
    private final FileService fileService;

    //삽입
    public void productInsert(ProductDTO productDTO, MultipartFile imgFile) {
        //출고량=0
        productDTO.setQuantity(0);
        //재고량=입고량
        productDTO.setQuantityCount(productDTO.getQuantityIn());

        String originalFileName = imgFile.getOriginalFilename(); //파일명 추출
        String newFileName = ""; //파일저장후 난수로 만든 파일명
        try {
            if (originalFileName != null) { //파일이 존재하면
                //파일을 해당위치에 난수로 이름을 지정해서 저장
                newFileName = fileService.uploadFile(imgUploadLocation,
                        originalFileName, imgFile.getBytes());
            }
        } catch (Exception e) {
            ;
        }

        productDTO.setProductImage(newFileName); //새로운 파일명을 등록

        //변환
        ProductEntity productEntity = modelMapper.map(productDTO,
                ProductEntity.class);

        productRepository.save(productEntity);
    }

    //수정
    public void productUpdate(ProductDTO productDTO, MultipartFile imgFile) {

        Optional<ProductEntity> productEntity = productRepository
                .findById(productDTO.getProductId()); //수정할 레코드를 조회

        String deleteFile = productEntity.get().getProductImage(); //저장된 파일명을 읽기

        String originalFileName = imgFile.getOriginalFilename();
        String newFileName;
        try {
            if (originalFileName.length() != 0) { //null, 새로운 파일이 추가되었으면
                if (deleteFile.length() != 0) { //기존에 저장된 파일이 존재하면
                    fileService.deleteFile(imgUploadLocation, deleteFile); //기존파일삭제
                }
                //새로운 파일 추가
                newFileName = fileService.uploadFile(imgUploadLocation, originalFileName,
                        imgFile.getBytes());
                productDTO.setProductImage(newFileName);
            }
        } catch (Exception e) {
            ;
        }

        productDTO.setQuantity(0);
        //재고 수량 업데이트
        Integer count = productEntity.get().getQuantityCount();
        //재고량 + 추가 입고량
        productDTO.setQuantityCount(productDTO.getQuantityIn() + count);
        //추가 입고량이 없을 경우
        Integer quantity = productEntity.get().getQuantityIn();
        productDTO.setQuantityIn(quantity);

        //데이터베이스 저장
        ProductEntity product = modelMapper.map(productDTO,
                ProductEntity.class);
        productRepository.save(product);
    }

    //삭제
    public void productDelete(Integer productId) {
        //상품 조회(하디드스크에 저장된 이미지 삭제)
        ProductEntity productEntity = productRepository.findById(productId).orElseThrow();
        //읽어온 레코드에서 파일명을 읽는다.
        String delFile = productEntity.getProductImage();
        //저장된 경로와 파일명을 전달하여 파일을 삭제
        fileService.deleteFile(imgUploadLocation, delFile);

        productRepository.deleteById(productId);
    }

    //전체 조회
    public List<ProductDTO> productList(String keyword, String type) {

        List<ProductEntity> productEntities;

        if (keyword != null) {
            //검색어가 존재하면 상품명에서 검색
            productEntities = productRepository.findByProductNameContaining(keyword);
            //검색어도 없으면
        } else {
            productEntities = productRepository.findAll();
        }
        
        //신상품 조회
        if (type.equals("n")) {
            productEntities = productRepository.findProductEntitiesBy();
        }

        List<ProductDTO> productDTOS = Arrays.
                asList(modelMapper.map(productEntities, ProductDTO[].class));

        return productDTOS;
    }

    //개별 조회
    public ProductDTO productDetail(Integer productId) {
        Optional<ProductEntity> productEntity = productRepository.findById(productId);
        ProductDTO productDTO = modelMapper.map(productEntity, ProductDTO.class);
        return productDTO;
    }
}

 

 

 

ProductController


/*
    설명 : 상품관리의 목록, 수정, 삭제, 조회로 이동하는 페이지 영역
    입력값 : /product/list, /product/insert, /product/update, /product/delete, /product/detail
    출력값 : product/list, product/insert, product/update, product/detail
    작성일 : 24.02.22
    작성자 : 정아름
    수정사항 : 상품관리의 전체 목록 페이지는 list 처리 하기로 함
             상품 등록 시 C:/salad에 사진 들어가는 거 확인함.
 */

package com.example.basic.Controller;

import com.example.basic.DTO.ProductDTO;
import com.example.basic.Service.ProductService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@Controller
@RequiredArgsConstructor
public class ProductController {
    //주입
    private final ProductService productService;

    //카테고리 별 정렬 추가
    //전체 조회
    @GetMapping(value = {"/product/list", "/salad/list", "/product/admin"})
    public String listForm(@RequestParam(value = "keyword", defaultValue = "") String keyword,
                           @RequestParam(value = "type", defaultValue = "") String type,
                           Model model) {

        List<ProductDTO> productDTO = productService.productList(keyword, type);

        model.addAttribute("list", productDTO);

        return "product/list";
    }

    //삽입
    @GetMapping("/product/insert")
    public String insertForm(Model model) {

        //검증처리시 빈 DTO를 전달
        ProductDTO productDTO = new ProductDTO();
        model.addAttribute("data", productDTO);
        
        return "product/insert";
    }

    @PostMapping("/product/insert")
    public String insertProc(@Valid ProductDTO productDTO, @RequestParam MultipartFile imgFile,
                             BindingResult bindingResult) {

        //검증시 오류가 발생하면
        if (bindingResult.hasErrors()) {
            //등록창으로 다시 이동
            return "product/insert";
        }

        productService.productInsert(productDTO, imgFile);

        return "redirect:/product/list";
    }

    //수정
    @GetMapping("/product/update")
    public String updateForm(Integer id, Model model) {
        ProductDTO productDTO = productService.productDetail(id);
        model.addAttribute("data", productDTO);

        return "product/update";
    }

    @PostMapping("/product/update")
    public String updateProc(ProductDTO productDTO, MultipartFile imgFile,
                             BindingResult bindingResult) {

        if (bindingResult.hasErrors()) {
            return "product/update";
        }

        if (productDTO.getQuantityIn() == null) {
            productDTO.setQuantityIn(0);
        }

        productService.productUpdate(productDTO, imgFile);

        return "redirect:/product/list";
    }

    //삭제
    @GetMapping("/product/delete")
    public String deleteProc(Integer id) {
        productService.productDelete(id);

        return "redirect:/product/list";
    }

    //개별 조회
    @GetMapping("/product/detail")
    public String readProc(Integer id, Model model) {
        ProductDTO productDTO = productService.productDetail(id);

        model.addAttribute("data", productDTO);

        return "product/detail";
    }
}

 

 

 

 

View

product/detail

<!--
파일명 : product/detail
작성자 : 정아름
작성일 : 24.02.21
수정사항 : 글씨체 적용
         틀 구성 이미지 파일 업로드 확인하기.
-->
<!DOCTYPE html>
<html lang="ko"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layouts/main}"
      xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="UTF-8">
    <title>SalPick</title>
    <!-- bootstrap -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
    <!-- google fonts -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&display=swap" rel="stylesheet">
    <!-- google icon -->
    <link rel="stylesheet"
          href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0"/>
    <style>
        body {
            font-family: Verdana, sans-serif;
            font-size: 15px;
        }

        h2 {
            font-family: "Nanum Pen Script", cursive;
            font-weight: 400;
            font-size: 50px;
            font-style: normal;
        }

        h3 {
            font-family: "Nanum Pen Script", cursive;
            font-style: normal;
            text-align: center;
            text-decoration-line: underline;
            text-shadow: .04em .04em 0 #fff,.08em .08em 0 #aaa;
        }

        .img1 {
            display: block;
            margin-left: auto;
            margin-right: auto;
            width: 80%;
            height: auto;
        }

        .container1 {
            flex-flow: column;
            text-align: center;
            align-items: flex-start;
            align-content: space-around;
        }
    </style>
</head>
<body>
<div layout:fragment="content">
    <div class="row">
        <!-- 여백 -->
        <div class="col-sm-3"></div>
        <!-- 상품 상세 페이지 -->
        <div class="col-sm-6">
            <div class="container mt-5 mb-5">
                <div class="card-header">
                    <h2>상품 정보</h2><br><br>
                </div>
                <div class="card-body">
                    <div class="row gx-4 gx-lg-5 align-items-center">
                        <div class="col-md-6">
                            <img class="img1" th:src="|/images/product/@{${data.productImage}}|" alt="이미지 없음"
                                 width="50%">
                        </div>
                        <div class="col-md-6">
                            <div th:text="${data.productName}"></div>
                            <div>
                                <span th:text="${data.productPrice}"></span>
                                <span> 원</span>
                            </div>
                            <div class="mb-3 mt-3">
                                <label class="form-label">수량 선택 :</label>
                                <select class="form-control" name="quantity">
                                    <option value="1" th:selected="${data.quantity}==1">1</option>
                                    <option value="2">2</option>
                                    <option value="3">3</option>
                                    <option value="4">4</option>
                                    <option value="5">5</option>
                                </select>
                            </div>
                            <div class="row">
                                <div class="col-sm-4">
                                    <form th:action="@{/cart/insert/{id}(id=${data.productId})}" method="post"
                                          sec:authorize="hasRole('USER')">
                                        <input type="hidden" id="quantity" name="quantity" th:value="${data.quantity}">
                                        <button type="submit" class="btn btn-outline-warning">
                                            <span class="material-symbols-outlined">shopping_cart</span>
                                        </button>
                                    </form>
                                </div>
                                <div class="col-sm-4">
                                    <button type="button" class="btn btn-outline-warning"
                                            sec:authorize="hasRole('ADMIN')"
                                            th:onclick="|location.href='@{/product/update(id=${data.productId})}'|">
                                        상품 수정
                                    </button>
                                </div>
                                <div class="col-sm-4">
                                    <button type="button" class="btn btn-outline-warning"
                                            sec:authorize="hasRole('ADMIN')"
                                            th:onclick="|location.href='@{/product/delete(id=${data.productId})}'|">
                                        상품 삭제
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="card-footer">
                    <br>
                    <hr>
                    <br>
                    <h3>
                        상세정보
                    </h3>
                    <div class="container1 px-4 px-lg-5 mt-5">
                        <div th:text="${data.productContent}"></div><br><br>
                        <img class="img1" th:src="|/images/product/@{${data.productImage}}|" alt="이미지 없음"><br><br>
                        <div>
                            <img class="img1" src="/image/배송일정공지.jpg" alt="배송일정"><br><br>
                        </div>
                        <div>
                            <img class="img1" src="/image/위생안전공지.jpg" alt="위생안전">
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <!-- 여백 -->
        <div class="col-sm-3"></div>
    </div>
</div>
</body>
</html>

 

 

 

product/insert


<!--
파일명 : product/insert
작성자 : 정아름
작성일 : 24.02.21
수정사항 : 글씨체 적용
         틀 구성 이미지 파일 업로드 확인하기.
-->
<!DOCTYPE html>
<html lang="ko"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layouts/main}"
      xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="UTF-8">
    <title>SalPick</title>
    <!-- bootstrap -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
    <!-- google fonts -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&display=swap" rel="stylesheet">
    <!-- google icon -->
    <link rel="stylesheet"
          href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0"/>
    <style>
        body {
            font-family: Verdana, sans-serif;
            font-size: 15px;
        }

        h2 {
            font-family: "Nanum Pen Script", cursive;
            font-weight: 400;
            font-size: 50px;
            font-style: normal;
        }
    </style>
</head>
<body>
<div layout:fragment="content">
    <div class="row">
        <!-- 여백 -->
        <div class="col-sm-3"></div>
        <!-- 상품 등록 페이지 -->
        <div class="col-sm-6">
            <div class="container mt-5 mb-5">
                <h2>상품 등록</h2>
                <form th:action="@{/product/insert}" method="post"
                      th:object="${data}" enctype="multipart/form-data">
                    <div class="mb-3 mt-3">
                        <label for="productName" class="form-label">상품 이름 : </label>
                        <input type="text" class="form-control" name="productName"
                               id="productName" th:field="*{productName}">
                        <p class="text-danger" th:if="${#fields.hasErrors('productName')}"
                           th:errors="*{productName}"></p>
                    </div>
                    <div class="mb-3">
                        <label for="productContent" class="form-label">상품 상세 내용 : </label>
                        <textarea class="form-control" rows="5" name="productContent"
                                  id="productContent" th:field="*{productContent}">
                        </textarea>
                        <p class="text-danger" th:if="${#fields.hasErrors('productContent')}"
                           th:errors="*{productContent}"></p>
                    </div>
                    <div class="mb-3">
                        <label for="productImage" class="form-label">상품 이미지 : </label>
                        <input type="file" class="form-control" id="productImage" name="imgFile">
                    </div>
                    <div class="mb-3">
                        <label for="quantityIn" class="form-label">상품 수량 : </label>
                        <input type="text" class="form-control" id="quantityIn" name="quantityIn">
                    </div>
                    <div class="mb-3">
                        <label for="productPrice" class="form-label">상품 가격 : </label>
                        <input type="text" class="form-control" id="productPrice" name="productPrice">
                    </div>
                    <button type="submit" class="btn btn-outline-warning float-end">
                        상품 등록
                    </button>
                </form>
            </div>
        </div>
        <!-- 여백 -->
        <div class="col-sm-3"></div>
    </div>
</div>
</body>
</html>

 

 

 

product/list

<!--
파일명 : product/list
작성자 : 정아름
작성일 : 24.03.06
수정사항 : 그리드 맞게 나왔는지 확인하기.
-->
<!DOCTYPE html>
<html lang="ko"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layouts/main}"
      xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="UTF-8">
    <title>SalPick</title>
    <!-- bootstrap -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
    <!-- google fonts -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&display=swap" rel="stylesheet">
    <!-- google icon -->
    <link rel="stylesheet"
          href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0"/>
    <style>
        body {
            font-family: Verdana, sans-serif;
            font-size: 15px;
        }

        h2 {
            font-family: "Nanum Pen Script", cursive;
            font-weight: 400;
            font-size: 50px;
            font-style: normal;
        }

        .a1 {
            color: coral;
            text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 3px black;
            text-decoration-line: none;
            text-effect: outline;
        }

        /* list */
        .serv_list {
            padding-top: 70px;
            padding-bottom: 70px;
        }

        .serv_list .container {
            max-width: 1100px;
            margin: auto;
        }

        /*card*/
        .serv_list .container .item_list {
            display: flex;
            flex-wrap: wrap;
            justify-content: space-between;
        }

        .serv_list .container .item_list .card {
            border: 1px solid #eee;
            border-radius: 5px;
            height: 450px;
            width: 250px;
            padding: 5px;
            margin-bottom: 10px;
        }

        /*card2*/
        .serv_list .container .item_list .card img {
            height: 180px;
            width: 200px;
        }

        .serv_list .container .item_list .card .text p {
            font-size: 12px;
            opacity: .8;
        }

        .serv_list .container .item_list .card .text button {
            background: black;
            color: white;
            border: 2px solid white;
            border-radius: 10px;
            padding: 5px 10px;
            font-size: 10px;
            transition: .5s ease;
        }

        .serv_list .container .item_list .card .text button:hover {
            transition: .5s ease;
            border: 2px solid black;
            background: transparent;
            color: black;
        }
    </style>
</head>
<body>
<div layout:fragment="content">
    <div class="row">
        <!-- 여백 -->
        <div class="col-sm-3"></div>
        <!-- 상품 목록-->
        <div class="col-sm-6">
            <div class="container mt-5 mb-5">
                <h2>상품 목록</h2>
                <button type="button" class="btn btn-outline-success float-end"
                        th:onclick="|location.href='@{/product/insert}'|"
                        sec:authorize="hasRole('ADMIN')">
                    상품 등록
                </button>
                <section class="serv_list">
                    <div class="container">
                        <div class="item_list">
                            <div class="row" th:each="data:${list}">
                                <div class="card">
                                    <div class="card-body">
                                        <div>
                                            <img th:src="|/images/product/@{${data.productImage}}|"
                                                 width="70" height="70" alt="">
                                        </div>
                                        <br>
                                        <div>
                                            <span>상품코드번호</span>
                                            <span th:text="${data.productId}"></span>
                                        </div>
                                    </div>
                                    <div class="card-footer">
                                        <h4>
                                            <a class="a1" th:href="@{/product/detail(id=${data.productId})}">
                                                <span th:text="${data.productName}"></span>
                                            </a>
                                        </h4>
                                        <p>
                                            <span th:text="${data.productPrice}"></span>
                                            <span> 원</span>
                                        </p>
                                        <div class="row">
                                            <div class="col-sm-6">
                                                <form th:action="@{/order/insert/{id}(id=${data.productId})}"
                                                      method="post">
                                                    <input type="hidden" id="count" name="count" value="1">
                                                    <button type="submit" class="btn btn-sm btn-outline-warning"
                                                            sec:authorize="hasRole('USER')">
                                                        <span class="material-symbols-outlined">buy</span>
                                                    </button>
                                                </form>
                                            </div>
                                            <div class="col-sm-6">
                                                <form th:action="@{/cart/insert/{id}(id=${data.productId})}"
                                                      method="post">
                                                    <input type="hidden" id="quantity" name="quantity" value="1">
                                                    <button type="submit" class="btn btn-outline-warning"
                                                            sec:authorize="hasRole('USER')">
                                                        <span class="material-symbols-outlined">shopping_cart</span>
                                                    </button>
                                                </form>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </section>
            </div>
        </div>
        <!-- 여백 -->
        <div class="col-sm-3"></div>
    </div>
</div>
</body>
</html>

 

 

 

product/update


<!--
파일명 : product/update
작성자 : 정아름
작성일 : 24.02.21
수정사항 : 글씨체 적용
         틀 구성 이미지 파일 업로드 확인하기.
-->
<!DOCTYPE html>
<html lang="ko"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layouts/main}"
      xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="UTF-8">
    <title>SalPick</title>
    <!-- bootstrap -->
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
    <!-- google fonts -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&display=swap" rel="stylesheet">
    <!-- google icon -->
    <link rel="stylesheet"
          href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0"/>
    <style>
        body {
            font-family: Verdana, sans-serif;
            font-size: 15px;
        }

        h2 {
            font-family: "Nanum Pen Script", cursive;
            font-weight: 400;
            font-size: 50px;
            font-style: normal;
        }
    </style>
</head>
<body>
<div layout:fragment="content">
    <div class="row">
        <!-- 여백 -->
        <div class="col-sm-3"></div>
        <!-- 상품 등록 페이지 -->
        <div class="col-sm-6">
            <h2>상품 수정</h2>
            <div class="container mt-5 mb-5">
                <form th:action="@{/product/update}" method="post"
                      th:object="${data}" enctype="multipart/form-data">
                    <input type="hidden" name="id" th:field="*{productId}">
                    <div class="mb-3 mt-3">
                        <label for="productName" class="form-label">상품 이름 : </label>
                        <input type="text" class="form-control" name="productName"
                               id="productName" th:field="*{productName}">
                    </div>
                    <div class="mb-3">
                        <label for="productContent" class="form-label">상품 상세 내용 : </label>
                        <textarea class="form-control" rows="5" name="productContent"
                                  id="productContent" th:field="*{productContent}">
                        </textarea>
                    </div>
                    <div class="mb-3">
                        <input type="hidden" name="productImage" th:field="*{productImage}">
                        <img th:src="|/images/product/@{${data.productImage}}|" width="70" height="70" alt="">
                        <label for="productImage" class="form-label">상품 이미지 : </label>
                        <input type="file" class="form-control" id="productImage" name="imgFile">
                    </div>
                    <div class="mb-3">
                        <label for="quantityIn" class="form-label">상품 수량 : </label>
                        <input type="text" class="form-control" id="quantityIn" name="quantityIn">
                    </div>
                    <div class="mb-3">
                        <label for="productPrice" class="form-label">상품 가격 : </label>
                        <input type="text" class="form-control" id="productPrice" name="productPrice"
                               th:field="*{productPrice}">
                    </div>
                    <button type="submit" class="btn btn-outline-warning float-end">상품 수정</button>
                </form>
            </div>
        </div>
        <!-- 여백 -->
        <div class="col-sm-3"></div>
    </div>
</div>
</body>
</html>

 

 

 

 

화면 캡쳐

상품 페이지(전체 상품 페이지. 등록 일자 순으로 정렬되어 있다.)

 

 

상품 페이지(신상품 페이지. 최근 등록 순으로 정렬되어있다.)

 

 

상품 등록 페이지(관리자만 등록할 수 있다.)

 

 

상품 상세 페이지(관리자 권한으로 로그인되어 상품을 수정, 삭제할 수 있게 하였다. 회원 권한으로 로그인 시 수정, 삭제 버튼 비 활성화, 장바구니 버튼 활성화 시킴)

 

'개발일지' 카테고리의 다른 글

Personal Project - 가계부 구현  (2) 2024.04.30
Team project - 쇼핑몰 구현(5)  (0) 2024.04.29
Team project - 쇼핑몰 구현(3)  (0) 2024.04.29
Team project - 쇼핑몰 구현(2)  (0) 2024.04.29
Team project - 쇼핑몰 구현(1)  (2) 2024.04.26