2026/02/18 Blog - 4

김기훈·2026년 2월 18일

TIL

목록 보기
144/194
post-thumbnail

기존 코드 최적화

에러메세지

임시글 수정 / 삭제

  • 기존에 세팅해놓은 exception을 사용함으로써 에러메세지를 통일함
  • update_fields를 사용함으로써 성능 최적화와 데이터 무결성처리를 진행
def restore_temp_post(post_id: int, user: User):
    """임시글을 공개글로 전환(복구)합니다."""
    post = get_object_or_404(Post, id=post_id, user=user, is_temp=True)
    post.is_temp = False
    post.save()


def soft_delete_post(post_id: int, user: User):
    """게시글을 삭제(Soft Delete) 처리합니다."""
    post = get_object_or_404(Post, id=post_id, user=user, deleted_at__isnull=True)
    post.deleted_at = timezone.now()
    post.save()

—————————————————————————————————————————————[비교]————————————————————————————————————————————————
def restore_temp_post(post_id: int, user: User):
    """임시글을 공개글로 전환(복구)합니다."""
    post = Post.objects.filter(id=post_id, user=user, is_temp=True).first()

    if not post:
        raise BaseCustomException(ErrorMessage.POST_NOT_FOUND)

    post.is_temp = False
    post.save(update_fields=['is_temp'])


def soft_delete_post(post_id: int, user: User):
    """게시글을 삭제(Soft Delete) 처리합니다."""
    post = Post.objects.filter(id=post_id, user=user, deleted_at__isnull=True).first()

    if not post:
        raise BaseCustomException(ErrorMessage.POST_NOT_FOUND)

    post.deleted_at = timezone.now()
    post.save(update_fields=['deleted_at'])

게시글 등록

create_post


@transaction.atomic
def create_post(*, author: User, validated_data: dict[str, Any]):
    """
    게시글을 생성하는 비즈니스 로직입니다.
    """
    tags_names = validated_data.pop("tags", [])

    post = Post.objects.create(
        user=author,
        title=validated_data["title"],
        content=validated_data["content"],
        thumbnail=validated_data.get("thumbnail"),
    )

    if tags_names:
        for name in tags_names:
            tag, created = Tag.objects.get_or_create(name=name)

            PostTag.objects.create(post=post, tag=tag)
    return post

—————————————————————————————————————————————[비교]————————————————————————————————————————————————
@transaction.atomic  
def create_post(*, author: User, validated_data: dict[str, Any]):
    """
    게시글을 생성하고 태그를 대량(Bulk)으로 처리하여 최적화한 로직입니다.
    """
    # 1. 입력 데이터에서 태그 목록을 분리합니다.
    tags_names = validated_data.pop("tags", [])

    # 2. 요약(summary)이 없을 경우 본문에서 앞부분을 추출하여 저장합니다.
    content = validated_data["content"]
    summary = validated_data.get("summary") or content[:150]

    # 3. 게시글을 먼저 생성합니다.
    post = Post.objects.create(
        user=author,
        title=validated_data["title"],
        content=content,
        summary=summary,
        thumbnail=validated_data.get("thumbnail"),
        is_temp=validated_data.get("is_temp", False)  
    )

    # 4. 태그 최적화 처리 (N+1 문제 해결)
    if tags_names:
        # 4-1. 이미 존재하는 태그들을 한 번에 조회합니다.
        existing_tags = Tag.objects.filter(name__in=tags_names)
        existing_tag_names = {tag.name for tag in existing_tags}

        # 4-2. DB에 없는 새로운 태그들만 선별하여 한 번에 생성(bulk_create)합니다.
        new_tag_names = set(tags_names) - existing_tag_names
        if new_tag_names:
            Tag.objects.bulk_create([Tag(name=name) for name in new_tag_names])

        # 4-3. 연결할 모든 태그 객체를 다시 가져옵니다.
        all_tags = Tag.objects.filter(name__in=tags_names)

        # 4-4. PostTag(중간 테이블) 데이터도 bulk_create로 한 번에 저장합니다.
        PostTag.objects.bulk_create([
            PostTag(post=post, tag=tag) for tag in all_tags
        ])

    return post

serializer

class PostCreateSerializer(serializers.ModelSerializer):
    tags = serializers.ListField(
        child=serializers.CharField(max_length=50),
        required=False,  # 태그는 없을 수도 있음
        write_only=True,  # 저장할 때만 사용 (응답에는 포함 X)
    )

    class Meta:
        model = Post
        fields = [
            "title",
            "content",
            "thumbnail",
            "tags",
        ]

—————————————————————————————————————————————[비교]————————————————————————————————————————————————
class PostCreateSerializer(serializers.ModelSerializer):
    tags = serializers.ListField(
        child=serializers.CharField(max_length=50),
        required=False,  # 태그는 없을 수도 있음
        write_only=True,  # 저장할 때만 사용 (응답에는 포함 X)
    )

    class Meta:
        model = Post
        fields = [
            "title",
            "content",
            "thumbnail",
            "summary",
            "is_temp",
            "tags",
        ]
        # 특정 필드에 대한 추가 설정을 지정
        extra_kwargs = {
            'summary': {'required': False, 'allow_blank': True}, # 요약은 필수가 아니며 빈 값도 허용
            'is_temp': {'default': False}, # 기본적으로는 정식 발행(False) 상태로 처리
        }

profile
안녕하세요.

0개의 댓글