-
[Django] Login(로그인), Comment(댓글) 관련 간단한 endpoint 만들어보기 - 2Software, Computer Science/Django, Flask 2020. 2. 10. 10:24
바로 직전에 이어서 Login과 Comment 기능을 수정하고자 한다. 멘토님의 조언을 받아 해당 api를 만드는데 있어 아래와 같은 기준을 적용하고자 한다.
기능과 성격의 연관성을 고려하여 app을 만들자(ex) account 관련 app과 comment 관련 app은 분리해서 구현)
처음부터 너무 여러가지 경우의 수를 한번에 고려하지 않고 가장 단순하고 최소의 기능만 먼저 완벽하게 구현해보자. 그러나 2~3가지 이상의 복잡한 조건에 대한 기능을 한번에 구현하려면 구현중에 많은 에러와 버그를 만날 가능성이 높아져 효율적인 기능 구현이 어려워 지기 때문에 일단 단일 유형의 계정을 하나 정하고 해당 계정과 비밀번호를 잘 받아오는지를 판단하게끔 구현하는 것으로 방향을 잡는다.
계정 앱
views.py
에는SignInView
,SignUpView
를 구현하여 간단히 가입 기능도 추가하여 테스트에러처리는 status 유형에 맞춰서 응답만 주는 식으로 하고 성공 및 실패여부 경우의 메세지는 최대한 간단하게(ex> 성공 : 'SUCCESS', 실패 : 'Fail' 등)
기존 app 정리 및 새롭게 app 구성
App 설계를 새로 하게 되어서 project 내의 app을 먼저 정리했다. 그리고 account와 comment 앱을 새로 생성했다.
또한 git이 설정된 프로젝트이고 app 구조를 전체적으로 수정하여 아래와 같은 과정을 거친 뒤 account와 comment 앱을 새로 만들었다.
settings.py
에INSTALLED_APP
부분에서 기존에 사용했던 app을 제거<project-name>/urls.py
에서 기존에 사용했던 url을 모두 지워준다- db.sqlite3에 들어가서 사용했던 db테이블을 제거해준다
account ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py └── views.py
comment ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py └── views.py
새롭게 구성한 앱의 tree구조는 위와 같고 해당 app에 맞춰서
urls.py
를 수정해주고 각 app에서도 URLConf 설정을 진행한다.# westargram_api/urls.py from django.urls import path, include urlpatterns = [ path('account', include('account.urls')), path('comment', include('comment.urls')) ]
# westargram_api/settings.py 중에서 INSTALLED_APPS = [ # 'django.contrib.admin', # 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'account', 'comment', ]
먼저 account에서
models.py
를 아래와 같이 구성한다. 실제 GET, POST 사용법에 맞춰서 아래와 같이 구현할 수 있다.# account/models.py from django.db import models class Account(models.Model): user_account = models.CharField(max_length=50), password = models.CharField(max_length=300) created_at = models.DateTimeField(auto_now_add=True), updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'user_account'
# account/urls.py from django.urls import path from .views import SignUpView, SignInView urlpatterns = [ path('/sign-up', SignUpView.as_view()), path('/sign-in', SignInView.as_view()) ]
# comment/models.py from django.db import models class Comment(models.Model): user_account = models.CharField(max_length=50), comments = models.CharField(max_length=700) created_at = models.DateTimeField(auto_now_add=True), updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'user_comment'
# comment/urls.py from django.urls import path from .views import CommentView urlpatterns = [ path('', CommentView.as_view()), ]
다음으로
views.py
를 마저 완성시켜 준다. 기존에 작성했던 api의 로직과는 크게 차이가 없고 app을 나누고 예외처리를 해줄 부분을 해주게 되면 아래와 같다.# account/views.py import json from django.views import View from django.http import HttpResponse, JsonResponse from .models import Account class SignInView(View): def post(): account_data = json.loads(request.body) try: if Account.objects.filter(user_account=account_data['user_account']).exists(): account = Account.objects.get(user_account=account_data['user_account']) if account.password == account_data['password']: return JsonResponse({'message':'Welcome back!'}, status=200) return HttpResponse(status=401) return HttpResponse(status=400) except KeyError: return HttpResponse(status=400) class SignUpView(View): def post(self, request): account_data = json.loads(request.body) try: if not Account.objects.filter(user_account=account_data['user_account']).exists(): Account( user_account=account_data['user_account'], password=account_data['password'] ).save() else: return HttpResponse(status=409) except KeyError: return HttpResponse(status=400) return JsonResponse({'message':'SUCCESS'}, status=200)
- sign-in : 기존에 user 등록 부분을 적당한 queryset을 활용하여 DB에 데이터가 있는지 확인한 다음 해당 계정 정보가 있다면 password 정보를 활용해서 response를 하게 되어 있으며 그 외의 경우는 status에 맞춰 예외처리를 하였다.
- sign-up : user가 이미 등록이 되었는지와 제대로 된 회원 가입 정보를 입력했는지 확인한 다음(계정충돌이 일어나면 409 에러를 발생시킴) 없다면 새로 모델 객체를 생성하여 DB 테이블에 저장하게끔 구성했다.
실제로 테스트를 진행하기 위해
python manage.py sqlmigrate
명령어를 실행하여 아래와 같은 작업을 진행>>> Account.objects.create(user_account='abc', password=1234) >>> Account.objects.all().values() <QuerySet [{'id': 1, 'user_account': 'abc', 'password': '1234', 'created_at': datetime.datetime(2020, 2, 8, 11, 20, 11, 842314, tzinfo=<UTC>), 'updated_at': datetime.datetime(2020, 2, 8, 11, 20, 11, 842371, tzinfo=<UTC>)}]> >>> Account(user_account='bcd', password=4567).save() >>> Account.objects.all().values() <QuerySet [{'id': 1, 'user_account': 'abc', 'password': '1234', 'created_at': datetime.datetime(2020, 2, 8, 11, 20, 11, 842314, tzinfo=<UTC>), 'updated_at': datetime.datetime(2020, 2, 8, 11, 20, 11, 842371, tzinfo=<UTC>)}, {'id': 2, 'user_account': 'bcd', 'password': '4567', 'created_at': datetime.datetime(2020, 2, 8, 11, 22, 13, 846972, tzinfo=<UTC>), 'updated_at': datetime.datetime(2020, 2, 8, 11, 22, 13, 847010, tzinfo=<UTC>)}]>
* project 경로에 있는
urls.py
설정에 유의 ! <url이름>/ or <url 이름>으로 할 것그렇지 않으면 404 NOT Found가 뜨면서 테스트 자체가 안되었다. urlpatterns 명시에 주의. project 상단에서 app까지 어떻게 이어지는지 잘 생각해서 패턴을 명시할 것.
# comment/views.py import json from django.views import View from django.http import HttpResponse, JsonResponse from .models import Comment class CommentView(View): def post(self, request): comments_data = json.loads(request.body) Comment( user_account=comments_data['user_account'], comments=comments_data['comments'] ).save() return JsonResponse({'message':'SUCCESS'}, status=200) def get(self, request): return JsonResponse({'comments':list(Comment.objects.values())}, status=200)
comment 앱 부분도 다음과 같이 명시해 주고 테스트들을 하면 다음과 같은 결과를 얻을 수 있었다.
실제 작동여부에 대한 테스트를 진행
Account 앱 테스트
$ http -v http://127.0.0.1:8000/account/sign-in user_account=abc password=1234 POST /account/sign-in HTTP/1.1 Accept: application/json, */* Accept-Encoding: gzip, deflate Connection: keep-alive Content-Length: 43 Content-Type: application/json Host: 127.0.0.1:8000 User-Agent: HTTPie/2.0.0 { "password": "1234", "user_account": "abc" } HTTP/1.1 200 OK Content-Length: 28 Content-Type: application/json Date: Sat, 08 Feb 2020 12:18:13 GMT Server: WSGIServer/0.2 CPython/3.8.1 X-Content-Type-Options: nosniff X-Frame-Options: DENY { "message": "Welcome back!" }
$ http -v http://127.0.0.1:8000/account/sign-in user_account=bcd password=45 POST /account/sign-in HTTP/1.1 Accept: application/json, */* Accept-Encoding: gzip, deflate Connection: keep-alive Content-Length: 41 Content-Type: application/json Host: 127.0.0.1:8000 User-Agent: HTTPie/2.0.0 { "password": "45", "user_account": "bcd" } HTTP/1.1 401 Unauthorized Content-Length: 0 Content-Type: text/html; charset=utf-8 Date: Sat, 08 Feb 2020 12:20:35 GMT Server: WSGIServer/0.2 CPython/3.8.1 X-Content-Type-Options: nosniff X-Frame-Options: DENY
$ http -v http://127.0.0.1:8000/account/sign-up user_account=abc password=5678 POST /account/sign-up HTTP/1.1 Accept: application/json, */* Accept-Encoding: gzip, deflate Connection: keep-alive Content-Length: 43 Content-Type: application/json Host: 127.0.0.1:8000 User-Agent: HTTPie/2.0.0 { "password": "5678", "user_account": "abc" } HTTP/1.1 409 Conflict Content-Length: 0 Content-Type: text/html; charset=utf-8 Date: Sat, 08 Feb 2020 12:21:39 GMT Server: WSGIServer/0.2 CPython/3.8.1 X-Content-Type-Options: nosniff X-Frame-Options: DENY
$ http -v http://127.0.0.1:8000/account/sign-up user_account=efg password=5678 POST /account/sign-up HTTP/1.1 Accept: application/json, */* Accept-Encoding: gzip, deflate Connection: keep-alive Content-Length: 43 Content-Type: application/json Host: 127.0.0.1:8000 User-Agent: HTTPie/2.0.0 { "password": "5678", "user_account": "efg" } HTTP/1.1 200 OK Content-Length: 22 Content-Type: application/json Date: Sat, 08 Feb 2020 12:21:53 GMT Server: WSGIServer/0.2 CPython/3.8.1 X-Content-Type-Options: nosniff X-Frame-Options: DENY { "message": "SUCCESS" }
Comment 앱 테스트
$ http -v http://127.0.0.1:8000/comment user_account=def comments=hajdhklfjasdklfjldsk POST /comment HTTP/1.1 Accept: application/json, */* Accept-Encoding: gzip, deflate Connection: keep-alive Content-Length: 59 Content-Type: application/json Host: 127.0.0.1:8000 User-Agent: HTTPie/2.0.0 { "comments": "hajdhklfjasdklfjldsk", "user_account": "def" } HTTP/1.1 200 OK Content-Length: 22 Content-Type: application/json Date: Sat, 08 Feb 2020 12:15:46 GMT Server: WSGIServer/0.2 CPython/3.8.1 X-Content-Type-Options: nosniff X-Frame-Options: DENY { "message": "SUCCESS" } $ http -v http://127.0.0.1:8000/comment user_account=dgdgd comments=gotohell POST /comment HTTP/1.1 Accept: application/json, */* Accept-Encoding: gzip, deflate Connection: keep-alive Content-Length: 49 Content-Type: application/json Host: 127.0.0.1:8000 User-Agent: HTTPie/2.0.0 { "comments": "gotohome", "user_account": "dgdgd" } HTTP/1.1 200 OK Content-Length: 22 Content-Type: application/json Date: Sat, 08 Feb 2020 12:16:25 GMT Server: WSGIServer/0.2 CPython/3.8.1 X-Content-Type-Options: nosniff X-Frame-Options: DENY { "message": "SUCCESS" }
http -v http://127.0.0.1:8000/comment GET /comment HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Connection: keep-alive Host: 127.0.0.1:8000 User-Agent: HTTPie/2.0.0 HTTP/1.1 200 OK Content-Length: 312 Content-Type: application/json Date: Sat, 08 Feb 2020 12:16:28 GMT Server: WSGIServer/0.2 CPython/3.8.1 X-Content-Type-Options: nosniff X-Frame-Options: DENY { "comments": [ { "comments": "hajdhklfjasdklfjldsk", "created_at": "2020-02-08T12:15:46.264Z", "id": 1, "updated_at": "2020-02-08T12:15:46.264Z", "user_account": "def" }, { "comments": "gotohome", "created_at": "2020-02-08T12:16:25.028Z", "id": 2, "updated_at": "2020-02-08T12:16:25.028Z", "user_account": "dgdgd" } ] }
Reference
- django에서 등록한 app 삭제하는 방법 정리 : https://zladnrms.tistory.com/104
'Software, Computer Science > Django, Flask' 카테고리의 다른 글
[Django] Login(로그인), Comment(댓글) 관련 간단한 endpoint 만들어보기 - 1 (0) 2020.02.08 [Django] 간단한 endpoint를 django 에서 구현해 보기 (0) 2020.02.07 [Django] Django MVT 패턴에 관한 내용 정리, url이 분석되는 순서 (0) 2020.02.05 [Django tutorial] django official tutorial part4 정리 (0) 2020.02.03 [Django tutorial] django official tutorial part3 정리 (0) 2020.02.03 댓글