
"question_id": bigint,
"title": str,
"content": str,
"images": [str],
"category_path": str,
"view_count": int,
"created_at": str,
"author": {
"nickname": str,
"profile_img_url": str
},
"answers": [
{
"answer_id": bigint,
"content": str,
"created_at": str,
"is_adopted": bool,
"author": {
"nickname": str,
"profile_img_url": str
},
"comments": [
{
"comment_id": bigint,
"content": str,
"created_at": str,
"author": {
"nickname": str,
"profile_img_url": str
}
}
]
}
]
}
{
"400": {
"error_detail": "유효하지 않은 질문 상세 조회 요청입니다."
},
"404": {
"error_detail": "해당 질문을 찾을 수 없습니다."
}
}
apps/qna/
├── views/
│ └── question/
│ └── question_detail_api.py # HTTP 레벨
│
├── serializers/
│ └── question/
│ └── question_detail.py # 출력 전용 Serializer
│
├── services/
│ └── question/
│ └── question_detail/
│ ├── service.py # 비즈니스 로직
│ └── selectors.py # DB 조회 최적화
│
├── exceptions/
│ └── question_exceptions.py # 404, 400 등
│
├── urls/
│ └── question_urls.py
question = (Question.objects.select_related(...).prefetch_related(...)검증 대상이 ‘request body’가 아니기 때문에 상세조회에서는
POST / PUT / PATCH → Body 검증 → Serializer 사용 ✅
GET /{question_id} → Path parameter 검증 → Serializer ❌ (대부분)
- 1.
class QuestionDetailPathSerializer(serializers.Serializer):
question_id = serializers.IntegerField(min_value=1)
serializer = QuestionDetailPathSerializer(
data={"question_id": question_id}
)
serializer.is_valid(raise_exception=True)
- 2.
class QuestionDetailQuerySerializer(serializers.Serializer):
question_id = serializers.IntegerField(min_value=1)
serializer = QuestionDetailQuerySerializer(
data={"question_id": question_id}
)
serializer.is_valid(raise_exception=True)