TIl

REST API 1

아크몽 2024. 4. 16. 11:45

API

Applictaion Programming Interface

두 소프트웨어가 서로 통신할 수 있게 하는 메커니즘

클라이언트-서버처럼 서로 다른 프로그램에서 요청과 응답을 받을 수 있도록 만든 체계

예시

기상 데이터가 들어있는 기상청의 시스템

에서 다양한 앱, 웹 사이트 등의 서비스들이 데이터를 요청해서 받아감

⇒ 요청하는 지정된 형식이 있음

⇒ 지역, 날짜, 조회할 내용들(온도, 바람 등)을 제공하는 매뉴얼

== 이렇게 요청을 보내면, 이렇게 정보를 제공해줄 것이다. 라는 매뉴얼

소프트웨어와 소프트웨어 간 지정된 정의(형식)으로 소통하는 수단 ⇒ API

⇒ 스마트폰의 날씨 앱은 기상청에서 제공하는 API를 통해 기상청 시스템과 대화하여 매일 최신 날씨 정보를 표시 할 수 있음

API 역할

가전 제품에 전기를 공급하기 위해 직접 배선을 하지 않는다.

⇒ 복잡한 코드를 추상화하여 대신 사용할 수 있는 몇 가지 더 쉬운 구문을 제공

Web API

  • 웹 서버 또는 웹 브라우저를 위한 API
  • 현대 웹 개발은 직접 개발 X, 여러 Open API 들을 활용하는 추세
  • 대표적인 Third Party Open API 서비스 목록
    • Youtube, Google Map, Naver Papago, Kakao Map API

REST

Representational State Transfer

API Server를 개발하기 위한 일종의 소프트웨어 설계 “방법론”

⇒ 모두가 API Server를 설계하는 구조가 다르니, 이렇게 맞춰서 설계하는 게 어때? 규칙X

Restful API

  • REST 원리를 따르는 시스템을 RESTful 하다고 부름
  • “자원을 정의” 하고 “자원에 대한 주소를 지정”하는 전반적인 방법을 서술

각각 API 서버 구조를 작성하는 모습이 너무 다르니 어느정도 약속을 만들어서 다같이 API 서버를 구성하자!

REST에서 자원을 사용하는 법 3가지

  1. 자원의 “식별”
    • URI
  2. 자원의 “행위"
    • HTTP Method
  3. 자원의 “표현"
    • JSON 데이터
    • 궁극적으로 표현되는 데이터 결과물

1. 자원의 식별

URI . Uniform Resource Identifier ( 통합 자원 식별자)

URL < URI

인터넷에서 리소스(지원)를 식별하는 문자열

⇒ 가장 일반적인 URI는 웹 주소로 알려진 URL

URL. Uniform Resource Locator ( 통합 자원 위치)

웹에서 주어진 리소스의 주소

⇒ 네트워크 상에 리소스가 어디 있는지를 알려주기 위한 약속

Schema ( or Protocol)

  • 브라우저가 리소스를 요청하는 데 사용해야 하는 규약
  • URL의 첫 부분은 브라우저가 어떤 규약을 사용하는지를 나타냄
  • 기본적으로 웹은 http(s)를 요구하며 메일을 열기위한 mailto:, 파일을 전송하기 위한 ftp:등 다른 프로토콜도 존재

Domain Name

  • 요청 중인 웹 서버를 나타냄
  • 어떤 웹 서버가 요구되는 지를 가리키며 직접 IP 주소를 사용하는 것도 가능하지만, 사람이 외우기 어렵기 때문에 주로 Domain Name으로 사용
  • 예를 들어 도메인 google.com의 IP주소는 142.251.42.142

Port

  • 웹 서버의 리소스에 접근하는데 사용되는 기술적인 문(Gate)
  • HTTP 프로토콜의 표준 포트
    • HTTP - 80
    • HTTPS - 443
  • 표준 포트만 작성 시 생략 가능

Path

  • 웹 서버의 리소스 경로
  • 초기에는 실제 파일이 위치한 물리적 위치를 나타냈지만, 오늘날은 실제 위치가 아닌 추상화된 형태의 구조를 표현
  • 예를 들어 /articles/create/라는 주소가 실제 articles 폴더안에 create 폴더 안을 나타내는 것이 아니다

