필자는 앞에서도 얘기했듯이 은둔형 외톨이를 위한 서비스를 기획하고 개발하고 있다.
https://srilankakim66.tistory.com/79
파이널 프로젝트-기획
필자는 부트캠프를 다니면서 마지막 과제인 파이널 프로젝트를 진행을 하고 있다.어느정도 진행이 된다음에 블로그를 작성하는 거라 설명이 세세하지 못하지만 블로그에 남겨보고자 한다. 조
srilankakim66.tistory.com
여기서 편지기능에 해당하는 API를 짜고 있는데 여기서 알게 된 점을 적어보고자 한다.
위에 블로그에서 작성을 해놨듯이 관리자가 질문지를 만들고 그 질문지를 사용자들에게 보내주고 사용자는 그 질문에 해당하는 답장을 작성하는 식의 편지기능을 만들고 있다. 그런데 여기서 질문지 밑에는 여러개의 답장이 존재하게 되고 요청을 한 사용자에 대한 답장만을 보여주는 식의 아키텍처로 설계를 했다. 그렇기에 클라이언트가 GET으로 답장을 요청한다면 우리는 선택적인 객체들만 가지고 와서 요청에 대한 응답으로 줘야 한다.
밑에 코드를 간단하게 봐보자.
get_object()
class AnswerDetailQuestion(APIView):
permission_classes = [IsAuthenticated]
authentication_classes = [JWTAuthentication]
def get_object(self, pk):
try:
question = Question.objects.get(pk=pk)
user = self.request.user # 프론트에서 전달된 JWT 토큰을 사용하여 사용자를 식별하는 코드이다. 프론트엔드에서 JWT토큰을 header로 보내주면 서버에서 JWT토큰을 받아서 user를 식별한다.
answer = Answer.objects.get(question=question, user=user)
return answer
except (Question.DoesNotExist, Answer.DoesNotExist):
raise NotFound({'message': '해당하는 데이터가 존재하지 않습니다.'})
def get(self, request, pk, format=None):
answer=self.get_object(pk)
self.check_object_permissions(request, answer)
serializer = AnswerSerializer(answer)
return Response(serializer.data)
여기서 보면 특정 질문지를 클릭을 했을 때 요청한 사용자와 같은 사용자의 특정 질문지에 대한 단일 답장을 보여주는 API이다.
여기서 특정한 단일 객체를 들고오기 위해서 get_object라는 함수를 정의해 줬다. 질문지에 id를 프론트에 요청으로 받는다면 프론트에서 header로 준 JWT토큰을 이용하여 사용자를 식별하고 그 사용자에 대해 요청으로 받은 질문지의 단일 답장을 전달 하는 방식이 된다.
단일 객체로 들고 오기 때문에 answer = Answer.objects.get(question=question, user=user)로 작성이 되어있는 것을 볼 수 있다.
그러나 밑에 잘못된 코드를 보자.
class AnswerOwnerList(APIView):
permission_classes = (IsOwnerOnly,)
authentication_classes = [JWTAuthentication]
def get_object(self, email):
try:
user = User.objects.get(email=email)
return Answer.objects.filter(user = user)
except User.DoesNotExist:
raise NotFound({'message': '이 요청은 존재하지 않는 요청입니다.'})
def get(self, request, email, format=None):
answer = self.get_object(email)
for answers in answer:
self.check_object_permissions(request, answers)
serializer = AnswerSerializer(answer, many=True)
return Response(serializer.data)
이것 자체가 아키텍처에 맞지 않은 API이지만 예시를 보기 위해서 가지고 왔다.
여기서 볼 때 필자는 get_object라고 함수를 만들었지만 get_object 함수 밑에 코드를 본다면 Answer.objects.filter()를 사용하는 것을 볼 수 있다.
필자는 이 코드를 짤때 문제점을 알지 못했다. 왜냐하면 잘 작동을 했기 때문이다. 하지만 잘못된 코드이다.
왜냐하면 get_object라는 함수 자체가 단일객체를 들고 올 때 사용하는 함수이다. 그렇기에 get 메서드를 사용해서 단일 객체를 가지고 온다면 문제가 되지 않지만 filter 메서드를 사용해서 들고 올 때에는 여러개의 객체(쿼리셋)로 들고 오기 때문에 따로 정의가 되어져 있는 함수가 있었다.
그렇다면 쿼리셋을 가지고 올 때 사용하는 함수는 뭔지 알아보았더니 get_queryset이라는 함수가 generic 라이브러리에 정의가 되어있었다. 밑에는 어떻게 정의가 되어있는지 올린 것이다.
그렇기에 filter 메서드를 통해서 여러개의 객체(쿼리셋)를 들고 올때에는 get_queryset을 사용해서 정의를 해줘야 한다.
이번에는 잘 만들어진 코드를 살펴보자.
get_queryset()
class AnswerList(APIView):
permission_classes = [IsAuthenticated]
authentication_classes = [JWTAuthentication]
def get_queryset(self):
user = self.request.user
answer = Answer.objects.filter(user=user)
if answer.exists():
return answer
else:
raise NotFound({'message': '해당하는 데이터가 존재하지 않습니다.'})
def get(self, request, format=None):
answer = self.get_queryset()
serializer = AnswerSerializer(answer, many=True)
return Response(serializer.data)
위에 코드는 답장 리스트들을 모두 가지고 오는 API이다. 여기서는 get_queryset라는 함수를 정의하고 filter 메서드를 이용해서 모든 답장의 객체들을 가지고 오는 것을 볼 수 있다.
이것을 통해서 데이터베이스에 특정 데이터만을 가지고 올 때 사용하는 get_object(), get_queryset() 함수를 알아보았다.
굉장히 많이 쓰이니 꼭 알아두어야 겠다.
@@
'프로젝트' 카테고리의 다른 글
파이널 프로젝트 - 트러블 슈팅(모델의 여러 필드중 특정 필드만 가져오기) (0) | 2024.05.29 |
---|---|
프로젝트 중 트러블 슈팅 - 예외처리 (0) | 2024.05.25 |
프로젝트 중 트러블 슈팅(권한 문제) (0) | 2024.05.25 |
파이널 프로젝트 - 편지기능 및 JWT디코딩(사용자 식별) (0) | 2024.05.24 |
파이널 프로젝트 - 트러블 슈팅(1) JWT를 이용한 로그인/회원가입 (0) | 2024.05.23 |