- 인터페이스 일관성
- 어떤 기기에서 접근을 하던, 동일한 작업은 동일한 URL로 처리- 무상태(Stateless)
- 클라이언트의 context(정보)를 서버에 저장하지 않아야 한다.
- 아니면 동의를 얻어라- 캐싱 기능 활용
- 계층화
- Code on Demand
- 기능을 확장할 수 있도록 생성- 클라이언트와 서버 분리
Client <-> 연결 서버 <-> Middle Ware <-> API Server <-> Data Server
Middle Ware : Security, Load Balancing(트래픽 분산) 등
API Server : 일을 하고 결과를 생성
- 역할 분리 시, 실제 일하는 애들이 피해를 덜 입음
Client가 일하고 있는 server에 직접 접근을 하게 하지 말아라
django
djnagorestframework
mysqlclient
django-admin startproject apiserverproejct
python manage.py startapp apiserverapplication
rest_framework
등록Aisa/Seoul
설정
python manage.py makemigrations
python manage.py migrate
use db 이름
show tables
desc 테이블이름(애플리케이션이름_model이름)
애플리케이션-models.py
class 클래스이름(models.Model<-상속): 컬럼이름=models.자료형(옵션) _____________________________________________________ class Book(models.Model): bid = models.IntegerField(primary_key=True) title = models.CharField(max_length=100) author = models.CharField(max_length=100) category = models.CharField(max_length=100) pages = models.IntegerField() price = models.IntegerField() published_date = models.DateField() #시간까지 두고싶다면 Datatime이라 하자 description = models.TextField()
makemigrations, migrate 하고 dbeaver에서 desc 앱네임_클래스명; 해보자.
client에서 받은 데이터를 python 객체로 변환해주고, python 객체를 client에게 전송하기 위한 JSON 문자열로 변환해주는 역할을 수행
from rest_framework import serializers from .models import Book class BookSerializer(serializers.ModelSerializer): class Meta: model=Book fields=['bid', 'title', 'author', 'category', 'pages', 'price', 'published_date', 'description']
from django.contrib import admin from django.urls import path, include #include 써주기 urlpatterns = [ path("admin/", admin.site.urls), # example로 시작하는 url은 해당 애플리케이션.urls.py파일에서 처리 path("example/", include("apiserverapplication.urls")) ]
from django.urls import path from .views import helloAPI #views.py에서 만든다. urlpatterns = [ #/example/hello/ 요청이 오면 helloAPI 함수가 처리 path("hello/", helloAPI) ]
from rest_framework.response import Response from rest_framework.decorators import api_view @api_view(['GET']) #요청 처리 방식 선택 def helloAPI(request): return Response("HELLO REST API")
http://127.0.0.1:8000/example/hello/
로 들어가야 한다.상태 코드
- 200 번대 : 정상 응답
- 300 번대 : Redirect 중
- 400 번대 : Client 오류
- 500 번대 : Server 오류
- 가용성 문제로 500번대 오류가 생길 수 있음
- 하지만 대부분 잘못 만든거임
403 : forbidden(권한 부족)
404 : url이 잘못됨
forwarding ? Redirect ?
- CQRS (조회, 조회x 작업으로 나눔)
- 조회 : forwarding
- 조회 x : redirect
from .serializers import BookSerializer from rest_framework import status @api_view(['POST']) def booksAPI(request): # 클라이언트가 전송한 데이터를 # Model이 사용할 수 있는 데이터로 변환 serializer=BookSerializer(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)
from django.urls import path from .views import helloAPI, booksAPI #views.py에서 만든다. urlpatterns = [ #/example/hello/ 요청이 오면 helloAPI 함수가 처리 path("hello/", helloAPI), #/example/fbv/books 요청이 오면 booksAPI 함수가 처리 path("fbv/books", booksAPI) ]
- 이거 글자 틀리면 모델이 받을 수 없다.
select * from apiserverapplication_book;
- 잘 들어간 것을 확인했다. (한글도 안깨지고 잘 들어갔다.)
localhost:8000/example/fbv/books
를 입력해보자.from .serializers import BookSerializer from rest_framework import status from .models import Book @api_view(['POST', 'GET']) #post, get 둘다 처리할 수 있는데, #구분은 해놔야 할것 같아. def booksAPI(request): #전송 방식을 확인하는 방법은 request.method를 확인하면 됩니다. if request.method=='GET': # 전체 데이터 가져오기 books=Book.objects.all() serializer=BookSerializer(books,many=True) return Response(serializer.data, status=status.HTTP_200_OK) elif request.method=='POST': #post일때 # 클라이언트가 전송한 데이터를 # Model이 사용할 수 있는 데이터로 변환 # print("1") # 이 코드가 실행 안된다면url과 method 연결 실수 serializer = BookSerializer(data=request.data) # print("2") # 이 코드가 실패했다면, serializable 실패 # 유효성 검사 if serializer.is_valid(): # print("3") # 이 코드가 안된다면, 이름이 잘못된 것이다. serializer.save() # 데이터 저장 # 성공했을 때 전송한 데이터를 다시 전송 return Response(serializer.data, status=status.HTTP_201_CREATED) # 실패했을 때 처리 return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) # return은 안써도 되는거야?
예전에는 요청마다 url을 다르게 했지만, 요즘에는 get, post, put, delete로 구분
from django.urls import path from .views import helloAPI, booksAPI,bookAPI #views.py에서 만든다. urlpatterns = [ #/example/hello/ 요청이 오면 helloAPI 함수가 처리 path("hello/", helloAPI), #/example/fbv/books 요청이 오면 booksAPI 함수가 처리 path("fbv/books", booksAPI), #이번엔 bid 1개마다 데이터 조회하기 path("fbv/books/<int:bid>/",bookAPI) ]
@api_view(['GET','PUT']) def bookAPI(request, bid): #bid가 url에 포함되어있다. #기본키를 가지고 데이터 1개를 가져오는 것입니다. try: books = Book.objects.get(bid=bid) except Book.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) if request.method=='GET': serializer=BookSerializer(books) return Response(serializer.data, status=status.HTTP_200_OK) elif request.method=='PUT': serializer = BookSerializer(books, data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) return Response(status=status.HTTP_404_NOT_FOUND)
강사님이 해주신 GET 하나만 한 version
#기본키를 가지고 데이터를 찾아오고 없다면 404error 발생을 하겠다. from rest_framework.generics import get_object_or_404 @api_view(['GET']) def bookAPI(request, bid): book = get_object_or_404(Book, bid=bid) serializer = BookSerializer(book) return Response(serializer.data, status=status.HTTP_200_OK)
close()
를 호출하지 않으면 연결이 유지됨
new XMLHttpREquest()
하면 생성됩니다.
responseText
- XML 이외의 데이터를 받았을 때 데이터responseXML
- XML 데이터를 받은 경우 받은 내용
setRequestHeader(헤더이름, 데이터)
- 헤더 설정open(전송방식, 전송할 URL, 비동기 전송 여부)
- 연결 설정send(데이터)
- 요청 전송sendBinary(데이터)
- 바이너리 데이터(파일) 전송 시 호출
load
- 데이터를 전부 전송받으면 호출되는 이벤트error
- 데이터를 전송받는 도중, 에러가 발생한 경우에 호출되는 이벤트
- 프로젝트의 urls.py 파일에 요청을 생성
from django.contrib import admin from django.urls import path, include from apiserverapplication import views urlpatterns = [ path("admin/", admin.site.urls), # example로 시작하는 url은 해당 애플리케이션.urls.py파일에서 처리 path("example/", include("apiserverapplication.urls")), path("ajax/",views.ajax) ]
- 애플리케이션의 views.py 파일에서 ajax 함수 생성
from django.shortcuts import render def ajax(request): return render(request, "ajax.html")
- 애플리케이션의 templates에서 ajax.html 생성
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>My ajax</title> </head> <body> AJAX </body> </html>
- ajax.html에서 전체 데이터 가져와서 출력하기
- 나중에 리액트에서 할 거에요
<body> <div id="display"> </div> <button id="allbtn">전체 데이터 가져오기</button> </body> <script> document.getElementById("allbtn").addEventListener("click",(e)=>{ let request=new XMLHttpRequest(); //재정의 안했음 // 출력하는 함수에 객체를 대입하면 toString 메서드 호출 // python은 __str__ 메서드 호출 // 위 메서드를 재정의하면 그 내용이 출력되지만 // 재정의를 안하면 기본 설정 내용이 출력됩니다. // alert(request) // 요청 생성하기 // request.open('전송방식','url','비동기전송여부') // http://localhost:8000/example/fbv/books //현재는 http://localhost:8000/ajax request.open('GET', 'example/fbv/books/',true); // 요청 전송 request.send('') // 응답이 오면 호출 request.addEventListener('load',(e)=>{ alert(request.responseText) }) }) </script>
- 오류임
- 경로가 잘못 되어있다.
/
잘 들어있는지 다 확인해야한다.request.open('GET', '../example/fbv/books',true); 이렇게 수정
- 잘 나오는 것을 확인하였다.
- 이를 화면에 출력해보자.
request.addEventListener('load',(e)=>{ // 문자열 //alert(request.responseText) // 문자열을 JS 데이터로 변환 (파싱) let data=JSON.parse(request.responseText) // alert(data) // [object]로 print 된다. 즉 객체들의 배열 let txt=''; for(let item of data){ // in으로 하면idx가 나옴 // (0,1,2) of는 object txt+='<h3>'+'<'+item.title+'>'+'</h3>'; txt+='<h3>'+item.author+'</h3>'; txt+='<h3>'+item.description+'</h3>'; } document.getElementById('display').innerHTML=txt; })
- 이번엔 bid로 하나만 받아오자.
- 하나만 받는 것은 문제가 많을 수 있다.
- 데이터가 없다면?? : 비어있는 데이터가 옵니다.
- 즉, 예외(if data.length>0
) 이런거 생각해야합니다.
- 데이터가 없다면 빈 화면만 보입니다. (제일 최악)
- 없는 번호를 입력하니 detail에 NotFound값이 넘어와야 하는데?
- 이거는 데이터 하나를 가져올 때, try exception을 해서 단순 예외처리를 당해서 파싱 자체를 못한 것이다.
def bookAPI(request, bid): #bid가 url에 포함되어있다. #기본키를 가지고 데이터 1개를 가져오는 것입니다. try: books = Book.objects.get(bid=bid) except Book.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND)
이 부분을 보면, bid가 있는 것들을 가져오는데, 존재하지 않는다면 단순히 404에러만 발생시키고 예외처리만 해두었다는 것이다. get_object_or_404라면 데이터가 없다면 detail에 not found를 실어준다.
단순 object.get()에서 데이터가 없다면 아무것도 없는 상태가 된다.
이를 데이터 가져올 때, trim 해서 length==0 이런거 해서 없는 데이터 처리 하자.<body> <div id="display"> </div> <button id="allbtn">전체 데이터 가져오기</button> <br><br><br> bid : <input type="text" id="bid" /> <button id="getbook"> 데이터 1개 가져오기</button> <div id="display2"></div> </body> <script> document.getElementById("getbook").addEventListener("click", (e)=>{ // id가 bid인 입력의 값을 가져오기 let bid=document.getElementById('bid').value; //alert(bid); let request=new XMLHttpRequest(); request.open('GET','../example/fbv/books/'+bid,true); request.send(''); request.addEventListener('load',(e)=>{ //없는 bid라면 detail에 NotFound값이 넘어와야 합니다. //alert(request.responseText) let txt=""; // 내가 짠 코드는 없는 데이터가 들어오면 " " or "" 상태 // trim으로 공백을 없애고 길이를 보자. // 이게 조심해야 하는 점이다. if(request.responseText.trim().length <= 0){ //데이터가 없을 때 처리 txt="<h3>해당 되는 bid가 없습니다.</h3>"; } else{ let data=JSON.parse(request.responseText); //데이터가 있을 때 처리 // alert(data.bid) txt+="<h3>"+data.bid+"</h3>"; txt+="<h3>"+data.title+"</h3>"; txt+="<h3>"+data.description+"</h3>"; } // 안에 태그를 전부 해석해서 disp하기 위해 사용 document.getElementById('display2').innerHTML=txt; }) }); </script>
https://sss20-02.tistory.com/6
예외 처리를 할거면 잘 알고 있어야 할 것 같아.
get_object_or_404
걍 이거 쓰는게 마음이 편할 것 같다.
- form에 데이터를 입력받아서 삽입
- form을 만들어서 데이터를 전송할 때는 form 안의 name과 파라미터 이름이 일치해야 합니다.