[스프링 부트 쇼핑몰 프로젝트 with JPA] 6-2. [상품 등록] Controller, Service, Repository

2023. 10. 15. 10:36·Web & Android/스프링 부트 쇼핑몰 프로젝트 with JPA
이 내용은 스프링 부트 쇼핑몰 프로젝트 with JPA 책을 학습한 내용입니다.

1. application.properties 설정 추가

  • 파일 크기 및 경로 지정 (경로는 프로젝트 외부 디렉토리에 저장함)
##########################
# 파일 업/다운로드 설정
##########################
# 파일 한 개당 최대 사이즈
spring.servlet.multipart.max-file-size=20MB
# 요청 당 최대 파일 크기
spring.servlet.multipart.max-request-size=100MB
# 상품 이미지 업로드 경로
itemImgLocation=D:/shop/item
# 리소스 업로드 경로
uploadPath=file:///D:/shop/

2. WebMvcConfigurer 인터페이스

  • addResourceHandlers 메소드를 오버라이딩하여 파일 업로드 경로 지정
  • "${uploadPath}" - application.properties 에 설정한 "uploadPath" 프로퍼티 값
  • " /images/** " 패턴의 URL 은 uploadPath 폴더를 기준으로 탐색
package kr.spring.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 {

    // 이미지/파일을 업로드하는 진짜 경로를 application properties에 uploadPath로 저장해놨는데 그걸 가져오게 하기 위함
    @Value(value = "${uploadPath}")
    private String uploadPath;  // 해당 경로를 사용할 수 있음

    @Override
    // registry를 등록해서 쓸 수 있게 함
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
         registry.addResourceHandler("/images/**") // 접근을 이 주소로 하게 함
                 .addResourceLocations(uploadPath); // 실제로는 여기인데 (위에 처럼함)
    }
}

3. 상품 이미지 파일 (File)Service

  • FileService.java
    • 이미지 파일 저장 로직을 담당할 Service 객체
    • 파일 저장은 DB 에 저장되는 것이 아니기 때문에 Repository 필요 없음 (FileOutputStream 가 대신함)
    • 이미지 파일 업로드
    • UUID(Universally Unique IDentifier) - 서로 다른 개체들을 구별하기 위한 클래스
    • FileOutputStream 클래스를 이용하여 파일을 저장
package kr.spring.item.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.UUID;

@Service
// 파일을 만들고 지우는 일을 함
@Slf4j
public class FileService {

    // 특정 폴더를 잡고 올리고 싶지만, 파일 명칭이 충돌이 날 수 있기 때문에 파일 명칭이 충돌이 나지 않게 하는 메소드
    public String uploadFile(String uploadPath, String oriFileName, byte[] fileData) throws IOException {

        UUID uuid = UUID.randomUUID();  // 임의의 UUID 자동으로 생성
        // file의 확장자를 떼기 위함
        // 요즘 파일은 중간중간에도 .을 찍기 때문에 맨 마지막에 있는 확장자만 지운다.
        String extension = oriFileName.substring(oriFileName.lastIndexOf(".")); // 마지막 .을 기준으로 문자열 가져옴
        // uuid는 문자열이 아니니까 toString 꼭 해줘야 함 거기에 확장자만 붙여주면 됨
        String savedFileName = uuid.toString() + extension;
        // 실제 파일 경로 -> D://어쩌고~ / uuid로 만든 파일 이름.extension으로 저장됨
        String fileUploadUrl = uploadPath + "/" + savedFileName;
        FileOutputStream fos = new FileOutputStream(fileUploadUrl); // 출력
        // 위에서 받아온 fileData를 써줌
        fos.write(fileData);
        fos.close();

        return savedFileName;

    }

    // 파일을 지우는 동작
    public void deleteFile(String filePath) {
        // 파일을 지우기 위해서는 file 객체 필요
        File deleteFile = new File(filePath);

        // 지우려는 파일 존재하면
        if(deleteFile.exists()) {
            deleteFile.delete();
            log.info("파일을 삭제했습니다.");
        } else {
            log.info("파일이 존재하지 않습니다.");
        }
    }
}

4. 상품 이미지 정보 Repository

package kr.spring.item.repository;

