[Django] RESP API

angie·2024년 9월 24일

1) API(Application Programming interface)

  • 프로그램과 프로그램을 연결시켜주는 매개체
  • 프로그램과 통신을 위해서 제공
  • 프레임워크 형태로 제공하기도 하고 데이터 형태로 제공하기도 함
  • 비슷한 말로 Software Developer Kit이라고 하기도 한다.
  • Open API라고 부르면 API를 누구나 사용할 수 있도록 해준것 입니다.

2) CSR(Client Side Rendering)

  • API Server를 만들어서 데이터를 제공하도록 만들고 클라이언트에서 이 데이터를 제공받아서 출력하는 방식
  • Server Side Rendering은 서버가 처리를 한 후 그 데이터를 가지고 템플릿 엔진을 이용해서 HTML을 만들어서 클라이언트에게 제공하는 방식
  • CSR 방식은 클라이언트에서 데이터를 가공해서 화면에 출력을 하기 때문에 데이터를 어떤 형식으로 주고 받을지 결정을 해야하는데 가장 많이 사용된 방식은 XML과 JSON 이다.
  • XML(eXtensible Markup Language)
    • HTML은 브라우저가 해석하지만 XML은 개발자나 프레임워크가 해석
    • XML은 구조적
    • JSON보다 인간이 읽기가 쉽지만 용량이 크다.
<Person>
	<name>이름</name>
    <age>30</age>
	<hobby>
    	<element>축구</element>
        <element>야구</element>
    </hobby>
</Person>
  • JSON(JavaScript Object Notation)
    • 자바스크립트의 객체 표현 방식을 이용
    • XML보다 경량이지만 인간이 읽기가 어려움
    • 인간이 읽기 어렵기 떄문에 설정 파일에 사용하기는 어렵고 데이터 전송에 많이 사용한다.
    • 스마트폰 운영체제의 데이터 전송 방식은 전부 JSON 이다.
{name:"이름", age:30, hobby:["축구","야구"]}

3) REST(REpresentational State Transfer)

  • 분산 하이퍼미디어 시스템을 위한 소프트웨어 아키텍쳐의 형식

  • 자원을 정의하고 자원에 대한 주소를 지정하는 방법 전반을 일컫는 말로 웹 상의 자료를 HTTP 위에서 SOAP(Simple Object Access Protocol)이나 쿠키를 통한 세션 트랙킹 같은 별도의 전송 계층없이 전송하기 위한 아주 간단한 인터페이스

  • REST 원리를 따르는 시스템을 RESTful 하다라고 한다.

  • 6가지 제약조건

    • Client - Server 구조
    • Stateless(무상태): 클라이언트가 서버에 요청을 보낼 때 이전 요청의 영향을 받지 않아야 한다.
    • Cacheable Data: 서버에서 리소스를 리턴할 떄 캐시가 가능한지 아닌지를 명시해야 하는 것인데 HTTP에서 cache-control이라는 헤더에 리소스의 캐시 여부를 명시할 수 있다.
    • Layered System: 여러 개의 레이어로 된 서버를 사용할 수 있음, 클라이언트는 이 사실을 알 필요가 없음
      하나의 시스템을 여러 개의 애플리케이션으로 분리해서 구현하기도 하고 인증 서버, 캐싱 서버, 로드 밸런서를 거쳐서 응답을 하기도 한다.
    • A Uniform Interface: 일관성 있는 인터페이스를 가져야 한다.
      HTML과 JSON 두 가지 형태의 데이터를 사용하는 것은 안된다.
      동일한 콘텐츠를 사용하는 경우 동일한 URL을 사용하는 것을 권장
      http://www.example.com/todo Get 방식이면 가져오는 것 POST 방식이면 삽입 PUT이면 수정 DELETE이면 삭제 이렇게 HTTP 메서드로 작업을 구분
    • Code-On-Demand
      선택 사항으로 클라이언트는 서버에 코드를 요청할 수 있고 서버가 리턴한 코드를 실행할 수 있음

    3. Django REST Framework

    1) 개요

  • REST API를 만들 수 있는 프레임워크

  • 설치: pip install djangorestframework

    2) 프로젝트 생성

  • django-admin startproject 프로젝트이름 경로

    3) 애플리케이션 생성 - 프로젝트의 manage.py 파일이 있는 디렉토리에서 수행

  • python manage.py startapp 애플리케이션이름

