이 내용은 스프링 부트 쇼핑몰 프로젝트 with JPA 책을 학습한 내용입니다.
1. 상품 이미지 Entity
- Item (상품) Entity 와 다대일 단방향 관계를 갖는 ItemImg Entity 생성
package kr.spring.item.entity;
import jakarta.persistence.*;
import kr.spring.utils.entity.BaseEntity;
import lombok.*;
@Entity
@Getter
@Setter // 필수 아님
@ToString // 문자열 자동 생성
@NoArgsConstructor // 빈생성자 생성
public class ItemImg extends BaseEntity {
@Id //기본키 설정
@GeneratedValue(strategy = GenerationType.IDENTITY) //mysql의 경우 identity를 사용.
@Column(name = "item_img_id") //name 속성은 column의 이름을 변경할 수 있다.
private Long id; // 이미지 코드
private String imgName; // 이미지 파일명
private String oriImgName; // 원본 파일명
private String imgUrl; // 이미지 경로
private String repImgYn; // 대표 이미지 여부
@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;
}
}
2. modelmapper 라이브러리 추가
// modelmappter 추가 -> dto <-> entity
implementation 'org.modelmapper:modelmapper:3.1.1'
- DTO 객체와 Entity 객체의 변환을 도와줌
- 서로 다른 클래스의 값을 필드의 이름과 자료형이 같으면 getter, setter를 통해 값을 복사해서 객체를 반환
3. 상품 관련 DTO 객체 생성
- 상품을 등록 및 조회할 때 지정된 필드뿐 아니라 추가적인 데이터들의 이동이 많으므로 여러 DTO 이용
- 상품 이미지에 대한 DTO
- ItemImg 엔티티 객체를 ItemImgDto 객체로 변환
package kr.spring.item.dto;
import kr.spring.item.entity.ItemImg;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.modelmapper.ModelMapper;
@Getter
@Setter
@ToString // 내용 확인을 위해 사용
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) {
return modelMapper.map(itemImg, ItemImgDto.class); // mapping 걸기
}
}
- 화면으로부터 입력 받은 상품 데이터 정보 DTO
- Item 엔티티 객체와 DTO 객체 간의 변환
package kr.spring.item.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import kr.spring.item.entity.Item;
import lombok.Getter;
import lombok.Setter;
import org.modelmapper.ModelMapper;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Getter // 반드시 붙여야 함
@Setter // 필수는 아님
public class ItemFormDto {
private Long id; // 상품 코드
@NotBlank(message = "상품명은 필수 항목입니다.")
private String itemNm; // 상품 이름
@NotNull(message = "가격은 필수 항목입니다.")
private int price; // 상품 가격
@NotNull(message = "재고는 필수 항목입니다.")
private int stockNumber; // 재고 수량
private String itemSellStatus;
@NotBlank(message = "상품 설명은 필수 항목입니다.")
private String itemDetail; // 상품 상세 설명
private List<ItemImgDto> itemImgDtoList = new ArrayList<>(); // imgDtoList 관리
private List<Long> itemImgList = new ArrayList<>(); // 이미지들에 대한 번호 관리
private static ModelMapper modelMapper = new ModelMapper(); // 엔티티와 매핑
public Item createItem() {
return modelMapper.map(this, Item.class); // dto를 엔티티로 변환
}
public static ItemFormDto of(Item item) {
return modelMapper.map(item, ItemFormDto.class); // 엔티티를 dto로 변환
}
}
4. ItemController
- ItemFormDto 객체를 model 객체에 담아서 뷰로 전달
@Controller
@RequiredArgsConstructor // @Autowired로도 쓸 수 있음
public class ItemController {
// 웹 페이지로 이동
@GetMapping("/admin/item/new")
public String itemForm(Model model) {
model.addAttribute("itemFormDto", new ItemFormDto());
return "item/itemForm";
}
5. ItemForm View
- 상품 판매 상태를 “판매중” 또는 “품절”로 선택할 수 있게 <select> 태그 이용
<!-- 상품 판매 상태는 판매 중/품절로 나뉜다. -->
<div class="form-group">
<select th:field="*{itemSellStatus}" class="custom-select">
<option value="SELL">판매중</option>
<option value="SOLD_OUT">품절</option>
</select>
</div>
- 상품을 처음 등록할 경우 Controller를 통해 전달 받은 itemFormDtoList가 존재하지 않음
- Thymeleaf builtin 메소드 #numbers.sequence() 를 통해서 1 부터 5 까지 상품이미지명 출력
<!-- #numbers.sequence(start,end)를 이용하여 반복처리. 최대 이미지 등록 갯수는 5개. -->
<div class="form-group" th:each="num: ${#numbers.sequence(1,5)}">
<div class="custom-file img-div">
<!-- 나중에 name에 해당되는 itemImgFile을 들고 날라간다 똑같은 이름이 다섯 번 나타날 때 얘가 어떻게 처리되는지를 봐야 함 -->
<input type="file" class="custom-file-input" name="itemImgFile">
<!-- 몇 번째 상품 이미지인지 표시 -->
<label class="custom-file-label" th:text="상품이미지 + ${num}"></label>
</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">
<!-- 일단 거기에 있는 정보를 다 가지고 올 거야 -->
<input type="file" class="custom-file-input" name="itemImgFile">
<!-- 특정 상품 수정/삭제 시 어떤 이미지가 수정됐는지 알기 위해 상품 이미지의 아이디를 hidden으로 숨겨둔다. -->
<input type="hidden" name="itemImgIds" th:value="${itemImgDto.id}">
<label class="custom-file-label"
th:text="${not #strings.isEmpty(itemImgDto.oriImgName)} ? ${itemImgDto.oriImgName} : '상품이미지' + ${status.index+1}"></label>
</div>
</div>
</div>
- 상품을 처음 등록할 때는 “저장”버튼을 출력
- 상품을 수정할 때는 “수정”버튼을 출력
!--상품아이디가 없는 경우 저장 로직을 호출하는 버튼 보여줌. 원래 form 태그에서 지정해줘야하지만 아이디를 체크해서 없는 경우 fromaction을 이용하여 new로 준다-->
<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>
6. 상품 등록 페이지 화면
'Web & Android > 스프링 부트 쇼핑몰 프로젝트 with JPA' 카테고리의 다른 글
[스프링 부트 쇼핑몰 프로젝트 with JPA] 7. 상품 수정 (0) | 2023.10.16 |
---|---|
[스프링 부트 쇼핑몰 프로젝트 with JPA] 6-2. [상품 등록] Controller, Service, Repository (0) | 2023.10.15 |
[스프링 부트 쇼핑몰 프로젝트 with JPA] 5. Entity 공통 속성 공통화(Auditing) (0) | 2023.10.15 |
[스프링 부트 쇼핑몰 프로젝트 with JPA] 4. 페이지 권한 설정 (0) | 2023.10.15 |
[스프링 부트 쇼핑몰 프로젝트 with JPA] 3. 로그인/로그아웃 (0) | 2023.10.15 |