import kr.spring.item.entity.ItemImg;
import org.springframework.data.jpa.repository.JpaRepository

public interface ItemImgRepository extends JpaRepository<ItemImg, Long> {

}

5. 상품 이미지 Service

  • 상품 이미지 업로드, 상품 이미지 정보 저장 Service
  • 클래스 생성 및 DI
  • application.properties 에 설정한 itemImgLocation 값을 가져옴
  • 상품 이미지 업로드 - FileService
  • 상품 이미지 정보 저장 - itemImgRepository
@Service
@RequiredArgsConstructor // 이걸 사용하면 autowired를 안해도 됨
@Transactional //그림을 세 개를 올리다가 네 개까지 갔는데 하나만 오류가 날 경우 다 취소하고 다시 해야함. 이게 transaction
public class ItemImgService {

    // application properties에 설정해놓은 itemImgLocation를 가져와서 service에서 uploadfile 할 때 사용
    @Value(value = "${itemImgLocation}")
    private String itemImgLocation;

    private final ItemImgRepository itemImgRepository;

    private final FileService fileService;
}
  • 상품 이미지 저장 (내부에 사진 업로드 포함)
  • if (!StringUtils.isEmpty(oriImgName))상품 이미지가 존재하지 않는다면 건너 뜀존재한다면 fileService.uploadFile() 메소드로 파일 업로드 후 UUID 를 통해 변경된 파일명 return
  • updateItemImg 메소드와 itemImgRepository.save() 메소드를 이용해서 상품 이미지 정보 저장
// 이미지 저장
    public void saveItemImg(ItemImg itemImg, MultipartFile itemImgFile) throws IOException {

        // 파일을 받아온 시점에 보면 multiopart 안에 오리지날 이름 등 파일 정보가 다 들어있는데 일단 저건 원래 이름을 가지고 오는 것
        // 왜냐면 DB에 넣을 때는 원래 이름을 알아야 하기 때문
        String oriImgName = itemImgFile.getOriginalFilename();
        // fileService에서 만든 imgName임
        String imgName = "";
        // 이미지 경로
        String imgUrl = "";

        // 원래 경로가 값이 비어있는지 타임리프 유틸을 이용해서 확인
        if(!StringUtils.isEmpty(oriImgName)) {
            // 진짜 이미지 이름 받아옴
            // 파일의 정보는 itemImgFile에 다 있으니까 이걸 byte배열로 가져옴
            imgName = fileService.uploadFile(itemImgLocation, oriImgName, itemImgFile.getBytes());
            imgUrl = "/images/item/" + imgName;
        }

        // 실제 상품 이미지 저장
        itemImg.updateItemImg(oriImgName, imgName, imgUrl);
        // 이미지만 저장 그러므로 아이템도 저장해줘야함
        itemImgRepository.save(itemImg);
    }

6. 상품 Service

  • 클래스 생성 및 DI
  • 상품 저장 - itemRepository
  • 상품 이미지 저장 - itemImgService
@Service
@RequiredArgsConstructor
@Transactional // 서비스 등록하다가 깨지면 지금 했던 일을 다시 해야하기 때문에
@Slf4j
public class ItemService {

    private final ItemRepository itemRepository;
    private final ItemImgRepository itemImgRepository;
}
  • 상품 저장
  • ItemFormDto 객체를 DB 에 저장하기 위해 Item 객체로 변환
  • 상품의 첫 번째 이미지는 대표 사진으로 설정
// 아이템 저장
    // 아이템을 등록했으니까 아이템 아이디가 넘어왔을 것
    // itemDto 값을 넘겨 받고, multipart 형식으로 되어있는 리스트를 받아옴
    public Long saveItem(ItemFormDto itemFormDto, List<MultipartFile> itemImgeFileList) throws IOException {

        // DTO를 entity로 바꾼다. createItem에서 mapper로 바꿨으니까
        Item item = itemFormDto.createItem();
        itemRepository.save(item);

        // 그림 저장하기
        // 내가 저장한 이미지의 갯수만큼 돌림
        for(int i=0; i<itemImgeFileList.size(); i++) {
            ItemImg itemImg = new ItemImg();
            // 기존에 있던 아이템의 id값을 세팅해야한다. 영속성 영역에 떠있기 때문에 지금 값이 채워져있는 상태가 돼서 그냥 값을 가져오기만 하면 됨
            itemImg.setItem(item);

            // 첫 번째 이미지면 대표 이미지로 씀
            if(i == 0) {
                itemImg.setRepimgYn("Y");
            } else {
                itemImg.setRepimgYn("N");
            }

            // 실제로 DB에 집어넣어야한다 파일 리스트에 있는 애들 중 i번째에 있는 애들을 꺼내서 등록을 해줌
            itemImgService.saveItemImg(itemImg, itemImgeFileList.get(i));
        }

        // 아이디를 반환함
        return item.getId();
    }

