• 작성일 : 2022.05.17
  • 작성자 : 황유진

  • 팀원 : 김진영, 박승지, 반현빈, 오성은, 오은현, 황유진
  • 팀장 : 황유진
  • 부팀장 : 오성은
  • GitHub Repository : https://github.com/miro7923/Uno-Mas


개발환경

  • MacBook Air (M1, 2020)
  • OpenJDK 8
  • Spring Tool Suite 4.14.0
  • Spring framework 4.3.1.RELEASE
  • Tomcat 8.5
  • MySQL Workbench 8.0.19


기간

  • 2022.4.13 ~ 2022.5.27


주제

  • 웹 백엔드 수업 중 마지막 과제로 팀 프로젝트를 진행하게 되었다.
  • 조건은 Spring 기반으로 웹 사이트를 제작하는 것이다.
  • 총 팀원은 6명이며, 우리 팀은 1인 가구를 위한 쇼핑몰을 주제로 정했다.
  • 팀 이름으로 정해진 Uno más는 스페인어로 하나 더라는 뜻이다.


진행상황

  • 상품 후기글에 이미지 하나를 첨부할 수 있도록 설계했기 때문에 후기글에 등록되는 이미지의 업로드 및 수정과 삭제가 가능하게 하는 기능을 구현했다. 업로드 자체는 MultipartFile로 쉽게 구현할 수 있었지만 수정 기능을 구현하는 과정이 만만치 않았기 때문에 기록으로 남긴다.

productMapper.xml

<update id="updateReview">
    UPDATE board_review
    SET review_title = #{review_title}, review_content = #{review_content}, review_rating = #{review_rating}, review_image = #{review_image}
    WHERE review_num = #{review_num}
</update>
  • DB 업데이트를 위한 쿼리문에서는 별도의 조건문 없이 입력받은 데이터를 그대로 사용하기 때문에 여기까지 오기 전에 후기글과 함께 업로드 되는 이미지 파일명에 대한 검증을 거친 후 추가 작업이 필요하다.

ProductController.java

@RequestMapping(value = "/modify_review", method = RequestMethod.POST)
public String modifyReviewPOST(HttpServletRequest request, 
        @RequestParam(value = "review_image", required = false) MultipartFile file) throws Exception {
    BoardReviewVO vo = new BoardReviewVO();
    vo.setReview_num(Integer.parseInt(request.getParameter("review_num")));
    vo.setProd_num(Integer.parseInt(request.getParameter("prod_num")));
    vo.setReview_content(request.getParameter("review_content"));
    vo.setReview_rating(Float.parseFloat(request.getParameter("review_rating")));
    vo.setReview_title(request.getParameter("review_title"));
    vo.setUser_num(Integer.parseInt(request.getParameter("user_num")));
		
    String reviewImg = service.getReviewImg(vo.getReview_num());
    String uploadImgName = request.getParameter("uploadImgName");

    // 업로드 된 파일이 있을 때 
    if (!file.isEmpty()) {
        // 기존에 저장된 이미지가 없는 경우 파일이름 생성
        if (reviewImg == null) {
            reviewImg = convertImgName(file.getOriginalFilename(), vo.getReview_num());
        }
        
        // 파일 새로 업로드 
        File targetFile = new File(reviewImgUploatPath, reviewImg);
        FileCopyUtils.copy(file.getBytes(), targetFile);
        vo.setReview_image(reviewImg);
    }
    // 업로드 된 파일이 없을 때
    else { 
        // 기존에 업로드 된 파일이 없거나 글을 수정하며 이미지를 삭제한 경우
        if (uploadImgName.equals("이미지 선택")) {
            if (reviewImg != null) {
                // 기존에 업로드 된 파일이 있으면 서버에서 삭제
                File f = new File(reviewImgUploatPath + File.separator + reviewImg);

                if (f.exists())
                    f.delete();
            }
            
            // 기존 파일명 삭제
            vo.setReview_image(null);
        }
        // 기존에 업로드 된 파일이 있으면 그대로 사용
        else {
            vo.setReview_image(uploadImgName);
        }
    }
		
    service.modifyReview(vo);
		
    return "redirect:/product/product_detail?prod_num=" + vo.getProd_num();
}
  • 매퍼에 후기글에 대한 정보를 보낼 때 BoardReviewVO를 사용해서 보내는데 게시글을 수정할 때 이미지 파일에 대한 정보도 설정해 주지 않으면 VO 속의 이미지 파일명 필드는 null로 남아있는 상태로 매퍼에 전달될 것이다. 그렇게 되면 기존에 서버에 업로드 된 이미지파일이 있는데도 DB 데이터 상에서는 이미지 파일명 정보가 없기 때문에 화면에 출력되지 않는다. 그래서 첨부파일이 포함된 게시글을 수정할 때엔 위와 같이 분기를 나누어 처리해 주게 했다.


구현하며 했던 고민

  • 가장 고민했던 것은 사용자가 글을 수정하면서 이미지를 삭제했는지 아닌지 여부를 확인하는 것이었다. 단순히 file로 넘어온 정보가 있고 없고로 따질 수 없었기 때문이다. file타입의 인풋태그 특성상 게시글을 작성할 때 파일을 업로드 하지 않으면 사진 첨부 영역의 삭제 버튼을 눌러서 이미지를 삭제해도 값이 비어있고 기존에 업로드했던 이미지를 삭제하는 동작을 하지 않아도 값이 비어있다. 즉 게시글을 수정할 때 새로운 이미지를 첨부하지 않는다면 항상 비어있는 상태로 전송되는 것이다.
<c:choose>
    <c:when test="${vo.review_image == null }">
        <input type="text" class="uploadImgName" id="uploadImgName" value="이미지 선택"
            readonly name="uploadImgName"> 
    </c:when>
    <c:otherwise>
        <input type="text" class="uploadImgName" id="uploadImgName" value="${vo.review_image }"
            readonly name="uploadImgName">
    </c:otherwise>
</c:choose>
<label class="site-btn" for="uploadImg" id="uploadBtn">업로드</label>
<button type="button" class="site-btn" onclick="removeImg();">삭제</button>
<input type="file" id="uploadImg" name="review_image" oninput="checkFileName();">
  • 뷰 페이지 UI 디자인을 하며 업로드 하려는 파일명을 출력하는 인풋 태그를 분리해 뒀기 때문에 파일명이 출력되는 인풋 태그의 값에 따라 기존에 업로드 된 이미지 파일의 존재 유무를 확인하도록 설계했다.


마감까지

  • D-10