TIL 최종프로젝트(23) 티켓 생성 자동화
23.07.09 일 <- 07일 금요일에 작성했어야 할 TIL
프로젝트에서 행사 영역을 맡고 프로젝트를 수행하면서, 꼭 넣어야겠다 생각했던 것이 있습니다.
자동화!
티켓을 구현하기 위해서 머리를 굴릴 때, 이것이 자동으로 생성할 수 있다면, 참 좋을 거 같다 생각했습니다.
다른 기능을 만들고 구현된 백엔드 부분을 전체적으로 점검할 때에도 자동화 시스템이 있었으면 참 좋을 거 같다 생각했습니다.
백엔드와 프론트를 연결시키고 css, js를 이용하여 프론트 부분을 구현할 때에도 행사 부분에 자동화를 적용시킬 수 있으면 좋은데 생각했습니다.
배포 후 사용자들이 경험해 볼 수 있게 데이터를 만들면서 티켓 생성 때문에 11개의 행사 중 2개의 행사만 실제 예매 기능을 할 수 있게 만들 때에도 티켓만 자동으로 생성할 수 있었다면... 생각했습니다.
피드백을 통해, 예매 가능한 행사가 적다는 의견을 받았을 때에도 마찬가지입니다.
왜 못했을까?
백엔드를 구현할 때에는 기능만 잘 돌아가면 된다는 생각에 자동화를 시도하지 않았습니다.
배포 후 전체적인 수정의 기회가 있었을 때에는 생각한 방법이 한계에 부딪혀 결국 보류 되었습니다.
즉, 어떻게 만들어야 하지? 이것이 가장 큰 걸림돌이었습니다.
[시도]
이전 TIL에서 잠시 나왔지만, 전 자동화를 구현하기 위해 Django Apscheduler를 사용하려고 했습니다.
그 때에는 단순히 티켓 자동생성이 아닌 종료된 행사 처리 기능까지 넣으려고 했습니다.
Django Apscheduler 자체가 크론탭처럼 정해진 시간에 정해둔 작업을 시행하는 것입니다.
그래서 의문이 들었습니다. 행사를 탐색하고 해당 행사가 가진 데이터를 가지고, 티켓을 생성한다면, 이미 티켓이 생성된 행사와 그렇지 않은 행사를 어떻게 구분할 것 인지?
마찬가지로, 아직 시작하지 않은 행사와, 진행중인 행사, 끝난 행사를 어떻게 구분시킬 것인지
이것을 최대한 모델의 수정없이 어떻게 만들 것인지
여기서부터 막혀버렸습니다.
또한 Django Apscheduler은 간단하면서도 처음 시도한다면 부딪혀 막힐 부분이 몇 군데 보였습니다. 날짜 설정은 넘기더라도 위에서 적었듯 인스턴스 구분방법과 각 행사의 맞게 어떻게 티켓을 생성해줄 것인지 등 로컬에서 작업한다고 해도 재시간안에 해결이 어려워 보였습니다 거기에 더해 프론트 부분도 피드백을 받아 계속해서 작업을 해야 했고 배포 환경에 따라 백엔드 수정 가능 부분이 한정되었기에 구현 보류가 되었습니다.
[해결]
저희 팀은 일 욕심이 많습니다, 열정도 넘치고, 서로 으쌰으쌰도 잘해서 서로의 영역에서 시너지를 일으킵니다.
이번 자동화도 시너지로 결국 구현이 되었습니다.
보류 상태이던 기능들에 대하여 마지막으로 시도해 볼 만한 것이 무엇이 있는지 이야기가 나왔고 부팀장님의 강력한 권유로 다시 티켓 생성 자동화에 도전하게 되었습니다.
어렵다, 불가능하다, 자신없다 하였지만, 부팀장님의 할 수 있다, 화이팅에 그럼 최종 발표회 끝나고 시도해보겠다! 정도로 마무리 되었지만 머리 속에 계속 맴돌더군요.
새벽부터 부팀장님이 알려준 Django-Signals에 대하여 알아보았습니다.
깊게 들어가지는 않고 어떻게 적용시킬 수 있을까 생각하며 정보를 모았습니다.
작동원리는 간단하였습니다.
어떤 모델의 인스턴스의 상태가 변경될 때, Signals를 사용하여 어떻게 행동 할 것인지를 작성해 주는 것이었습니다.
즉, 인스턴스의 상태 변화가 트리거가 되어 작업이 자동으로 일어나는 것이었습니다.
그렇다면, 행사가 생성될 때를 트리거로 사용한다면, 티켓이 자동으로 생성되는 것을 구현할 수 있겠다 생각했습니다.다행히 구현은 그렇게 어렵지 않았습니다.
이미 티켓 생성 시 검증을 위해 행사모델에 티켓을 생성하는데 필요한 데이터들이 다 들어있었습니다.
...
from datetime import timedelta
from django.db.models.signals import post_save
from django.dispatch import receiver
class Event(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=50)
content = models.TextField()
image = models.ImageField(blank=True, upload_to="%Y/%m/")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
event_start_date = models.DateTimeField()
event_end_date = models.DateTimeField()
time_slots = models.JSONField()
max_booking = models.PositiveIntegerField(validators=[MinValueValidator(1)])
money = models.IntegerField()
likes = models.ManyToManyField(User, related_name="like_event", blank=True)
event_bookmarks = models.ManyToManyField(
User, related_name="bookmark_events", blank=True
)
@receiver(post_save, sender=Event)
def create_tickets(sender, instance, created, **kwargs):
if created:
event_start_date = instance.event_start_date.date()
event_end_date = instance.event_end_date.date()
time_slots = instance.time_slots
current_date = event_start_date
while current_date <= event_end_date:
for time_slot_key, time_slot_value in time_slots.items():
ticket = Ticket.objects.create(
author=instance.author,
event=instance,
event_date=current_date,
event_time=time_slot_value,
max_booking_count=instance.max_booking,
money=instance.money,
current_booking=0,
quantity=0,
)
current_date += timedelta(days=1)
class Ticket(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
event = models.ForeignKey(Event, on_delete=models.CASCADE)
event_date = models.DateField()
event_time = models.CharField(max_length=11)
max_booking_count = models.PositiveIntegerField(validators=[MinValueValidator(1)])
current_booking = models.PositiveIntegerField(default=0)
money = models.IntegerField()
quantity = models.IntegerField(default=0)
create_tickets 함수는 Event 모델을 사용하여 인스턴스가 생성될 때, 작동합니다.(데코레이터 기능)
여기서 instance는 Event모델을 사용해서 만들어진 인스턴스의 값입니다.
while문을 보면, current_date의 값이 event_end_date보다 작거나 같을 때 까지 반복합니다.instance.time_slot은 json필드로 구현되었습니다.
{"1": "18:40~20:30", "2": "19:40~21:30"}
time_slot을 구현할 때 1회차 18:40~20:30, 2회차 19:40~21:30 형태로 구현하고 싶어 json 필드를 사용하였습니다.
행사는 각 날짜 별 회차에 맞게 티켓을 생성해야합니다
23-07-20 날짜에 {"1": "18:40~20:30", "2": "19:40~21:30"} 형태의 tim_slot이 존재하면
23-07-20 "1": "18:40~20:30"
23-07-20 "2": "19:40~21:30"
이러한 형태로 같은 날짜에도 time_slot에 맞게 티켓이 생성되어야 합니다
그렇기에 날짜는 while문을 사용하고 for 문을 사용하여 해당 날짜 당 생성되어야 하는 회차별 티켓이 생성됩니다
for time_slot_key, time_slot_value in time_slots.items():
time_slots을 기준으로 for문을 돌리는데 .items()을 사용하여, json 형태로 저장된 데이터를 각각 분리하여
time_slot_key와 time_slot_value에 저장해 줍니다.
{"1": "18:40~20:30", "2": "19:40~21:30"} 이 데이터를 기준으로
time_slot_key에는 1, 2 가 저장될 것이고
time_slot_value에는 18:40~20:30, 19:40~21:30가 저장될 것입니다.
ticket = Ticket.objects.create(
author=instance.author,
event=instance,
event_date=current_date,
event_time=time_slot_value,
max_booking_count=instance.max_booking,
money=instance.money,
current_booking=0,
quantity=0,
)
while문과 for문을 돌면서 티켓을 생성할 것입니다.
ticket이라는 변수는 생성된 티켓의 정보를 담아 Ticket 모델에 해당 값을 사용한 오브젝트(티켓)을 생성해 줍니다.
while문을 돌며 event_date에 다른 날짜의 데이터가 생성될 것입니다
for문을 돌며 event_time이 생성될 것입니다.
이외의 모든 값들은 Event인스턴스의 값을 이용하여 채워줍니다
프론트에서 행사 시간을 나타내주기 위해 우리는 time_slot의 value값을 받아야 합니다.
그렇기에 items를 사용하여 key와 value를 각각 저장했던 것입니다.
for문이 끝나면
current_date += timedelta(days=1)
이 코드에 의해 current_date가 하루 증가하고 다시 while문으로 갑니다.
날짜를 사용하기 위해서
from datetime import timedelta
import하여 사용했습니다.
(처음써보는 동영상 기능)
이렇게 행사 생성 시 자동으로 티켓이 생성됩니다!
[느낀점]
정말 간단하게 구현했습니다!
아마 제가 모르는 더 좋은 기능들이 많이 있을거 같습니다.
정말 구현하고 싶었던 기능이였기에 완성되니 정말 기뻤습니다.
문제를 해결할 때 하나의 방법만 가지고 몰두하는 것 보다 다른 방법을 찾으며 다양한 시도를 해보는 것도 좋다는 것을 알게되었습니다.
세상에 똑똑한 사람이 얼마나 많은데, 이 문제를 해결하기 위한 방법이 하나만 있을리가 없다!