4) setting.py 파일 수정

  • INSTALLED_APPS 부분에 rest_framework 와 자신의 애플리케이션을 등록
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myweb',
    'rest_framework',
    'apiapp'
]
  • DATABASES 부분을 사용할 수 있는 데이터베이스로 수정
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'rest',
        'USER':'root',
        'PASSWORD':'1234',
        'HOST':'127.0.0.1',
        'PORT':'3306'
    }
}
  • TIME_ZONE 수정
TIME_ZONE = 'Asia/Seoul'

5) urls.py 파일을 수정해서 example로 시작하는 url에 대한 처리는 apiapp의 urls.py에서 처리하도록 수정

  • 프로젝트에 여러 개의 애플리케이션 존재하는 경우 프로젝트에서 모든 요청을 처리하도록 하면 프로젝트의 urls.py가 너무 복잡해지므로 요청을 처리하는 로직이나 url 설정을 애플리케이션 단위로 분할하는 것이 좋다.
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('example/', include('apiapp.urls'))
]

6) 요청을 만들고 JSON을 리턴하는 코드를 작성

  • apiapp 디렉토리에 urls.py 파일을 추가하고 작성
from django.urls import path
from .views import helloAPI

urlpatterns = [
    path("hello/", helloAPI)
]
  • apiapp 디렉토리의 views.py 파일에 helloAPI 함수를 추가
from django.shortcuts import render #템플릿 엔진을 이용해서 HTML 출력에 사용
from rest_framework.decorators import api_view
from rest_framework.response import Response #json 데이터 생성에 사용

# Create your views here.
@api_view(['GET'])
def helloAPI(request):
    return Response("Hello REST API")
  • 관리자용 테이블을 생성
python manage.py makemigrations
python manage.py migrate
  • 확인
    • 웹서버를 실행
      python manage.py runserver 127.0.0.1:80
    • 브라우저에서 확인
      localhost/example/hello

7) Response

  • 응답 결과를 만들기 위한 클래스

  • 인스턴스를 생성할 떄 첫번쨰 클래스 매개변수가 클라이언트에게 전송될 데이터
    status는 상태 코드

  • 주요 상태 코드
    200: OK
    201: Created - 요청은 처리되어서 새로운 리소스를 생성
    202: Accepted - 요청은 접수되었지만 처리가 완료되지 않음
    3xx: 리다이렉션 중
    400: 잘못된 요청
    401: 권한이 없음
    403: 권한 처리 이외의 사유로 리소스에 대한 엑세스가 금지
    404: 지정한 리소스를 찾을 수 없음. 서버에서 처리하는 로직을 찾을 수 없음
    500: 내부 서버 오류

  • 응답 결과를 전송할 떄 상태 코드를 같이 전송해서 정상 처리 여부를 알려주는 것이 좋다.

8) Django와 다른점

  • 실행 부분에 HTTP의 헤더가 보임
  • 템플릿의 형태가 아닌 JSON과 같은 형태의 응답을 제공
  • Serializer(직렬화)
    • 인스턴스 단위로 파일이나 네트워크를 통해 전소하는 것
    • Djangorestframework 에서는 Model을 JSON 데이터로 변환하는 작업

9) Model 생성

  • 애플리케이션의 models.py 파일에 Moedel 클래스로 부터 상속받는
# Create your models here.
class Book(models.Model):
    bid = models.IntegerField(primary_key=True)
    title = models.CharField(max_length=50)
    author = models.CharField(max_length=50)
    category = models.CharField(max_length=50)
    pages = models.IntegerField()
    price = models.IntegerField()
    published_date = models.DateField()
    descripton = models.TextField()
  • 변경 내용을 데이터베이스에 반영
