2026/04/15

김기훈·2026년 4월 15일

TIL

목록 보기
191/194

약속

  • 생성 / 초대 / 권한 부여 / 강퇴

생성

시리얼라이저

class EventCreateSerializer(serializers.Serializer):
    """약속 생성을 위해 클라이언트가 보내는 데이터를 검증하는 클래스"""
    
    # 약속의 제목이 문자열 형태인지, 최대 길이를 넘지 않는지 검증
    title = serializers.CharField(max_length=100)
    # 비즈니스 목적 여부가 참/거짓 논리형인지 검증하며, 기본값은 거짓
    is_business = serializers.BooleanField(default=False)

서비스

class EventService:
    """약속 자체와 관련된 핵심 로직을 담당하는 서비스 클래스"""
    def create_event(validated_data, user):
        # 1. 전달받은 검증된 데이터와 요청한 유저를 방장으로 설정하여 약속 데이터를 만듬
        event = Event.objects.create(
            # 딕셔너리에서 제목 값을 추출해 저장
            title=validated_data["title"],
            # 딕셔너리에서 비즈니스 여부를 추출하며, 없으면 거짓을 대입
            is_business=validated_data.get("is_business", False),
            # 약속을 만든 현재 사용자를 방장으로 지정
            host=user
        )
        # 2. 방장 본인을 약속의 첫 번째 멤버로 등록하며, 초대 권한을 부여
        EventMember.objects.create(event=event, user=user, can_invite=True)
        return event

    def post(self, request):
        # 1. 입력데이터 검증
        serializer = EventCreateSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 2. 통과된 데이터를 서비스 레이어로 넘겨 실제 약속을 만듬
        event = EventService.create_event(serializer.validated_data, request.user)

        return Response(
            {"message": "약속이 성공적으로 생성되었습니다."},
            status=status.HTTP_201_CREATED,
        )

테스트


초대

serializer

class EventMemberManageSerializer(serializers.Serializer):
    """약속 참여자를 관리(초대, 권한부여, 강퇴)할 때 필요한 데이터를 검증하는 클래스"""
    # 대상이 되는 유저의 고유 번호(ID)가 숫자 형태인지 검사하여 입력받음
    target_user_id = serializers.IntegerField()

service

class EventMemberService:
    """약속 내부의 참여자들을 관리하는 로직을 담당하는 서비스 클래스"""

    def invite_user(event_id, target_user_id, request_user):
        # 1. 전달받은 아이디로 약속을 찾고, 없으면 에러를 발생시킴
        event = get_object_or_404(Event, id=event_id)
        # 2. 초대를 요청한 사람이 해당 약속에 소속되어 있는지 확인
        inviter = get_object_or_404(EventMember, event=event, user=request_user)
        # 3. 요청자가 방장이거나, 멤버십 속성에 초대 권한이 있는지 검사
        if event.host == request_user or inviter.can_invite:
            # 장고 프로젝트에 설정된 기본 회원 모델을 불러옴
            User = get_user_model()
            # 대상 유저 객체를 데이터베이스에서 찾아냄
            target_user = get_object_or_404(User, id=target_user_id)
            # 대상 유저를 해당 약속의 멤버로 새롭게 등록
            EventMember.objects.create(event=event, user=target_user)
            return True
        return False

view

class EventInviteView(APIView):
    """초대 기능만을 전담하는 뷰 클래스"""
    permission_classes = [IsAuthenticated]

    # API 문서에 초대 기능으로 명시합니다.
    @extend_schema(tags=["참여자관리"], summary="약속 초대", request=EventMemberManageSerializer)
    def post(self, request, event_id):
        # 1. 입력데이터 검증
        serializer = EventMemberManageSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 2. 검증된 대상 유저의 아이디를 가져옴
        target_user_id = serializer.validated_data["target_user_id"]
        # 3. 초대 서비스 로직을 실행
        success = EventMemberService.invite_user(event_id, target_user_id, request.user)

        # 4. 권한이 있어 초대가 성공한 경우
        if success:
            return Response({"message": "성공적으로 초대했습니다."}, status=status.HTTP_200_OK)
        # 5. 초대 권한이 없어 실패한 경우
        return Response({"message": "초대 권한이 없습니다."}, status=status.HTTP_403_FORBIDDEN)

