개발자

영화 예매 프로젝트의 예매 기능과 관련하여

2023년 07월 28일조회 379

안녕하세요. 토이 프로젝트로 영화 예매 서비스를 만들어보고 있습니다. 어느 정도 기능 구현이 완료되었다고 생각했었는데, 오늘 생각지도 못한 고려 사항을 발견해버렸네요 ㅠㅠ.. 좌석 모델과 예매 모델 사이의 관계의 모호함이 있었습니다.. 저는 MovieSchedule 모델을 두어 참조하는 영화의 id, 상영 시작 시간, 상영 종료 시간, 참조하는 screen(상영관)의 id를 column으로 두었습니다. 그리고 Reservation 모델이 이 MovieSchedule 모델을 참조하도록 하고, 예매 인원, 예매하는 유저의 아이디 등의 column을 갖도록 하였습니다. 아래 코드를 참조부탁드립니다(Prisma ORM을 사용하였습니다). 하지만 이렇게 구성을 하다보니, 같은 영화, 같은 상영관이지만 시간대만 다른 좌석(같은 screen, 다른 MovieSchedule을 참조)의 경우에, seat와 reservation이 one-to-many로 설정되어 있기 때문에 이미 다른 시간대(다른 MovieSchedule)에서 해당 좌석을 예매 해두었다면, 좌석은 더 이상 예매를 할 수 없는 상태가 되더군요.. schema를 변경할 필요가 있을 것 같은데 ERD를 설계해본 경험이 별로 없다보니 어떤식으로 변경해야 할지 감이 잘 안 잡힙니다.. 제가 생각해본 대안들은 다음과 같습니다. 1. movieSchedule에 대응하는 screen과 seat를 생성하고, 상영 후 해당 screen 및 seat를 모두 삭제. 이러면 여러 시간대에 같은 screenNum이 여러개 생길 수 있으니 screenNum의 unique 조건을 삭제해야할듯. 2. seat와 reservation의 관계를 many to many로 변경하고 reservation-seat과 같은 table을 하나 더 생성. 해당 테이블은 seatId와 reservationId를 갖고 있을 것이므로, 좌석 조회 시 reservation의 movieSchedule과 조회하는 movieSchedule이 일치하면 예약되었음을 확인할 수 있음. 위 대안들이 적절한지, 그렇지 않다면 어떤 대안이 있을지 조언 부탁드립니다 ㅠㅠ

1model Reservation {
2  id        String @id @default(cuid())
3  personAmt Int
4
5  userId String?
6  user   User?   @relation(fields: [userId], references: [id])
7
8  movieScheduleId String?
9  movieSchedule   MovieSchedule? @relation(fields: [movieScheduleId], references: [id], onDelete: SetNull, onUpdate: Cascade)
10
11  seats Seat[]
12
13  createdAt DateTime @default(now())
14  updatedAt DateTime @updatedAt
15
16  @@index([id])
17}
18
19model MovieSchedule {
20  id      String  @id @default(uuid())
21  movieId String?
22  movie   Movie?  @relation(fields: [movieId], references: [id], onDelete: SetNull, onUpdate: Cascade)
23
24  screenId String?
25  screen   Screen? @relation(fields: [screenId], references: [id], onDelete: Cascade, onUpdate: Cascade)
26
27  startTm DateTime
28  endTm   DateTime
29
30  reservations Reservation[]
31}
32
33model Screen {
34  id          String @id @default(cuid())
35  screenNum   Int    @unique
36  seatAmt     Int
37  restSeatAmt Int
38
39  movieSchedules MovieSchedule[]
40  seats          Seat[]
41}
42
43model Seat {
44  id   String @id @default(cuid())
45  name String
46
47  reservationId String?
48  reservation   Reservation? @relation(fields: [reservationId], references: [id], onDelete: SetNull, onUpdate: Cascade)
49
50  screenId String?
51  screen   Screen? @relation(fields: [screenId], references: [id], onDelete: Cascade, onUpdate: Cascade)
52}
이 질문이 도움이 되었나요?
'추천해요' 버튼을 누르면 좋은 질문이 더 많은 사람에게 노출될 수 있어요. '보충이 필요해요' 버튼을 누르면 질문자에게 질문 내용 보충을 요청하는 알림이 가요.

