#8 [스프링 스터디] 쇼핑몰 만들기 프로젝트 - 상품 이미지 업로드와 thymeleaf

myeonji·2022년 1월 28일
1

쇼핑몰에 상품을 등록하기 위해서는 상품 이미지가 반드시 필요합니다.

쇼핑몰 프로젝트에 상품 이미지 업로드를 구현해보겠습니다.

  1. Entity에 이미지 관련 변수를 추가합니다.
    • 이미지 이름과 조회 경로 변수를 추가합니다.
    • img 파일은 데이터베이스에 저장하지 않기 때문에 String 형식 입니다.
    • 데이터베이스에는 이미지에 대한 정보만 저장합니다.

  1. 상품 등록 html 파일에서 이미지 관련 설정을 합니다.

    • form에는 enctype="multipart/form-data" 속성을 넣습니다. enctype은 form data가 서버로 날아갈 때 해당 데이터가 인코딩 되는 방법을 명시합니다. 주의할 점은 form 요소의 method 속성값이 "post"인 경우에만 가능합니다.
    • multipart/form-data 는 form 요소가 파일이나 이미지를 서버로 전송할 때 주로 사용합니다.
    • input 태그의 type="file" 이고, id와 name은 상품등록 Controller에서 매개변수 명과 동일하게 설정합니다.
<form th:action="@{/item/new/pro}" method="post" enctype="multipart/form-data">
            <div class="row g-3">
                <hr/>
                <div class="col-12">
                    <label for="name" class="form-label">상품 이름</label>
                    <input type="text" class="form-control" id="name" name="name" placeholder="상품 이름을 입력하세요.">
                    <div class="invalid-feedback">
                        Your name is required.
                    </div>
                </div>

                <div class="col-12">
                    <label for="text" class="form-label">상품 설명</label>
                    <input type="text" class="form-control" name="text" id="text" placeholder="상품 설명을 입력하세요.">
                    <div class="invalid-feedback">
                        Input is empty.
                    </div>
                </div>

                <div class="col-12">
                    <label for="price" class="form-label">상품 가격</label>
                    <input type="text" class="form-control" id="price" name="price" placeholder="상품 가격을 입력하세요.">
                    <div class="invalid-feedback">
                        Input is empty.
                    </div>
                </div>

                <div class="col-12">
                    <label for="stock" class="form-label">상품 재고</label>
                    <input type="text" class="form-control" id="stock" name="stock"
                           placeholder="상품 재고를 입력하세요.">
                    <div class="invalid-feedback">
                        Input is empty.
                    </div>
                </div>

                <div class="col-12">
                    <label for="text" class="form-label">상품 이미지 업로드</label>
                    <input type="file" class="form-control" id="imgFile" name="imgFile">
                </div>
  1. 상품 등록 Controller에서 매개변수로 imgFile을 받습니다.
    • 판매자가 등록한 상품과 이미지를 저장하기 위해 itemService의 saveItem에 item과 imgFile을 보냅니다.
    • 파일이 하나면 MultipartFile, 파일이 여러개면 MultipartFile[]을 이용하여 받습니다.
// 상품 등록 (POST) - 판매자만 가능
    @PostMapping("/item/new/pro")
    public String itemSave(Item item, @AuthenticationPrincipal PrincipalDetails principalDetails, MultipartFile imgFile) throws Exception {
        if(principalDetails.getUser().getRole().equals("ROLE_ADMIN") || principalDetails.getUser().getRole().equals("ROLE_SELLER")) {
            // 판매자
            item.setSeller(principalDetails.getUser());
            itemService.saveItem(item, imgFile);

            return "redirect:/main";
        } else {
            return "redirect:/main";
        }
    }
  1. itemService의 saveItem에서 받은 데이터를 처리합니다.
    • MultipartFile 타입으로 받은 imgFile을 getOriginalFilename()으로 업로드된 파일의 이름과 확장자를 oriImgName 변수에 저장합니다.
    • 겹치지 않는 파일명을 만들기 위해 uuid를 이용하여 savedFileName 변수에 유일한(?) 파일명을 저장합니다.
    • UUID는 난수화 시키는 것으로, 현재 시간을 기준으로 하기 때문에 중복이 되지 않습니다.
    • projectPath 변수에 파일이 저장될 경로를 지정합니다.
    • 파일이 저장될 경로 projectPath와 업로드 된 파일 이름인 oriImgName을 File 로 만듭니다.
    • transferTo를 통해 saveFile을 file로 저장합니다.
// 상품 등록
    public void saveItem(Item item, MultipartFile imgFile) throws Exception {

        String oriImgName = imgFile.getOriginalFilename();
        String imgName = "";

        String projectPath = System.getProperty("user.dir") + "/src/main/resources/static/files/";

        // UUID 를 이용하여 파일명 새로 생성
        // UUID - 서로 다른 객체들을 구별하기 위한 클래스
        UUID uuid = UUID.randomUUID();

        String savedFileName = uuid + "_" + oriImgName; // 파일명 -> imgName

        imgName = savedFileName;

        File saveFile = new File(projectPath, imgName);

        imgFile.transferTo(saveFile);

        item.setImgName(imgName);
        item.setImgPath("/files/" + imgName);

        itemRepository.save(item);
    }
  1. 메인페이지에서 상품 사진이 나와야 하기 때문에 main.html에서 thymeleaf를 이용하여 렌더링 합니다.
th:src="@{${item.getImgPath()}}"

이제 메인화면에 가보면 이미지 파일이 잘 등록된 것을 확인할 수 있습니다.

성공입니닷!!

1개의 댓글

comment-user-thumbnail
2023년 1월 10일

선생님 덕분에 이미지 구현 완성했습니다.. 근데 문제가
이미지가 바로 안나오고, 스프링툴에서 refresh 한번 하고 와야 이미지가 나오네요..

답글 달기