091

[KINO][JAVA/Spring Boot] 좌석 선정 및 예약: WebSocket 응용(2) 본문

Programming Language/Java

[KINO][JAVA/Spring Boot] 좌석 선정 및 예약: WebSocket 응용(2)

공구일 2026. 3. 28. 04:40
728x90

*이 글은 프로젝트 KINO CINEMA의 개발 내용을 정리해놓은 글입니다.*

2. SeatSelectRequestDTO & SeatStautsResponseDTO->SeatBookingResponseDTO

 

- SeatSelectRequestDTO: 클라이언트에서 서버로 요청하는 데이터로, 이름 그대로 선택된 좌석과 관련된 자료가 넘어옵니다.

package com.cinema.kino.dto;

//import문 생략

@Getter
@Setter
public class SeatSelectRequestDTO {
    private Long screeningId;
    private Long memberId;
    private Long guestId;
    private List<TicketRequest> tickets;

    @Getter @Setter
    public static class TicketRequest {
        private Long seatId;
        private PriceType priceType;
    }
}

 

- SeatBookingResponseDTO: 서버에서 클라이언트로 응답하는 데이터로, 상영정보가 넘어왔을 때 좌석 자리 및 상영 시간과 관련된 것을 보여주기 위한 자료입니다.

package com.cinema.kino.dto;

//import문 생략

@Getter
@AllArgsConstructor
public class SeatBookingResponseDTO {

    private ScreeningInfo screeningInfo; //상영/영화/가격 정보 (단 1번만 전송)
    private List<SeatInfo> seats;        //좌석 목록 배열

    // 팩토리 메서드, 서비스 단에서 호출하기 쉽게 만듦
    public static SeatBookingResponseDTO of(Screening screening, List<ScreeningSeat> screeningSeats, Map<String, Integer> prices) {
        ScreeningInfo info = ScreeningInfo.from(screening, prices);
        List<SeatInfo> seatList = screeningSeats.stream()
                .map(SeatInfo::from)
                .collect(Collectors.toList());
        return new SeatBookingResponseDTO(info, seatList);
    }

    /* ========================
       1. 공통 상영 정보
       ======================== */
    @Getter
    @AllArgsConstructor
    public static class ScreeningInfo {
        private Long theaterId;
        private String theaterName;
        private Long screeningId;
        private String screenName;
        private String screenType;
        private Long movieId;
        private String movieTitle;
        private String ageRating;
        private String posterUrl;
        private LocalDateTime startTime;
        private LocalDateTime endTime;

        // 가격 정보
        private Integer priceAdult;
        private Integer priceYouth;
        private Integer priceSenior;
        private Integer priceSpecial;

        public static ScreeningInfo from(Screening screening, Map<String, Integer> prices) {
            var movie = screening.getMovie();
            var screen = screening.getScreen();
            var theater = screen.getTheater();

            return new ScreeningInfo(
                    theater.getId(), theater.getName(),
                    screening.getId(), screen.getName(), screen.getScreenType().getValue(),
                    movie.getId(), movie.getTitle(), movie.getAgeRating().name(), movie.getPosterUrl(),
                    screening.getStartTime(), screening.getEndTime(),
                    prices.getOrDefault("ADULT", 15000),
                    prices.getOrDefault("YOUTH", 12000),
                    prices.getOrDefault("SENIOR", 10000),
                    prices.getOrDefault("SPECIAL", 10000)
            );
        }
    }

    /* ========================
       2. 개별 좌석 정보
       ======================== */
    @Getter
    @AllArgsConstructor
    public static class SeatInfo {
        private Long seatId;
        private String seatRow;
        private int seatNumber;
        private String status;
        private String seatType;
        private Integer posX;
        private Integer posY;
        private Long memberId;
        private Long guestId;

        public static SeatInfo from(ScreeningSeat ss) {
            var seat = ss.getSeat();
            return new SeatInfo(
                    seat.getId(), seat.getSeatRow(), seat.getSeatNumber(),
                    ss.getStatus().name(), seat.getSeatType().name(),
                    seat.getPosX(), seat.getPosY(),
                    ss.getHeldByMember() != null ? ss.getHeldByMember().getId() : null,
                    ss.getHeldByGuest() != null ? ss.getHeldByGuest().getId() : null
            );
        }
    }
}

-> 팩토리 메서드란 객체 생성자 대신 static 메서드로 처리하는 방식을 사용하고있으며, of를 사용해 여러 개의 매개변수를 입력받아, 하나의 적합한 인스턴스로 집계 및 병합하여 반환하는 메서드입니다. 팩토리 메서드는 생성자가 아니지만, 생성자를 래핑하여 내부에서 대신 호출해주는 프로기 역할을 하는 정적 메서드입니다.

 

- SeatStautsResponseDTO(삭제, 이전 WebSocketController 때 사용): 이전에 사용했던 응답 데이터인데, 같은 자료가 반복적으로 발송되기 때문에 리팩토링할 때 위처럼 분리작업을 진행하였습니다.

/* ========================
서버 → 프론트로 내려주는 좌석 상태
좌석+상영+영화+홀드 정보를 전부 포함하고 있는 Dto
======================== */
package com.cinema.kino.dto;

import com.cinema.kino.entity.ScreeningSeat;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.time.LocalDateTime;
import java.util.Map;

@Getter
@AllArgsConstructor
public class SeatStatusResponseDTO {

    // 좌석 정보
    private Long seatId;
    private String seatRow;
    private int seatNumber;
    private String status;
    private String seatType;
    private Integer posX;
    private Integer posY;

    // 상영 정보
    private Long theaterId;
    private String theaterName;
    private Long screeningId;
    private String screenName;       // 상영관 이름
    private String screenType;
    private Long movieId;
    private String movieTitle;
    private String ageRating;
    private String posterUrl;
    private LocalDateTime startTime;
    private LocalDateTime endTime;

    private Long memberId;         // 회원
    private Long guestId;         //  비회원

    private Integer priceAdult;
    private Integer priceYouth;
    private Integer priceSenior;
    private Integer priceSpecial;

    public static SeatStatusResponseDTO from(ScreeningSeat ss, Map<String, Integer> prices) {
        var seat = ss.getSeat();
        var screening = ss.getScreening();
        var movie = screening.getMovie();
        var screen = screening.getScreen();
        var theater = screen.getTheater();

        return new SeatStatusResponseDTO(
                seat.getId(),
                seat.getSeatRow(),
                seat.getSeatNumber(),
                ss.getStatus().name(),
                seat.getSeatType().name(),
                seat.getPosX(),
                seat.getPosY(),
                theater.getId(),
                theater.getName(),
                screening.getId(),
                screen.getName(),
                screen.getScreenType().getValue(),
                movie.getId(),
                movie.getTitle(),
                movie.getAgeRating().name(),
                movie.getPosterUrl(),
                screening.getStartTime(),
                screening.getEndTime(),
                ss.getHeldByMember() != null ? ss.getHeldByMember().getId() : null,
                ss.getHeldByGuest() != null ? ss.getHeldByGuest().getId() : null,
                prices.getOrDefault("ADULT", 15000), // 기본값은 안전장치
                prices.getOrDefault("YOUTH", 12000),
                prices.getOrDefault("SENIOR", 10000),
                prices.getOrDefault("SPECIAL", 10000)
        );
    }
}

 

 

728x90