SpringBoot 프로젝트

Spring Boot - 리뷰 CRUD기능 구현하기 (2)

orin602 2024. 11. 19. 13:35

Controller - 리뷰 메인화면 호출

// 리뷰 메인 화면

@GetMapping("/review")

public String reviewMain(Model model, HttpSession session) {

     List<Review> reviews = reviewService.getAllReview();

 

     model.addAttribute("reviews", reviews);

 

     return "review/reviewMain";

}

 

  • @GetMapping("/review") 메서드는 메인 페이지를 요청할 때 호출됩니다.
  • 리뷰 목록 조회: reviewService.getAllReview() 메서드를 통해 전체 리뷰 목록을 가져오고, 이를 reviews라는 이름으로 모델에 추가하여 템플릿에서 사용할 수 있도록 합니다.

 

Review Main 페이지 ( reviewMain.html )

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<title>Review Main Page</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11.4.10/dist/sweetalert2.min.css">
<link rel="stylesheet" th:href="@{/css/review.css}">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.4.10/dist/sweetalert2.min.js"></script>
<script th:src="@{/js/review.js}"></script>
</head>
<body>
<th:block th:insert="~{include/header}"></th:block>
<div class="review-main-container">
	<h1>리뷰</h1>
	<h3>다른 회원이 작성한 리뷰를 읽거나, 나만의 리뷰를 작성해 보세요.</h3>
	<a class="review-write-btn" th:href="@{/review-write}">리뷰 작성</a>
	<div class="review-list">
		<h2>리뷰 목록</h2>
		<div class="search-container">
	        <form id="search_review" method="get">
	            <select name="searchType">
	                <option value="search_id">ID</option>
	                <option value="search_title">제목</option>
	            </select>
	            <input type="search" id="search" name="keyword" placeholder="검색어를 입력하세요." />
	            <button type="button" onclick="search_review()">검색</button>
	        </form>
	    </div>
		<div th:if="${reviews.isEmpty()}">
		    <p>작성된 리뷰가 없습니다.</p>
		</div>
		<div th:unless="${reviews.isEmpty()}">
			<div class="review-items">
			    <div class="review-box" th:each="review : ${reviews}">
			    	<a th:href="@{/review_detail(review_seq=${review.review_seq})}">
				        <th:block th:if="${#lists.isEmpty(review.uploadedImages)}">
				        	<img th:src="@{/images/no_img.jpg}" alt="이미지 없음" style="width:200px; height:200px;">
				        </th:block>
				        <th:block th:unless="${#lists.isEmpty(review.uploadedImages)}">
				            <div th:with="firstImage=${review.uploadedImages[0]}">
				            	<img th:src="@{${firstImage}}" th:alt="${review.title}" style="width:200px; height:200px;">
				            </div>
		        		</th:block>
	        		</a>
			        <h4>제목 : <span th:text="${review.title}"></span></h4>
			        <h4>작성자 : <span th:text="${review.member.id}"></span></h4>
			        <h4>조회수 : <span th:text="${review.viewCount}"></span> / 
			        추천수 : <span th:text="${review.recoCount}"></span></h4>
			    </div>
			</div>
		</div>
		
	</div>
</div>
</body>
</html>

 

리뷰작성

<a class="review-write-btn" th:href="@{/review-write}">리뷰 작성</a>를 누르면 /review-write URL로 이동합니다.

Controller - 리뷰 작성 화면 호출

// 리뷰 작성 화면

@GetMapping("/review-write")

public String reviewWriteView(Model model, HttpSession session) {

     Member loginUser = (Member)session.getAttribute("loginUser");

 

      if(loginUser == null) {

            model.addAttribute("message", "로그인 페이지로 이동");

            model.addAttribute("text", "리뷰 작성은 로그인 후 이용 가능합니다.");

            model.addAttribute("messageType", "info");

 

            return "login/login";

      }

 

      return "review/reviewWrite";

}

세션에서 로그인된 사용자의 정보를 확인합니다. 로그인되지 않은 경우, 사용자에게 로그인 페이지로 이동하라는 메시지를 출력하며, 리뷰 작성 페이지에 접근하지 못하도록 합니다.

로그인되지 않은 경우

Review Write 페이지 ( reviewWrite.html )

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<title>Review Write</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11.4.10/dist/sweetalert2.min.css">
<link rel="stylesheet" th:href="@{/css/review.css}">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.4.10/dist/sweetalert2.min.js"></script>
<script th:src="@{/js/review.js}"></script>
</head>
<body>
<th:block th:insert="~{include/header}"></th:block>
<div class="review-write-container">
	<h1>리뷰 작성</h1>
	<div class="review-write-content">
		<form class="review-write-form" id="review-write-form" method="post" enctype="multipart/form-data">
			 <input type="text" id="title" name="title" placeholder="제목을 입력해주세요." />
            <textarea rows="10" cols="20" name="content" id="content" placeholder="내용을 입력해주세요."></textarea>
            <label for="uploadFile">이미지 업로드 (선택 사항):</label>
            <input class="uploadFile" type="file" name="uploadFile" id="uploadFile" multiple>
		</form>
	</div>
	<button class="write-btn" type="button" onclick="window.location.href='/review'">목록으로</button>
	<button class="write-btn" type="button" onclick="review_write()">리뷰 작성</button>
