💡 AI 인사이트

🤖 AI가 여기에 결과를 출력합니다...

댓글 커뮤니티

쿠팡이벤트

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

검색

    [코담] 웹개발·실전 프로젝트·AI까지, 파이썬·장고의 모든것을 담아낸 강의와 개발 노트

    온라인카페 운영팀 | ✅저자: 이유정(박사)

    • 당신은 온라인 카페의 백엔드 개발자입니다.
    • 운영팀에서 “일별 주문 수”와 “상품별 매출 합계”를 볼 수 있는 API를 요청했습니다.
    • 주문 데이터는 매일 orders_data.json 파일로 받으며, 프로젝트 루트( manage.py 와 같은 폴더) 에 저장됩니다.
    • 실무에서처럼 JSON 파일을 파싱해 바로 API로 응답해 보세요.

    제공된 JSON 원시 데이터 (orders_data.json)

    [
      { "order_id": "C1001", "product": "아메리카노", "price": 4000, "quantity": 2, "order_date": "2025-06-15" },
      { "order_id": "C1002", "product": "라떼",       "price": 4500, "quantity": 1, "order_date": "2025-06-15" },
      { "order_id": "C1003", "product": "아메리카노", "price": 4000, "quantity": 1, "order_date": "2025-06-16" }
    ]
    

    1) sales/utils.py — JSON 파일 로드 함수 완성

    import os
    import json
    from django.conf import settings
    
    def load_orders():
        """프로젝트 루트의 orders_data.json 파일을 읽어와 Python 객체로 반환"""
        path = os.path.join(settings.______, 'orders_data.json')
        with open(path, encoding='utf-8') as f:
            data = json.______(f)
        return data
    

    (______) 부분에 들어갈 코드를 채워 보세요.

    load_orders() 같은 반복해서 쓰이는 “도우미(helper)” 함수를 별도의 utils.py에 두는 것은 코드 구조화(structuring)와 유지보수(maintainability)를 위해 권장되는 패턴입니다. 물론 기술적으로는 views.py 안에 바로 작성해도 애플리케이션이 동작하는 데에는 전혀 문제가 없습니다. 다만 아래와 같은 이유로 분리해 두면 여러모로 이점이 큽니다:

    1. 관심사의 분리 (Separation of Concerns)

      • views.py는 URL 요청을 받아 로직을 수행하고 응답을 반환하는 역할에 집중
      • 파일 I/O나 데이터 파싱 같은 “보조 작업”은 utils.py에 두어 책임을 분리
    2. 재사용성 (Reusability)

      • 이후 다른 뷰나 스케줄러, 커맨드 등에서 동일한 JSON 로드 기능이 필요할 때
      • from sales.utils import load_orders 한 줄로 가져다 쓰면 중복 코드 없이 재사용 가능
    3. 테스트 용이성 (Testability)

      • 유닛 테스트를 작성할 때 load_orders()만 별도로 테스트할 수 있어 검증이 쉬움
      • 뷰 로직과 파일 I/O 로직을 분리해두면, mocking이나 격리 테스트가 편해짐
    4. 가독성·유지보수 (Readability & Maintainability)

      • views.py 안에 여러 역할의 코드가 섞여 있으면 복잡도가 높아짐
      • 파일이 작게 분리되면, 팀원 간 역할 분담이나 코드 리뷰 시에도 이해가 빠름
    • views.py에 직접 작성해도 되지만,
    • 실무에서는 가급적 역할별로 모듈을 분리해 두어 관리하는 것이 좋습니다.
    • utils.py는 “다른 곳에서도 쓰일 수 있는 범용 함수”를 모아 놓는 곳이라고 생각하시면 됩니다.

    sales/views.py — 두 가지 API 뷰 함수 완성

    # sales/views.py
    from django.http import JsonResponse
    from .utils import load_orders
    
    def daily_order_count(request):
        """
        일별 주문 수 집계
        결과 형태: [{"date": "2025-06-15", "count": 2}, {"date": "2025-06-16", "count": 1}, …]
        """
        data = load_orders()
        counts = {}   # {"2025-06-15": 2, ...}
    
        for item in data:
            date = item["____"]   # order_date
            if date in counts:
                counts[date] += 1
            else:
                counts[date] = ____
    
        response = []
        for date, cnt in sorted(counts.items()):
            response.append({"date": date, "count": cnt})
        return JsonResponse(response, safe=____)
    
    def product_revenue_summary(request):
        """
        상품별 매출 합계 집계
        결과 형태: [{"product": "아메리카노", "revenue": 12000}, …]
        """
        data = load_orders()
        revenue = {}  # {"아메리카노": 12000, ...}
    
        for item in data:
            prod = item["____"]
            rev  = item["____"] * item["____"]
            if prod in revenue:
                revenue[prod] += ____
            else:
                revenue[prod] = ____
    
        response = []
        for prod, total in sorted(revenue.items()):
            response.append({"product": prod, "revenue": total})
        return JsonResponse(response, safe=____)
    

    (____)에 들어갈 코드를 채워 보세요.

    URL 연결 (sales/urls.py)

    from django.urls import path
    from . import views
    
    urlpatterns = [
        path('orders/daily/', views.daily_order_count),
        path('orders/product/', views.product_revenue_summary),
    ]
    

    답안

    # sales/utils.py
    import os
    import json
    from django.conf import settings
    
    def load_orders():
        path = os.path.join(settings.BASE_DIR, 'orders_data.json')
        with open(path, encoding='utf-8') as f:
            data = json.load(f)
        return data
    

    ✔️의사코드:

    함수 load_orders():
        1. 설정에서 프로젝트 루트 경로(BASE_DIR)를 가져온다.
        2. 'orders_data.json' 파일 이름을 프로젝트 루트 경로와 결합하여 전체 파일 경로(path)를 만든다.
        3. 해당 경로의 파일을 UTF-8 인코딩 모드로 연다.
        4. 열린 파일 객체(f)를 JSON 파서에 전달하여 Python 객체(data)로 변환한다.
        5. 파싱된 data를 호출자에게 반환(return).
    

    # sales/views.py
    from django.http import JsonResponse
    from .utils import load_orders
    
    def daily_order_count(request):
        data = load_orders()
        counts = {}
        for item in data:
            date = item["order_date"]
            if date in counts:
                counts[date] += 1
            else:
                counts[date] = 1
    
        response = []
        for date, cnt in sorted(counts.items()):
            response.append({"date": date, "count": cnt})
        return JsonResponse(response, safe=False)
    
    def product_revenue_summary(request):
        data = load_orders()
        revenue = {}
        for item in data:
            prod = item["product"]
            rev  = item["price"] * item["quantity"]
            if prod in revenue:
                revenue[prod] += rev
            else:
                revenue[prod] = rev
    
        response = []
        for prod, total in sorted(revenue.items()):
            response.append({"product": prod, "revenue": total})
        return JsonResponse(response, safe=False)
    

    ✔️의사코드:

    함수 daily_order_count(request):
        1. JSON 데이터 로드
           - data = load_orders()
    
        2. 일별 주문 개수 집계용 빈 딕셔너리 counts 생성
           - counts = {}
    
        3. 각 주문(item)에 대해 반복
           - date = item["order_date"]
           - 만약 date가 counts에 이미 있으면
               counts[date] += 1
             그렇지 않으면
               counts[date] = 1
    
        4. API 응답용 리스트 생성
           - response = []
           - counts 딕셔너리의 (date, count) 쌍을 날짜 순으로 정렬하여 반복
               response 에 {"date": date, "count": count} 추가
    
        5. JSON 형태로 반환
           - return JsonResponse(response, safe=False)
    

    Insomnia 테스트:

    GET http://127.0.0.1:8000/api/orders/daily/
    

    결과:

    [
      { "date": "2025-06-15", "count": 2 },
      { "date": "2025-06-16", "count": 1 }
    ]
    

    Insomnia 테스트:

    GET http://127.0.0.1:8000/api/orders/product/
    

    결과:

    [
      { "product": "아메리카노", "revenue": 12000 },
      { "product": "라떼",       "revenue": 4500 }
    ]
    
    TOP
    preload preload