package com.shop.controller;
import com.shop.dto.ItemFormDto;
import com.shop.dto.ItemSearchDto;
import com.shop.entity.Item;
import com.shop.service.ItemService;
import jakarta.persistence.EntityNotFoundException;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
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.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.data.domain.Pageable;
import java.util.List;
import java.util.Optional;
@Controller
@RequiredArgsConstructor
public class ItemController {
private final ItemService itemService;
@GetMapping(value = "/admin/item/new")
public String itemForm(Model model) {
model.addAttribute("itemFormDto", new ItemFormDto());
return "/item/itemForm";
}
@PostMapping(value = "/admin/item/new")
public String itemNew(@Valid ItemFormDto itemFormDto, BindingResult bindingResult, Model model,
@RequestParam("itemImgFile")List<MultipartFile> itemImgFileList) {
if (bindingResult.hasErrors()) {
return "item/itemForm";
}
if (itemImgFileList.get(0).isEmpty() && itemFormDto.getId() == null) {
model.addAttribute("errorMessage",
"첫번째 상품 이미지는 필수 입력 값입니다.");
return "item/itemForm";
}
try {
itemService.saveItem(itemFormDto, itemImgFileList);
} catch (Exception e) {
model.addAttribute("errorMessage",
"상품 등록 중 에러가 발생하였습니다.");
return "item/itemForm";
}
return "redirect:/";
}
@GetMapping(value = "/admin/item/{itemId}")
public String itemDtl(@PathVariable("itemId") Long itemId, Model model) {
try {
ItemFormDto itemFormDto = itemService.getItemDtl(itemId);
model.addAttribute("itemFormDto", itemFormDto);
} catch (EntityNotFoundException e) {
model.addAttribute("errorMessage", "존재하지 않는 상품입니다.");
model.addAttribute("itemFormDto", new ItemFormDto());
// return "item/itemForm";
}
return "item/itemForm";
}
@PostMapping(value = "/admin/item/{itemId}")
public String itemUpdate(@Valid ItemFormDto itemFormDto, BindingResult bindingResult,
@RequestParam("itemImgFile") List<MultipartFile> itemImgFileList,
Model model) {
if (bindingResult.hasErrors()) {
return "item/itemForm";
}
if (itemImgFileList.get(0).isEmpty() && itemFormDto.getId() == null) {
model.addAttribute("errorMessage", "첫번째 상품 이미지는 필수 입력 값입니다.");
return "item/itemForm";
}
try {
itemService.updateItem(itemFormDto, itemImgFileList);
} catch (Exception e) {
model.addAttribute("errorMessage", "상품 수정 중 에러가 발생하였습니다.");
return "item/itemForm";
}
return "redirect:/"; // 다시 실행
}
@GetMapping(value = {"/admin/item", "/admin/items/{page}"})
public String itemManage(ItemSearchDto itemSearchDto, @PathVariable("page")Optional<Integer> page,
Model model) {
Pageable pageable = PageRequest.of(page.isPresent() ? page.get() : 0, 5);
Page<Item> items = itemService.getAdminItemPage(itemSearchDto, pageable);
model.addAttribute("items", items);
model.addAttribute("itemSearchDto", itemSearchDto);
model.addAttribute("maxPage", 5);
return "item/itemMng";
}
@GetMapping(value = "/item/{itemId}")
public String itemDtl(Model model, @PathVariable("itemId") Long itemId) {
ItemFormDto itemFormDto = itemService.getItemDtl(itemId);
model.addAttribute("item", itemFormDto);
return "item/itemDtl";
}
}
package com.shop.service;
import com.shop.dto.ItemFormDto;
import com.shop.dto.ItemImgDto;
import com.shop.dto.ItemSearchDto;
import com.shop.dto.MainItemDto;
import com.shop.entity.Item;
import com.shop.entity.ItemImg;
import com.shop.repository.ItemImgRepository;
import com.shop.repository.ItemRepository;
import jakarta.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.List;
@Service
@Transactional
@RequiredArgsConstructor
public class ItemService {
private final ItemRepository itemRepository;
private final ItemImgService itemImgService;
private final ItemImgRepository itemImgRepository;
public Long saveItem(ItemFormDto itemFormDto, List<MultipartFile> itemImgFileList)
throws Exception {
// 상품 등록
Item item = itemFormDto.createItem(); // 모델매퍼
itemRepository.save(item);
// 이미지 등록
for (int i = 0; i <itemImgFileList.size(); i++) {
ItemImg itemImg = new ItemImg();
itemImg.setItem(item);
if (i == 0) {
itemImg.setRepImgYn("Y");
} else {
itemImg.setRepImgYn("N");
}
itemImgService.saveItemImg(itemImg, itemImgFileList.get(i));
}
return item.getId();
}
@Transactional(readOnly = true)
public ItemFormDto getItemDtl(Long itemId) {
// Entity
List<ItemImg> itemImgList = itemImgRepository.findByItemIdOrderByIdAsc(itemId);
// DB에서 데이터를 가지고 온다.
// DTO
List<ItemImgDto> itemImgDtoList = new ArrayList<>();
for (ItemImg itemimg : itemImgList) {
// Entity -> Dto
ItemImgDto itemImgDto = ItemImgDto.of(itemimg);
itemImgDtoList.add(itemImgDto);
}
Item item = itemRepository.findById(itemId).orElseThrow(EntityNotFoundException::new);
// Item -> ItemFormDto modelMapper
ItemFormDto itemFormDto = ItemFormDto.of(item);
itemFormDto.setItemImgDtoList(itemImgDtoList);
return itemFormDto;
}
public Long updateItem(ItemFormDto itemFormDto, List<MultipartFile> itemImgFileList)
throws Exception {
// 상품 변경
Item item = itemRepository.findById(itemFormDto.getId())
.orElseThrow(EntityNotFoundException::new);
item.updateItem(itemFormDto);
// 상품 이미지 변경
List<Long> itemImgIds = itemFormDto.getItemImgIds();
for (int i = 0; i < itemImgFileList.size(); i++) {
itemImgService.updateItemImg(itemImgIds.get(i), itemImgFileList.get(i));
}
return item.getId();
}
@Transactional(readOnly = true)
public Page<Item> getAdminItemPage(ItemSearchDto itemSearchDto, Pageable pageable) {
return itemRepository.getAdminItemPage(itemSearchDto, pageable);
}
@Transactional(readOnly = true)
public Page<MainItemDto> getMainItemPage(ItemSearchDto itemSearchDto, Pageable pageable) {
return itemRepository.getMainItemPage(itemSearchDto, pageable);
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/layout1}">
<head>
<meta name="_csrf" th:content="${_csrf.token}"/>
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
</head>
<th:block layout:fragment="script">
<script th:inline="javascript">
$(document).ready(function() {
calculateTotalPrice();
$("#count").change(function() {
calculateTotalPrice();
});
});
function calculateTotalPrice() {
var count = $("#count").val();
var price = $("#price").val();
var totalPrice = price * count;
$("#totalPrice").html(totalPrice + '원');
}
function order() {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
var url = "/order";
var paramData = {
itemId : $("#itemId").val(),
count : $("#count").val()
}
var param = JSON.stringify(paramData);
$.ajax({
url : url,
type : "POST",
contentType : "application/json",
data : param,
beforeSend : function(xhr) {
// 데이터 전송하기 전에 헤더에 csrf 값을 설정
xhr.setRequestHeader(header, token);
},
dataType : "json",
cache : false,
success : function(result, status) {
alert("주문이 완료 되었습니다.");
location.href='/';
},
error : function(jqXHR, status, error) {
if(jqXHR.status == '401') {
alert('로그인 후 이용해주세요.');
location.href='/members/login';
} else {
alert(jqXHR.responseText);
}
}
});
}
</script>
</th:block>
<!--사용자 css 추가-->
<th:block layout:fragment="css">
<style>
.mgb-15 {
margin-bottom: 15px;
}
.mgt-30 {
margin-top: 30px;
}
.mgt-50 {
margin-top: 50px;
}
.repImgDiv {
margin-right: 15px;
height: auto;
width: 50%;
}
.repImg {
width: 100%;
height: 400px;
}
.wd50 {
height: auto;
width: 50%;
}
</style>
</th:block>
<div layout:fragment="content" style="margin-left:25%, margin-right: 25%">
<input type="hidden" id="itemId" th:value="${item.id}">
<div class="d-flex">
<div class="repImgDiv">
<img th:src="${item.itemImgDtoList[0].imgUrl}" class="rounded repImg" th:alt="${item.itemNm}">
</div>
<div class="wd50">
<span th:if="${item.itemSellStatus == T(com.shop.constant.ItemSellStatus).SELL}"
class="badge bg-primary mgb-15">판매중</span>
<span th:unless="${item.itemSellStatus == T(com.shop.constant.ItemSellStatus).SELL}"
class="badge bg-primary mgb-15">품절</span>
<div class="h4" th:text="${item.itemNm}"></div>
<hr class="my-4">
<div class="text-right">
<div class="h4 text-danger text-left">
<input type="hidden" th:value="${item.price}" id="price" name="price">
<span th:text="${item.price}"></span>원
</div>
<div class="input-group w-50">
<div class="input-group-prepend">
<span class="input-group-text">수량</span>
</div>
<input type="number" name="count" id="count" class="form-control" value="1" min="1">
</div>
</div>
<hr class="my-4">
<div class="text-right mgt-50">
<h5>결제 금액</h5>
<h3 name="totalPrice" id="totalPrice" class="font-weight-bold"></h3>
</div>
<div th:if="${item.itemSellStatus == T(com.shop.constant.ItemSellStatus).SELL}"
class="text-right">
<button type="button" class="btn btn-light border border-primary btn-lg"
onclick="addCart()">
장바구니 담기</button>
<button type="button" class="btn btn-primary btn-lg" onclick="order()">주문하기</button>
</div>
<div th:unless="${item.itemSellStatus == T(com.shop.constant.ItemSellStatus).SELL}"
class="text-right">
<button type="button" class="btn btn-danger btn-lg">품절</button>
</div>
</div>
</div>
<div class="mgt-30">
<div class="container">
<h4 class="border border-success-subtle rounded-pill display-5">상품 상세 설명</h4>
<hr class="my-4">
<p class="lead" th:text="${item.itemDetail}"></p>
</div>
</div>
<div th:each="itemImg : ${item.itemImgDtoList}" class="text-center">
<img th:if="${not #strings.isEmpty(itemImg.imgUrl)}" th:src="${itemImg.imgUrl}"
class="rounded mgb-15"
width="800">
</div>
</div>
</html>
package com.shop.dto;
import com.shop.entity.OrderItem;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class OrderItemDto {
private String itemNm;
private int count;
private int orderPrice;
private String imgUrl;
public OrderItemDto(OrderItem orderItem, String imgUrl) {
this.itemNm = orderItem.getItem().getItemNm();
this.count = orderItem.getCount();
this.orderPrice = orderItem.getOrderPrice();
this.imgUrl = imgUrl;
}
}
package com.shop.dto;
import com.shop.constant.OrderStatus;
import com.shop.entity.Order;
import lombok.Getter;
import lombok.Setter;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
public class OrderHistDto {
private Long orderId;
private String orderDate;
private OrderStatus orderStatus;
private List<OrderItemDto> orderItemDtoList = new ArrayList<>();
public OrderHistDto(Order order) {
this.orderId = order.getId();
this.orderDate = order.getOrderDate()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
this.orderStatus = order.getOrderStatus();
}
public void addOrderItemDto(OrderItemDto orderItemDto) {
orderItemDtoList.add(orderItemDto);
}
}
package com.shop.repository;
import com.shop.entity.Order;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface OrderRepository extends JpaRepository<Order, Long> {
@Query("select o from Order o where o.member.email = :email order by o.orderDate desc")
List<Order> findOrders(@Param("email") String email, Pageable pageable);
@Query("select count(o) from Order o where o.member.email = :email")
Long countOrder(@Param("email") String email);
}
package com.shop.repository;
import com.shop.entity.ItemImg;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface ItemImgRepository extends JpaRepository<ItemImg, Long> {
List<ItemImg> findByItemIdOrderByIdAsc(Long itemId);
ItemImg findByItemIdAndRepImgYn(Long itemId, String repImgYn);
}
package com.shop.exception;
public class OutOfStockException extends RuntimeException {
public OutOfStockException(String message) {
super(message);
}
}
package com.shop.entity;
import com.shop.constant.ItemSellStatus;
import com.shop.dto.ItemFormDto;
import com.shop.exception.OutOfStockException;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.time.LocalDateTime;
import java.util.List;
@Entity
@Table(name = "item")
@Getter
@Setter
@ToString
public class Item extends BaseEntity{
@Id
@Column(name = "item_id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id; // 상품 코드
@Column(nullable = false, length = 50)
private String itemNm; // 상품명
@Column(name = "price", nullable = false)
private int price; // 가격
@Column(nullable = false)
private int stockNumber; // 수량
@Lob
@Column(nullable = false)
private String itemDetail; // 상품 상세 설명
@Enumerated
private ItemSellStatus itemSellStatus; // 상품 판매 상태
// private LocalDateTime regTime; // 등록 시간
//
// private LocalDateTime updateTime; // 수정 시간
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(
name = "member_item",
joinColumns = @JoinColumn(name = "member_id"),
inverseJoinColumns = @JoinColumn(name = "item_id")
)
private List<Member> member;
public void updateItem(ItemFormDto itemFormDto) {
this.itemNm = itemFormDto.getItemNm();
this.price = itemFormDto.getPrice();
this.stockNumber = itemFormDto.getStockNumber();
this.itemDetail = itemFormDto.getItemDetail();
this.itemSellStatus = itemFormDto.getItemSellStatus();
}
public void removeStock(int stockNumber) {
int restStock = this.stockNumber - stockNumber; // 10, 5 / 10, 20
if(restStock < 0) {
throw new OutOfStockException("상품의 재고가 부족합니다.(현재 재고 수량: " + this.stockNumber + ")");
}
this.stockNumber = restStock;
}
}
package com.shop.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Entity
@Getter
@Setter
public class OrderItem extends BaseEntity{
@Id
@GeneratedValue
@Column(name = "order_item_id")
private Long id;
@ManyToOne
@JoinColumn(name = "item_id")
private Item item;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private Order order;
private int orderPrice;
private int count;
// private LocalDateTime regTime;
// private LocalDateTime updateTime;
// item(상품) -> OrderItem(주문 상품)
public static OrderItem createOrderItem(Item item, int count) {
OrderItem orderItem = new OrderItem();
orderItem.setItem(item);
orderItem.setCount(count);
orderItem.setOrderPrice(item.getPrice());
item.removeStock(count);
return orderItem;
}
public int getTotalPrice() {
return orderPrice * count;
}
}
package com.shop.entity;
import com.shop.constant.OrderStatus;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "orders")
@Getter
@Setter
public class Order extends BaseEntity {
@Id
@GeneratedValue
@Column(name = "order_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
private LocalDateTime orderDate;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true,
fetch = FetchType.LAZY) // 양방향이면 주인과 부하 -> order가 orderItem 관리한다
private List<OrderItem> orderItems = new ArrayList<>();
@Enumerated(EnumType.STRING)
private OrderStatus orderStatus;
// private LocalDateTime regTime;
//
// private LocalDateTime updateTime;
// 주문서 주문 아이템 리스트에 주문 아이템 추가
// 주문 아이템에 주문서 추가
public void addOrderItem(OrderItem orderItem) {
orderItems.add(orderItem);
orderItem.setOrder(this);
}
// 주문서 생성
// 현재 로그인된 멤버 주문서에 추가
// 주문 아이템 리스트를 반복문을 통해서 주문서에 추가
// 상태는 주문으로 세팅
// 주문 시간은 현재 시간으로 세팅
// 주문서 리턴
public static Order createOrder(Member member, List<OrderItem> orderItemList) {
Order order = new Order();
order.setMember(member);
for (OrderItem orderItem : orderItemList) {
order.addOrderItem(orderItem);
}
order.setOrderStatus(OrderStatus.ORDER);
order.setOrderDate(LocalDateTime.now());
return order;
}
// 주문서에 있는 주문마다 아이템 리스트를 반복
// 주문 아이템마다 총 가격을 totalPrice에 추가
public int getTotalPrice() {
int totalPrice = 0;
for (OrderItem orderItem : orderItems) {
totalPrice += orderItem.getTotalPrice();
}
return totalPrice;
}
}
package com.shop.dto;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class OrderDto {
@NotNull(message = "상품 아이디는 필수 입력 값입니다.")
private Long itemId;
@Min(value = 1, message = "최소 주문 수량은 1개입니다.")
@Max(value = 999, message = "최대 주문 수량은 999개입니다.")
private int count;
}
package com.shop.service;
import com.shop.dto.OrderDto;
import com.shop.dto.OrderHistDto;
import com.shop.dto.OrderItemDto;
import com.shop.entity.*;
import com.shop.repository.ItemImgRepository;
import com.shop.repository.ItemRepository;
import com.shop.repository.MemberRepository;
import com.shop.repository.OrderRepository;
import jakarta.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Service
@Transactional
@RequiredArgsConstructor
public class OrderService {
private final ItemRepository itemRepository;
private final MemberRepository memberRepository;
private final OrderRepository orderRepository;
private final ItemImgRepository itemImgRepository;
public Long order(OrderDto orderDto, String email) {
Item item = itemRepository.findById(orderDto.getItemId())
.orElseThrow(EntityNotFoundException::new);
Member member = memberRepository.findByEmail(email);
List<OrderItem> orderItemList = new ArrayList<>();
OrderItem orderItem = OrderItem.createOrderItem(item, orderDto.getCount());
orderItemList.add(orderItem);
Order order = Order.createOrder(member, orderItemList);
orderRepository.save(order);
return order.getId();
}
// Entity Order, OrderItem
// change OrderHistDto, OrderItemDto-> result Page<OrderHistDto>
@Transactional(readOnly = true)
public Page<OrderHistDto> getOrderList(String email, Pageable pageable) {
List<Order> orders = orderRepository.findOrders(email, pageable);
Long totalCount = orderRepository.countOrder(email);
List<OrderHistDto> orderHistDtos = new ArrayList<>();
// Order -> OrderHistDto
// OrderItem -> OrderItemDto+
for (Order order : orders) {
OrderHistDto orderHistDto = new OrderHistDto(order);
List<OrderItem> orderItems = order.getOrderItems();
for (OrderItem orderItem : orderItems) {
ItemImg itemImg = itemImgRepository.findByItemIdAndRepImgYn(orderItem.getItem().getId(),
"Y");
OrderItemDto orderItemDto = new OrderItemDto(orderItem, itemImg.getImgUrl());
orderHistDto.addOrderItemDto(orderItemDto);
}
orderHistDtos.add(orderHistDto);
}
return new PageImpl<OrderHistDto>(orderHistDtos, pageable, totalCount);
}
}
package com.shop.controller;
import com.shop.dto.OrderDto;
import com.shop.dto.OrderHistDto;
import com.shop.service.OrderService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import org.springframework.data.domain.Pageable;
import java.security.Principal;
import java.util.List;
import java.util.Optional;
@Controller
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
// ajax
@PostMapping(value = "/order")
public @ResponseBody
ResponseEntity order(@RequestBody @Valid OrderDto orderDto, BindingResult bindingResult,
Principal principal) {
// String a = "abc" + "def"
// StringBuilder a;
// a.append("abc");
// a.append("def");
if (bindingResult.hasErrors()) {
StringBuilder sb = new StringBuilder();
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
for (FieldError fieldError : fieldErrors) {
sb.append(fieldError.getDefaultMessage());
}
return new ResponseEntity<String>(sb.toString(), HttpStatus.BAD_REQUEST);
}
// 로그인 정보 -> Spring security
// principal.getName() (현재 로그인 된 정보)
String email = principal.getName();
Long orderId;
try {
orderId = orderService.order(orderDto, email);
} catch (Exception e) {
return new ResponseEntity<String>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<Long>(orderId, HttpStatus.OK);
}
@GetMapping(value = {"/orders", "/orders/{page}"})
public String orderHist(@PathVariable("page") Optional<Integer> page,
Principal principal, Model model) {
Pageable pageable = PageRequest.of(page.isPresent() ? page.get() : 0, 5);
Page<OrderHistDto> orderHistDtoList = orderService.getOrderList(principal.getName(), pageable);
model.addAttribute("orders", orderHistDtoList);
model.addAttribute("page", pageable.getPageNumber());
model.addAttribute("maxPage", 5);
return "order/orderHist";
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/layout1}">
<head>
<meta name="_csrf" th:content="${_csrf.token}"/>
<meta name="_csrf_header" th:content="${_csrf.headerName}">
</head>
<th:block layout:fragment="css">
<style>
.content-mg {
margin-left: 30%;
margin-right: 30%
margin-top: 2%;
margin-bottom: 100px;
}
.repImgDiv {
margin-right: 15px;
margin-left: 15px;
height: auto;
}
.repImg {
height: 100px;
width: 100px;
}
.card {
width: 750px;
height: 100%;
padding: 30px;
margin-bottom: 20px;
}
.fs18 {
font-size: 18px;
}
.fs24 {
font-size: 24px;
}
</style>
</th:block>
<div layout:fragment="content" class="content-mg">
<h2 class="mb-4">구매 이력</h2>
<div th:each="order : ${orders.getContent()}">
<div class="d-flex mb-3 align-self-center">
<h4 th:text="${order.orderDate} + '주문'"></h4>
<div class="ml-3">
<th:block th:if="${order.orderStatus == T(com.shop.constant.OrderStatus).ORDER}">
<button type="button" class="btn btn-outline-secondary" th:value="${order.orderId}"
onclick="cancelOrder(this.value)">주문취소</button>
</th:block>
<th:block th:unless="${order.orderStatus == T(com.shop.constant.OrderStatus).ORDER}">
<h4>(취소완료)</h4></th:block>
</div>
</div>
<div class="card d-flex">
<div th:each="orderItem : ${order.orderItemDtoList}" class="d-flex mb-3">
<div class="repImgDiv">
<img th:src="${orderItem.imgUrl}" class="rounded repImg" th:alt="${orderItem.itemNm}">
</div>
<div class="align-self-center w-75">
<span th:text="${orderItem.itemNm}" class="fs24 font-weight-bold"></span>
<div class="fs18 font-weight-light">
<span th:text="${orderItem.orderPrice} + '원'"></span>
<span th:text="${orderItem.count} + '개'"></span>
</div>
</div>
</div>
</div>
</div>
<div th:with="start=${(orders.number/maxPage) * maxPage + 1},
end=${(orders.totalPages == 0) ? 1 : (start + (maxPage -1) < orders.totalPages ?
start + (maxPage -1) : orders.totalPages)}">
<ul class="pagination justify-content-center">
<li class="page-item" th:classappend="${orders.number eq 0} ? 'disabled' : ''">
<a th:href="@{'/orders/'+ ${orders.number-1}}" aria-label="Previous" class="page-link">
<span aria-hidden="true">Previous</span>
</a>
</li>
<li class="page-item" th:each="page : ${#numbers.sequence(start, end)}"
th:classappend="${orders.number eq page - 1} ?'active':''">
<a th:href="@{'/orders/'+ ${page - 1}}" th:inline="text" class="page-link">[[${page}]]</a>
</li>
<li class="page-item" th:classappend="${orders.number+1 ge orders.totalPages}? 'disabled' : ''">
<a th:href="@{'/orders/'+ ${orders.number + 1}}" aria-label="Next" class="page-link">
<span aria-hidden="true">Next</span>
</a>
</li>
</ul>
</div>
</div>
</html>
오늘은 쇼핑몰 프로젝트에서 주문 기능을 구현했다.
AJAX를 이용해서 주문 데이터를 JSON으로 서버에 전달하고, Controller와 Service를 통해 상품과 회원 정보를 조회한 뒤 주문 정보를 DB에 저장하는 흐름을 익혔다.
주문 과정에서 발생할 수 있는 오류를 처리하기 위해 @Valid와 BindingResult를 사용해서 입력값을 검증하고, try-catch와 EntityNotFoundException을 이용해서 예외처리하는 방법도 배웠다.
그리고 Pageable을 이용해서 주문 내역을 페이징 처리하고, Thymeleaf를 이용해서 주문 상품과 주문 상태, 페이지 번호를 화면에 출력하는 방법까지 구현했다.
자바는 너무 어렵다 흑흑.