#버전 1.1
22.06.09
프로젝트 소개
이번 과제는 항해99에서 설계된 API정의서에 맞춰 기능을 구현하는 프로젝트였습니다.
따로 UI없이 API로만 구성되어 있습니다. 하지만 추 후 ui도 추가할 예정입니다.
API 기능 구성도
과제 목적
이번 과제에서는 내가 생각하기엔 이런것들을 이 목표를 가지고 공부할 수 있었다.
- 명세서를 보고 기능을 구현할 수 있게 된다.
- 테이블의 관계도를 이해할 수 있다.
- for문을 사용할 상황과 Stream을 사용할 상황을 구별할 수 있다.
명세서를 보고 기능을 구현할 수 있게 된다
API 명세서
@GetMapping("/restaurants")
public List<Restaurant> findAllRestaurant() {
return restaurantService.findAllRestaurant();
}
@PostMapping("/restaurant/register")
public Restaurant restaurantSave(@RequestBody @Validated(ValidationGroups.Save1.class) RequestRestaurantDto requestRestaurantDto) {
return restaurantService.restaurantSave(requestRestaurantDto);
}
@RequiredArgsConstructor
@RestController
@RequestMapping("/restaurant")
public class FoodsController {
private final FoodsService foodsService;
@GetMapping("/{restaurantId}/foods")
public List<Food> findAllFoodsByRestaurantId(@PathVariable Long restaurantId) {
return foodsService.findAllFoodsByRestaurantId(restaurantId);
}
@PostMapping("/{restaurantId}/food/register")
public void foodsSave(@PathVariable Long restaurantId,
@RequestBody List<RequestFoodsDto> requestFoodsDto) {
foodsService.foodsSave(requestFoodsDto, restaurantId);
}
}
@PostMapping("/order/request")
public UserOrder orderSave(@RequestBody @Validated(ValidationGroups.Save1.class) RequestOrderDto requestReceiptDto) {
return orderService.orderSave(requestReceiptDto);
}
@GetMapping("/orders")
public List<UserOrder> findAllOrder() {
return orderService.findAll();
}
테이블의 관계도를 이해할 수 있다.
Food(메뉴들) 클래스와 UserOrderFood(주문한 정보) 클래스에 같은 변수를 쓰는게 있어서 abstract클래스를 활용해보았다.
public abstract class FoodInfo {
@Column(nullable = false)
private String name;
@Column(nullable = false)
private int price;
}
유효성 검사를 할 때 공동으로 쓰는 숫자들이 많아 enum으로 모음을 만들어 이용하였다.
public enum Number {
ZERO(0),
ONE(1),
HUNDRED(100),
FIVE_HUNDRED(500),
THOUSAND(1000),
TEN_THOUSAND(10000),
A_HUNDRED_THOUSAND(100000),
MILLION(1000000);
private int num;
Number(int i) {
this.num = i;
}
public int getNum() {
return num;
}
public Number setNum(int num) {
this.num = num;
return this;
}
}
유효성검사는 DTO클래스에서 Entity클래스로 데이터타입을 변경할 때 체크를 해주고 있습니다.
@Setter
@Getter
@Entity
@NoArgsConstructor
public class Food extends FoodInfo {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false)
private Long restaurantId;
public Food(RequestFoodsDto requestFoodsDto, Long restaurantId) {
this.setName(requestFoodsDto.getName());
this.setPrice(valid(requestFoodsDto.getPrice(), HUNDRED.getNum()));
this.restaurantId = restaurantId;
}
private int valid(int price, int sharedNumber) {
if (price % sharedNumber != ZERO.getNum()) {
throw new IllegalArgumentException(sharedNumber + "원 단위로 입력해주세요.");
}
if (!(sharedNumber <= price && price <= MILLION.getNum())) {
throw new IllegalArgumentException("허용 값은 100원 ~ 1,000,000원");
}
return price;
}
}
주문한 음식들만 DB에 저장할 Entity클래스 입니다.
@Setter
@Getter
@Entity
@NoArgsConstructor
public class UserOrderFood extends FoodInfo {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false)
private Long foodsId;
@Column(nullable = false)
private Integer quantity;
public UserOrderFood(RequestFoodsDto requestFoodsDto, List<RequestFoodsDto> foodList) {
this.foodsId = requestFoodsDto.getId();
this.quantity = requestFoodsDto.getQuantity();
setFoodNameAndPrice(requestFoodsDto, foodList);
}
public UserOrderFood(RequestUserOrderFoodDto requestOrderFoodDto) {
this.id = requestOrderFoodDto.getId();
this.foodsId = requestOrderFoodDto.getFoodsId();
this.quantity = requestOrderFoodDto.getQuantity();
this.setPrice(requestOrderFoodDto.getPrice());
this.setName(requestOrderFoodDto.getName());
}
private void setFoodNameAndPrice(RequestFoodsDto requestFoodsDto, List<RequestFoodsDto> foodList) {
for (RequestFoodsDto foodsDto : foodList) {
if (foodsDto.getId() == requestFoodsDto.getId()) {
this.setName(foodsDto.getName());
this.setPrice(foodsDto.getPrice() * orderQuantityCheck(requestFoodsDto.getQuantity()));
}
}
}
private int orderQuantityCheck(int quantity) {
if (quantity > HUNDRED.getNum() || quantity < ONE.getNum()) {
throw new IllegalArgumentException("음식당 주문 허용 수량은 1 ~ 100 이하입니다.");
}
return quantity;
}
}
Food(음식들 테이블) 과 UserOrderFood(주문한 음식들 테이블) 을 비교해 음식의 가격을 합하고 최종 주문결과를 저장하는 클래스입니다.
@OneToMany를 사용해 UserOrderFood클래스와 UserOrder 단반향관계를 이어주고 있습니다.
@Setter
@Getter
@Entity
@NoArgsConstructor
public class UserOrder {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long Id;
@Column
private String restaurantName;
@Column(nullable = false)
private int deliveryFee;
@Column(nullable = false)
private int totalPrice;
@OneToMany
@JoinColumn(name = "UserOrderFood_id")
private List<UserOrderFood> foods = new ArrayList<>();
public UserOrder(RequestOrderDto requestReceiptDto, List<RequestUserOrderFoodDto> requestOrderFoodDtos) {
this.restaurantName = requestReceiptDto.getRestaurantName();
this.deliveryFee = requestReceiptDto.getDeliveryFee();
this.foods = requestOrderFoodDtos.stream().map(UserOrderFood::new).collect(Collectors.toList());
this.totalPrice = requestReceiptDto.getTotalPrice();
}
}
for문 vs Stream
📍 for문과 Stream 중 선택기준은 단순 반복문만 사용한다면 for문을 데이터 가공이 이루어진다면 Stream을 사용했습니다.
⭐️for문(향상된 for문) ex)
Stream.forEach로 돌리 수 있는 상황이지만 단순 반복문은 Stream의 취지에 맞지 않기에 사용하지 않았다.
List<Food> foodList = new ArrayList<>();
for (RequestFoodsDto requestFoodsDto : requestFoodsDtoList) {
foodList.add(new Food(requestFoodsDto, restaurantId));
}
⭐️ Strema사용 ex)
RequestFoodsDto의 id만 Integer로 이루어진 List로 가공해야하는 로직인데 for문 보다 더 간결하게 코드를 작성할 수 있으며 단순 반복이 아니기에 사용하였다.
requestReceiptDto.getFoods().stream()
.map(RequestFoodsDto::getId)
.collect(Collectors.toList())
유지보수
- 프론트 추가
- 쿠폰제도 추가
- 리뷰 시스템
- 업체 이미지 등록
- 메뉴 이미지 등록
Comment
이번 프로젝트는 UI를 만들지 않아서 비교적 빨리 완성되었다.
그래서 그런가 비지니스 로직에 이게 효율적이 코드가 맞는지 아닌지를 분석할 시간이 있었고
for문과 Stream에 대해 깊이 공부할 수 있던 시간이였다. UI가 없지만 UI가지 만들면 그럴싸한 서비스가 될 수 있을거같다.
#버전-1.2
변경된 사항
domain클래스들의 네임이 의미와 맞지 않아서 수정을 하였다.
📍UserOrderFood -> OrderFood
= 이 클래스는 고객이 주문한 음식을 저장하는 domain이다 근데 User가 들어감으로
의미가 변질되는 느낌이라 OrderFood라는 변경
📍UserOrder -> Receipt
= 고객이 최종 주문한 음식들과 식당명 그리고 가격들을 저장하는 domain이다
근데 UserOrder는 그냥 주문같은 느낌인데 영수증에 가깝기 때문에 Receipt로 변경하였다.
'항해99' 카테고리의 다른 글
[항해99] 7주차 자바 및 리액트 미니 프로젝트 클론코딩 (0) | 2022.07.04 |
---|---|
[항해99] 6주차 자바 및 리액트 미니 프로젝트 (0) | 2022.06.30 |
[항해99] 4주차 스프링 숙련 프로젝트 (0) | 2022.06.30 |
[항해99] 3주차 스프링 입문 프로젝트 (0) | 2022.06.30 |
[항해99] 2주차 객체지향프로그래밍 (0) | 2022.06.30 |