Parameters

  • 웹 서버에 제공하는 추가적인 데이터
  • & 기호로 구분되는 key-value 쌍 목록
  • 서버는 리소스를 응답하기 전에 이러한 파라미터를 사용하여 추가 작업을 수행할 수 있음

Anchor

  • 일종의 “북마크”를 나타내며 브라우저에 해당 지점에 있는 콘텐츠를 표시
  • fragment identifier(부분 식별자)라고 부르는 ‘#’이후 부분은 서버에 전송되지 않음
  • https://docs.djangoproject.com/en/4.2/intro/install/#quick-install-guide 요청에서 #quick-install-guide는 서버에 전달되지 않고 브라우저에게 해당 지점으로 이동할 수 있도록 함

2. 자원의 행위

HTTP Request Methods

리소스에 대한 행위(수행하고자 하는 동작)를 정의

⇒ HTTP verbs 라고도 함

대표 HTTP Request Methods

  1. GET
    • 서버에 리소스의 표현을 요청
    • GET을 사용하는 요청은 데이터만 검색해야 함
  2. POST
    • 데이터를 지정된 리소스에 제출
    • 서버의 상태를 변경
  3. PUT
    • 요청한 주소의 리소스를 수정
  4. DELETE
    • 지정된 리소스를 삭제

HTTP response status codes

특정 HTTP 요청이 성공적으로 완료 되었는지 여부를 나타냄

5개의 응답 그룹

  • Informational responses (100-199)
  • Successful response (200-299)
  • Redirection messages (300-399)
  • Client error responses (400-499)
  • Server error responses (500-599)

자원의 표현

그동안 서버가 응답(자원을 표현)했던 것

  • 지금까지 Django 서버는 사용자에게 페이지(html)만 응답하고 있었음
  • 하지만 서버가 응답할 수 있는 것은 페이지 뿐만 아니라 다양한 데이터 타입을 응답할 수 있음
  • REST API는 이 중에서도 JSON 타입으로 응답하는 것을 권장

응답 데이터 타입의 변화

페이지(html)만을 응답했던 서버 → 이제는 JSOn 데이터를 읍답하는 REST API 서버로의 변환

→ Django의 Template 부분은 Front-end Framework가 대체함 == Front-end와 Back-end가 분리됨

→ Django를 사용해 RESTful API 서버를 구출할 것

python으로 json 응답 받기

import requests
from pprint import pprint

response = requests.get('<http://127.0.0.1:8000/api/v1/articles/>')

# json을 python 타입으로 변환
result = response.json()

# print(type(result))
# pprint(result)
# pprint(result[0])
pprint(result[0].get('title'))

DRF. Django REST framework

Django에서 Restful API 서버를 쉽게 구축할 수 있도록 도와주는 오픈소스 라이브러리

GET POST PUT DELETE

articles/ 전체 글 조회 글 작성    
articles/1/ 1번 글 조회   1번 글 수정 1번 글 삭제

Serialization 직렬화

여러 시스템에서 활용하기 위해 데이터 구조나 객체 상태를 나중에 재구성할 수 있는 포맷으로 변환하는 과정

예시

데이터 구조나 객체 상태를 나중에 재구성할 수 있는 포맷으로 변환하는 과정

Serializer

Serialization을 진행하여 Serialized data를 반환해주는 클래스

ModelSerializer

Django 모델과 연결된 Serializer 클래스

⇒ 일반 Serializer와 달리 사용자 입력 데이터를 받아 자동으로 모델 필드에 맞추어 Serialization을 진행

ModelSerializer의 인자 및 속성

serializer = ArticleListSerializer(articles, many=True)
return Response(serializer.data)
  • many 옵션
    • Serialize 대상이 QuerySet인 경우 입력
  • data 속성
    • Serialized data 객체에서 실제 데이터를 추출

GET - List

  • 게시글 데이터 목록 조회하기
  • 게시글 데이터 목록을 제공하는 ArticleListSerializer 정의
# articles/serializers.py
from rest_framework import serializers
from .models import Article

class ArticleListSerializer(serializers.ModelSerializer):

    class Meta:
        model = Article
        fields = ('id','title','content',)
# articles/urls.py

# app_name = ''
urlpatterns = [
    path('articles/', views.article_list, name='aaa'),
]
# articles/views.py
from rest_framework.response import Response
from rest_framework.decorators import api_view