7. 상품 등록 Controller

  • 상품 등록 페이지 접근 (GetMapping)
@Controller
@RequiredArgsConstructor // @Autowired로도 쓸 수 있음
public class ItemController {

    // @Autowired ->  @RequiredArgsConstructor를 써도 됨 (final 붙여야함)
    private final ItemService itemService;

    // 웹 페이지로 이동
    @GetMapping("/admin/item/new")
    public String itemForm(Model model) {
        model.addAttribute("itemFormDto", new ItemFormDto());

        return "item/itemForm";
    }
}
  • 상품 등록 (PostMapping)
  • 입력값 검증 및 에러 메시지 표시 참고
  • POST 입력으로 들어온 이미지 파일 (name = "itemImgFile")을 MultipartFile 객체로 받음
  • 입력값이 비정상이거나, 첫 번째 상품 이미지를 지정하지 않았으면 다시 상품 등록 페이지로 돌아감
  • 입력값이 정상이면 itemService.saveItem(itemFormDto, itemImgFileList) 수행
// 웹 페이지에서 정보를 가져와 저장
    @PostMapping("/admin/item/new")
    // itemFormDto에 에러 발생 시 메시지 주려고 처리해놨으므로 @Valid 해줌
    public String itemNew(@Valid ItemFormDto itemFormDto, BindingResult bindingResult,
                          Model model, @RequestParam("itemImgFile") List<MultipartFile> itemImgFileList) {

        // 에러가 있는 경우 상품등록으로 다시 가기
        if(bindingResult.hasErrors()) {
            return "item/itemForm";
        }

        // 0번째 이미지 리스트가 값이 비었고 itemFormDto의 아이디가 없다면
        if(itemImgFileList.get(0).isEmpty() && itemFormDto.getId() == null) {
            model.addAttribute("errorMessage", "첫 번째 상품 이미지는 필수입니다.");
            return "item/itemForm";
        }

        try {
            itemService.saveItem(itemFormDto, itemImgFileList);
        } catch (IOException e) {
            e.printStackTrace();
            model.addAttribute("errorMessage", "상품 등록 중에 오류 발생");
            return "item/itemForm";
        }

        return "redirect:/";
    }

8. 상품 등록 동작 과정

① "ADMIN" 권한을 가진 아이디로 상품 등록 페이지 Get 요청

② Item Controller 에서 상품 등록 페이지를 반환하면서 itemFormDto 객체도 넘김

③ 상품 등록 페이지에서 상품 정보 및 이미지를 입력하고 "저장" (POST 요청)

④ Item Controller 에서 입력값을 검증하고 itemService.saveItem() 메소드를 수행

   - 이 때, 파라미터는 입력받은 itemFormDto 객체와 이미지 정보를 담고있는 itemImgFileList 를 넘김    

⑤ itemService 에서 itemFormDto 객체를 item 엔티티로 변환하고 itemRepository.save() 메소드 수행

⑥ itemService 에서 itemImg 객체를 생성하고 itemImgService.saveItemImg() 메소드 수행        

   - 이 때, 파라미터는 itemImg 객체와 이미지 정보를 담고있는 itemImgFileList.get(i) 객체 하나를 지정

⑦ ItemImgService 에서 상품 이미지가 존재한다면 fileService.uploadFile() 메소드 수행

   - 이 때, 파라미터는 "저장위치", "원래 파일명", "이미지 Byte 파일"

⑧ FileService 에서 UUID 객체를 이용해 파일명을 새로 만들고 FileOutputStream 을 이용하여 저장

