사전참고 : https://kururu.tistory.com/276
1) 화면 <-> 전달용 DTO <-> 엔티티
과정중 엔티티에서 DTO 객체로 바꿔주는 작업이 변수가 많을수록 상당한 시간이 걸리므로 modelMapper 라이브러리를 사용
pom.xml
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>3.0.0</version>
</dependency>
코드는 아래와 같음.
- 베이스엔티티와 베이스타입엔티티는 공통
1) 상품등록 페이지
- form 태그 : method 설정, enctype 설정
- 이미지 파일 name 설정
itemForm.html
<div layout:fragment="content">
<form role="form" method="post" enctype="multipart/form-data" th:object="${itemFormDto}">
<p class="h2">
상품 등록
</p>
<input type="hidden" th:field="*{id}">
<div class="input-group mb-3">
<select th:field="*{itemSellStatus}" class="form-select">
<option value="SELL">판매중</option>
<option value="SOLD_OUT">품절</option>
</select>
</div>
<div class="input-group mb-3">
<span class="input-group-text">상품명</span>
<input type="text" th:field="*{itemNm}" class="form-control" placeholder="상품명을 입력해주세요">
</div>
<p th:if="${#fields.hasErrors('itemNm')}" th:errors="*{itemNm}" class="fieldError">Incorrect data</p>
<div class="input-group mb-3">
<span class="input-group-text">가격</span>
<input type="number" th:field="*{price}" class="form-control" placeholder="상품의 가격을 입력해주세요">
</div>
<p th:if="${#fields.hasErrors('price')}" th:errors="*{price}" class="fieldError">Incorrect data</p>
<div class="input-group mb-3">
<span class="input-group-text">재고</span>
<input type="number" th:field="*{stockNumber}" class="form-control" placeholder="상품명을 입력해주세요">
</div>
<p th:if="${#fields.hasErrors('stockNumber')}" th:errors="*{stockNumber}" class="fieldError">Incorrect data</p>
<div class="input-group mb-3">
<span class="input-group-text">상품 상세 내용</span>
<textarea class="form-control" aria-label="With textarea" th:field="*{itemDetail}"></textarea>
</div>
<p th:if="${#fields.hasErrors('itemDetail')}" th:errors="*{itemDetail}" class="fieldError">Incorrect data</p>
<div th:if="${#lists.isEmpty(itemFormDto.itemImgDtoList)}">
<div class="input-group mb-3" th:each="num : ${#numbers.sequence(1,5)}">
<input type="file" class="form-control" name="itemImgFile">
</div>
</div>
<div th:if="${not #lists.isEmpty(itemFormDto.itemImgDtoList)}">
<div class="input-group mb-3" th:each="itemImgDto, status: ${itemFormDto.itemImgDtoList}">
<input type="file" class="form-control" name="itemImgFile" th:value="${itemImgDto.oriImgName}">
<input type="hidden" name="itemImgIds" th:value="${itemImgDto.id}">
</div>
</div>
<div th:if="${#strings.isEmpty(itemFormDto.id)}" style="text-align: center">
<button th:formaction="@{/admin/item/new}" type="submit" class="btn btn-primary">저장</button>
</div>
<div th:unless="${#strings.isEmpty(itemFormDto.id)}" style="text-align: center">
<button th:formaction="@{'/admin/item/'+${itemFormDto.id}}" type="submit" class="btn btn-primary">수정</button>
</div>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
</form>
</div>
2) 상품등록 컨트롤러
- @Valid 를 통한 Rule 체크
- itemImgFile Name 에서 이미지 여부 체크
ItemController.java
@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:/";
}
3) 서비스단 로직처리
- 상품 데이터를 상품 테이블에 저장(단건)
- 이미지 저장 로직 : 첫번째 이미지는 대표이미지
ItemService.java
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();
}
ItemFormDto.java
- modelMapper 로 동기화
private static ModelMapper modelMapper = new ModelMapper();
public Item createItem(){
return modelMapper.map(this, Item.class);
}
ItemImgService.java
- 이미지 파일 서비스단 호출
- 상품 이미지를 상품 이미지 테이블에 저장(단건)
@Value("${itemImgLocation}")
private String itemImgLocation;
private final ItemImgRepository itemImgRepository;
private final FileService fileService;
public void saveItemImg(ItemImg itemImg, MultipartFile itemImgFile) throws Exception{
String oriImgName = itemImgFile.getOriginalFilename();
String imgName = "";
String imgUrl = "";
if(!StringUtils.isEmpty(oriImgName)){
imgName = fileService.uploadFile(itemImgLocation, oriImgName, itemImgFile.getBytes());
imgUrl = "/images/item/"+imgName;
}
itemImg.updateItemImg(oriImgName, imgName, imgUrl);
itemImgRepository.save(itemImg);
}
FileService.java
public String uploadFile(String uploadPath, String originalFileName, byte[] fileData) throws Exception{
UUID uuid = UUID.randomUUID();
String extension = originalFileName.substring(originalFileName.lastIndexOf("."));
String savedFileName = uuid.toString() + extension;
String fileUploadFullUrl = uploadPath + "/"+savedFileName;
FileOutputStream fos = new FileOutputStream(fileUploadFullUrl);
fos.write(fileData);
fos.close();
return savedFileName;
}
ItemImg.java
public void updateItemImg(String oriImgName, String imgName, String imgUrl){
this.oriImgName = oriImgName;
this.imgName = imgName;
this.imgUrl = imgUrl;
}
Member
Item
ItemImg
JPA - save 메소드와 saveAll 메소드 차이
https://velog.io/@rainmaker007/spring-data-jpa-save-saveAll-%EB%B9%84%EA%B5%90
https://maivve.tistory.com/342
https://sas-study.tistory.com/388A - save 메소드와 saveAll 메소드 차이
JPA - save 반환하여 수정시 가급적 반환된 인스턴스를 써야되는 이유
https://minkukjo.github.io/framework/2020/07/05/Spring-130/
'2022 > JPA입문(完)' 카테고리의 다른 글
등록된 상품수정 (0) | 2022.02.04 |
---|---|
등록된 상품보기 (0) | 2022.02.04 |
엔티티 관계도 / 엔티티 클래스 다이어그램 (0) | 2022.02.04 |
스프링 시큐리티 - 2 (0) | 2022.01.24 |
부트스트랩(bootstrap) 5.1 적용 (0) | 2022.01.24 |