테스트


초대 권한

service

class EventMemberService:
    """약속 내부의 참여자들을 관리하는 로직을 담당하는 서비스 클래스"""
    
						...

    def grant_permission(event_id, target_user_id, request_user):
        # 1. 전달받은 아이디로 약속을 찾고, 없으면 에러를 발생시킴
        event = get_object_or_404(Event, id=event_id)
        # 2. 이 기능은 오직 방장만 수행할 수 있도록 조건을 확인
        if event.host == request_user:
            # 권한을 받을 멤버의 정보를 찾아냄
            target_member = get_object_or_404(EventMember, event=event, user_id=target_user_id)
            # 멤버의 초대 권한 속성을 참으로 켜줌
            target_member.can_invite = True
            # 변경된 내용을 데이터베이스에 최종 저장
            target_member.save()
            return True
        return False

view

class EventGrantPermissionView(APIView):
    """권한 부여 기능만을 전담하는 뷰 클래스"""

    permission_classes = [IsAuthenticated]

    @extend_schema(tags=["약속관리"], summary="초대 권한 부여", request=EventMemberManageSerializer)
    def post(self, request, event_id):
        # 1. 입력데이터 검증
        serializer = EventMemberManageSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 2. 권한을 줄 대상의 아이디를 추출
        target_user_id = serializer.validated_data["target_user_id"]
        # 3. 권한 부여 서비스 로직 요청
        success = EventMemberService.grant_permission(event_id, target_user_id, request.user)

        # 4. 방장이 수행하여 성공한 경우
        if success:
            return Response({"message": "초대 권한이 부여되었습니다."}, status=status.HTTP_200_OK)
        # 5. 방장이 아니라서 막힌 경우
        return Response({"message": "권한 부여는 방장만 가능합니다."}, status=status.HTTP_403_FORBIDDEN)

테스트


멤버 강퇴

service

    def kick_user(event_id, target_user_id, request_user):
        """약속 방에서 특정 인원을 강제로 내보내는 메서드"""
        # 1. 전달받은 아이디로 약속을 찾고, 없으면 에러를 발생시킴
        event = get_object_or_404(Event, id=event_id)
        # 2. 이 기능은 오직 방장만 수행할 수 있도록 조건을 확인
        if event.host == request_user:
            # 강퇴할 대상의 멤버십 정보를 찾음
            target_member = get_object_or_404(EventMember, event=event, user_id=target_user_id)
            # 해당 멤버의 데이터를 데이터베이스에서 지움
            target_member.delete()
            return True
        return False

view

class EventKickView(APIView):
    """멤버 강퇴 기능만을 전담하는 뷰 클래스"""
    permission_classes = [IsAuthenticated]

    @extend_schema(tags=["약속관리"], summary="멤버 강퇴", request=EventMemberManageSerializer)
    def post(self, request, event_id):
        # 1. 입력데이터 검증
        serializer = EventMemberManageSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 2. 권한을 줄 대상의 아이디를 추출
        target_user_id = serializer.validated_data["target_user_id"]
        # 3. 권한 부여 서비스 로직 요청
        success = EventMemberService.kick_user(
            event_id, target_user_id, request.user
        )

        # 4. 강퇴에 성공했을 경우
        if success:
            return Response(status=status.HTTP_204_NO_CONTENT)
        # 5. 거절된 경우
        return Response({"message": "강퇴는 방장만 가능합니다."}, status=status.HTTP_403_FORBIDDEN)

테스트


오늘의 작업

profile
안녕하세요.

0개의 댓글