오늘 학습 내용 ✅
PR Riview
1. is_editable 제거
문제
- 클라이언트에서 가지고 있는 작성자 정보와 비교해서 수정 버튼의 활성화여부를 결정할 것이기 때문에
- is_editable = serializers.SerializerMethodField() 이 값을 없애라
해석
- 수정 버튼을 보여줄지 말지의 판단 책임은 백엔드(API)가 아니라 프론트엔드(클라이언트)
나의 구현 현황
- 현재 로그인 유저가 누구인지 확인하고 질문 작성자인지 비교해서
- is_editable: true / false 를 응답으로 내려줌
- 이 값으로 프론트에게 이 질문이 수정이 가/불가능 을 알리려 했음
- 이미 질문 상세 조회에 author.id가 내려가고 있기 때문에
- 프론트는 로그인한 유저 id / question.author.id를 비교하여 같으면 "수정" 버튼을 보여준다.
- 그렇기 때문에 굳이 is_editable같은 UI용 bool값을 내려줄 필요가 없다.
- 그렇기 때문에 지금 is_editable 값은 지우고 나는 실제 수정 가능 여부만 엄격하게 검사한다.
is_editable = serializers.SerializerMethodField()
def get_is_editable(self, obj: Question) -> bool:
request = self.context.get("request")
if not isinstance(request, Request):
return False
user = request.user
if not user.is_authenticated:
return False
return obj.author_id == user.id
해결
- QuestionDetailSerializer 에서 필드 제거
- Meta.fields 에서 제거
- 메서드 제거
이미지 관리 방식
[1] FE: 이미지 업로드 요청
↓
[2] BE: Presigned URL 발급
↓
[3] FE: S3에 직접 PUT 업로드
↓
[4] FE: S3 URL을 content에 삽입
↓
[5] FE: 질문 등록/수정 요청
↓
[6] BE: content 파싱 → 이미지 URL 추출
↓
[7] BE: QuestionImage 생성/삭제
[1] 이미지 선택: 사용자가 에디터에서 이미지 선택
[2] BE에게 Presigned URL 요청
POST /images/presigned
[3] BE 응답
{
"upload_url": "https://s3....(PUT용)",
"file_url": "https://bucket.s3/.../uuid.png"
}
[4] FE가 S3에 직접 업로드
BE 서버는 관여 안함
S3에 바로 저장됨
[5] FE가 content를 만든다(이 시점 이미지는 content 일부)
<p>질문 설명</p>
<img src="https://bucket.s3/.../uuid.png" />
[6] 질문 등록 / 수정 요청
{
"title": "...",
"content": "<p>...</p><img src='https://...'>",
"category": 1
}
[1] BE는 content를 그대로 믿지 않는다
content = validated_data["content"]
[2] content에서 이미지 URL 파싱
content_images = extract_image_urls_from_content(content)
[3] 결과(이 질문에서 실제로 사용 중인 이미지)
{
"https://bucket.s3/.../uuid.png"
}
[4] QuestionImage 테이블과 동기화
[5] 질문 생성
for url in content_images:
QuestionImage.objects.create(
question=question,
img_url=url,
)
[6] 질문 수정
삭제 대상 = DB 이미지 - content 이미지
추가 대상 = content 이미지 - DB 이미지
[1] 사용자가 에디터에서 이미지 삭제
<img ...> ← 제거됨
[2] FE는 그냥 content만 보냄
{
"content": "<p>이미지 없음</p>"
}
[3] BE가 파싱
content_images = set() # 비어 있음
[4] DB 동기화
기존: 이미지 있음
content 이미지: 없음
→ QuestionImage 삭제
[1]
[2]
[3]
[4]
[5]
[6]
[1]
[2]
[3]
[4]
[5]
[6]
새롭게 알게된 내용 ✅
오늘 발생한 문제(발생 했다면) ✅