답변 2

인기 답변

달레님의 프로필 사진

공유해주신 Prisma 스키마를 보면 현재 Seat 모델과 Screen 모델 간에 직접적인 관계가 형성되어 있는데요. 상영관에서 우리가 실제로 만질 수 있는 의자라는 "물체"라고 생각하면 이러한 데이터 모델링이 맞을 수도 있겠지만, Seat 모델이 Reservation 모델과도 연관되어 있다라는 것을 고려해봤을 때, 여기서 Seat 모델은 물리적인 의자라기 보다는 특정 상영 시간 내에서만 유효한 논리적인 "좌석"의 개념에 더 가까울 수도 있을 것 같습니다. 영화관의 비즈니스를 생각해보면 "일정 시간 동안 의자의 사용 권리"를 파는 것이지, 가구점처럼 물리적인 의자를 판매하는 게 아니니까요. 만약에 제가 바라보는 관점이 맞다면, Seat 모델과 Screen 모델 간에 관계를 끊고 대신에 MovieSchedule 모델과 다대일 관계를 맺으시는 것도 고려해보실 수 있을 것 같습니다. 이렇게 해주시면 자연스럽게 여러 개의 좌석을 하나의 상영 일정과 하나의 예약에 연결하실 수 있으실 거고요. 뿐만 아니라 Seat 모델은 더 이상 상영관에 비치되어 있는 의자를 의미하지 않으므로 Seat 모델과 Reservation 모델의 관계를 다대다로 바꿔야할까라는 고민도 사라지지 않을까요? 추가적으로 over booking을 방지하기 위해서 어떤 영화의 특정 상영 일정에 어떤 좌석이 나갔는지 여부는 Seat 모델에 isReserved와 같은 칼럼을 추가하여 관리할 수도 있고요. 논리적인 식별자 뿐만 아니라 물리적인 의자 번호가 필요하다면 Seat 모델에 seatNumber 칼럼을 추가하는 것도 고려하실 수 있을 것 같습니다. 마지막으로 혹시 물리적인 의자와 논리리적인 좌석을 모두 관리하고 싶으시다면 Ticket이라는 새로운 모델을 추가하시는 것도 한 가지 방법이 될 수 있을 것 같아요. 그리고 Seat는 Screen과 Ticket과만 관계를 맺고, 대신 Ticket은 MovieSchedule과 Reservation과 관계를 맺어 주는 것이지요. 그러면 각 상영 일정에 대해서 해당 영화를 상영하는 스크린의 의자(Seat) 개수만큼의 예약이 가능한 좌석(Ticket)들을 미리 생성해놓을 수 있겠죠?

고지완님의 프로필 사진

고지완

작성자

백엔드 취준2023년 07월 29일

감탄하고 갑니다.. 고민했던 부분들이 모두 녹아져있네요.. 항상 정말 감사합니다!!

인기 답변

삭제된 사용자님의 프로필 사진

삭제된 사용자

2023년 07월 28일

