[코담]
웹개발·실전 프로젝트·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
안에 바로 작성해도 애플리케이션이 동작하는 데에는 전혀 문제가 없습니다. 다만 아래와 같은 이유로 분리해 두면 여러모로 이점이 큽니다:
-
관심사의 분리 (Separation of Concerns)
views.py
는 URL 요청을 받아 로직을 수행하고 응답을 반환하는 역할에 집중- 파일 I/O나 데이터 파싱 같은 “보조 작업”은
utils.py
에 두어 책임을 분리
-
재사용성 (Reusability)
- 이후 다른 뷰나 스케줄러, 커맨드 등에서 동일한 JSON 로드 기능이 필요할 때
from sales.utils import load_orders
한 줄로 가져다 쓰면 중복 코드 없이 재사용 가능
-
테스트 용이성 (Testability)
- 유닛 테스트를 작성할 때
load_orders()
만 별도로 테스트할 수 있어 검증이 쉬움 - 뷰 로직과 파일 I/O 로직을 분리해두면, mocking이나 격리 테스트가 편해짐
- 유닛 테스트를 작성할 때
-
가독성·유지보수 (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 }
]