프로젝트

Django에서 Social Account 연동 시 IntegrityError 해결 방법

content0474 2025. 1. 8. 21:06

문제

Django 프로젝트에서 여러 소셜 계정을 연동할 때, 동일한 social_id를 가진 계정이 다른 provider로 등록하려고 하면 IntegrityError가 발생

문제 발생 배경
CustomSocialAccount 모델의 기존 설정에서 uid 필드가 unique=True로 설정되어 있었음
이로 인해 social_id가 동일한 계정을 다른 소셜 제공자(provider)로 등록하려고 할 때도 중복으로 간주하여 IntegrityError가 발생함
예를 들어 kakao와 discord에서 동일한 social_id로 가입하려는 경우 문제가 발생함
이러한 설정은 여러 소셜 계정에 동일한 ID를 사용하는 일반적인 사용자 패턴을 처리하지 못하므로 수정이 필요함

기존 모델 및 뷰 코드
CustomSocialAccount 모델

class CustomSocialAccount(models.Model):  
    user = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="custom_social_accounts"
    )
    provider = models.CharField(max_length=50)
    uid = models.CharField(max_length=255, unique=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return f"{self.provider} - {self.user.username}"


uid 필드가 unique=True로 설정되어 있어 social_id 값의 중복 여부만 검사함
provider와의 조합에 따라 중복을 허용하도록 설정하지 못함


LinkSocialAccountView 뷰

class LinkSocialAccountView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        provider = request.data.get('provider')
        social_id = request.data.get('social_id')
        user = request.user

        if CustomSocialAccount.objects.filter(user=user, provider=provider).exists():
            return Response({"message": "이미 연결된 소셜 계정입니다."}, status=400)

        CustomSocialAccount.objects.create(user=user, provider=provider, uid=social_id)
        if provider not in user.connected_social_providers:
            user.connected_social_providers.append(provider)
        user.is_social_connected = True
        user.save()

        return Response({"message": f"{provider} 계정이 성공적으로 연결되었습니다."})


중복 검사가 user와 provider만을 기준으로 이루어짐
동일한 social_id를 다른 provider로 등록하려고 하면 IntegrityError가 발생함

 


수정된 모델 및 뷰 코드
CustomSocialAccount 모델 수정

class CustomSocialAccount(models.Model):  
    user = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="custom_social_accounts"
    )
    provider = models.CharField(max_length=50)
    uid = models.CharField(max_length=255, unique=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = ('provider', 'uid')
        
    def __str__(self):
        return f"{self.provider} - {self.user.username}"


uid 필드에서 unique=True를 제거하고 대신 provider와 uid의 조합을 unique_together로 설정함
이를 통해 동일한 social_id를 가진 계정을 다른 소셜 제공자에서 사용할 수 있도록 함
LinkSocialAccountView 뷰 수정

class LinkSocialAccountView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        provider = request.data.get('provider')
        social_id = request.data.get('social_id')
        user = request.user

        if CustomSocialAccount.objects.filter(provider=provider, uid=social_id).exists():
            return Response({
                "status": "error",
                "message": "이미 연결된 소셜 계정입니다."
            }, status=400)

        CustomSocialAccount.objects.create(user=user, provider=provider, uid=social_id)
        if provider not in user.connected_social_providers:
            user.connected_social_providers.append(provider)
        user.is_social_connected = True
        user.save()

        return Response({"message": f"{provider} 계정이 성공적으로 연결되었습니다."})


중복 검사를 provider와 uid 조합으로 변경하여 같은 social_id라도 다른 provider에서는 새로운 계정으로 등록 가능하게 수정


수정 후 결과
동일한 social_id를 사용하는 계정이라도 provider가 다르면 중복 없이 정상적으로 계정을 연결 가능

추가 설명
unique_together를 사용하면 데이터베이스 수준에서 provider와 uid의 조합이 고유하도록 보장
이러한 방식은 소셜 계정 연동뿐 아니라 여러 필드 조합으로 고유성을 보장해야 하는 다양한 경우에 활용 가능