Spring Data REST를 사용하지 않은 이유?
기본 CRUD는 사용하기는 좋은데 커스텀에 한계가 많아서 커스텀 로직을 사용하려면 직접 만들어서 사용.
Backend 개발 순서
1. database script 실행
2. entities 생성
3. data transfer objects 생성
4. repository 생성
5. service 생성
6. controller 생성
1.
USE `full-stack-ecommerce`;
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `order_item`;
DROP TABLE IF EXISTS `orders`;
DROP TABLE IF EXISTS `customer`;
DROP TABLE IF EXISTS `address`;
SET FOREIGN_KEY_CHECKS=1;
CREATE TABLE `address` (
`id` bigint NOT NULL AUTO_INCREMENT,
`city` varchar(255) DEFAULT NULL,
`country` varchar(255) DEFAULT NULL,
`state` varchar(255) DEFAULT NULL,
`street` varchar(255) DEFAULT NULL,
`zip_code` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `customer` (
`id` bigint NOT NULL AUTO_INCREMENT,
`first_name` varchar(255) DEFAULT NULL,
`last_name` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `orders` (
`id` bigint NOT NULL AUTO_INCREMENT,
`order_tracking_number` varchar(255) DEFAULT NULL,
`total_price` decimal(19,2) DEFAULT NULL,
`total_quantity` int DEFAULT NULL,
`billing_address_id` bigint DEFAULT NULL,
`customer_id` bigint DEFAULT NULL,
`shipping_address_id` bigint DEFAULT NULL,
`status` varchar(128) DEFAULT NULL,
`date_created` datetime(6) DEFAULT NULL,
`last_updated` datetime(6) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UK_billing_address_id` (`billing_address_id`),
UNIQUE KEY `UK_shipping_address_id` (`shipping_address_id`),
KEY `K_customer_id` (`customer_id`),
CONSTRAINT `FK_customer_id` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`id`),
CONSTRAINT `FK_billing_address_id` FOREIGN KEY (`billing_address_id`) REFERENCES `address` (`id`),
CONSTRAINT `FK_shipping_address_id` FOREIGN KEY (`shipping_address_id`) REFERENCES `address` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `order_item` (
`id` bigint NOT NULL AUTO_INCREMENT,
`image_url` varchar(255) DEFAULT NULL,
`quantity` int DEFAULT NULL,
`unit_price` decimal(19,2) DEFAULT NULL,
`order_id` bigint DEFAULT NULL,
`product_id` bigint DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `K_order_id` (`order_id`),
CONSTRAINT `FK_order_id` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`),
CONSTRAINT `FK_product_id` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
테이블 생성 스크립트
2.
Customer 클래스
package com.seol.ecommerce.entity;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name="customer")
@Getter
@Setter
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="id")
private Long id;
@Column(name="first_name")
private String firstName;
@Column(name="last_name")
private String lastName;
@Column(name="email")
private String email;
@OneToMany(mappedBy="customer", cascade=CascadeType.ALL)
private Set<Order> orders = new HashSet<>();
public void add(Order order) {
if(order != null) {
if(orders == null) {
orders=new HashSet<>();
}
orders.add(order);
order.setCustomer(this);
}
}
}
Address 클래스
package com.seol.ecommerce.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name="address")
@Getter
@Setter
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="id")
private Long id;
@Column(name="street")
private String street;
@Column(name="city")
private String city;
@Column(name="state")
private String state;
@Column(name="country")
private String country;
@Column(name="zip_code")
private String zipCode;
@OneToOne
@PrimaryKeyJoinColumn
private Order order;
}
OrderItem
package com.seol.ecommerce.entity;
import java.math.BigDecimal;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name="order_item")
@Getter
@Setter
public class OrderItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="id")
private Long id;
@Column(name="image_url")
private String imageUrl;
@Column(name="unit_price")
private BigDecimal unitPrice;
@Column(name="quantity")
private int quantity;
@Column(name="product_id")
private Long productId;
@ManyToOne
@JoinColumn(name= "order_id")
private Order order;
}
Order
package com.seol.ecommerce.entity;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name="orders")
@Getter
@Setter
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="id")
private Long id;
@Column(name="order_tracking_number")
private String orderTrackingNumber;
@Column(name="total_quantity")
private int totalQuantity;
@Column(name="total_price")
private BigDecimal totalPrice;
@Column(name="status")
private String status;
@Column(name="date_created")
@CreationTimestamp
private Date dateCreated;
@Column(name="last_updated")
@UpdateTimestamp
private Date lastUpdated;
@OneToMany(cascade = CascadeType.ALL, mappedBy ="order")
private Set<OrderItem> orderItems = new HashSet<>();
@ManyToOne
@JoinColumn(name="customer_id")
private Customer customer;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "shipping_address_id", referencedColumnName = "id")
private Address shippingAddress;
@OneToOne(cascade=CascadeType.ALL)
@JoinColumn(name="billing_address_id", referencedColumnName = "id")
private Address billingAddress;
public void add(OrderItem item) {
if(item != null) {
if (orderItems == null) {
orderItems = new HashSet<>();
}
orderItems.add(item);
item.setOrder(this);
}
}
}
언제나 해도 다시 잊어버리는 @ManyToOne 과 @OneToMany를 다시 복습
ManyToOne에서 JoinColumn으로 걸어주고
OneToMany에선 mappedBy 으로 던지는 필드값 이름을 받아준다.
3.
DTO (Data Transfer Object)
Purchase
package com.seol.ecommerce.dto;
import java.util.Set;
import com.seol.ecommerce.entity.Address;
import com.seol.ecommerce.entity.Customer;
import com.seol.ecommerce.entity.Order;
import com.seol.ecommerce.entity.OrderItem;
import lombok.Data;
@Data
public class Purchase {
private Customer customer;
private Address shippingAddress;
private Address billingAddress;
private Order order;
private Set<OrderItem> orderItems;
}
PurchasedResponse
4.
package com.seol.ecommerce.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import com.seol.ecommerce.entity.Customer;
public interface CustomerRepository extends JpaRepository<Customer, Long >{
}
DAO(Data Access Object)인 CustomerRepository 생성
인터페이스로 생성할 것.
5.
Service 인터페이스와 Implement 생성
package com.seol.ecommerce.service;
import java.util.Set;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.seol.ecommerce.dao.CustomerRepository;
import com.seol.ecommerce.dto.Purchase;
import com.seol.ecommerce.dto.PurchasedResponse;
import com.seol.ecommerce.entity.Customer;
import com.seol.ecommerce.entity.Order;
import com.seol.ecommerce.entity.OrderItem;
@Service
public class CheckoutServiceImpl implements CheckoutService{
private CustomerRepository customerRepository;
@Autowired
public CheckoutServiceImpl(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
@Override
@Transactional
public PurchasedResponse placeOrder(Purchase purchase) {
Order order = purchase.getOrder();
String orderTrackingNumber = generateOrderTrackingNumber();
order.setOrderTrackingNumber(orderTrackingNumber);
Set<OrderItem> orderItems = purchase.getOrderItems();
orderItems.forEach(item->order.add(item));
order.setBillingAddress(purchase.getBillingAddress());
order.setShippingAddress(purchase.getShippingAddress());
Customer customer = purchase.getCustomer();
customer.add(order);
customerRepository.save(customer);
return new PurchasedResponse(orderTrackingNumber);
}
private String generateOrderTrackingNumber() {
return UUID.randomUUID().toString();
}
}
주문 service 메서드 내용은 다음 순서대로 작동한다.
1) dto로 주문 정보를 가져온다.
2) tacking number 생성하고 주문 정보에 주입
랜덤 UUID 번호(UUID version-4)
UUID(Universally Unique IDentifier)
Unique Id를 만드는 표준화된 방법이다.
3) 주문 정보에 주문 아이템 채우기
4) 주문 정보에 billing address 와 shipping address 채우기
5) 고객에 주문 정보 채우기
6) 데이터베이스에 저장하기
7) 응답 반환
6.
Service는 데이터베이스에 접근하여 정보를 저장하고
트랙킹 넘버를 갖고 있는 dto 객체를 반환한다.
아직 프론트엔드가 없으니 postman으로 테스트
'컴퓨터공학 > Boot & Angular' 카테고리의 다른 글
Angular> 로그인 로그아웃 기능 사전 설정 (0) | 2022.03.20 |
---|---|
Angular> 데이터베이스에 주문 정보 저장2 (0) | 2022.03.19 |
Angular> 장바구니 총액 (0) | 2022.03.09 |
Angular> 유효성 검사 (0) | 2022.03.06 |
Angular> 결제화면 3 (0) | 2022.03.01 |