Post

사용자 중심 설계: AInfo의 회원가입과 프로필 페이지 기능 구현

AInfo 프로젝트에서 사용자 인증과 마이페이지 기능을 어떤 구조로 설계하고 구현했는지 상세히 설명합니다. 사용자 경험을 고려한 API 설계와 Django 커스텀 유저 모델 구성까지 함께 소개합니다.

사용자 중심 설계: AInfo의 회원가입과 프로필 페이지 기능 구현

커스텀 유저 모델

Django 기본 유저 모델은 username 기반이라, 이메일 로그인 중심의 서비스에는 적합하지 않았습니다. 프로젝트 초기부터 AbstractBaseUser를 상속받아 이메일 기반 커스텀 유저 모델을 정의했습니다.

1
2
3
4
5
class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    password = models.CharField(max_length=128)
    ...
    USERNAME_FIELD = "email"

또한 지역(SubRegion), 관심분야(Interest), 학력(EducationLevel), 현재상태(CurrentStatus)를 별도 테이블로 분리해 선택지를 유연하게 관리할 수 있도록 했습니다. 이 구조는 마이페이지에서 유저가 정보를 쉽게 수정할 수 있게 만들어줍니다.

회원가입 API: 유효성 검사와 이메일 인증까지

회원가입은 SignupViewSignupSerializer로 구성했습니다. Django 기본 비밀번호 유효성 검사기를 활용해 비밀번호 규칙을 검증하고, 이메일 수집 동의는 필수 조건으로 설정했습니다.

회원가입이 완료되면 perform_create() 메서드에서 이메일 인증 링크를 발송합니다. 이 부분은 Celery와 send_verify_email.delay()를 활용해 비동기로 처리했습니다.

1
2
3
4
def perform_create(self, serializer):
    user = serializer.save()
    current_site = get_current_site(self.request)
    send_verify_email.delay(user.id, user.email, current_site.domain)

이메일 인증을 하지 않으면 로그인할 수 없도록 LoginView에서 체크를 추가해 사용자 신뢰성을 높였습니다.

로그인 & 로그아웃: JWT 기반 인증

우리는 rest_framework_simplejwt를 사용해 JWT 인증을 적용했습니다. 로그인 시 accessrefresh 토큰을 함께 발급하며, 로그아웃 시에는 refresh 토큰을 블랙리스트에 등록하여 만료 처리합니다.

1
2
3
4
5
refresh = RefreshToken.for_user(user)
return Response({
    "access": str(refresh.access_token),
    "refresh": str(refresh),
})

추가로 TokenRefreshView/api/v1/accounts/token/refresh/ 경로에 등록해 자동 로그인 기능도 지원합니다.

소셜 로그인: 카카오 & 구글

AInfo는 카카오와 구글 소셜 로그인을 지원합니다. 둘 다 access token(ID token)을 받아 사용자 정보를 가져오고, 기존 유저가 없으면 새로운 유저를 생성하는 방식입니다.

  • 이메일이 없을 경우 임시 이메일 생성 (kakao_12345@kakao.com)
  • 이름은 nickname 또는 Google 프로필에서 받아옴

소셜 로그인 시 약관 동의가 누락된 경우 agree_check: true를 응답값에 포함해 프론트에서 별도 안내를 할 수 있도록 설계했습니다.

프로필 페이지: 정보 조회 & 수정

유저가 자신의 프로필을 확인하고 수정할 수 있도록 GET, PUT 메서드를 지원하는 ProfileView를 구현했습니다.

UserSerializer는 Nested Serializer와 PrimaryKeyRelatedField를 조합하여:

  • 조회 시에는 직렬화된 객체 반환
  • 수정 시에는 ID 값으로 외래키 필드를 업데이트
1
2
3
4
5
6
7
class UserSerializer(serializers.ModelSerializer):
    interests = InterestSerializer(many=True, read_only=True)
    interests_ids = serializers.PrimaryKeyRelatedField(queryset=Interest.objects.all(), many=True, write_only=True)

    def update(self, instance, validated_data):
        instance.interests.set(validated_data.pop("interests_ids", []))
        ...

이 구조는 복잡한 사용자 정보를 효과적으로 조회하고 수정할 수 있도록 도와주며, 프론트엔드에서도 구현이 용이합니다.

기타 기능들

  • 회원 탈퇴: DELETE 요청으로 자신의 계정을 삭제
  • 비밀번호 재설정: 이메일로 링크를 보내 비밀번호 재설정 페이지로 이동
  • 이메일 인증 처리: 이메일 링크 클릭 시 서버에서 email_verified = True 처리
  • 포인트(Credit) 조회: 사용자가 보유한 포인트 반환
This post is licensed under CC BY 4.0 by the author.