MovieReview 프로젝트 (4) (영화 등록 Register dto / service / controller)
MovieReview 프로젝트 (4) (영화 등록 Register dto / service / controller)
MovieReview 프로젝트 (3) (파일 업로드) MovieReview 프로젝트 (3) (파일 업로드) MovieReview 프로젝트 (2) (@Query, 효율적 Join) MovieReview 프로젝트 (2) (@Query, 효율적 Join) MovieReview 프로젝트 (1) (MovieReview 프로젝
soohykeee.tistory.com
앞서 등록처리는 모두 완료가 되었다. 이제 등록된 Movie 를 list로 보여지도록 해줘야 한다. 목록페이지에는 영화의 제목, 이미지, 평균 평점 등과 같은 정보들을 표시해줘야한다. 이를 위해서 이전에 guestbook에서 만들었던 PageResultDTO, PageRequestDTO를 dto 디렉토리 하위에 추가해줘야 한다.
또한 앞서 MovieRepository에서 getListPage(Pageable pageable) 메소드에 Query를 사용하여 영화와 평점 데이터가 보여지도록 이미 작성했기에, 이를 이용하는 MovieServie, MovieServieImpl만 작성해주면 된다.
MovieServie / MovieServieImpl
우선 화면에 영화 평점, 리뷰개수를 표시해야하기에 MovieDTO에 이를 담아주기 위한 파라미터를 추가해준다.
package com.example.moviereview.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MovieDTO {
private Long mno;
private String title;
@Builder.Default
private List<MovieImageDTO> imageDTOList = new ArrayList<>();
// 영화의 평균 평점
private double avg;
// 리뷰 수 jpa count()
private int reviewCnt;
private LocalDateTime regDate;
private LocalDateTime modDate;
}
dtoToEntity를 이용하였던 것처럼 이의 역순으로 EntityToDTO도 default 메소드로 MovieServie 인터페이스에 추가해줘야 한다.
entitiesToDTO() 메소드는 다음과 같은 파라미터를 받는다.
- Movie Entity
- List<MovieImage> Entity -> movieImage는 다수일 수 있기에 List로 받아준다.
- Double 타입의 평점 평균
- Long 타입의 리뷰 개수
package com.example.moviereview.service;
import com.example.moviereview.dto.MovieDTO;
import com.example.moviereview.dto.MovieImageDTO;
import com.example.moviereview.dto.PageRequestDTO;
import com.example.moviereview.dto.PageResultDTO;
import com.example.moviereview.entity.Movie;
import com.example.moviereview.entity.MovieImage;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public interface MovieService {
Long register(MovieDTO movieDTO);
PageResultDTO<MovieDTO, Object[]> getList(PageRequestDTO requestDTO);
default MovieDTO entitiesToDTO(Movie movie, List<MovieImage> movieImages, Double avg, Long reviewCnt) {
MovieDTO movieDTO = MovieDTO.builder()
.mno(movie.getMno())
.title(movie.getTitle())
.regDate(movie.getRegDate())
.modDate(movie.getModDate())
.build();
List<MovieImageDTO> movieImageDTOList = movieImages.stream().map(movieImage -> {
return MovieImageDTO.builder()
.imgName(movieImage.getImgName())
.path(movieImage.getPath())
.uuid(movieImage.getUuid())
.build();
}).collect(Collectors.toList());
movieDTO.setImageDTOList(movieImageDTOList);
movieDTO.setAvg(avg);
movieDTO.setReviewCnt(reviewCnt.intValue());
return movieDTO;
}
default Map<String, Object> dtoToEntity(MovieDTO movieDTO) {
//... 생략
}
}
MovieServieImpl에서 getList()는 아래와 같이 구현한다.
@Override
public PageResultDTO<MovieDTO, Object[]> getList(PageRequestDTO requestDTO) {
Pageable pageable = requestDTO.getPageable(Sort.by("mno").descending());
Page<Object[]> result = movieRepository.getListPage(pageable);
Function<Object[], MovieDTO> fn = (arr -> entitiesToDTO(
(Movie) arr[0],
(List<MovieImage>) (Arrays.asList((MovieImage) arr[1])),
(Double) arr[2],
(Long) arr[3]
));
return new PageResultDTO<>(result, fn);
}
MovieController / list.html (목록화면)
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:replace="~{/layout/basic :: setContent(~{this::content} )}">
<th:block th:fragment="content">
<h1 class="mt-4">Movie List Page
<span>
<a th:href="@{/movie/register}">
<button type="button" class="btn btn-outline-primary">REGISTER
</button>
</a>
</span>
</h1>
<form action="/movie/list" method="get" id="searchForm">
<input type="hidden" name="page" value="1">
</form>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Picture</th>
<th scope="col">Review Count</th>
<th scope="col">AVG Rating</th>
<th scope="col">Regdate</th>
</tr>
</thead>
<tbody>
<tr th:each="dto : ${result.dtoList}" >
<th scope="row">
<a th:href="@{/movie/read(mno = ${dto.mno}, page= ${result.page})}">
[[${dto.mno}]]
</a>
</th>
<td><img th:if="${dto.imageDTOList.size() > 0 && dto.imageDTOList[0].path != null }"
th:src="|/display?fileName=${dto.imageDTOList[0].getThumbnailURL()}|" >[[${dto.title}]]</td>
<td><b>[[${dto.reviewCnt}]]</b></td>
<td><b>[[${dto.avg}]]</b></td>
<td>[[${#temporals.format(dto.regDate, 'yyyy/MM/dd')}]]</td>
</tr>
</tbody>
</table>
<ul class="pagination h-100 justify-content-center align-items-center">
<li class="page-item " th:if="${result.prev}">
<a class="page-link" th:href="@{/movie/list(page= ${result.start -1})}" tabindex="-1">Previous</a>
</li>
<li th:class=" 'page-item ' + ${result.page == page?'active':''} " th:each="page: ${result.pageList}">
<a class="page-link" th:href="@{/movie/list(page = ${page})}">
[[${page}]]
</a>
</li>
<li class="page-item" th:if="${result.next}">
<a class="page-link" th:href="@{/movie/list(page= ${result.end + 1} )}">Next</a>
</li>
</ul>
<script th:inline="javascript">
</script>
</th:block>
</th:block>
지금 책에서 mariaDB를 사용하는 방식과 다르게 H2 db를 사용하고있는데, 이에 제대로 movieImage로 cast되지 않는 문제가 발생했다. 해당 현상을 고치기위해 며칠 구글링과 노력을 해봤지만 계속 오류가 발생하여, 임시방편으로 movie에 대표이미지 하나만 list에 보이도록 하는것이 힘들어 모든 이미지가 보이도록 수정하였다. 추후에 방법을 알게되면 수정해놓을 예정이다.
package com.example.moviereview.repository;
import com.example.moviereview.entity.Movie;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface MovieRepository extends JpaRepository<Movie, Long> {
@Query("select m, mi, avg(coalesce(r.grade, 0)), count(distinct r) " +
"from Movie m " +
"left outer join MovieImage mi on mi.movie = m " +
"left outer join Review r on r.movie = m " +
"group by m, mi")
Page<Object[]> getListPage(Pageable pageable);
/*@Query("select m, min(mi.inum), min(mi.imgName), mi.path, min(mi.uuid), avg(coalesce(r.grade,0)), count(distinct r.reviewnum), m.regDate " +
"from Movie m " +
"left outer join MovieImage mi on mi.movie = m " +
"left outer join Review r on r.movie = m " +
"group by m.mno")
Page<Object[]> getListPage(Pageable pageable);*/
// 특정 영화의 모든 이미지와 리뷰를 가져오는 쿼리
@Query("select m, mi, avg(coalesce(r.grade,0)), count(r) " +
"from Movie m " +
"left outer join MovieImage mi on mi.movie = m " +
"left outer join Review r on r.movie = m " +
"where m.mno =:mno " +
"group by mi")
List<Object[]> getMovieWithAll(Long mno);
}
아래와 같이 list가 출력이 되는데, 원래 하려던 목록페이지라면 영화 리스트가 아래처럼 여러개 출력되면 안되고 1개만 출력이 되어야한다.