프로젝트

영화 예매 사이트 (MoVieCinema) - 마이페이지 : 나의 예매 내역 조회 항목 ( 컬렉션과 스트림 )

sejin2 2024. 4. 14. 19:20

컨트롤러 - 페이징 처리도 같이 해준다 ! 

서비스

public Page<Ticket> getMyTickets(Long memberId, Pageable pageable, Model model) {
        
Page<Ticket> myTicketList = ticketRepository.findAllByMemberId(memberId, pageable);

  int nowPage = myTicketList.getPageable().getPageNumber();
  int totalPages = myTicketList.getTotalPages();
  int pageGroupSize = 5;
  int currentPageGroup = nowPage / pageGroupSize;
  int startPage = currentPageGroup * pageGroupSize;
  int endPage = (totalPages > 0) ? Math.min(startPage + pageGroupSize - 1, totalPages - 1) : 0;

  model.addAttribute("myTicketList", myTicketList.getContent()); 
  model.addAttribute("nowPage", nowPage);
  model.addAttribute("startPage", startPage);
  model.addAttribute("endPage", endPage);
  model.addAttribute("totalPages", totalPages);

  return myTicketList;
}

myTickets.jsp

<table class="myTicketList">
    <c:forEach var="myTicket" items="${myTicketList}">
        <tr id="myTicketItem">
            <td>${myTicket.ticketId}</td>
            <td>${username[myTicket.memberId]}</td>
            <td>${movieTitle[myTicket.movieId]}</td>
            <td>${myTicket.theaterId}</td>
            <td><fmt:formatNumber value="${myTicket.ticketPrice}" pattern="0원" /></td>
            <td>
                ${fn:substringBefore(myTicket.ticketedDate.toString(), 'T')}
                ${fn:substringAfter(myTicket.ticketedDate.toString(), 'T')}
            </td>
            <td>${myTicket.ticketedTheater}</td>
            <td>${myTicket.ticketedSeat}</td>
            <td>${fn:substringBefore(myTicket.createdAt.toString(), 'T')}</td>
       </tr>
    </c:forEach>
</table>
<div class="myTicketPagination">
  <c:if test="${nowPage > 0}">
      <a href="javascript:loadTicketPage(${nowPage - 1})">이전</a>
  </c:if>
  <c:forEach begin="${startPage}" end="${endPage}" var="page">
      <a href="javascript:loadTicketPage(${page})" class="${page == nowPage ? 'active' : ''}">[${page + 1}]</a>
  </c:forEach>
  <c:if test="${nowPage + 1 < totalPages}">
      <a href="javascript:loadTicketPage(${nowPage + 1})">다음</a>
  </c:if>
</div>

그런데 이렇게 넘겨주면, 기존 댓글 목록처럼 아래와 같 형식으로 나온다. 

Ticket 엔티티에는 영화에 대한 정보가 mvTitle이 아닌 movieId만 가지고 있다. 따라서 내가 원하는 대로 영화 제목을 보여주는 것이 아니라 영화 id를 보여주는 것이다. 
=> 테이블 간의 관계를 맺지 않았기 때문에 현재는 영화 제목을 가지고 올 수 없다는 문제가 있다. 
사용자에게 어떤 영화인지 영화 제목을 보여주어야 하므로 서비스 단에서 처리하도록 한다.                 

먼저 페이징 처리를 하기 위해 반환형을 page<Ticket>으로 해놓은 것을 가공하여 List<Ticket>으로 바꾸어 준다.
이때 streamcollect를 사용한다. 

스트림 API와 collect메서드를 사용하는 주된 이유는 데이터 컬렉션을 효율적이고 선언적으로 처리하기 위해서이다. 
스트림 API를 사용하면 데이터 컬렉션에 대한 복잡한 조작과 변환 작업을 간결하고 이해하기 쉬운 방식으로 표현할 수 있다.                                         

List<Long> movieIdsList는 myTicketList 객체에서 내용을 가지고와 stream을 사용해 각 티켓에서 영화 ID를 추출한다.
그리고 이 ID들을 collect를 사용해 리스트로 수집한다.           
List<Movie> movieList는 찾아온 ID를 매개변수로 하여 해당 ID들에 해당하는 모든 영화 객체들을 리스트로 반환한다.
Map<Long, String> movieTile은 movieList를 스트림으로 만들고 각 영화 객체에서 영화 ID와 제목을 추출하여 Map 구조로 수집한다. 

이러한 과정을 통해 사용자 ID로 사용자의 티켓목록을 찾아서 그 티켓 목록의 영화 ID를 가지고 영화 목록을 조회한 다음 영화 ID와 영화 제목을 매핑하여 사용자에게 보여주고 싶은 데이터 형식 ( 영화 제목 )으로 사용할 수 있게 되었다. 

모델에 담아 다시 jsp로 넘겨준다.

그러면  myTicket.jsp에서 해당 값을 아래와 같은 맵 객체 형태로? 불러와 사용한다. 

예매 내역 조회 화면을 티켓 모양으로 바꿔준다. 내가 구현하고 싶은 화면은 아래와 같다.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>

