#19 좌석 엔티티, API 구현

seojin's 개발블로그·2023년 12월 2일
0

영화 사이트 제작

목록 보기
19/19
post-thumbnail

이전 포스트에서 설계한대로 좌석 엔티티와 API의 실제 구현을 하였다.

📌 좌석 엔티티

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "seat_id", nullable = false)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "schedule_id", referencedColumnName = "schedule_id")
private Schedule schedule;

@Column(name = "seat_row", nullable = false)
private int row;

@Column(name = "seat_col", nullable = false)
private int column;

@Column(name = "price", nullable = false)
private int price;

@Column(name = "reserve_avail", nullable = false)
private boolean reserveAvail;

@Column(name = "soldout", nullable = false)
private boolean soldout;

@CreatedDate
@Column(name = "created_at")
private LocalDateTime createAt;

@LastModifiedDate
@Column(name = "updated_at")
private LocalDateTime updatedAt;

@Builder
public Seat(Schedule schedule, int row, int column, int price) {
	this.schedule = schedule;
	this.row = row;
	this.column = column;
	this.price = price;
	this.reserveAvail = true;
	this.soldout = false;
}

설계에 변경사항 없이 그대로 적용했으며 생성자에서
예매가능 여부와 판매 상태를 지정 해주도록 했다.

📌 API 구현

다른 사람의 이해를 돕기위해 블랙 박스 시퀀스 다이어그램을 그려놓고 구현을 했다.

1. 좌석 정보 저장 Api

dto로 좌석 정보를 입력해 요청
-> 상영 일정 id로 상영일정 검색
-> 해당 상영일정과 행, 열 정보로 좌석을 반복 생성
-> 응답
의 순서로 설계했고 코드 작성 결과는 아래와 같다.

@Transactional
public void saveSeats(SaveSeatRequestDto requestDto){
    
    Schedule schedule = scheduleService.findScheduleById(requestDto.getScheduleId());

    for(int seatRow = 0; seatRow < requestDto.getRow() ;seatRow++ ){
        for(int seatColumn = 0; seatColumn < requestDto.getColumn(); seatColumn++){
            seatRepository.save(requestDto.toEntity(schedule, seatRow, seatColumn));
        }
    }

}

2. 좌석정보 확인 Api

영화 예매시 예매순서는 영화 선택 -> 상영 일정 선택 -> 좌석 선택임을 고려해
상영일정에 속한 좌석을 확인할 수 있는 get api를 생각했다.

@Transactional
public List<ShowSeatsResponseDto> showSeatList(Long scheduleId) {
	List<Seat> seats = seatRepository.findAllByScheduleId(scheduleId);
    return convertToDtoList(seats);
}

private List<ShowSeatsResponseDto> convertToDtoList(List<Seat> seats) {
	return seats.stream()
            .map(this::convertToDto)
            .collect(Collectors.toList());
}

private ShowSeatsResponseDto convertToDto(Seat seat) {

	ShowSeatsResponseDto dto = new ShowSeatsResponseDto();
    dto.setSeatId(seat.getId());
    dto.setRow(seat.getRow());
    dto.setColumn(seat.getColumn());
    dto.setPrice(seat.getPrice());
    dto.setReserveAvail(seat.isReserveAvail());

	return dto;
}

세 부분으로 나누어 작성을 했다.
상영일정에 속한 좌석들을 불러와서 응답을 위한 dto로 변환 해주는 과정을
작성했는데 한가지 메서드에 있는것 보다 역할별로 분할 되어있는것이 내가 보기에도
또 타인이 보기에도 이해하기 쉽다고 생각했다.

3. 좌석 선점 Api

좌석 선택후 결제 전까지 좌석을 선택 불가 상태로 만들어주는 Api이고
여러 좌석을 선택할 수 있도록 여러 좌석을 id값을 수용 가능하게 구현했다.

@Transactional
public void selectSeats(SelectSeatRequestDto requestDto){

	List<Seat> seats = seatRepository.findAllById(requestDto.getSeatIds());

    seats.forEach(seat -> seat.setReserveAvail(false));

    seatRepository.saveAll(seats);
}

좌석 데이터들을 불러와서 예매 가능 칼럼의 값을 변경해준뒤 저장한다.

4. 좌석 판매 Api

좌석의 현재 판매 상태를 변경해주는 Api
선점된 좌석의 예매 확정을 위해 구현을 했다

@Transactional
    
public void soldSeats(SoldSeatsRequestDto requestDto){

    List<Seat> seats = seatRepository.findAllById(requestDto.getSeatIds());
        
    seats.forEach(seat -> seat.setReserveAvail(true));
        
	seatRepository.saveAll(seats);
}

좌석 튜플의 불리언 값을 바꿔주기에 3번 Api와 구현 방법이 동일하다

5. 좌석 점검 Api

예매 불가 상태인 좌석을 점검하여 판매가 되지 않은 좌석은 다시 예매 가능 상태로 전환해주는 Api

서비스 레이어에서만 작동하기에 블랙박스 시퀀스는 따로 그리지 않았다.

@Transactional
@Scheduled(fixedRate = 30 * 60 * 1000)
public void verifySeatState() {
    List<Seat> seatsToUpdate = seatRepository.findByReserveAvailFalseAndSoldoutFalse();
        
    seatsToUpdate.forEach(seat -> seat.setReserveAvail(true));

	seatRepository.saveAll(seatsToUpdate);
}

좌석 선점, 판매 Api와 마찬가지로 데이터의 불리언 값을 변경해주는 Api라서
Scheduled 어노테이션이 추가됐을뿐 구현 로직은 크게 달라진 것이 없다.

📌 발생한 문제

1. 좌석과 상영일정의 CASCADE 문제

I. 문제 상황 : DB에서 상영일정의 삭제가 안됨

내가 의도한 바는 상영일정 삭제시 해당 상영일정을 참조하는 좌석 튜플들이 자동으로 삭제가 되는 것 이었으나 DB에서 삭제 테스트시 상영 일정 데이터의 삭제가 불가능 함

II. 문제 원인:

jpa에서의 cascade 설정은 DB에 적용 시키는 것이 아닌 jpa 자체에서 데이터를 조작하는 것 이었음

III. 문제 해결 :

DB 자체에서 설정을 변경함으로써 해결이 가능하지만 데이터의 조작 방법이
제한적인 것이 좋다는 생각을 해서 따로 설정을 적용시키지 않기로 함

📌 후기

처음으로 별다른 에러 없이 하나의 엔티티를 완성 시켰다.
그래서 불안하다...

분명히 뭔가 디테일한 부분을 빼먹었을 거라는 생각이 자꾸만 들지만
우선 현재 목표 달성에 만족하고 뒤에 있을 리팩토링에 힘을 줘서
수정을 해보려고 한다.

가장 처음에 작성했던 코드들의 부족함이 보여서 어서 수정을 하고 싶다.

profile
개발 공부하는 블로그

0개의 댓글