판매자는 상품등록 시 반드시 상품이미지를 첨부해야하며, 상품이미지는 1장만 첨부가능하다
Item 클래스에 파일컬럼으로 FileEntity 클래스 변수를 넣고
OneToOne 단방향 매핑으로 연관관계를 맺어줬다
엔티티별로 requestDto와 responseDto 를 만들어서 Entity 에 직접접근을 막음
▶ 상품등록 (이미지 등록)
Item
package com.proj.KnitMarket.domain.Item;
import com.proj.KnitMarket.Constant.SellStatus;
import com.proj.KnitMarket.domain.BaseEntity;
import com.proj.KnitMarket.domain.Member.Seller;
import lombok.*;
import javax.persistence.*;
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name="item")
@Entity
public class Item extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; //상품코드
private String itemName; //상품명
private int price; //상품가격
private String itemDesc; //상품 상세 설명
@ManyToOne
@JoinColumn(name="seller_id")
private Seller seller; //상품 등록자
@OneToOne(cascade = CascadeType.REMOVE)
@JoinColumn(name="fileEntity_id")
private FileEntity file;
@Enumerated(EnumType.STRING)
private SellStatus sellStatus; //상품 판매 상태
@Builder
public Item(String itemName, int price, String itemDesc,Seller seller,FileEntity file,SellStatus sellStatus) {
this.itemName = itemName;
this.price = price;
this.itemDesc = itemDesc;
this.seller = seller;
this.file = file;
this.sellStatus = sellStatus;
}
}
FileEntity
package com.proj.KnitMarket.domain.Item;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class FileEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String orginFileName;
private String filePath;
@Builder
public FileEntity(Long id, String orginFileName, String filePath) {
this.id = id;
this.orginFileName = orginFileName;
this.filePath = filePath;
}
}
Controller
컨트롤러에서 핵심은
인자로 받은 ItemRequestDto 에서 FileEntity 객체의 값들을 꺼내서
FileRequestDto 객체로 빌드한 것이다.
이미지 경로를 application.properties 에 설정했었다가,
특정부분들이 내가생각한대로 작동하지않아서
클래스를 따로 만들어서 선언해놨다 (ConstUtil.UPLOAD_IMG_PATH_TEST)
본인이 파일을 저장할 경로를 설정해주시면 됩니다
@PostMapping(value = "/register")
public String item_register_post(@ModelAttribute("item") ItemRequestDto itemDto, HttpSession httpSession, Model model) throws IOException {
log.info("ItemRequestDto={}", itemDto.toString());
String email = (String) httpSession.getAttribute("email");
String url = "";
String msg = "";
if (itemDto.getFile() != null) {
log.info("이미지 有");
MultipartFile file = itemDto.getFile();
String filePath = uploadDir + file.getOriginalFilename();
file.transferTo(new File(filePath));
log.info("file.getOriginalFilenaem={}", file.getOriginalFilename());
log.info("filePath={}", filePath);
FileRequestDto fileDto = FileRequestDto.builder()
.orginFileName(file.getOriginalFilename())
.filePath(uploadDir + file.getOriginalFilename())
.build();
Long itemId = itemService.save(itemDto, email, fileDto);
log.info("상품번호 ={}", itemId);
url = "/knitmarket/";
msg = "상품등록이 완료되었습니다";
} else {
url = "/knitmarket/";
msg = "상품등록에 실패했습니다 상품 정보를 확인해주세요 ! ";
}
model.addAttribute("url", url);
model.addAttribute("msg", msg);
return "/common/message";
}
FileRequestDto
package com.proj.KnitMarket.dto;
import com.proj.KnitMarket.domain.Item.FileEntity;
import com.proj.KnitMarket.domain.Item.Item;
import lombok.*;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class FileRequestDto {
private String orginFileName;
private String filePath;
public FileEntity toEntity(){
return FileEntity.builder()
.orginFileName(orginFileName)
.filePath(filePath)
.build();
}
@Builder
public FileRequestDto(String orginFileName, String filePath) {
this.orginFileName = orginFileName;
this.filePath = filePath;
}
}
ItemRequestDto
package com.proj.KnitMarket.dto;
import com.proj.KnitMarket.Constant.SellStatus;
import com.proj.KnitMarket.domain.Item.FileEntity;
import com.proj.KnitMarket.domain.Item.Item;
import com.proj.KnitMarket.domain.Member.Seller;
import lombok.*;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class ItemRequestDto {
@NotBlank(message = "상품명은 필수 입력 값입니다")
private String itemName;
@NotNull(message = "가격은 필수 입력 값입니다")
private int price;
private String itemDesc;
private Seller seller;
@NotNull(message = "상품이미지는 필수 입력 값입니다")
private MultipartFile file;
private FileEntity fileEntity;
private SellStatus sellStatus;
//dto => entity
public Item toEntity() {
return Item.builder()
.itemName(itemName)
.price(price)
.itemDesc(itemDesc)
.seller(seller)
.file(fileEntity)
.sellStatus(sellStatus)
.build();
}
@Builder
public ItemRequestDto(String itemName, String itemDesc, int price, Seller seller, FileEntity file,SellStatus sellStatus) {
this.price = price;
this.itemDesc = itemDesc;
this.itemName = itemName;
this.seller = seller;
this.fileEntity = file;
this.sellStatus = sellStatus;
}
}
FileService
save 메서드 반환타입을 평범하게 id 로 했었다가 ItemService 에서 절차를 줄이기 위해서
FileEntity 객체로 변경했다
@Transactional
public FileEntity save(FileRequestDto fileRequestDto){
return fileRepository.save(fileRequestDto.toEntity());
}
ItemService
File 테이블에 저장 후 Item 테이블에 저장
@Transactional //아이템 등록
public Long save(ItemRequestDto itemDto, String email, FileRequestDto fileRequestDto){
Seller seller = sellerRepository.findByEmail(email);
FileEntity file = fileService.save(fileRequestDto); // file 저장
itemDto.setFileEntity(file);
itemDto.setSeller(seller);
Item item = itemDto.toEntity();
return itemRepository.save(itemDto.toEntity()).getId();
}
▶ 상품조회 (이미지 불러와서 조회)
Controller
@GetMapping("/")
public String index(Model model) {
List<ItemResponseDto> itemDtoList = itemService.getItemList();
model.addAttribute("itemList",itemDtoList);
return "index";
}
ItemService
@Transactional
public List<ItemResponseDto> getItemList(){
List<Item> items = itemRepository.findAll();
List<ItemResponseDto> itemDtoList = new ArrayList<>();
for(Item item : items){
ItemResponseDto responseDto = ItemResponseDto.builder()
.id(item.getId())
.itemName(item.getItemName())
.itemDesc(item.getItemDesc())
.orginFileName(item.getFile().getOrginFileName())
.price(item.getPrice())
.sellerName(item.getSeller().getName())
.build();
itemDtoList.add(responseDto);
}//for
return itemDtoList;
}
ItemResponseDto
package com.proj.KnitMarket.dto;
import com.proj.KnitMarket.domain.Item.Item;
import lombok.Builder;
import lombok.Getter;
@Getter
public class ItemResponseDto {
private Long id;
private String itemName;
private int price;
private String itemDesc;
private String sellerName;
private String orginFileName;
//entity=>dto
public ItemResponseDto(Item entity){
this.id=entity.getId();
this.itemName=entity.getItemName();
this.itemDesc=entity.getItemDesc();
this.price=entity.getPrice();
this.sellerName = entity.getSeller().getName();
this.orginFileName = entity.getFile().getOrginFileName();
}
@Builder
public ItemResponseDto(Long id, String itemName, int price, String itemDesc, String sellerName, String orginFileName) {
this.id = id;
this.itemName = itemName;
this.price = price;
this.itemDesc = itemDesc;
this.sellerName = sellerName;
this.orginFileName = orginFileName;
}
}
상품목록.html
<div class="card" style="width: 18rem;" th:each="item : ${itemList}">
<img th:src="@{/uploadImg/} + ${item.getOrginFileName()}" class="card-img-top" th:alt="${item.itemName}">
<div class="card-body">
<h5 class="card-title" th:text="${item.itemName}"></h5>
<p class="card-text" th:text="${item.price}"></p>
<a href="#" class="btn btn-primary">Go somewhere</a>
</div>
</div>
▶ 발생에러 : MaxUploadSizeExceededException
▷ 해결 : application.properties 에서 파일기본용량 설정
spring.servlet.multipart.maxFileSize=5MB
spring.servlet.multipart.maxRequestSize=5MB
만들고나니까 굳이 file 객체를 만들어서 저장할게아니라
이미지 경로만 Item 에 멤버변수로 넣을껄그랬나 싶기도 하다.
그렇게 해도 문제없이 프로그램이 돌아가겠지만 ( + 훨씬 편리하겠지만)
덕분에 requestDto, responseDto, Builder 로 머리좀싸매봤고 (이번에 처음 응용해서 사용해봤다)
코드가 좀 더 객체지향스럽게 보이는 것 같다 (그런것 "같다"고 행복회로 돌릴순있잖아요)
분리하는 걸 좋아해서 있는대로 쪼개서
DTO 와 Builder 를 남발하며 나아가고 있는데
잘가고있는건지 모르겠다.
'프로젝트' 카테고리의 다른 글
| [SpringBoot & JPA 프로젝트] 장바구니에 상품 추가하기 / 삭제하기 (0) | 2022.08.25 |
|---|---|
| [SpringBoot & JPA 프로젝트] 판매자 : 상품삭제하기 (delFlag) (0) | 2022.08.24 |
| [SpringBoot & JPA 프로젝트] enum (0) | 2022.08.17 |
| [SpringBoot & JPA 프로젝트] 뜨개장터 소개 (2) | 2022.08.17 |
| [SpringBoot & JPA 프로젝트] common/message (0) | 2022.08.11 |