from .models import Article
from .serializers import ArticleListSerializer

@api_view(['GET'])
def article_list(request):
    articles = Article.objects.all()
    serializer = ArticleListSerializer(articles, many=True) # 단일 데이터가 아니기 때문

    '''
    context = {
        'articles':articles
    }
    '''
    return Response(serializer.data)

과거 view 함수와의 비교

  • 똑같은 데이터를 HTML에 출력되도록 페이지와 함께 응답했던 과거의 view 함수, JSON 데이터로 serialization 하여 페이지 없이 응답하는 현재의 view 함수
  • templates가 없다 → context 필요 없어짐

api_view decorator

  • DRF view 함수에서는 필수로 작성되며 view 함수를 실행하기 전 HTTP 메서드를 확인
  • 기본적으로 GET 메서드만 허용되며 다른 메서드 요청에 대해서는 405 Method Not allowed로 응답
  • DRF view 함수가 응답해야 하는 HTTP 메서드 목록을 작성

GET - Detail

  • 단일 게시글 데이터 조회하기
    • 각 게시글 상세 정보를 제공하는 ArticleSerializer 정의
    # articles/serializers.py
    
    class ArticleSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = Article
            fields = '__all__'
    
    urlpatterns = [
        path('articles/<int:article_pk>/', views.article_detail),
    ]
    
    
    @api_view(['GET'])
    def article_detail(request, article_pk):
        article = Article.objects.get(pk=article_pk)
        serializer = ArticleSerializer(article)
        return Response(serializer.data)
    

POST

  • 게시글 데이터 생성하기
  1. 데이터 생성이 성공했을 경우 201 Created 응답
  2. 데이터 생성이 실패 했을 경우 400 Bad request 응답

article_list view 함수 구조 변경 ( method에 따른 분기처리)

# articles/ views.py

from rest_framework import status

@api_view(['GET', 'POST'])
def article_list(request):
    if request. == "GET":
        ...
        return Response(serializer.data)
    elif request.method == "POST":
        serializer = ArticleSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.data, status=status.HTTP_400_BAD_REQUEST)

DELETE

  • 게시글 데이터 삭제하기
    • 요청에 대한 데이터 삭제가 성공했을 경우는 204 No Content 응답
    # articles/ view.py 
    
    @api_view(['GET','DELETE'])
    def article_detail(request, article_pk):
        article = Article.objects.get(pk=article_pk)
        if request.method == 'GET':
            ...
            return Response(serializer.data)
        elif request.method == 'DELETE':
            article.delete()
            return Response(status=status.HTTP_204_NO_CONTENT)
    

PUT

  • 게시글 데이터 수정하기
    • 요청에 대한 데이터 수정이 성공했을 경우는 200 OK 응답
# articles/ view.py 

@api_view(['GET','DELETE','PUT'])
def article_detail(request, article_pk):
    ...
    elif request.method == 'PUT':
        serializer = ArticleSerializer(article, data=request.data, partial=True)
        # serializer = ArticleSerializer(instance = article, data=request.data, partial=True)

        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_200_OK)    
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

partial argument

  • 부분 업데이트를 허용하기 위한 인자
  • 예를 들어 partial 인자 값이 False 일 경우 게시글 title만을 수정하려고 해도 반드시 content 값도 요청 시 함께 전송해야 함
  • 기본적으로 serializer는 모든 필수 필드에 대한 값을 전달 받기 때문
    • 즉, 수정하지 않는 다른 필드 데이터도 모두 전송해야 하며 그렇지 않으면 유효성 검사에서 오류가 발생
ArticleSerializer(article, data=request.data, partial=True)

참고

raise_exception

  • is_valid()의 선택 인자
  • 유효성 검사를 통과하지 못할 경우 ValidationError 예외를 발생시킴
  • DRF에서 제공하는 기본 예외 처리기에 의해 자동으로 처리되며 기본적으로 HTTP 400 응답을 반환
# articles/views.py

@api_view(['GET','POST'])
def article_list(request):
    ...
		elif request.method == "POST":
        serializer = ArticleSerializer(data=request.data)
        if serializer.is_valid(raise_exception=True):
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        # return Response(serializer.data, status=status.HTTP_400_BAD_REQUEST) # 생략 가능