python manage.py makemigrations
python manage.py migrate

10) 모델의 데이터를 json 문자열로 변환해서 출력해주는 Serializer를 생성

  • apiapp 디렉토리에 시리얼라이저를 구현할 serializers.py 파일을 생성하고 작성

11) apiapp 디렉토리에 요청을 처리하는 함수를 작성 - 전체 가져오기, 데이터 삽입, 데이터 1개 가져오기

from django.shortcuts import render #템플릿 엔진을 이용해서 HTML 출력에 사용
from rest_framework.decorators import api_view
from rest_framework.response import Response #json 데이터 생성에 사용

from rest_framework import status
from rest_framework.generics import get_object_or_404
from .models import Book
from .serializers import BookSerializer

#하나의 함수를 가지고 GET과 POST를 구분해서 처리
@api_view(['GET', 'POST'])
def booksAPI(request):
    if request.method == 'GET':
        # 테이블의 전체 데이터 가져오기
        books = Book.objects.all()
        #가져온 데이터를 JSON 문자열로 변환
        serializer = BookSerializer(books, many=True)
        #JSON으로 출력
        return Response(serializer.data, status = status.HTTP_200_OK)
    elif request.method == 'POST':
        # 클라이언트에게 전송된 문자열을 모델로 변환
        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)

#기본 키 값을 받아서 하나의 데이터를 찾아서 출력하는 함수
@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)

12) apiapp 디렉터리의 urls.py 파일에 url과 요청을 처리하는 함수를 연결

from django.urls import path
from .views import helloAPI, booksAPI, bookAPI

urlpatterns = [
    path("hello/", helloAPI),
    path("fbv/books/", booksAPI),
    path("fbv/book/", bookAPI)
]
{
"bid":1,
"title":"REST API",
"author":"adam",
"category":"development",
"pages":3000,
"price":25000,
"published_date":"1970-05-30",
"description":"안녕하세요"
}


13) 브라우저에서 확인

127.0.0.1/example/fbv/bookd와 fbv/book/<bid>로 확인

4. 웹 클라이언트 애플리케이션에서 웹 서버의 데이터 가져오기

  • 템플릿 엔진을 이용하는 방식은 서버를 만들 때 사용한 언어를 가지고 클라이언트에게 출력물을 만들어서 제공하는 방식
  • 서버의 데이터를 가져와서 출력하는 방식은 서버는 JSON이나 XML 데이터를 만드는 것 까지만 하고 클라이언트가 데이터를 파싱해서 출력하는 방식

1) 방법

  • 가장 전통적인 방법: ajax(Asynchronous + XML)
    • Javascript를 이용해서 XML 데이터를 비동기적으로 받아서 처리하는 기술
    • 최근에는 XML 뿐 아니라 JSON 데이터를 가져와도 ajax라고 합니다.
    • 비동기적으로 동작을 하기 떄문에 콜백을 이용해서 처리해야 하고 동작이 수행중이더라도 다른 작업을 수행하는 것이 가능하다.
    • 웹 페이지가 사용자가 하고 있는 것을 방해하지 않으면서 서버에서 데이터를 받아서 일부분을 수정하고자 할 때 사용
  • Fetch API
    • ajax와 동일한 기능을 수행하는데 콜백이 아닌 다른 방식으로 요청을 처리
    • 전역 fetch 함수를 이용
  • axios와 같은 라이브러리 이용
  • 서버에서 일방적으로 데이터를 전송하고자 하는 경우는 EventSource를 이용한 SSE(Server Side Event)를 이용
    메시지 구독과 게시 방식으로 서버가 클라이언트에게 데이터를 전송
  • 완전 양방향 통신(채팅)을 원하는 경우에는 Web Socket을 사용

