
Item๊ณผItemImage๐1 : N, ์ฆItemImage๊ฐ ์ฃผ์ธ.Item์ธ๋ํค๋ฅผ ๊ฐ๊ณ ์ค๋ฉด ๋๋ค.
package com.shop.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "item_img")
@Getter
@Setter
public class ItemImg extends BaseEntity{
@Id
@Column(name = "item_img_id")
@GeneratedValue
private Long id;
private String imgName; // ์ด๋ฏธ์ง ํ์ผ๋ช
(๋๋ค)
private String oriImgName; // ์๋ณธ ์ด๋ฏธ์ง ํ์ผ๋ช
(์ฒ์ ์ฌ๋ฆฌ๋ ์ด๋ฏธ์ง)
private String imgUrl; // ์ด๋ฏธ์ง ๊ฒฝ๋ก
private String repImgYn; // ๋ํ ์ด๋ฏธ์ง Y/N ์ฌ๋ถ
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "item_id")
private Item item;
public void updateItemImg(String oriImgName, String imgName, String imgUrl){
this.oriImgName = oriImgName;
this.imgName = imgName;
this.imgUrl = imgUrl;
}
}
DTO์Entity๋ฅผ ์๋์ผ๋ก ์ฐ๊ฒฐํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ.
- ์ํ ๋ฑ๋ก ํ๋ฉด์ผ๋ก๋ถํฐ ์ ๋ฌ๋ฐ์
DTO๊ฐ์ฒด๋ฅผEntity๋ก ๋ณํํด์ผํจ- ์ํ ์กฐํ ๊ฒฐ๊ณผ๋ฅผ ํ๋ฉด์ ๋ฐํํ ๋
Entity๋ฅผDTO๊ฐ์ฒด๋ก ๋ณํํด์ผํจmodelmapper๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด์ ์๋ก ๋ค๋ฅธ ํด๋์ค์ ๊ฐ์ํ๋์ ์ด๋ฆ๊ณผ ์๋ฃํ์ด ๊ฐ์ผ๋ฉด(๋ค๋ฅด๋ฉด ์ฌ์ฉ X)getter, setter๋ฅผ ํตํด ๊ฐ์ ๋ณต์ฌํด์ ๊ฐ์ฒด๋ฅผ ๋ฐํํด์ค๋ค.
<!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper -->
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>3.2.0</version>
</dependency>
๐ ์์กด์ฑ ์ถ๊ฐ
package com.shop.dto;
import com.shop.entity.ItemImg;
import lombok.Getter;
import lombok.Setter;
import org.modelmapper.ModelMapper;
// ์ํ ์ด๋ฏธ์ง์ ๋ํ DTO
@Getter
@Setter
public class ItemImgDto {
private Long id;
private String imgName;
private String oriImgName;
private String imgUrl;
private String repImgYn;
private static ModelMapper modelMapper = new ModelMapper();
public static ItemImgDto of(ItemImg itemImg){
// ItemImg ์ํฐํฐ ๊ฐ์ฒด๋ฅผ ItemImgDto ๊ฐ์ฒด๋ก ๋ณํ
return modelMapper.map(itemImg, ItemImgDto.class); // itemImg Entity์ ItemImgDto ๋งคํ
}
}
package com.shop.dto;
import com.shop.constant.ItemSellStatus;
import com.shop.entity.Item;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
import org.modelmapper.ModelMapper;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
// View๋ก ๋ถํฐ ์
๋ ฅ ๋ฐ์ ์ํ ๋ฐ์ดํฐ ์ ๋ณด DTO
@Getter
@Setter
public class ItemFormDto {
//------------------------- Item -------------------------//
private Long id;
@NotBlank(message = "์ํ๋ช
์ ํ์ ์
๋ ฅ ๊ฐ์
๋๋ค.")
private String itemNm;
@NotNull(message = "๊ฐ๊ฒฉ์ ํ์ ์
๋ ฅ ๊ฐ์
๋๋ค.") // Integer ํ์
์ @NotNull
private Integer price;
@NotBlank(message = "์์ธ ์ค๋ช
์ ํ์ ์
๋ ฅ ๊ฐ์
๋๋ค.")
private String itemDetail;
@NotNull(message = "์ฌ๊ณ ๋ ํ์ ์
๋ ฅ ๊ฐ์
๋๋ค.")
private Integer stockNumber;
private ItemSellStatus itemSellStatus;
//------------------------- ์ํ ์ด๋ฏธ์ง -------------------------//
private List<ItemImgDto> itemImgDtoList = new ArrayList<>(); // ์ํ ์ด๋ฏธ์ง ์ ๋ณด
private List<Long> itemImgIds = new ArrayList<>(); // ์ํ ์ด๋ฏธ์ง ์์ด๋
private static ModelMapper modelMapper = new ModelMapper();
public Item createItem(){
// ItemFormDto ๐ Entity Item ์ฐ๊ฒฐ. ๋ฐ๋ก ๋ฐ๊ฟ์ค๋ค
return modelMapper.map(this, Item.class);
}
public static ItemFormDto of(Item item){
// Entity Item ๐ ItemFormDto ์ฐ๊ฒฐ. ๋ฐ๋ก ๋ฐ๊ฟ์ค๋ค
return modelMapper.map(item, ItemFormDto.class);
}
}
package com.shop.controller;
import com.shop.dto.ItemFormDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ItemController {
@GetMapping(value = "/admin/item/new") // ADMIN ROLE ๋ง ๋ค์ด์ฌ ์ ์๋ค
// ItemFormDto ๊ฐ์ฒด๋ฅผ model ๊ฐ์ฒด์ ๋ด์์ ๋ทฐ๋ก ์ ๋ฌ
public String itemForm(Model model){
model.addAttribute("itemFormDto", new ItemFormDto());
return "/item/itemForm";
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/layout1}">
<!-- ์ํ ๋ฑ๋ก ํ์ด์ง์์ ์ํ ์ด๋ฏธ์ง๋ฅผ browse ๊ธฐ๋ฅ์ ํตํด ์ ํํ์ ๋ (change) ์ํ๋๋ ์คํฌ๋ฆฝํธ -->
<th:block layout:fragment="script">
<script th:inline="javascript">
$(document).ready(function(){
var errorMessage = [[${errorMessage}]];
if(errorMessage != null){
alert(errorMessage);
}
bindDomEvent();
});
function bindDomEvent(){
// ํด๋์ค ์ค์ <input type="file" class="imageFile form-control" name="itemImgFile"> ์ฌ๊ธฐ์ ์ฌ์ฉ
$(".imageFile.form-control").on("change", function(){
// ์ด๋ฏธ์ง ํ์ผ๋ช
. ex) a.jpg
var fileName = $(this).val().split("\\").pop();
var fileExt = fileName.substring(fileName.lastIndexOf(".")+1);
// ํ์ฅ์ ์ถ์ถ, ์๋ฌธ์ ๋ณํ
fileExt = fileExt.toLowerCase();
if(fileExt != "jpg" && fileExt != "jpeg" && fileExt != "gif"
&& fileExt != "png" && fileExt != "bmp"){
alert("์ด๋ฏธ์ง ํ์ผ๋ง ๋ฑ๋ก์ด ๊ฐ๋ฅํฉ๋๋ค.");
$(this).val("");
return;
}
});
}
</script>
</th:block>
<!-- ์ฌ์ฉ์ CSS ์ถ๊ฐ -->
<th:block layout:fragment="css">
<style>
.input-group {
margin-bottom : 15px;
}
.img-div {
margin-bottom : 10px;
}
.fieldError {
color: red;
}
</style>
</th:block>
<!-- ์ํ ์ด๋ฏธ์ง ๋ถ๋ถ์ ์ํ์ ๋ฑ๋กํ๋ ๊ฒฝ์ฐ์ ์ํ์ ์์ ํ๋ ๊ฒฝ์ฐ๋ก ๋๋จ -->
<div layout:fragment="content">
<!-- ํ์ผ์ ์ ์กํ ๋๋ form ํ๊ทธ์ enctype(์ธ์ฝ๋ฉ ํ์
) ๊ฐ์ "multipart/form-data" ๋ก ์ค์ -->
<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">
<select th:field="*{itemSellStatus}" class="form-select">
<option value="SELL">ํ๋งค์ค</option>
<option value="SOLD_OUT">ํ์ </option>
</select>
</div>
<div class="input-group">
<div class="input-group-text">
<span>์ํ๋ช
</span>
</div>
<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">
<div class="input-group-text">
<span>๊ฐ๊ฒฉ</span>
</div>
<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">
<div class="input-group-text">
<span>์ฌ๊ณ </span>
</div>
<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">
<div class="input-group-text">
<span>์ํ ์์ธ ๋ด์ฉ</span>
</div>
<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>
<!-- ์ํ์ ์ฒ์ ๋ฑ๋กํ ๊ฒฝ์ฐ Controller ๋ฅผ ํตํด ์ ๋ฌ ๋ฐ์ itemFormDtoList ๊ฐ ์กด์ฌํ์ง ์์ ๐ ์คํ -->
<div th:if="${#lists.isEmpty(itemFormDto.itemImgDtoList)}">
<!-- Thymeleaf builtin ๋ฉ์๋ #numbers.sequence() ๋ฅผ ํตํด์ 1 ๋ถํฐ 5 ๊น์ง ์ํ์ด๋ฏธ์ง๋ช
์ถ๋ ฅ -->
<div class="form-group" th:each="num: ${#numbers.sequence(1,5)}">
<div class="custom-file img-div">
<label class="form-control-file" th:text="์ํ์ด๋ฏธ์ง + ${num}"></label>
<input type="file" class="imageFile form-control" name="itemImgFile">
</div>
</div>
</div>
<!-- ์ํ์ ์์ ํ ๊ฒฝ์ฐ๋ ์ด๋ฏธ ์กด์ฌํ๋ ์ํ ์ด๋ฏธ์ง๊ฐ ์์ผ๋ฏ๋ก itemFormDtoList๊ฐ ์กด์ฌ ๐ ์คํ
์ด๋ฏธ์ง๊ฐ ์กด์ฌํ๋ฉด ํด๋น ์ด๋ฏธ์ง์ ์ด๋ฆ์ ์ถ๋ ฅํ๊ณ
๋น์นธ์ด๋ฉด ์ํ์ด๋ฏธ์ง+index ๋ฅผ ์ถ๋ ฅ -->
<div th:if = "${not #lists.isEmpty(itemFormDto.itemImgDtoList)}">
<div class="form-group" th:each="itemImgDto, status: ${itemFormDto.itemImgDtoList}">
<div class="custom-file img-div">
<!-- itemImgDto.id ๐ hidden์ผ๋ก ์จ๊ฒจ์ ํ๋ฉด์ ๋์ค์ง ์๋๋ค -->
<input type="hidden" name="itemImgIds" th:value="${itemImgDto.id}">
<!-- itemImgDto.oriImgName ์ค๋ฆฌ์ง๋ ์ด๋ฏธ์ง ๋ค์์ด ๋น์ด์์ง ์์ผ๋ฉด
? ์ฐธ(์ด๋ฏธ์ง ์ด๋ฆ) : ๊ฑฐ์ง(์๊ธฐ ์ด ์ํ์ด๋ฏธ์ง ์ธ๋ฑ์ค)-->
<label class="form-control-file" th:text="${not #strings.isEmpty(itemImgDto.oriImgName)}
? ${itemImgDto.oriImgName} : '์ํ์ด๋ฏธ์ง' + ${status.index+1}"></label>
<input type="file" class="imageFile form-control" name="itemImgFile">
</div>
</div>
</div>
<!-- String ๋ฌธ์์ด์ด ๋น์ด์์ผ๋ฉด ์คํ ๐ itemFormDto.id ํ์ฌ ์ ์ฅ๋ ์ํ โ ๐ ์ ์ฅ -->
<div th:if="${#strings.isEmpty(itemFormDto.id)}" style="text-align: center">
<!-- formaction=๏ผ@{/admin/item/new}๏ผ href mapping /admin/item/new method ๐ post -->
<button th:formaction="@{/admin/item/new}" type="submit" class="btn btn-success">์ ์ฅ</button>
</div>
<!-- ์ ์กฐ๊ฑด์ด ์๋๋ฉด ์คํ ="${#strings.isEmpty(itemFormDto.id)}" ๊ฐ ์์ผ๋ฉด ์ ์ฅ์ด ๋๋ฏ๋ก ์ํ โญ ๐ ์์ -->
<!-- ="@{'/admin/item/'+${itemFormDto.id} ๐ /admin/item/3 -->
<div th:unless="${#strings.isEmpty(itemFormDto.id)}" style="text-align: center">
<button th:formaction="@{'/admin/item/' + ${itemFormDto.id} }" type="submit" class="btn btn-warning">์์ </button>
</div>
<!-- CSRF ํ ํฐ -->
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
</form>
</div>
</html>

๐ Member ํด๋์ค ๋ณ๊ฒฝ




๐ ์ด๋ค ๊ตฌ์กฐ๋ก ์คํ ๋๋์ง๋ ํ์ผ ์ ๋ก๋์ ํจ๊ป
spring.servlet.multipart.maxFileSize=20MB
spring.servlet.multipart.maxRequestSize=100MB
itemImgLocation=C:/shop1/item
uploadPath=file:///C:/shop1/
๐ ์ถ๊ฐ. ํ์ผ ํฌ๊ธฐ ๋ฐ ๊ฒฝ๋ก ์ง์

๐ ์ ๊ฒฝ๋ก์ ๋ง๋ ํ์ผ ์์ฑ
package com.shop.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
// "${uploadPath}" ๐ application.properties ์ ์ค์ ํ "uploadPath" ํ๋กํผํฐ ๊ฐ
@Value("${uploadPath}") // application.properties ์ค์ ํ uploadPath
String uploadPath;
// addResourceHandlers ๋ฉ์๋ Override ๐ ํ์ผ ์
๋ก๋ ๊ฒฝ๋ก ์ง์
// uploadPath = "C:/shop
// images/item/xxx.jpg
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry){
// " /images/** " ํจํด์ URL ์ uploadPath ํด๋๋ฅผ ๊ธฐ์ค์ผ๋ก ํ์
registry.addResourceHandler("/images/**")
// /images๋ก ์์ํ๋ ๊ฒฝ์ฐ uploadPath์ ์ค์ ํ ํด๋๋ฅผ ๊ธฐ์ค์ผ๋ก ํ์ผ์
// ์ฝ์ด ์ค๋๋ก ์ค์
.addResourceLocations(uploadPath); // ๋ก์ปฌ ์ปดํจํฐ ์์ root ๊ฒฐ๋ก๋ฅผ ์ค์
}
}
package com.shop.service;
import lombok.extern.java.Log;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.FileOutputStream;
import java.util.UUID;
// ์ด๋ฏธ์ง ํ์ผ ์ ์ฅ ๋ก์ง์ ๋ด๋นํ Service ๊ฐ์ฒด
// ํ์ผ ์ ์ฅ์ DB ์ ์ ์ฅ๋๋ ๊ฒ์ด ์๋๊ธฐ ๋๋ฌธ์ Repository๊ฐ ํ์ ์์ (FileOutputStream ๊ฐ ๋์ ํจ)
@Service
@Log
public class FileService {
public String uploadFile(String uploadPath, String originalFileName, byte[] fileData)
throws Exception{
// UUID(Universally Unique IDentifier) - ์๋ก ๋ค๋ฅธ ๊ฐ์ฒด๋ค์ ๊ตฌ๋ณํ๊ธฐ ์ํ ํด๋์ค. ๋๋ค์ผ๋ก UUID๋ฅผ ์์ฑ
UUID uuid = UUID.randomUUID();
String extension = originalFileName.substring(originalFileName.lastIndexOf("."));
String savedFileName = uuid.toString() + extension;
String fileUploadFullUrl = uploadPath+"/"+savedFileName;
System.out.println(fileUploadFullUrl);
// FileOutputStream ํด๋์ค๋ฅผ ์ด์ฉํ์ฌ ํ์ผ์ ์ ์ฅ
FileOutputStream fos = new FileOutputStream(fileUploadFullUrl);
fos.write(fileData);
fos.close();
return savedFileName;
}
// ์ด๋ฏธ์ง ํ์ผ ์ญ์
public void deleteFile(String filePath) throws Exception{
File deleteFile = new File(filePath);
if(deleteFile.exists()) { //deleteFile ๊ฐ์ฒด ์ฌ๋ถ๋ฅผ ํ์ธ
deleteFile.delete();
log.info("ํ์ผ์ ์ญ์ ํ์์ต๋๋ค.");
}else{
log.info("ํ์ผ์ด ์กด์ฌํ์ง ์์ต๋๋ค.");
}
}
}
package com.shop.repository;
import com.shop.entity.ItemImg;
import org.springframework.data.jpa.repository.JpaRepository;
// ์ํ ์ด๋ฏธ์ง ์ ๋ณด ์ฟผ๋ฆฌ๋ฌธ ๋ ๋ฆฌ๋ Repository
public interface ItemImgRepository extends JpaRepository<ItemImg,Long> {
}

package com.shop.service;
import com.shop.entity.ItemImg;
import com.shop.repository.ItemImgRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.thymeleaf.util.StringUtils;
import java.io.IOException;
// ์ํ ์ด๋ฏธ์ง ์
๋ก๋, ์ํ ์ด๋ฏธ์ง ์ ๋ณด ์ ์ฅ Service
@Service
@RequiredArgsConstructor
@Transactional
public class ItemImgService {
@Value("${itemImgLocation}") // application.properties์ itemImgLocation
private String itemImgLocation; // @Value("${itemImgLocation}") ๋ฅผ String itemImgLocation๋ก ์นํ
private final ItemImgRepository itemImgRepository; // ์ํ ์ด๋ฏธ์ง ์ ๋ณด ์ ์ฅ
private final FileService fileService; // ์ํ ์ด๋ฏธ์ง ์
๋ก๋
// MultipartFile : ํด๋ผ์ด์ธํธ๊ฐ ๋ณด๋ธ ์ด๋ฏธ์ง ์ ๋ณด
// ์ํ ์ด๋ฏธ์ง ์ ์ฅ (๋ด๋ถ์ ์ฌ์ง ์
๋ก๋ ํฌํจ)
public void saveItemImg(ItemImg itemImg, MultipartFile itemImgFile) throws Exception {
// a.jpg๊ฐ ๊ทธ๋ฅ ์จ๋ค
String oriImgName = itemImgFile.getOriginalFilename(); // ์ค๋ฆฌ์ง๋ ์ด๋ฏธ์ง ๊ฒฝ๋ก
String imgName = "";
String imgUrl = "";
System.out.println(oriImgName);
// ํ์ผ ์
๋ก๋
if (!StringUtils.isEmpty(oriImgName)){ // oriImgName ์ด ์กด์ฌํ๋ฉด ์คํ
System.out.println("******");
// fileService.uploadFile() ๋ฉ์๋๋ก ํ์ผ ์
๋ก๋ ํ UUID ๋ฅผ ํตํด ๋ณ๊ฒฝ๋ ํ์ผ๋ช
return
imgName = fileService.uploadFile(itemImgLocation, oriImgName,
itemImgFile.getBytes()); // Byte ๋ฐฐ์ด ๐ ์ค์ ์ด๋ฏธ์ง ํ์ผ
System.out.println(imgName);
imgUrl = "/images/item/" + imgName; // ์ด๋ฏธ์ง URL์ ๋ง๋ ๋ค.
}
System.out.println("1111");
// ์ํ ์ด๋ฏธ์ง ์ ๋ณด ์ ์ฅ
// update ํ ์ด์ ? ๐ ์ 3๊ฐ๊ฐ ์ฑ์์ง์ง ์์๊ธฐ ๋๋ฌธ์ ์ฑ์ฐ๋ ๊ฒ
// updateItemImg ๋ฉ์๋์ itemImgRepository.save() ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ์ํ ์ด๋ฏธ์ง ์ ๋ณด ์ ์ฅ
itemImg.updateItemImg(oriImgName, imgName, imgUrl);
System.out.println("(((((");
itemImgRepository.save(itemImg); // ์์์ ์น ์ ๋ฆฌํ๊ณ DB์ ์ ์ฅ. ๋
}
}
package com.shop.service;
import com.shop.dto.ItemFormDto;
import com.shop.entity.Item;
import com.shop.entity.ItemImg;
import com.shop.repository.ItemImgRepository;
import com.shop.repository.ItemRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@Service
@RequiredArgsConstructor
@Transactional
public class ItemService {
private final ItemRepository itemRepository; // ์ํ ์ ์ฅ
private final ItemImgService itemImgService; // ์ํ ์ด๋ฏธ์ง ์ ์ฅ
private final ItemImgRepository itemImgRepository;
// ์ํ ์ ์ฅ
public Long saveItem(ItemFormDto itemFormDto, List<MultipartFile> itemImgFileList) throws Exception {
// ์ํ ๋ฑ๋ก. return modelMapper.map(this, Item.class); ๋ฐ๋ก ๋ฐ๊ฟ์ค๋ค.
// ItemFormDto ๊ฐ์ฒด๋ฅผ DB ์ ์ ์ฅํ๊ธฐ ์ํด Item ๊ฐ์ฒด๋ก ๋ฐ๊ฟ
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 { // ์ฒซ ๋ฒ์งธ ์ด๋ฏธ์ง๋ฅผ ์ ์ธํ ์ด๋ฏธ์ง๋ค์ ๋ค NO
itemImg.setRepImgYn("N");
}
itemImgService.saveItemImg(itemImg, itemImgFileList.get(i));
}
return item.getId(); // item_id๋ฅผ ๋๊ฒจ์ค๋ค
}
}
package com.shop.controller;
import com.shop.dto.ItemFormDto;
import com.shop.service.ItemService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@Controller
@RequiredArgsConstructor
public class ItemController {
private final ItemService itemService;
// ์ํ ๋ฑ๋ก ํ์ด์ง GET์ผ๋ก ์ ๊ทผ
@GetMapping(value = "/admin/item/new") // ADMIN ROLE ๋ง ๋ค์ด์ฌ ์ ์๋ค
// ItemFormDto ๊ฐ์ฒด๋ฅผ model ๊ฐ์ฒด์ ๋ด์์ ๋ทฐ๋ก ์ ๋ฌ
public String itemForm(Model model){
model.addAttribute("itemFormDto", new ItemFormDto());
return "/item/itemForm";
}
// ์ํ ๋ฑ๋ก ํ์ด์ง POST์ผ๋ก ์ ๊ทผ
@PostMapping(value = "/admin/item/new")
// ์
๋ ฅ๊ฐ ๊ฒ์ฆ ๋ฐ ์๋ฌ ๋ฉ์์ง ํ์
public String itemNew(@Valid ItemFormDto itemFormDto, BindingResult bindingResult, Model model,
// POST ์
๋ ฅ์ผ๋ก ๋ค์ด์จ ์ด๋ฏธ์ง ํ์ผ (name = "itemImgFile")์ MultipartFile ๊ฐ์ฒด๋ก ๋ฐ์
@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:/";
}
}
package com.shop.repository;
import com.shop.entity.ItemImg;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
// ์ํ ์ด๋ฏธ์ง ์ ๋ณด ์ฟผ๋ฆฌ๋ฌธ ๋ ๋ฆฌ๋ Repository
public interface ItemImgRepository extends JpaRepository<ItemImg,Long> {
// ์ด๋ฏธ์ง๊ฐ ์ ์ ์ฅ๋์๋์ง ํ์ธํ๊ธฐ ์ํด ์ฟผ๋ฆฌ ์กฐํ๋ฌธ ์ถ๊ฐ
List<ItemImg> findByItemIdOrderByIdAsc(Long itemId);
}
package com.shop.service;
import com.shop.constant.ItemSellStatus;
import com.shop.dto.ItemFormDto;
import com.shop.dto.ItemImgDto;
import com.shop.entity.Item;
import com.shop.entity.ItemImg;
import com.shop.repository.ItemImgRepository;
import com.shop.repository.ItemRepository;
import jakarta.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.TestPropertySource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@Transactional
@TestPropertySource(locations = "classpath:application-test.properties")
class ItemServiceTest {
@Autowired
ItemService itemService;
@Autowired
ItemRepository itemRepository;
@Autowired
ItemImgRepository itemImgRepository;
// MockMultipartFile ๊ฐ์ฒด๋ฅผ ์ด์ฉํด ๊ฐ์์ ์ด๋ฏธ์ง ํ์ผ 5๊ฐ ์์ฑํ๋ ๋ฉ์๋
List<MultipartFile> createMultipartFiles(){
List<MultipartFile> multipartFileList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
String path = "C:/shop1/item";
String imageName = "image" + i + ".jpg";
MockMultipartFile multipartFile =
new MockMultipartFile(path, imageName,
"image/jpg", new byte[]{1, 2, 3, 4});
multipartFileList.add(multipartFile);
}
return multipartFileList;
}
@Test
@DisplayName("์ํ ๋ฑ๋ก ํ
์คํธ")
// ์ํ ๋ฑ๋ก์ ADMIN๋ง
@WithMockUser(username = "admin", roles = "ADMIN")
// ItemFormDto ๊ฐ์ฒด์ MultipartFile ๊ฐ์ฒด๋ฅผ ์์๋ก ์์ฑ ํ itemService.saveItem ์ํ
void saveItem() throws Exception {
ItemFormDto itemFormDto = new ItemFormDto();
itemFormDto.setItemNm("ํ
์คํธ ์ํ");
itemFormDto.setItemSellStatus(ItemSellStatus.SELL);
itemFormDto.setItemDetail("ํ
์คํธ ์ํ ์์ธ ์ค๋ช
.");
itemFormDto.setPrice(1000);
itemFormDto.setStockNumber(100);
List<MultipartFile> multipartFileList = createMultipartFiles();
Long itemId = itemService.saveItem(itemFormDto, multipartFileList);
// ์ ์ฅ๋ ์ด๋ฏธ์ง๋ฅผ itemImgRepository.findByItemIdOrderByIdAsc(itemId) ๋ก ์กฐํ
List<ItemImg> itemImgList = itemImgRepository.findByItemIdOrderByIdAsc(itemId);
// ์ ์ฅ์ ํ๋ฉด itemId๋ฅผ ์์ฉํด Item ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ item์ ๋์
Item item = itemRepository.findById(itemId)
.orElseThrow(EntityNotFoundException::new);
// itemFormDto ๋ด์ฉ๊ณผ Item ๋ด์ฉ์ ๋น๊ตํด ๊ฐ์์ง ํ์ธ
assertEquals(itemFormDto.getItemNm(), item.getItemNm());
assertEquals(itemFormDto.getItemSellStatus(), item.getItemSellStatus());
assertEquals(itemFormDto.getItemDetail(), item.getItemDetail());
assertEquals(itemFormDto.getPrice(), item.getPrice());
assertEquals(itemFormDto.getStockNumber(), item.getStockNumber());
// multipartFileList์ 0๋ฒ ์ธ๋ฑ์ค ๋ด์ฉ๊ณผ itemImgList 0๋ฒ ์ธ๋ฑ์ค ๋ด์ฉ์ ๋น๊ตํด ๊ฐ์์ง ํ์ธ
assertEquals(multipartFileList.get(0).getOriginalFilename(), itemImgList.get(0).getOriImgName());
}
}

Hibernate:
select
next value for item_seq
image0.jpg
******
C:/shop1/item/e4fea683-7aec-4d9b-bb30-687ad442cc64.jpg
e4fea683-7aec-4d9b-bb30-687ad442cc64.jpg
1111
(((((
Hibernate:
select
next value for item_img_seq
image1.jpg
******
C:/shop1/item/0c340b74-bb36-4a8e-94c8-5f139b947eb0.jpg
0c340b74-bb36-4a8e-94c8-5f139b947eb0.jpg
1111
(((((
Hibernate:
select
next value for item_img_seq
image2.jpg
******
C:/shop1/item/25f4801c-3e2d-4b29-bfb7-387e41ef3f9a.jpg
25f4801c-3e2d-4b29-bfb7-387e41ef3f9a.jpg
1111
(((((
image3.jpg
******
C:/shop1/item/a5114d7f-a79f-497a-97fb-91ef8e9972ab.jpg
a5114d7f-a79f-497a-97fb-91ef8e9972ab.jpg
1111
(((((
image4.jpg
******
C:/shop1/item/983331d1-1576-44e7-b26b-8dca4e029ef8.jpg
983331d1-1576-44e7-b26b-8dca4e029ef8.jpg
1111
(((((

๐ ํ์ ๊ฐ์




๐ ItemFormDto ๋ฅผ ํตํ ์ ํจ์ฑ ์ฒ๋ฆฌ

๐ ์ํ ๋ฑ๋ก ์

๐ ์ด๋ฏธ์ง ๋ฑ๋ก์ ํ์ง ์์์ ๋



๐ ์ฑ๊ณต. ๋น 4์นธ์ ์์ ์ ์ํด ์์ฑ ๋ ๊ฒ


๐ ์๋๋ฐฉ ๊ฒฝ๋ก์ ์ ๋ค์ด๊ฐ
1. "ADMIN" ๊ถํ์ ๊ฐ์ง ์์ด๋๋ก ์ํ ๋ฑ๋ก ํ์ด์ง Get ์์ฒญ
2. Item Controller ์์ ์ํ ๋ฑ๋ก ํ์ด์ง๋ฅผ ๋ฐํํ๋ฉด์ itemFormDto ๊ฐ์ฒด๋ ๋๊น
3. ์ํ ๋ฑ๋ก ํ์ด์ง์์ ์ํ ์ ๋ณด ๋ฐ ์ด๋ฏธ์ง๋ฅผ ์ ๋ ฅํ๊ณ "์ ์ฅ" (POST ์์ฒญ)
4. Item Controller ์์ ์ ๋ ฅ๊ฐ์ ๊ฒ์ฆํ๊ณ itemService.saveItem() ๋ฉ์๋๋ฅผ ์ํ. ์ด ๋, ํ๋ผ๋ฏธํฐ๋ ์ ๋ ฅ๋ฐ์ itemFormDto ๊ฐ์ฒด์ ์ด๋ฏธ์ง ์ ๋ณด๋ฅผ ๋ด๊ณ ์๋ itemImgFileList ๋ฅผ ๋๊น
5. itemService ์์ itemFormDto ๊ฐ์ฒด๋ฅผ item ์ํฐํฐ๋ก ๋ณํํ๊ณ itemRepository.save() ๋ฉ์๋ ์ํ
6. itemService ์์ itemImg ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ itemImgService.saveItemImg() ๋ฉ์๋ ์ํ. ์ด ๋, ํ๋ผ๋ฏธํฐ๋ itemImg ๊ฐ์ฒด์ ์ด๋ฏธ์ง ์ ๋ณด๋ฅผ ๋ด๊ณ ์๋ itemImgFileList.get(i) ๊ฐ์ฒด ํ๋๋ฅผ ์ง์
7. ItemImgService ์์ ์ํ ์ด๋ฏธ์ง๊ฐ ์กด์ฌํ๋ค๋ฉด fileService.uploadFile() ๋ฉ์๋ ์ํ. ์ด ๋, ํ๋ผ๋ฏธํฐ๋ "์ ์ฅ์์น", "์๋ ํ์ผ๋ช ", "์ด๋ฏธ์ง Byte ํ์ผ"
8. FileService ์์ UUID ๊ฐ์ฒด๋ฅผ ์ด์ฉํด ํ์ผ๋ช ์ ์๋ก ๋ง๋ค๊ณ FileOutputStream ์ ์ด์ฉํ์ฌ ์ ์ฅ
9. ItemImgService ์์ itemImgRepository.save() ๋ฉ์๋ ์ํ
10. ์ ์ฅ ์๋ฃ
์ํ ๊ฐ์ฒด๋ง ํ์ํ๋ค? โ
์ํ์ ๋ฑ๋กํ๋ค๊ณ ํด์ ์ํ
Entity๋ง ์์ผ๋ฉด ๋๋ ๊ฒ์ด ์๋๋ผ ์ํ์ ๋ณด์ฌ์ค ์ด๋ฏธ์ง๋ ํ์ํ๋ค.๊ทธ๋ ๋ค๊ณ ํด์ ์ํ๊ณผ ์ด๋ฏธ์ง ์ ๋ณด๋ฅผ ํ๋์ ๊ฐ์ฒด๋ก ๊ตฌํํ์ง๋ง๊ณ ,
๊ฐ๊ฐ ๋ฐ๋ก ๊ตฌํํด์ผ ์ฌ๋ฐ๋ฅธ ์ค๊ณ์ด๋ค.์ถ๊ฐ์ ์ผ๋ก ์ํ๊ณผ ์ํ ์ด๋ฏธ์ง ์ ๋ณด๋
DB์ ์ ์ฅ๋์ง๋ง์ค์ ์ด๋ฏธ์ง File์๋ก์ปฌ๋ด์ ์ ์ฅ๋๋ ๊ตฌ์กฐ๊ธฐ ๋๋ฌธ์ ์๋์ฒ๋ผ ์ด 3๊ฐ์ ๋ถ๋ถ์ผ๋ก ๋๋ ์ผํจ
์ํ Entity์ํ ์ด๋ฏธ์ง ์ ๋ณด Entity์ํ ์ด๋ฏธ์ง File
3๊ฐ์ ๋ถ๋ถ์ผ๋ก ๋๋ด๋ค๋ฉด
Service๋ก์ง๋ ์๋์ฒ๋ผ 3๊ฐ๋ก ๋๋ ์ผํ๊ณ ,
์ํ ์ด๋ฏธ์ง File์DB์ ์ ์ฅ๋๋ ๊ฒ์ด ์๋๊ธฐ ๋๋ฌธ์Repository๊ฐ ์๋FileOutputStream์ผ๋ก ์ ์ฅ
์ํ Entity ๐ ์ํ Service ๐ ์ํ Repository
์ํ ์ด๋ฏธ์ง ์ ๋ณด Entity ๐ ์ํ ์ด๋ฏธ์ง Service ๐ ์ํ ์ด๋ฏธ์ง Repository
์ํ ์ด๋ฏธ์ง File ๐ File Service ๐ FileOutputStream
์ฆ, ์ํ์ ๋ฑ๋กํ๋ค๊ณ ํ์ ๋ ์ํ ๊ฐ์ฒด ํ๋๋ง์ ๊ตฌํํ๋ ๊ฒ์ด ์๋๋ผ ์ต๋ํ ์ธ๋ถํ ํ ๋ค์
Service๋ก์ง๋ ๋๋ ์ ๊ตฌํํด์ผ ํ๋ค.
...
// ์ํ ์์
// ์ํ์ ์์ ํ๋ ค๋ฉด ํด๋น ์ํ์ ๋ถ๋ฌ์์ผ ํจ
// ๐ ItemService ์ ์ํ ์กฐํ ๋ฉ์๋ getItemDtl ์ถ๊ฐ (์กฐํ ๊ธฐ๋ฅ์ด๋ฏ๋ก ์ฝ๊ธฐ ์ ์ฉ์ผ๋ก ๋ถ๋ฌ์ด)
@Transactional(readOnly = true)
public ItemFormDto getItemDtl(Long itemId){
List<ItemImg> itemImgList = itemImgRepository.findByItemIdOrderByIdAsc(itemId);
// DB์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ ์ด
// DTO
List<ItemImgDto> itemImgDtoList = new ArrayList<>();
for(ItemImg itemImg : itemImgList){
// Entity ๐ DTO (modelMapper)
ItemImgDto itemImgDto = ItemImgDto.of(itemImg);
itemImgDtoList.add(itemImgDto);
}
// item ์ํฐํฐ์ img ์ ๋ณด ์ํฐํฐ๋ฅผ itemFormDto ๊ฐ์ฒด๋ก ๋ณํ ํ ๋ฐํํ๋ ์กฐํ ๊ธฐ๋ฅ
Item item = itemRepository.findById(itemId).orElseThrow(EntityNotFoundException::new);
// Item ๐ ItemFormDto (modelMapper)
ItemFormDto itemFormDto = ItemFormDto.of(item);
itemFormDto.setItemImgDtoList(itemImgDtoList);
return itemFormDto;
}
}
...
@GetMapping(value = "/admin/item/{itemId}")
public String itemDtl(@PathVariable("itemId") Long itemId, Model model){
try {
ItemFormDto itemFormDto = itemService.getItemDtl(itemId);
model.addAttribute("itemFormDto", itemFormDto);
}catch (EntityNotFoundException e){
model.addAttribute("errorMessage", "์กด์ฌํ์ง ์๋ ์ํ์
๋๋ค.")
model.addAttribute("itemFormDto", new ItemFormDto());
return "item/itemForm";
}
return "item/itemForm";
}
}

๐ ๋ก๊ทธ์ธ


๐ item_id๋ฅผ ์ด์ฉํด /admin/item/{itemId} ์ธ /admin/item/1 ๋ก ๊ฐ๋ณด์

๐ item_id์ ๋ฐ๋ฅธ ์์ ํ์ด์ง๋ก ์ ์ด๋ ํ๋ค. ์ด์ ์ค์ ์์ ์ ํด๋ณด์
...
public void updateItemImg(Long itemImgId, MultipartFile itemImgFile) throws Exception {
// ์ํ์ ์ด๋ฏธ์ง๋ฅผ ์์ ํ ๊ฒฝ์ฐ, ๊ธฐ์กด์ ์ด๋ฏธ์ง ์ ๋ณด ๊ฐ์ฒด๋ฅผ ๋ถ๋ฌ์ ์ํ ์ด๋ฏธ์ง ์
๋ฐ์ดํธ
if (!itemImgFile.isEmpty()){
ItemImg savedItemImg = itemImgRepository.findById(itemImgId).
orElseThrow(EntityExistsException::new); // ๊ธฐ์กด ์ํฐํฐ ์กฐํ
// ๊ธฐ์กด์ ๋ฑ๋ก๋ ์ํ ์ด๋ฏธ์ง ํ์ผ์ด ์๋ ๊ฒฝ์ฐ ํ์ผ ์ญ์ (์กด์ฌํ์ง ์๋๋ค๋ฉด ์ด๋ฏธ์ง๋ฅผ ์ถ๊ฐํ ๊ฒ)
if(!StringUtils.isEmpty(savedItemImg.getImgName())){
fileService.deleteFile(itemImgLocation + "/" + savedItemImg.getImgName());
}
String oriImgName = itemImgFile.getOriginalFilename();
// ์์ ํ ์ด๋ฏธ์ง ํ์ผ์ fileService.uploadFile() ๋ฉ์๋๋ฅผ ํตํด ์
๋ก๋
String imgName = fileService.uploadFile(itemImgLocation, oriImgName,
itemImgFile.getBytes()); // ํ์ผ ์
๋ก๋
String imgUrl = "/images/item/" + imgName;
// ๋ณ๊ฒฝ๋ ์ํ ์ด๋ฏธ์ง ์ ๋ณด ์ธํ
// ์ํ ๋ฑ๋ก์ ํ๋ ๊ฒฝ์ฐ์๋ ItemImgRepository.save()๋ก์ง์ ํธ์ถ ํ์ง๋ง
// ํธ์ถ์ ํ์ง ์์๋ค. itemImgRepository.save() ๊ฐ ์๋
// savedItemImg.updateItemImg() ๋ฉ์๋๋ก ๊ฐ์ฒด๋ฅผ ์์ ๐ ๋ณ๊ฒฝ ๊ฐ์ง
// savedItemimg ์ํฐํฐ๋ ํ์ฌ ์์์ฑ ์ํ์ด๋ฏ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ๋ ๊ฒ๋ง์ผ๋ก ๋ณ๊ฒฝ์ ๊ฐ์งํ๋ค.
// ํธ๋์ญ์
์ด ๋๋ ๋ update ์ฟผ๋ฆฌ๊ฐ ์คํ ๋๋ค.
// โ ์์์ฑ ์ํ์ฌ์ผ ์ฌ์ฉ ๊ฐ๋ฅ!
// ์ํ ์์ ํ์ด์ง์์ ๊ธฐ์กด ์ด๋ฏธ์ง ํ์ผ์ ์์ ํ์ง ์์๋ค๋ฉด "null" ๊ฐ์ฒด๋ก ๋์ด์ด
// ๐ ์ํ ์ด๋ฏธ์ง ๋ผ๋ฒจ์๋ ์ด๋ฆ์ด ํ์๋์ด ์์ง๋ง ํ์ผ์ด ๋ฑ๋ก๋ ๊ฒ์ ์๋
savedItemImg.updateItemImg(oriImgName, imgName, imgUrl);
}
}
โ ๋ณ๊ฒฝ๊ฐ์ง?
์ ํ๋ฆฌ์ผ์ด์ ์
Entity๊ฐ์ฒด์ 1์ฐจ ์บ์์ ์ ์ฅ๋ ํด๋นEntity์ ์ค๋ ์ท์ ๋น๊ตํ์ฌ ๋ณ๊ฒฝ ์ฌํญ์ด ์๋ค๋ฉด ์๋์ผ๋ก UPDATE ๋ฌธ์ ์ฐ๊ธฐ ์ง์ฐ SQL ์ ์ฅ์์ ๋ด์ ๋์ด ์ปค๋ฐ ์์ ์ ๋ณ๊ฒฝ ๋ด์ฉ์ ์๋์ผ๋ก ๋ฐํ
- JPA ๋ 1์ฐจ ์บ์์ DB ์์ ์ฒ์ ๋ถ๋ฌ์จ ์ํฐํฐ์ ์ค๋ ์ท์ ์ ์ฅ
- ์ค๋ ์ท๊ณผ ๋น๊ตํ์ฌ ๋ณ๊ฒฝ ๋ด์ฉ์ด ์๋ฐ๋ฉด UPDATE SQL ์ํ
...
// ์ํฐํฐ ํด๋์ค์ ๋ก์ง์ ์ถ๊ฐํ๋ค๋ฉด ์ข ๋ ๊ฐ์ฒด์งํฅ์ ์ด๊ณ ์ฝ๋ ์ฌํ์ฉ์ด ๊ฐ๋ฅ
// ๋ํ ๋ณ๊ฒฝ ํฌ์ธํธ๋ฅผ ํ๊ตฐ๋ฐ์์ ๊ด๋ฆฌํ ์ ์์
public void updateItem(ItemFormDto itemFormDto){
this.itemNm = itemFormDto.getItemNm();
this.price = itemFormDto.getPrice();
this.stockNumber = itemFormDto.getStockNumber();
this.itemDetail = itemFormDto.getItemDetail();
this.itemSellStatus = itemFormDto.getItemSellStatus();
}
}
๐ ๋ณ๊ฒฝ ๊ฐ์ง๋ฅผ ์ํด ๋ง๋ฆ
...
public Long updateItem(ItemFormDto itemFormDto, List<MultipartFile> itemImgFileList) throws Exception {
// ์ํ Id ๋ฅผ ์ด์ฉํ์ฌ ๊ธฐ์กด์ ์ํ Entity ๊ฐ์ฒด๋ฅผ ๋ถ๋ฌ์จ ๋ค
// Post ์
๋ ฅ๊ฐ์ผ๋ก ๋ค์ด์จ ์ํ ์ ๋ณด๋ก ์์
Item item = itemRepository.findById(itemFormDto.getId())
.orElseThrow(EntityNotFoundException::new);
item.updateItem(itemFormDto);
List<Long> itemImgIds = itemFormDto.getItemImgIds();
// itemImgService.updateItemImg() ๋ฉ์๋๋ฅผ ์ด์ฉํ์ฌ for๋ฌธ์ ๋๋ ค ์ํ ์ด๋ฏธ์ง ์์
for (int i = 0; i < itemImgFileList.size(); i++) {
itemImgService.updateItemImg(itemImgIds.get(i), itemImgFileList.get(i));
}
return item.getId();
}
}
...
// ์ํ ๋ฑ๋ก๊ณผ update๋ง ๋ค๋ฅด๋ค. ์ํฐํฐ ๋นผ์ ๋ณ๊ฒฝ ๊ฐ์งํ๊ณ ๋ฐ๊ฟ
@PostMapping(value = "/admin/item/{itemId}")
public String itemUpdate(@Valid ItemFormDto itemFormDto, BindingResult bindingResult,
@RequestParam("itemImgFile") List<MultipartFile> itemImgFileList,
Model model){
if (bindingResult.hasErrors()){
return "item/itemForm";
}
if (itemImgFileList.get(0).isEmpty() && itemFormDto.getId() == null){
model.addAttribute("errorMessage", "์ฒซ ๋ฒ์งธ ์ํ ์ด๋ฏธ์ง๋ ํ์ ์
๋ ฅ ๊ฐ ์
๋๋ค.");
return "item/itemForm";
}
try {
itemService.updateItem(itemFormDto, itemImgFileList);
}catch (Exception e){
model.addAttribute("errorMessage", "์ํ ์์ ์ค ์๋ฌ๊ฐ ๋ฐ์ํ์์ต๋๋ค.");
return "item/itemForm";
}
return "redirect:/";
}
}


๐ ํคํฐ์ผ๊ตด์ R๋ก ๋ณ๊ฒฝ

๐ ์๋ ํ์ผ์ ์ญ์ ํ๊ณ ๋ค์ ๋ฃ์ (๋ณ๊ฒฝ ๊ฐ์ง)

๐ ์์ ๋ ์ง ๋ฑ ์ ๋ฐ๋ ๊ฒ์ ํ์ธ ํ ์ ์๋ค.
โ ์ํ ๋ฑ๋ก์
save()๋ฉ์๋๋ง ์ํ
์ํ ์์ ์์กฐํ๐update()๋ฉ์๋๋ฅผ ์ํ.๋ณ๊ฒฝ ๊ฐ์ง์ด์ฉ1. "ADMIN" ๊ถํ์ ๊ฐ์ง ์์ด๋๋ก ์ํ ์์ ํ์ด์ง Get ์์ฒญ th:formaction="@{'/admin/item/' + ${itemFormDto.id} } ๐ Controller ("/admin/item/{itemId}")
2. Item Controller ์์ itemService.getItemDtl(itemId) ์ํํ์ฌ ํด๋น ์ํ ์กฐํ
3. Item Controller ์์ ์ํ ์์ ํ์ด์ง ๋ฐํํ๋ฉด์ ํด๋น ์ํ Dto ๊ฐ์ฒด ๋๊น
4. ์ํ ์์ ํ์ด์ง์์ ์์ ํ ํ "์์ " (POST ์์ฒญ)
5. Item Controller ์์ ์ ๋ ฅ๊ฐ์ ๊ฒ์ฆํ๊ณ itemService.updateItem() ๋ฉ์๋๋ฅผ ์ํ. ์ด ๋, ํ๋ผ๋ฏธํฐ๋ ์ ๋ ฅ๋ฐ์ itemFormDto ๊ฐ์ฒด์ ์ด๋ฏธ์ง ์ ๋ณด๋ฅผ ๋ด๊ณ ์๋ itemImgFileList ๋ฅผ ๋๊น
6. itemService ์์ Item ๊ฐ์ฒด๋ฅผ ์กฐํํ์ฌ ๋ถ๋ฌ์จ ๋ค item.updateItem(itemFormDto) ์ํ
7. itemService ์์ itemImgService.updateItemImg() ๋ฉ์๋ ์ํ. ์ด ๋, ํ๋ผ๋ฏธํฐ๋ "์ํ ์ด๋ฏธ์ง์ id", "์ํ ์ด๋ฏธ์ง ํ์ผ"
8. itemImgService ์์ ๊ธฐ์กด ์ด๋ฏธ์ง ํ์ผ ์ญ์ ํ savedItemImg.updateItemImg () ์ํ
9. ์ด๋ฏธ์ง ์์ ์๋ฃ