<section>
    <div class="myTicket">
        <h1>나의 예매 내역</h1>
        <div class="myTicketList">
            <c:forEach var="myTicket" items="${myTicketList}">
                <div class="ticketHeader">
                    <span class="titleAndId">Movie Ticket [${myTicket.ticketId}]</span>
                    <span class="ticketBuyDate">${fn:substringBefore(myTicket.createdAt.toString(), 'T')}</span>
                </div>
                <div class="ticketContent">
                    <div class="ticketInfo">
                        <p class="movieTitle">${movieTitle[myTicket.movieId]}</p>
                        <p class="moviePrice"><fmt:formatNumber value="${myTicket.ticketPrice}" pattern="0원" /></p>
                    </div>
                    <div class="TicketDetailInfo">
                        <p class="movieDetailInfo">
                            상영일자 : ${fn:substringBefore(myTicket.ticketedDate.toString(), 'T')}
                            ${fn:substringAfter(myTicket.ticketedDate.toString(), 'T')}~
                            ${fn:substringAfter(myTicket.ticketedDate.plusMinutes(movieRuntimes[myTicket.movieId]).toString(), 'T')} |
                            런타임: ${movieRuntimes[myTicket.movieId]} |
                            상영관: ${myTicket.theaterId} |
                            좌석번호: ${myTicket.ticketedSeat} |
                            인원: ${fn:length(fn:split(myTicket.ticketedSeat, ','))}명
                        </p>
                    </div>
                </div>
            </c:forEach>
        </div>
        <div class="myTicketPagination">
            <c:if test="${nowPage > 0}">
                <a href="javascript:loadTicketPage(${nowPage - 1})">이전</a>
            </c:if>
            <c:forEach begin="${startPage}" end="${endPage}" var="page">
                <a href="javascript:loadTicketPage(${page})" class="${page == nowPage ? 'active' : ''}">[${page + 1}]</a>
            </c:forEach>
            <c:if test="${nowPage + 1 < totalPages}">
                <a href="javascript:loadTicketPage(${nowPage + 1})">다음</a>
            </c:if>
        </div>
    </div>
</section>

css

/*나의 예매 내역*/
.myTicket h1 {
    text-align: center;
    margin-top: 20px;
}

.myTicketList {
    max-width: 1000px;
    margin: 0 auto;
    border: 2px solid black;
}

.ticketHeader {
    background-color: #b91c1c;
    color: white;
    padding: 8px 16px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-top-left-radius: 10px;
    border-top-right-radius: 10px;
}

.ticketHeader .titleAndId{
    font-size: 1.25rem;
    font-weight: bold;
}

.ticketHeader .ticketBuyDate{
    font-size: 0.875rem;
}

.ticketContent{
    padding: 16px;
    background-color: white;
    color: black;
    border-bottom-left-radius: 10px;
    border-bottom-right-radius: 10px;
    margin-bottom: 15px;
}

.ticketContent .ticketInfo{
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 16px;
}

.ticketContent .ticketInfo .movieTitle {
    font-size: 1.5rem;
    font-weight: bold;
}

.ticketContent .ticketInfo .ticketDetailInfo{
    font-size: 1.2rem;
}

.ticketContent .ticketDetailInfo .movieDetailInfo{
    text-align: left;
}

.ticketContent .ticketInfo .moviePrice {
    font-size: 1.25rem;
}

위에 런타임도 표시해주었는데, 런타임으로 상영 종료 시간도 표시해주었다. 
런타임도 MOVIE 엔티티에만 있고, TICKET 엔티티에는 없는 정보이므로, 해당 movieId로 런타임을 조회해오는 작업을 해주어야한다.  
위에서 movieId로 movieTitle을 가지고 온 것과 동일한 방법으로 하면 된다.

 이 부분은 동일하게 하고, 

movieId로 런타임을 조회해오는 코드를 추가해준다.

모델에 담아서 jsp로 넘겨주면, 

 

컬렉션 ( Collection )

: 컬렉션은 여러 개의 데이터를 모아서 저장하는 자료 구조를 의미한다. 자바에서는 java.util.Collection 인터페이스를 통해 컬렉션들을 정의하고 있으며, 이 인터페이스는 List, Set, Queue와 같은 여러 하위 인터페이스와 구현체들을 포함한다. 
- List : 순서가 있는 컬렉션으로, 중복을 허용한다. 
- Set : 중복을 허용하지 않는 컬렉션이다. 
컬렉션을 사용하면 데이터를 추가, 삭제, 검색, 순회하는 등의 기본적인 작업을 수행할 수 있다. 

스트림 ( Stream ) 

: 스트림은 컬렉션의 데이터를 편리하게 처리할 수 잇는 추상화된 개념이다. 스트림 API를 통해 선언적으로 컬렉션 데이터를 처리할 수 있으며, 데이터의 변환, 필터링, 그룹화 등 복잡한 조작을 쉽고 간결하게 표현할 수 있다. 
복잡한 데이터 처리를 더 빠르고 읽기 쉬운 코드로 작성할 수 있지만, 스트림은 한 번 사용하고 나면 재사용 할 수 없다.