프론트에서 장고로 요청하는 거 까지 #1~#3까지 완성했다. 이제는 장고에서 request에 대한 response를 쏴줄 차례이다. 인터넷 검색하면서 나름 공부를 하면서 어떻게 설계해야할지 고민했는데, 보통 DjangoRestFramework(DRF)를 많이 사용하는 듯 했다. 자동으로 시리얼라이징 해서 json화 해준다고도 하고 해서 그러나 난 이걸 쓰기전에 장고의 기본 기능만을 사용해서 구현해보고 싶었고, DRF를 알기전 진짜 완전 베이스를 알아야한다고 생각해서, 내가 아는 장고지식을 총 동원해서 써봤다. 이렇게 말하니까 엄청 거창해보인다. 완성 시키려고 하는데도 몇시간동안 이런저런 오류를 잡느니라고 고생했었는데 막상 완성한 코드들 보니까 뭐가 너무 없다.. ㅎㅎ 지금은 처음이니까!!
#CORS 해결법
연동하면서 발견한 것 중 하나는 CORS이다. 옛날에 학부에서 대충 설명을 들었었는데 같은 URL에서만 요청한 것들만 요청-응답을 주고 받을 수 있게 해주는 것이라고 했다. 보안상 이유라고 했으나 당연히 내 프로젝트에서는 CORS를 신경쓸 이유가 없었다. 간단히 장고에서 pip 를 통해 corsheaders를 설치 해주는것으로 해결 가능했다. corsheaders를 설치해준후 장고의 setting.py에서 몇가지를 더 추가해저야 한다.
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
# ...
# ...
# ..
]
해당 코드를 추가해 준 후,
CORS_ORIGIN_ALLOW_ALL = True
맨 아래 추가해줌으로써 CORS에 대한 대처가 가능했다.
# url구조
from django.urls import path
from . import views
app_name = 'api'
urlpatterns = [
path('post/',views.post,name="post"), # login
path('post/<int:id>/',views.delete,name="delete") #del
]
post/ 로는 get,post 요청을 받고, post/<int:id> 로는 delete요청을 받을것이다. 그에대한 view는 post와 delete두개인데 생각해보니까 post에 3개를 써도 됐다. Method별로 분리해서 말이다. 다음에는 그렇게 잡아서 일관성을 줘야겠다.
# POST
import json
def post(req) :
if req.method == "POST" :
json_body = json.loads(req.body)
print(json_body['name'])
item = Post(name=json_body['name'], price=json_body['price'])
item.save()
else :
print("h")
return HttpResponse(req.body)
여기서도 json.loads로 req.body를 json화 시켜줬다. 그냥 type(req.body)를 해보면 bytes 타입이라고 떴다. 이렇게 json화 시키지 않으면 한글이 이렇게 이상하게 깨져서 나온다. 저건 몇진수일까.. 그래서 어떻게 어떻게 계속 찾아보다가 req헤더를 리액트에서 json으로 날려줘보기도 하고 다해봤지만, json.loads로 파이썬 객체화 시켜줘야하는 것이 답이었다.
그렇게 하고 난후, 파이썬에서는 어떻게보면 json형식은 dictionary이다. 그렇기 때문name=json_body['name'], price=json_body['price']와 같이 value값을 찾아서 Post 모델 객체를 생성하고 저장시켰다.
++추가 json.loads() 는 json형식을 파이썬 객체화 시켜주는 파이썬의 json모듈의 기능중 하나이다
# GET
from django.core import serializers
import json
def post(req) :
if req.method == "POST" :
print(req.body)
print(type(req.body))
json_body = json.loads(req.body)
print(json_body['name'])
item = Post(name=json_body['name'], price=json_body['price'])
item.save()
elif req.method == "GET" :
posts = Post.objects.all()
json_posts = serializers.serialize("json", posts)
print(json_posts)
return JsonResponse(json_posts,safe=False) # safe 옵션은 뭘까?
else :
print("err")
return HttpResponse(req.body)
그 밑에 "GET"요청이 올때도 해결해 줘야 한다. 이제 가장 먼저 드는 생각은 모든지 json화가 필요하다는 것이다. 찾아본 결과 DRF를 사용하면 이 시리얼라이즈(직렬화)를 아주 자동(?)으로 해줄 수 있다고 한다. 하지만 나는 DRF를 사용하지 않고 구현하다 보니까 이 json화 과정에서 항상 한번씩 손을 써줘야 했다.
GET요청은 현재 DB에 있는 모든 Item들을 [{..},{..},{..}]느낌으로 날려줘야한다. 이걸 리액트에서 받아서 사용해야 하는 것인데, 일단 Post.objects.all()을 통해 모든 Item들을 가져올 것이다. 여기서 한가지 문제가 발생한다. 이렇게 가져온 것은 쿼리셋(querySet)인데 이건 그냥 json화 시킬 수 없었다. 그래서 serializers.serialize() 를 사용해야 했다.
JsonResponse는 HttpResponse의 subclass이다. HttpResponse로 Json을 전송 할 수 있다고 하는데, 나는 그냥 JsonResponse를 사용했다. DRF를 사용해서 통신할때는 DRF의 모듈인 Response를 사용한다고 한다. 이건 잘 정리된 블로그가 있어서 맨아래에 출처를 남기도록 하겠다. JsonResponse은 옵션중에 status를 날려줄수도 있어서
JsonResponse(json_posts,safe=False, status=200)
safe 옵션을 준 이유는 딕셔너리가 아닌 다른 타입이 파싱이 되어야 한다면 'safe=False' 옵션 필요하다고 한다. 사실 저 옵션을 주지 않았을때 안가서 False를 줘보니까 갔다. 다른분들이 설명한걸 보니까, 굳이 serializers.serialize을 하지않고 list로 변환시켜서 보내도 되는거 같았다. 이것 또한 아래에 링크를 참조시키도록 하겠다.
# DELETE
def delete(req,id) :
if req.method == 'DELETE' :
if Post.objects.filter(id=id).exists() :
post = Post.objects.get(id=id)
post.delete()
return HttpResponse("DEL COMPLETE",status=200)
delete는 정말 간단하다. 해당 id가 DB에 존재하는지 확인하고, 해당 모델을 지워준다. 그리고 꼭 return을 해줘야한다. return이 없으면 장고에서는 해당 view를 실행시키지 않았다. 간단한 메시지와 함께, status=200 으로 잘 도착했다고 알려주자.
사실 try - catch 구문으로 에러처리를 확실히 해야했지만, 뭔가 조금 급급한 마음이 있어서. 그리고 이게 되는지 너무 궁금한 마음에 후다닥 코드만 쳣던거 같다. 백엔드개발자가 되기위해서 하나씩 해보고 있는데 프론트도 나름 재밌다. 그리고 프론트도 알아야지 백엔드를 할 수 있다고 하니까, 뭐 좋은게 좋은거다~ 다음에는 로컬 로그인 구현해보고 이걸 세션과 쿠키를 통해 로그인 유지하는거 구현해볼 생각이다. 사실 이전에 이거 먼저 구현해보려고 찾아봤었는데 생각보다 어려... 워서 이거 먼저 해봤는데 그래도 나름 자신감이 생겼다!
json관련되서 좋은글 두개를 첨부하도록 하겠다. 다음에 막히면 들어와서 이분들꺼 다시 봐야겠다.