안녕하세요. 일단 적어주신 대안 중 더 적절한 것은 2번으로 보입니다. 하지만 대안을 적용하기 전에 몇 가지 고려해보시면 좋을 것 같아 조금 더 적어보겠습니다. 세 번째 문단에 적어주신 내용을 보면 "seat과 reservation이 one to many로 설정되어있기 때문에 다른 시간대의 영화에서 해당 좌석을 예매했다면 좌석이 예매할 수 없는 상태가 된다" 고 말씀해 주셨는데요. 이 문제가 seat이 reservation을 참조하고 있기 때문에 발생하는 문제이지 않을까 생각이 듭니다. 영화관 좌석이라면, 어느 상영관에 속해있는지, 좌석 번호가 무엇인지만 가지고 있으면 된다고 생각합니다. reservation은 지금처럼 상영 스케줄과 seat을 참조하면 될 것 같구요. 이렇게 되면 상영 스케줄을 선택했을 때 reservation 데이터 중 해당 스케줄에 속하는 값들의 seat만 예매 불가로 표현해주면 될 것 같습니다. 혹시 제가 잘못 이해하고 말씀드린 내용이 있다면 코멘트 부탁드립니다 :)

고지완님의 프로필 사진

고지완

작성자

백엔드 취준2023년 07월 29일

답변 너무 감사드립니다!! 그러면 reservation과 seat의 관계가 어떻게 되는건가요?? 예매 시 좌석을 여러개 예매할 수 있기에 seat-reservation을 one to many로 해두었지만 reservation에서 seat을 참조하되 seat은 reservationid를 갖고 있지 않아도 된다는 부분이 조금 어렵네요.. one to many 관계가 된다면 참조되는 테이블인 seat쪽에서 reservationId를 갖고있어야하는거 아닌가요??

고지완님의 프로필 사진

고지완

작성자

백엔드 취준2023년 07월 29일

아.. 이해했습니다..!! reservation과 seat사이의 관계를 끊고 reservation이 seatIds와 같은 컬럼을 가져서 어떤 좌석들을 예매했는지 확인하면 된다는 말씀이셨군요. 덕분에 속이 뻥 뚫렸습니다.. 근데 이러면 movieSchedule을 조회할 때마다 seat의 예약 여부를 확인하기 위해 movieSchedule.reservation을 탐색하고 또 reservation의 seatIds 배열을 탐색하는 과정이 필요한거 같은데 이렇게 발생한 성능 저하는 캐싱 등을 도입하여 해결해야 하는걸까요? 아니면 다른 방법이 있을까요? 질문이 길어져 죄송하고 정말 감사드립니다 ㅠㅠ

profile picture

익명

작성자

2023년 07월 29일

제가 확인이 늦었네요..! 네 말씀하신대로 reservation이 seatids를 갖는 식을 말씀드린건데, 사실 리스트 형태의 컬럼을 직접 갖는 것이 좋은 방향일지는 모르겠습니다. 대안 2처럼 다대다 관계를 표현하기 위한 테이블을 만들어서 관리할 것 같네요. 그리고 질문주신대로 movieSchedule을 조회할때마다 reservation을 탐색하고 seatIds를 탐색하는 과정이 필요할텐데요. 일단 이 방식이 기존 방식에 비해 왜 성능 저하가 발생한다고 생각하시는지가 궁금합니다. 기존에도 movieSchedule을 조회할때마다 seat의 예약 여부를 확인하려면 동일한 순서로 검색해야 했던 것 아닌가요..? 제가 잘못 이해했다면 말씀 부탁드리겠습니다! 제가 잘못 이해한거라면, 보통 성능 개선을 위해 자주 조회되거나 검색의 기준이 되는 컬럼에 인덱스를 거는 방법도 있구요. 아마 사용하시는 ORM 프레임워크에서 상황에 맞는 조인 옵션을 제공하고 있을테니 그 부분도 살펴보시면 좋을 것 같습니다. 아니면 아예 NoSQL로의 전환도 가능한 방법 중 하나입니다. 아래 다른 분께서 달아주신 답변도 좋은 내용인 것 같네요! 잘 참고하셔서 좋은 결과물 만드시길 바랄게요 :)

지금 가입하면 모든 질문의 답변을 볼 수 있어요!

현직자들의 명쾌한 답변을 얻을 수 있어요.

또는

이미 회원이신가요?

목록으로

지금 가입하면 모든 질문의 답변을 볼 수 있어요!