NoSleepPlace 프로젝트 3 - 장소 예약 및 상태별 예약 조회 API

문승준·2021년 12월 9일
0

Wecode Project

목록 보기
7/8
post-thumbnail

상품마다 캘린더에 이미 예약된 시간대를 표기하기 위한 API와
장소를 예약하고 상태별 예약을 조회하는 기능을 구현해보았다.

내가 구현한 백엔드 부분의 로직을 되돌아보겠다.

1. 장소별 예약 현황 캘린더 조회

사용자가 장소를 선택해 캘린더를 클릭했을때 이미 예약된 날짜와 시간은 선택이 불가능해야한다.

-> 이를 위해 장소별 예약 현황 데이터를 전송해주는 API를 구현했다.

  • Path parameter로 place_id를 받는다. -> 장소 상세 페이지

  • Query parameter로 현재 시간과 기간을 받는다

    • 현재 시간(time) : 캘린더를 확인 하는 순간의 시간 정보를 받아서 그 이후의 데이터만 보여준다.
    • 기간(day) : 몇 일간의 데이터를 보여줄지 정수 데이터를 받는다.
  • 예약 상태 코드 중에서 Pending과 Confirmed에 해당하는 데이터를 보내준다.

  • 응답 데이터는 예약 건마다 시작시간, 종료시간, 사용시간 정보를 보내준다.

  • 응답 데이터를 바탕으로 프론트엔드는 캘린더에서 유저가 해당 시간을 선택하지 못하게 렌더링 한다. (date-picker 라이브러리)

  • 아래 코드는 전체로직에서 일부분만 가져왔다.

current_time = datetime.strptime(time, "%Y-%m-%dT%H:%M:%S") # 현재시간을 datetime으로 변환
day_limit    = (current_time + timedelta(days)).date()      # 현재시간에 기간 날짜수를 더하기
  • 아래 코드는 DB에서 해당 데이터를 가져오는 로직이다.
books = Book.objects.filter(
                place_id           = place_id,
                status_code_id__in = [BookStatus.Status.PENDING.value, BookStatus.Status.CONFIRMED.value], 
                start_time__gt     = current_time,
                end_time__lt       = day_limit
            )

            result = [{
                    'start_time' : book.start_time,
                    'end_time'   : book.end_time,
                    'usage_time' : str(book.end_time - book.start_time).split(":")[0]
                } for book in books]
  • status_code로 필터링하는 과정에서 Enum class를 활용했다.
class BookStatus(TimeStampedModel):
    
    class Status(models.IntegerChoices):
        PENDING   = 1
        CONFIRMED = 2
        COMPLETED = 3
        CANCELLED = 4

    status = models.CharField(max_length=50)
    
    class Meta:
        db_table = 'book_statuses'
  • 배운점
    • Django model에서 Enum class를 사용하려면 Choices를 사용한다.
    • Status class 안에 선언한 상수 정보는 DB와 반드시 일치해야한다.
    • 코드의 가독성이 높아지고 매번 ORM을 통해 DB에 접근하지 않아도 파이썬 단에서 값을 가져올 수 있다.

2. 장소 예약

장소별로 날짜, 시작시간, 종료시간을 선택해 예약을 할 수 있는 기능을 구현했다.

  • Query string으로 받은 데이터 가공하기
start_time = datetime.strptime(data['start_time'],"%Y-%m-%dT%H:%M:%S")
end_time   = datetime.strptime(data['end_time'],"%Y-%m-%dT%H:%M:%S")
date       = start_time.date()

if end_time <= start_time:
    return JsonResponse({"message":"INVALID_BOOK_TIME"}, status=400)
  
  • 중복 예약 방지를 위해 Q 객체로 필터링하기

  • 기존 예약과 겹치는 시간대가 있으면 예약이 불가능해야한다.

    • 중복이 되는 경우의 수를 그림으로 그려가며 조건을 만들었다.

    • 경우에 따라 시작시간 혹은 종료시간의 포함, 불포함 관계를 고려했다.

    • 가능한 모든 경우를 생각해보려했으나 완벽하게 필터링되는 것인지는 보장할 수 없다.

q1 = Q(start_time__gte=start_time) & Q(start_time__lt=end_time)
q2 = Q(end_time__gt=start_time) & Q(end_time__lte=end_time)
q3 = Q(start_time=start_time) & Q(end_time=end_time)           

if Book.objects.filter(place_id=place_id).filter(q1|q2|q3).exists():
    return JsonResponse({"message":"ALREADY_BOOKED_TIME"}, status=400)
  • 배운점
    • datetime 객체를 다루며 strptime, strftime 등 메서드를 사용해보았다.
    • Q 객체와 look up을 이용해 시간 데이터를 비교하고 필터링할 수 있다.
  • 고민해볼 점
    • 중복 예약이 되는 경우에 대한 예외처리가 완벽한지 확신이 없다.
      • 더 나은 필터링 방법이 있는지 혹은 확실한 테스트 방법이 있는지 알아봐야겠다.

3. 장소 예약 조회

사용자의 마이 페이지에서 예약 상태에 따른 장소 조회 기능을 구현했다.

  • 유저가 로그인 후 예약 조회를 할때, 예약 상태에 따른 장소리스트를 보내주는 API이다.

  • 기존에는 if문과 Q 객체를 이용해 이런 로직을 작성했지만, 이번에는 filter_set을 만들어 적용해보았다.

filter_prefixes = {
                'status' : 'status_code__status__in'
            }

filter_set = {filter_prefixes.get(key): value for (key, value) in dict(request.GET).items()}

books = Book.objects.filter(user_id=user.id, **filter_set).order_by('start_time')

  • datetimestrftime 메서드로 시간 데이터를 원하는 문자열 형태로 가공해서 전송했다.
'start_time'   : datetime.strftime(book.start_time,"%Y년 %m월 %d일 %H:00"),
'end_time'     : datetime.strftime(book.end_time,"%H:00")
  • 배운점
    • Dictionary comprehension, packing과 unpacking 의 개념을 이해할 수 있었다.
      • 함수가 여러개의 인자를 받지만 몇개인지 모를때 하나의 매개변수로 패킹해서 받는다.
        • 매개변수에 *args(위치인자), **kwargs(키워드인자) 넣는다.
      • 패킹된 변수를 인자로 받을때 여러개의 값을 꺼내오는 언패킹을 해서 함수가 호출된다.

4. 느낀점

  • 알고리즘 풀이에만 적용해보았던 Enum class를 실제 프로젝트 로직에 적용해볼 수 있었다. 알고리즘을 공부하는 것에서 끝나는게 아니라 배운 것을 실제로 적용할 줄 알아야한다고 느꼈다.
    알고리즘과 자료구조를 공부하는 목표는 결국 좋은 코드를 작성하기 위함이 아닐까?
  • 예약 조회 로직은 status code를 파라미터로 받아서 필터링하면 되는 간단한 로직이지만, Q 객체가 아닌 filter_set을 적용하는 과정이 쉽지않았다. 이전처럼 if문과 Q객체를 조합해 필터링을 하는 것은 분기점이 많아져서 디버깅이 어려울 것이다. (경우의 수가 많기 때문에)
    정확한 표현일지는 모르겠으나, 이 경우엔 선형적인 흐름보다 비선형적인 흐름이 더 나은 것이라는 생각이 든다.
profile
개발자가 될 팔자

0개의 댓글