From d87d70997b7ceac831f5953fb754b388cd3d2fc1 Mon Sep 17 00:00:00 2001 From: icurfer Date: Sun, 25 Jan 2026 12:56:05 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20Post=20=EB=B9=84=EA=B3=B5=EA=B0=9C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Post 모델에 is_private 필드 추가 - 비공개 게시글은 작성자만 목록/상세에서 조회 가능 - 공개/비공개 토글 기능 지원 Co-Authored-By: Claude Opus 4.5 --- blog/migrations/0007_post_is_private.py | 18 ++++++++++++ blog/models.py | 1 + blog/serializers.py | 6 ++-- blog/views.py | 39 ++++++++++++++++++++++--- version | 2 +- 5 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 blog/migrations/0007_post_is_private.py diff --git a/blog/migrations/0007_post_is_private.py b/blog/migrations/0007_post_is_private.py new file mode 100644 index 0000000..870d7fd --- /dev/null +++ b/blog/migrations/0007_post_is_private.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.14 on 2026-01-25 03:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('blog', '0006_attachment_batch_id'), + ] + + operations = [ + migrations.AddField( + model_name='post', + name='is_private', + field=models.BooleanField(default=False), + ), + ] diff --git a/blog/models.py b/blog/models.py index 8822550..4ae530e 100644 --- a/blog/models.py +++ b/blog/models.py @@ -25,6 +25,7 @@ class Post(models.Model): updated_at = models.DateTimeField(auto_now=True) author_id = models.CharField(max_length=150, blank=True, default='') author_name = models.CharField(max_length=150) + is_private = models.BooleanField(default=False) # 비공개 여부 (True: 작성자만 볼 수 있음) class Meta: ordering = ['-created_at'] diff --git a/blog/serializers.py b/blog/serializers.py index 8b79192..7b031f0 100644 --- a/blog/serializers.py +++ b/blog/serializers.py @@ -108,7 +108,8 @@ class PostSerializer(serializers.ModelSerializer): 'author_id', 'author_name', 'created_at', 'updated_at', 'comment_count', 'tags', 'tag_names', - 'attachments', 'attachment_ids' + 'attachments', 'attachment_ids', + 'is_private' ] read_only_fields = ['author_id', 'author_name', 'created_at', 'updated_at'] @@ -178,7 +179,8 @@ class PostListSerializer(serializers.ModelSerializer): fields = [ 'id', 'title', 'author_name', 'created_at', 'updated_at', - 'comment_count', 'tags', 'thumbnail' + 'comment_count', 'tags', 'thumbnail', + 'is_private' ] def get_comment_count(self, obj): diff --git a/blog/views.py b/blog/views.py index d408911..429a234 100644 --- a/blog/views.py +++ b/blog/views.py @@ -11,6 +11,7 @@ from rest_framework.response import Response from rest_framework.views import APIView from rest_framework.parsers import MultiPartParser, FormParser from django.shortcuts import get_object_or_404 +from django.db.models import Q from .models import Post, Comment, Tag, Attachment from .serializers import PostSerializer, PostListSerializer, CommentSerializer, TagSerializer, AttachmentSerializer import logging @@ -66,7 +67,25 @@ class PostListView(generics.ListAPIView): permission_classes = [permissions.AllowAny] def get_queryset(self): - queryset = Post.objects.all() + user = self.request.user + + if user.is_authenticated: + # 로그인 사용자: 공개 게시글 + 자신의 비공개 게시글 + user_id = str(getattr(user, 'id', '') or getattr(user, 'email', '')) + username = getattr(user, 'username', '') or getattr(user, 'email', '') + + # 자신의 비공개 게시글 조건 (빈 문자열은 제외) + private_conditions = Q() + if user_id: + private_conditions |= Q(is_private=True, author_id=user_id) + if username: + private_conditions |= Q(is_private=True, author_name=username) + + queryset = Post.objects.filter(Q(is_private=False) | private_conditions) + else: + # 비로그인 사용자: 공개 게시글만 + queryset = Post.objects.filter(is_private=False) + tag = self.request.query_params.get('tag') if tag: queryset = queryset.filter(tags__name=tag) @@ -104,9 +123,21 @@ class PostDetailView(generics.RetrieveUpdateDestroyAPIView): return [permissions.AllowAny()] def retrieve(self, request, *args, **kwargs): - response = super().retrieve(request, *args, **kwargs) - logger.info(f"Post detail requested. ID: {kwargs.get('pk')}, Title: {response.data.get('title')}") - return response + instance = self.get_object() + + # 비공개 게시글은 작성자만 조회 가능 + if instance.is_private: + user = request.user + if not user.is_authenticated: + raise PermissionDenied("비공개 게시글입니다.") + user_id = str(getattr(user, 'id', '') or getattr(user, 'email', '')) + username = getattr(user, 'username', '') or getattr(user, 'email', '') + if instance.author_id != user_id and instance.author_name != username: + raise PermissionDenied("비공개 게시글입니다.") + + serializer = self.get_serializer(instance) + logger.info(f"Post detail requested. ID: {kwargs.get('pk')}, Title: {serializer.data.get('title')}") + return Response(serializer.data) def perform_update(self, serializer): instance = serializer.instance diff --git a/version b/version index 71ad83c..6778882 100644 --- a/version +++ b/version @@ -1 +1 @@ -v0.0.13 \ No newline at end of file +v0.0.14 \ No newline at end of file