⑨ ItemImgService 에서 itemImgRepository.save() 메소드 수행

저작자표시 비영리 변경금지 (새창열림)

'Web & Android > 스프링 부트 쇼핑몰 프로젝트 with JPA' 카테고리의 다른 글

[스프링 부트 쇼핑몰 프로젝트 with JPA] 8-1. [상품 관리] 상품 목록 조회 Querydsl  (0) 2023.10.16
[스프링 부트 쇼핑몰 프로젝트 with JPA] 7. 상품 수정  (0) 2023.10.16
[스프링 부트 쇼핑몰 프로젝트 with JPA] 6-1. [상품 등록] Entity, DTO, View  (0) 2023.10.15
[스프링 부트 쇼핑몰 프로젝트 with JPA] 5. Entity 공통 속성 공통화(Auditing)  (0) 2023.10.15
[스프링 부트 쇼핑몰 프로젝트 with JPA] 4. 페이지 권한 설정  (0) 2023.10.15
'Web & Android/스프링 부트 쇼핑몰 프로젝트 with JPA' 카테고리의 다른 글
  • [스프링 부트 쇼핑몰 프로젝트 with JPA] 8-1. [상품 관리] 상품 목록 조회 Querydsl
  • [스프링 부트 쇼핑몰 프로젝트 with JPA] 7. 상품 수정
  • [스프링 부트 쇼핑몰 프로젝트 with JPA] 6-1. [상품 등록] Entity, DTO, View
  • [스프링 부트 쇼핑몰 프로젝트 with JPA] 5. Entity 공통 속성 공통화(Auditing)
woojin._.
woojin._.
여러가지 개발을 해보며 발생하는 이야기들에 대한 블로그입니다:)
  • woojin._.
    Jin's Dev Story
    woojin._.
  • 전체
    오늘
    어제
    • 분류 전체보기 (829)
      • Tools (25)
        • eGovFrame (3)
        • GeoServer (3)
        • QGIS (2)
        • LabelImg (2)
        • Git (6)
        • GitHub (1)
        • Eclipse (7)
        • Visual Studio (1)
      • Web & Android (121)
        • SpringBoot (37)
        • Three.js (2)
        • Spring Data JPA (9)
        • 스프링 부트 쇼핑몰 프로젝트 with JPA (25)
        • Thymeleaf (4)
        • Spring Security (15)
        • Flutter (29)
      • Programming Language (61)
        • JAVA (27)
        • JavaScript (14)
        • Dart (2)
        • Python (15)
        • PHP (3)
      • Database (43)
        • PostgreSQL (32)
        • MYSQL (7)
        • Oracle (3)
        • MSSQL (1)
      • SERVER (17)
        • TCP_IP (3)
        • 리눅스 (7)
        • AWS (7)
      • Coding Test (445)
        • 백준[JAVA] (108)
        • 프로그래머스[JAVA] (260)
        • 알고리즘 고득점 Kit[JAVA] (3)
        • SQL 고득점 Kit[ORACLE] (74)
      • CS 지식 (49)
        • [자료구조] (14)
        • [네트워크] (12)
        • [데이터베이스] (10)
        • [알고리즘] (9)
        • [운영체제] (4)
      • 기타 (6)
      • 자격증 & 공부 (62)
        • 정보처리기사 (2)
        • SQLD (6)
        • 네트워크관리사 2급 (5)
        • 리눅스마스터 1급 (44)
        • 리눅스마스터 2급 (1)
        • ISTQB (3)
        • 시스템보안 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 인기 글

  • 태그

    시큐리티
    스프링 부트 쇼핑몰 프로젝트 with JPA
    스프링부트
    데이터
    자바
    리눅스마스터
    리눅스마스터 1급
    프로그래머스
    python
    DB
    백준
    Flutter
    데이터베이스
    Linux
    programmers
    Oracle
    JPA
    postgresql
    CS
    리눅스
    backjoon
    CS지식
    플러터
    spring
    springboot
    Spring Security
    스프링
    Java
    baekjoon
    pcce 기출문제
  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
woojin._.
[스프링 부트 쇼핑몰 프로젝트 with JPA] 6-2. [상품 등록] Controller, Service, Repository
상단으로

티스토리툴바