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 |