오늘 학습 내용 ✅
어드민 상세조회 과정 처리
- 수강 신청(User) 흐름은 구현되어 있음
- 하지만 관리자가 승인했을 때 실제로 수강생 데이터(CohortStudent)를 생성하는 로직이 누락
- 그리하여 관리자가 수강 승인을 하더라도 CohortStudent 데이터가 생성되지 않기 때문에,
- User 모델에 정의된 in_progress_cohortstudent 프로퍼티는 항상 None을 반환
- 어제 과정이 안나온 이유
- 실제 cohort_student 테이블에 데이터를 넣는 로직이 없었음
- 코치님께서 추가해주시고 나서 바로 해봤는데 성공!

답변쪽도 수정해야 하지만 답변기능이 아직 안나왔기 때문에 킵
내역 삭제
질의응답 내역 삭제
- 수강생들이 등록한 질의응답 내역을 삭제 가능
- 조회된 질의응답 목록에서 항목을 선택하고 삭제하기 버튼을 클릭하여 삭제
- 질의응답 상세 조회 모달 내에 위치한 삭제하기 버튼을 클릭하여 삭제
- 질의응답 내역 삭제 시 해당 질문, 질문에 작성된 답변, 답변에 작성된 댓글들이 모두 함께 삭제
Django Admin은 기본적으로 delete_selected라는 "선택 항목 삭제" 액션을 제공
class Question(TimeStampedModel):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="questions")
category = models.ForeignKey(QuestionCategory, on_delete=models.PROTECT, related_name="questions")
title = models.CharField(max_length=50)
content = models.TextField()
view_count = models.BigIntegerField(default=0)
class Answer(TimeStampedModel):
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="answers",
help_text="답변 작성자 ID",
)
question = models.ForeignKey(
"qna.Question",
on_delete=models.CASCADE,
related_name="answers",
help_text="답변이 속한 질문 ID",
)
class AnswerComment(TimeStampedModel):
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="answer_comments",
help_text="댓글 작성자 ID",
)
answer = models.ForeignKey(
"qna.Answer",
on_delete=models.CASCADE,
related_name="comments",
help_text="댓글이 달린 답변 ID",
)
기본 모델설정 자체가 질의응답 삭제시 질문,답변,댓글 싹다 삭제되기 때문에 굳이 할게 없음
답변 삭제
- 수강생들이 질문에 대해 작성한 답변을 삭제 가능
- 질의응답 상세 조회 모달 내
- 답변 목록에서 각 항목에 위치한 삭제버튼 ( x 아이콘
답변 추가
- 아직 답변 기능이 구현되지 않았기 때문에 빠른테스트를 위해 답변은 목데이터로 처리한다.
목데이터(Mock Data)
- 실제 데이터가 존재하지 않을 때, 테스트나 개발을 목적으로 진짜처럼 보이게 만든 가짜 데이터를 의미
사용 목적
- 독립적인 개발
- 백엔드(BE)의 API 개발이 완료되지 않았더라도,
- 프론트엔드(FE)에서 목데이터를 활용해 화면 UI를 미리 구현하고 테스트 가능
- 다양한 상황 테스트
- '데이터가 없는 경우', '데이터가 너무 많은 경우', '비정상적인 값이 들어온 경우' 등
- 실제 환경에서 만들기 어려운 상황을 가짜로 만들어 검증 가능
- 비용 및 보안
- 실제 운영 중인 고객 데이터를 테스트에 사용하는 것은 보안상 위험하므로,
- 이를 대체할 안전한 데이터가 필요
상세조회 문제
- 프론트엔드 8기 조교 같이 현재 속한 코스와 기수를 출력해야하는데 그냥 "조교"만 나옴


프론트 요구사항
- content_preview를 가져올때 content_preview가 태그까지 같이 가져옴
- django.utils.html.strip_tags 유틸리티를 사용하면 HTML 태그를 쉽게 제거
[ 🔴 문제: 질문 목록(Question List) API를 호출했을 때, 프론트엔드에 전달되는 content_preview 필드에 <p>, <b>, <br>과 같은 HTML 태그가 그대로 포함되어 출력 ]
[ 🟡 원인: 에디터 등을 통해 저장된 본문 데이터(content)가 HTML 형식으로 DB에 저장되어 있었음 ]
기존 QuestionListSerializer에서 content_preview 필드를 단순히 serializers.CharField()로 정의하여, DB에 저장된 HTML 문자열을 아무런 가공 없이 그대로 클라이언트에 전달하고 있었음.
[ 🔵 해결: Django에서 기본으로 제공하는 django.utils.html.strip_tags 유틸리티를 사용하여 HTML 태그만 제거하고 순수 텍스트 추출 ]
content_preview를 가공하기 위해 serializers.SerializerMethodField()로 변경.
get_content_preview 메서드를 추가하여, 해당 필드 데이터가 반환되기 직전에 strip_tags()가 적용되도록 로직 구현.
검증 로직 마련: HTML 태그가 포함된 더미 데이터를 생성하여 API 응답 값에 태그가 존재하지 않는지 확인하는 테스트 케이스(test_question_list_content_preview_strips_tags) 작성 가이드 제공
새롭게 알게된 내용 ✅
오늘 발생한 문제(발생 했다면) ✅
[ 🔴 문제: 데이터 타입 불일치 (TypeError) ]
- `instance.trainingassistant_set.set([cohort])`를 사용할 때,
- `set()` 함수는 인자로 `TrainingAssistant` 모델의 인스턴스 리스트를 기대함
- 하지만 기존 코드에서는 Cohort 객체를 직접 전달했기 때문에
- `"TrainingAssistant` 인스턴스가 필요한데 Cohort가 들어왔다"는
- 타입 에러가 발생하며 서버 500 오류가 발생
[ 🟡 원인: 모델 관계의 복잡성 ]
- 유저(User)와 기수(Cohort)는 직접 연결된 것이 아니라 TrainingAssistant라는 중간 모델을
- 통해 1:1 또는 N:1 관계로 연결되어 있다.
- 단순히 리스트를 교체하는 방식으로는 이 중간 매개 객체를 올바르게 생성하거나 수정할 수 없음
[ 🔵 해결: update_or_create 로직 적용 ]
- user=instance 조건을 통해 해당 유저가 이미 조교로 등록되어 있는지 확인
- 업데이트
- 이미 데이터가 존재한다면 defaults에 설정된 cohort 정보만 최신 기수로 변경
- 생성
- 만약 조교 데이터가 없다면 새로운 TrainingAssistant 레코드를 생성하여 유저와 기수를 연결