2) ajax(XMLHttpRequest 인스턴스 이용) 구현

  • 작업 순서
    • XMLHttpRequest 인스턴스를 생성
    • 이벤트별 리스너(콜백 함수)
    • 서버로 보낼 데이터를 생성
    • 연결 요청을 준비: open 메서드 사용
    • 요청을 전송: send 메서드 이용
    • 응답을 처리: status를 확인
    • 결과를 가지고 다음 작업을 수행: 화면에 출력할 것인지 아니면 다른 페이지로 이동할지 결정
  • 프로젝트의 urls.py 파일에 ajax 요청을 추가
from django.contrib import admin
from django.urls import path, include

from apiapp import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('example/', include('apiapp.urls')),
    path('ajax/', views.ajax)

]
  • apiapp 디렉토리의 views.py에 ajax 함수를 추가
def ajax(request):
    return render(request, "ajax.html")
  • apiapp 디렉토리에 templates 폴더를 추가한 후 ajax.html 파일 추가
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ajax</title>
</head>
<body>
    
</body>
<script>
    //ajax 객체를 생성
    let request = new XMLHttpRequest();
    //전체 데이터를 가져오는 요청을 생성
    request.open('GET', '../example/fbv/books/');
    //요청 전송
    request.send('')
    //응답이오면 호출될 함수를 등록
    request.addEventListener("load", () => {
        alert(request.responseText)
    })
</script>
</html>
  • 서버를 다시 실행하고 브라우저에서 http://127.0.0.1:8000/ajax/를 요청해서 브라우저의 대화상자에 문자열이 출력되는지 확인

  • JSON Parsing

    • JSON은 문자열
    • 프로그래밍 언어에서 사용하기 위해서는 이 문자열을 원하는 인스턴스로 변환을 해야 하는데 이 작업을 Parsing이라고 한다.
    • JSON.parse(JSON 문자열): 문자열을 자바스크립트 객체로 변환
    • JSON.stringify(JavaScript 객체): 자바스크립트 객체를 JSON 문자열로 변환
  • ajax.html 파일의 스크립트 코드를 수정해서 파싱한 후 출력

 //ajax 객체를 생성
    let request = new XMLHttpRequest();
    //전체 데이터를 가져오는 요청을 생성
    request.open('POST', '../example/fbv/books/');

    //전송할 데이터를 생성
    let formdata = new FormData();
    formdata.append('bid', 3);
    formdata.append('title', '파이썬');
    formdata.append('authoer', "귀도반로썸");
    formdata.append('category','programming')
    formdata.append('pages', 123)
    formdata.append('price', 20900)
    formdata.append('published_data', '2024-09-24')
    formdata.append('description', '설명')

    //데이터를 헤더에 포함시켜서 전송하기
    request.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
    let param = ""
    for(let pair of formdata.entries()){
        param += pair[0] + '=' + pair[1] + '&'
    } 
  • Fetch API
    • 콜백을 이용하는 것이 아니고 then을 이용해서 비동기 요청을 수행
    • 대다수의 응답이 JSON으로 오기 때문에 파싱을 별도의
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ajax</title>
</head>
<body>
    
</body>
<script>
    const data = {
        bid:6,
        title:'자바',
        author:'고슬링',
        category:'프로그래밍',
        pages:245,
        price:38000,
        published_date:'2024-09-24',
        description:'OOP'
    }

    fetch('../example/fbv/books/',{
        method:'POST',
        headers:{
            'Content-Type':'application/json'
        },
        body: JSON.stringify(data)
    })
    .then((response) => response.json())
    .then((data) => {
        for(idx in data){
            alert(data[idx].bid)
        }
    })

</script>
</html>
  • axios 라이브러리 이용
    • 코드를 단순화 시켜서 비동기 통신을 수행
 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ajax</title>
</head>
<body>
    
</body>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js">
    axios.get("../example/fbv/books/")
    .then((response)=>{
        alert(response.data)
    })
    .catch((error)=>{
        //실패했을 떄 처리
        alert(error)
    })
    .then(()=>{
        //무조건 수행할 구문
        alert("무조건 수행")
    })
</script>
</html>
  • ????안되는듯??
profile
열심히 달리는 개발자

0개의 댓글