</div>
</body>
</html>

 

  • 리뷰 작성 페이지(reviewWrite.html)에서는 사용자가 리뷰 제목과 내용을 입력하고, 선택적으로 이미지를 업로드할 수 있습니다.
  • 이 폼은 POST 방식으로 /review-write-action 경로에 제출되며, 파일 업로드를 위해 enctype="multipart/form-data" 속성이 설정되어 있습니다.

 

로그인을 한 경우

리뷰 작성 JavaScript 함수 - review_write()

function review_write() {

     if($("#title").val() == "") {

            swal.fire({

                  title: '제목을 입력해주세요.',

                  text: '제목은 필수 입력 항목 입니다.',

                  icon: 'warning',

                  confirmButtonText: '확인'

            });

            $("#title").focus();

            return false;

      } else if($("#content").val() == "") {

            swal.fire({

                  title: '내용을 입력해주세요.',

                  text: '내용은 필수 입력 항목 입니다.',

                  icon: 'warning',

                  confirmButtonText: '확인'

            });

            $("#content").focus();

            return false;

      } else {

            swal.fire({

                  title: '리뷰 작성 성공!',

                  text: '리뷰 목록 페이지로 이동합니다.',

                  icon: 'success',

                  confirmButtonText: '확인'

            }).then((result) => {

                  if (result.isConfirmed) {

                        $("#review-write-form").attr("action", "/review-write-action").submit();

                  }

            });

      }

}

 

  • 필수 입력 항목이 비어있을 경우 경고 메시지를 표시하고 입력을 유도합니다.
  • 입력이 올바르면 SweetAlert2를 사용하여 작성 성공 메시지를 표시하고, review-write-form을 /review-write-action 경로로 제출합니다.

필수 입력 항목이 비어있을 경우

 

제목, 내용 입력 후 성공 메세지

리뷰 작성 처리 Controller 메서드 작

// 리뷰 작성 처리
	@PostMapping("/review-write-action")
	public String reviewWriteAction(Review vo, Model model, HttpSession session,
			@RequestParam("uploadFile") MultipartFile[] uploadFile) {
		Member loginUser = (Member)session.getAttribute("loginUser");
		
		if(loginUser == null) {
			model.addAttribute("message", "로그인 페이지로 이동");
			model.addAttribute("text", "리뷰 작성은 로그인 후 이용 가능합니다.");
			model.addAttribute("messageType", "info");
			
			return "login/login";
		}
		vo.setMember(loginUser); // 로그인한 사용자 정보를 리뷰에 설정
		
		List<String> fileUrls = new ArrayList<>();
		for (MultipartFile file : uploadFile) {
			if (!file.isEmpty()) {
				// 파일 경로
				String uploadDir = "C:/ThisIsJava/SpringBootWorkspace/Book/uploads/";
				// 파일 이름 수정
				String originalName = file.getOriginalFilename();
				String fileExtension = originalName.substring(originalName.lastIndexOf("."));
				String uuid = UUID.randomUUID().toString();
				String fileName = uuid + fileExtension;
				
				try {
					// 파일 저장
					FileUploadUtil.saveFile(uploadDir, fileName, file);
					// URL 생성
					String fileUrl = "/uploads/" + fileName;
					fileUrls.add(fileUrl);
				} catch (IOException e) {
					e.printStackTrace();
                    model.addAttribute("message", "파일 업로드 중 오류 발생");
                    model.addAttribute("messageType", "error");
                    
                    return "review/review-write"; 
				}
				vo.setUploadedImages(fileUrls); // 저장된 파일 경로를 Review 객체에 설정
			}
		}
        reviewService.insertReview(vo);
		
		return "redirect:/review";
	}

 

  • 로그인 확인: 사용자가 로그인 상태인지 세션을 통해 확인하며, 미로그인 상태에서는 로그인 페이지로 리다이렉트합니다.
  • 업로드 파일 처리:
    • 업로드된 파일 배열(MultipartFile[])을 반복하며 각 파일을 지정된 경로에 저장합니다.
    • 각 파일의 이름을 고유하게 설정하여 중복을 피하고, 이를 URL 형태로 변환하여 fileUrls 리스트에 추가합니다.
  • 리뷰 정보 저장:
    • 작성된 리뷰 정보와 업로드된 이미지 경로(fileUrls) 리스트를 Review 객체에 설정하고, reviewService.insertReview(vo)를 호출하여 DB에 저장합니다.
    • 저장 성공 시 리뷰 메인 페이지로 리다이렉트됩니다.

 

 

DB에 저장된 리뷰

 

 ( 이미지 파일 업로드는 따로 작성할게요...  '(_ _)' ;; )