필수 기능 - MVP(Minimum Viable Product)
회원가입
Endpoint:
/api/accounts
Method:
POST
조건: username, 비밀번호, 이메일, 이름, 닉네임, 생일 필수 입력하며 성별, 자기소개 생략 가능
검증: username과 이메일은 유일해야 하며, 이메일 중복 검증(선택 기능).
구현: 데이터 검증 후 저장.
로그인
Endpoint:
/api/accounts/login
Method:
POST
조건: 사용자명과 비밀번호 입력 필요.
검증: 사용자명과 비밀번호가 데이터베이스의 기록과 일치해야 함.
구현: 성공적인 로그인 시 토큰을 발급하고, 실패 시 적절한 에러 메시지를 반환.
프로필 조회
Endpoint:
/api/accounts/<str:username>
Method:
GET
조건: 로그인 상태 필요.
검증: 로그인 한 사용자만 프로필 조회 가능
구현: 로그인한 사용자의 정보를 JSON 형태로 반환.
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from .models import CustomUser
from .serializers import UserSerializer
class AccountsView(APIView):
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
username = serializer.validated_data['username']
email = serializer.validated_data['email']
# username과 email 중복 체크
if User.objects.filter(username=username).exists():
return Response({"error": "이미 사용 중인 이름인데...."}, status=status.HTTP_400_BAD_REQUEST)
if User.objects.filter(email=email).exists():
return Response({"error": "이미 사용 중인 이메일인데...."}, status=status.HTTP_400_BAD_REQUEST)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class signupView(APIView):
permission_classes = [AllowAny] #접근 권한
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class LoginView(APIView):
def post(self, request):
username = request.data.get('username')
password = request.data.get('password')
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
refresh_token = RefreshToken.for_user(user)
data = {
'user_id': user.id,
'access_token': str(refresh_token.access_token),
'refresh_token': str(refresh_token)
}
return Response(data, status=status.HTTP_200_OK)
else:
return Response({"error": "아이디 및 비밀번호가 이상한데요?"}, status=status.HTTP_400_BAD_REQUEST)
class LogoutView(APIView):
def post(self, request):
token = RefreshToken(request.data.get('refresh'))
token.blacklist()
logout(request)
return Response({"message": "로그아웃 인가?"}, status=status.HTTP_200_OK)
class ProfileUpdateView(APIView):
permission_classes = [IsAuthenticated]
def put(self, request, username):
user = get_object_or_404(CustomUser, username=username)
# 인증된 사용자가 프로필의 소유자인지 확인
if request.user != user:
return Response({"error": "권한 읍서요"}, status=status.HTTP_403_FORBIDDEN)
serializer = UserSerializer(user, data=request.data, partial=True)
if serializer.is_valid():
# 이메일 중복 확인
new_email = serializer.validated_data.get('email')
if new_email and CustomUser.objects.exclude(pk=user.pk).filter(email=new_email).exists():
return Response({"error": "이미 존재한 이메일이에요."}, status=status.HTTP_400_BAD_REQUEST)
# 사용자명(ID)
new_username = serializer.validated_data.get('username')
if new_username and CustomUser.objects.exclude(pk=user.pk).filter(username=new_username).exists():
return Response({"error": "이미 존재한 아이디입니당."}, status=status.HTTP_400_BAD_REQUEST)
# 이름
new_name = serializer.validated_data.get('name')
if new_name and CustomUser.objects.exclude(pk=user.pk).filter(name=new_name).exists():
return Response({"error": "이미 존재한 이름입니당."}, status=status.HTTP_400_BAD_REQUEST)
# 닉네임
new_nickname = serializer.validated_data.get('nickname')
if new_nickname and CustomUser.objects.exclude(pk=user.pk).filter(name=new_nickname).exists():
return Response({"error": "이미 존재한 닉네임입니당."}, status=status.HTTP_400_BAD_REQUEST)
# 생일
new_birthday = serializer.validated_data.get('birthday')
if new_birthday and CustomUser.objects.exclude(pk=user.pk).filter(birthday=new_birthday).exists():
return Response(serializer.data, status=status.HTTP_200_OK)
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class ProfileView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request, username):
user = get_object_or_404(CustomUser, username=username)
serializer = UserSerializer(user)
return Response(serializer.data, status=status.HTTP_200_OK)
urls.py
from django.urls import path
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
from .views import AccountsView, signupView, LoginView, LogoutView, ProfileView, ProfileUpdateView
app_name = "accounts"
urlpatterns = [
path('', AccountsView.as_view(), name='user_list'),
path('signup/', signupView.as_view(), name='user_signup'),
path('login/',LoginView.as_view(), name='user_login'),
path('logout/', LogoutView.as_view(), name='user_logout'),
path('<str:username>/', ProfileView.as_view(), name='user_profile'),
path('<str:username>/update/', ProfileUpdateView.as_view(), name='user_profile_update'),
path("admin/signup/", TokenObtainPairView.as_view(), name="token_obtain_pair"),
path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),
]
models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
email = models.EmailField(unique=True)
name = models.CharField(max_length=100)
nickname = models.CharField(max_length=100)
birthday = models.DateField(null=True, blank=True)
gender = models.CharField(max_length=10, null=True, blank=True)
REQUIRED_FIELDS = ['name', 'nickname', 'birthday']
serializers.py
from rest_framework import serializers
from .models import CustomUser
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = ['username', 'password', 'email', 'name', 'nickname', 'birthday', 'gender']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = CustomUser.objects.create_user(
username=validated_data['username'],
email=validated_data['email'],
name=validated_data['name'],
nickname=validated_data['nickname'],
birthday=validated_data['birthday'],
gender=validated_data['gender'],
password=validated_data['password']
)
return user
상품 관련 기능 및 조건
상품 등록
상품 목록 조회
상품 수정
상품 삭제
필수 기능 - MVP(Minimum Viable Product)
상품 등록
Endpoint:
/api/products
Method:
POST
조건: 로그인 상태, 제목과 내용, 상품 이미지 입력 필요.
구현: 새 게시글 생성 및 데이터베이스 저장.
상품 목록 조회
Endpoint:
/api/products
Method:
GET
조건: 로그인 상태 불필요.
구현: 모든 상품 목록 페이지네이션으로 반환.
상품 수정
Endpoint:
/api/products/<int:productId>
Method:
PUT
조건: 로그인 상태, 수정 권한 있는 사용자(게시글 작성자)만 가능.
검증: 요청자가 게시글의 작성자와 일치하는지 확인.
구현: 입력된 정보로 기존 상품 정보를 업데이트.
상품 삭제
Endpoint:
/api/products/<int:productId>
Method:
DELETE
조건: 로그인 상태, 삭제 권한 있는 사용자(게시글 작성자)만 가능.
검증: 요청자가 게시글의 작성자와 일치하는지 확인.
구현: 해당 상품을 데이터베이스에서 삭제.
view.py
from django.shortcuts import render, get_object_or_404
from django.core import serializers
from rest_framework. decorators import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
from .models import Product
from .serializers import ProductSerializer
class product_view(APIView):
permission_classes = [IsAuthenticatedOrReadOnly]
def get(self, request):
products = Product.objects.all()
serializer = ProductSerializer(products, many=True)
return Response(serializer.data)
def post(self, request):
serializer = ProductSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
urls.py
from django.urls import path
from . import views
app_name = "products"
urlpatterns = [
path("", views.product_view.as_view(), name="list"),
path("<int:pk>/", views.detail_view.as_view(), name="detail"),
]
serializers.py
from rest_framework import serializers
from .models import Product
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = "__all__"
from django.db import models
# Create your models here.
class Product(models.Model):
title = models.CharField(max_length=100)
content = models.TextField(max_length=200)
image = models.ImageField(upload_to='products/', null=True, blank=True)
my_datetime = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
하면 기능 구현은 돼긴 하더라
그리고 https://github.com/aidenlim-dev/django-class 심화과제 해설 코드