상품 기능을 추가하기위해서 entity들을 설정했기때문에 먼저 Item Entity의 API 기능부터 추가해보자, 제일먼저 상품 등록 API 부터 만들어보자
package com.qkrtprjs.happyexercise.controller;
import com.qkrtprjs.happyexercise.config.auth.LoginMember;
import com.qkrtprjs.happyexercise.config.auth.dto.SessionMember;
import com.qkrtprjs.happyexercise.dto.ItemSaveRequestDto;
import com.qkrtprjs.happyexercise.service.ItemService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@RequiredArgsConstructor
@RestController
public class ItemApiController {
private final ItemService itemService;
@PostMapping("/api/item")
private Long save(ItemSaveRequestDto itemSaveRequestDto, MultipartFile img, @LoginMember SessionMember member) throws Exception{
return itemService.save(itemSaveRequestDto,img,member);
}
}
등록기능의 API 컨트롤러를 만들어주고
해당 서비스와 연결시켜준다.
package com.qkrtprjs.happyexercise.service;
import com.qkrtprjs.happyexercise.config.auth.LoginMember;
import com.qkrtprjs.happyexercise.config.auth.dto.SessionMember;
import com.qkrtprjs.happyexercise.dto.ItemSaveRequestDto;
import com.qkrtprjs.happyexercise.entitiy.item.Item;
import com.qkrtprjs.happyexercise.entitiy.item.ItemRepository;
import com.qkrtprjs.happyexercise.entitiy.member.Member;
import com.qkrtprjs.happyexercise.entitiy.member.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.util.UUID;
@RequiredArgsConstructor
@Service
public class ItemService {
private final ItemRepository itemRepository;
private final MemberRepository memberRepository;
@Transactional
public Long save(ItemSaveRequestDto itemSaveRequestDto, MultipartFile img, SessionMember member) throws Exception {
//저장할 위치를 설정
String projectPath = System.getProperty("user.dir") + "\\src\\main\\resources\\static\\image";
UUID uuid = UUID.randomUUID(); //식별자 역할을 위함
String imgName = uuid + "_" + img.getOriginalFilename();
String imgPath = "/image/" + imgName;
//파일을 넣어줄 껍데기 생성
File file = new File(projectPath, imgName);
//받아온 img 파일로 이동
img.transferTo(file);
Member loginMember = memberRepository.findByEmail(member.getEmail()).orElseThrow(() -> new IllegalArgumentException("해당 이메일은 존재하지않습니다"));
Item item = itemSaveRequestDto.toEntity(itemSaveRequestDto, imgName, imgPath,loginMember);
return itemRepository.save(item).getId();
}
}
우선 먼저 주의해야될점이 상품을 등록하기위해서는 이미지가 필요하고 이미지를 추가하기위해서는 파일을 추가해줘야한다.
이미지를 저장하는 방식은 서버에서 이미지를 관리하게 하고싶어서 이미지를 저장시킬 경로와 이름을 따로 저장해준다.
따라서 Item Entity는 수정된다.
html에서 받아온 파일(이미지)를 저장시키는 경로를 지정하기위해서는 \(역슬래쉬)를 사용하고 System.getProperty("user.dir") 메서드를 사용해서 현재 프로젝트의 경로를 가르킨다.
package com.qkrtprjs.happyexercise.entitiy.item;
import com.qkrtprjs.happyexercise.entitiy.BaseTimeEntity;
import com.qkrtprjs.happyexercise.entitiy.member.Member;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Getter
@NoArgsConstructor
@Entity
public class Item extends BaseTimeEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
private Long id;
private String detail;
private String name;
private int price;
private int stock; //재고
private String status;
//이미지를 갖고오기위한 필드
private String imgName;
private String imgPath;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "memberId")
private Member member;
@Builder
public Item(String detail, String name, int price, int stock, String status, String imgName, String imgPath, Member member) {
this.detail = detail;
this.name = name;
this.price = price;
this.stock = stock;
this.status = status;
this.imgName = imgName;
this.imgPath = imgPath;
this.member = member;
}
}
<!DOCTYPE html>
<html xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/default_layout}">
<div layout:fragment="content">
<div class="container mt-3">
<h2>상품 등록</h2>
<form action="/api/item" method="post" enctype="multipart/form-data">
<div class="mb-3 mt-3">
<label for="name">상품명:</label>
<input type="text" class="form-control" id="name" placeholder="상품명을 입력해주세요..." name="name">
</div>
<div class="mb-3">
<label for="detail">상품 설명:</label>
<input type="text" class="form-control" id="detail" placeholder="상품 설명을 입력해주세요..." name="detail">
</div>
<div class="mb-3">
<label for="price">가격:</label>
<input type="number" class="form-control" id="price" placeholder="가격을 입력해주세요..." name="price">
</div>
<div class="mb-3">
<label for="stock">재고:</label>
<input type="number" class="form-control" id="stock" placeholder="재고 개수를 입력해주세요..." name="stock">
</div>
<div class="mb-3">
<label for="img">사진:</label>
<input type="file" class="form-control" id="img" placeholder="사진을 첨부해주세요..." name="img" multiple="multiple">
</div>
<button type="submit" class="btn btn-primary" id="save-btn">등록</button>
</form>
</div>
</div>
</html>
html에서 정보를 넘겨받을때 dto와 file을 넘겨받고 이전에 만들어놓았던 @LoginMember 어노테이션을 사용해서 현재 로그인된 사용자의 정보를 갖고와서 이 정보들을 갖고 해당 API 서비스에서 하나의 Item Entity로 만들어준다.
ItemSaveRequestDto
package com.qkrtprjs.happyexercise.dto;
import com.qkrtprjs.happyexercise.entitiy.item.Item;
import com.qkrtprjs.happyexercise.entitiy.member.Member;
import lombok.*;
import org.springframework.stereotype.Service;
import javax.persistence.*;
@Getter
@Setter
@NoArgsConstructor
@ToString
public class ItemSaveRequestDto {
private String name;
private String detail;
private int price;
private int stock; //재고
// private String status; Entity에서 Enum으로 처리하자
// private Long memberId; LoginUser에서 받아오기
// private String img; 이미지는 따로 받아온다
@Builder
public ItemSaveRequestDto(String name, String detail, int price, int stock) {
this.name = name;
this.detail = detail;
this.price = price;
this.stock = stock;
}
public Item toEntity(ItemSaveRequestDto itemSaveRequestDto, String imgName, String imgPath,Member member) {
return Item.builder()
.detail(itemSaveRequestDto.getDetail())
.name(itemSaveRequestDto.getName())
.price(itemSaveRequestDto.getPrice())
.stock(itemSaveRequestDto.getStock())
.status("판매가능")
.imgName(imgName)
.imgPath(imgPath)
.member(member)
.build();
}
}
경로와 일치하는 것을 확인할 수 있고
정상적으로 DB에 Item Entity가 저장되는 것을 확인할 수 있고 이미지의 경로와 이름또한 저장되어있고 서버에 이미지 또한 저장되어있기때문에 쉽게 접근할 수 있을 것이다.
추가로 이전에는 ajax를 사용해서 정보를 교환했지만 이번에 item을 저장하는 기능에는 html의 form형태를 사용해서 데이터를 넘겨주었다.
이렇게 사용하니 이전에 작성했던 REST API 방식과는 다른것 같아서 다시 form 형태가아닌 ajax로 정보를 넘겨주려고한다.
알고보니 form형태를 이용해서 REST API 방식으로 데이터를 전송할 수 있었다.
REST API 장점
- 쉬운 사용성
- 클라이언트와 서버의 완전한 분리
- 특정한 데이터의 명확한 표현
ajax를 사용해서 데이터를 넘겨주려고했으나 json형태와 MultipartFile 형태를 한번에 보내려고하였고 따라서 formData 형태도 알게되어서 formData에 JSON과 이미지파일을 같이 저장시켜서 ajax 통신으로 보내려고하였으나 이 formData는 form형태의 데이터를 저장시키는 형태였고 결국 form 데이터를 사용해야한다면 이전에 했던방식으로 보내주고 대신에 js를 사용해서 전송하게 변경하였다.
*추가로 비동기 통신을 사용해서 이미지를 JSON 형태와 같이 합쳐서 넘겨주어야할 수도 있기떄문에 ajax를 사용해서 데이터를 넘겨보자
<!DOCTYPE html>
<html xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/default_layout}">
<div layout:fragment="content">
<div class="container mt-3">
<h2>상품 등록</h2>
<form action="/api/item" id="save-form" method="post" enctype="multipart/form-data">
<div class="mb-3 mt-3">
<label for="name">상품명:</label>
<input type="text" class="form-control" id="name" placeholder="상품명을 입력해주세요..." name="name">
</div>
<div class="mb-3">
<label for="detail">상품 설명:</label>
<input type="text" class="form-control" id="detail" placeholder="상품 설명을 입력해주세요..." name="detail">
</div>
<div class="mb-3">
<label for="price">가격:</label>
<input type="number" class="form-control" id="price" placeholder="가격을 입력해주세요..." name="price">
</div>
<div class="mb-3">
<label for="stock">재고:</label>
<input type="number" class="form-control" id="stock" placeholder="재고 개수를 입력해주세요..." name="stock">
</div>
<div class="mb-3">
<label for="img">사진:</label>
<input type="file" class="form-control" id="img" placeholder="사진을 첨부해주세요..." name="img"
multiple="multiple">
</div>
<button type="button" class="btn btn-primary" id="save-btn">등록</button>
</form>
</div>
</div>
</html>
let item = {
init: function () {
let _this = this;
$('#save-btn').click(function () {
_this.save();
});
},
save: function () {
alert("제품을 등록하였습니다!");
$("#save-form").submit();
location.href = '/';
}
}
item.init();
제품 등록 API 설계 완료