[Re:Django] 3. GET, POST 메소드 차이점 및 api 설계

Magit·2020년 5월 4일
5

Django

목록 보기
3/13
  • GET, POST 메소드 차이점을 알고 그에 맞게 api를 설계 할 수 있다.
    • GET - 쿼리 스트링과 POST - JSON으로 각각 응답할 수 있다.
    • 클라이언트(프론트앤드)에서 필요한 데이터 구조가 어떤지 알고, 그에 맞게 응답할 수 있다.

GET 메소드와 POST 메소드의 차이점

펌 블로그
GET방식과 POST방식

두 방식 모두 서버에 요청을 하는 메소드라는 공통점이 있다. 클라이언트가 서버에 요청해서 서버가 제공해야하는 자원이 있다고 생각해보자. 예를 들어, 어떤 홈페이지의 로그인 페이지에서 로그인을 하는 경우, 아이디와 패스워드는 클라이언트가 작성하고 그 정보를 서버에 요청해서 클라이언트가 작성한 아이디와 패스워드가 올바른 건지 확인해야한다.
이렇게 요청(request)를 보낼때는 자원을 보내야하는 경우가 있다.

GET 메소드

클라이언트의 데이터를 URL에 붙여서 보낸다. 위에서 예를 들었던 것 처럼 아이디와 패스워드를 보냈다고 생각하면

www.velog.com?id=velog&pass=1234

이렇게 보내게된다. URL뒤에 ? 마크를 통해 URL의 끝을 알리면서 데이터 표현의 시작점을 알린다. 데이터는 keyvalue 쌍으로 넣어야한다. 위 예시로 보면 key는 id와 pass 이고, value는 velog와 1234 이다. 중간 & 는 구분자이다. 2개 이상의 key-value 쌍 데이터를 보낼 때 & 마크로 구분하게된다.

URL에 붙이므로 HTTP 패킷의 헤더에 포함되어 서버에 요청을 보내게된다. 그래서 GET 메소드로 요청을 보낼때는 HTTP패킷의 BODY가 빈 상태로 보내지게된다. 그러므로, 헤더의 내용 중 BODY데이터를 설명하는 Content-Type 이라는 헤더필드도 들어가지 않는다.

URL 형태로 표현되므로, 특정 페이지를 다른 사람에게 접속하게 할 수 있고, 간단한 데이터를 넣도록 설계되어서 데이터를 보내는 양의 한계가 존재한다.

POST 메소드

POST 방식은 GET방식가 달리 데이터 전송을 기반으로 한 요청 메소드이다. GET 방식은 URL에 데이터를 붙여서 보내는 반면, POST 방식은 URL이 아니라 BODY에 데이터를 넣어서 보내게된다. 따라서 헤더필드중 BODY의 데이터를 설명하는 Content-Type 이라는 헤더필드가 들어가고 어떤 데이터 타입인지 명시해야한다.
Content-Type 으로는 여러가지가 있지만 대표적으로

  1. application/x-www-form-urlencoded
  2. text/plain
  3. multipart/form-data

등이 있다.
따라서 POST 방식으로 데이터를 보낼때는 위와 같이 Content-Type 을 꼭 명시해줘야한다.

보통 작성하지 않으면 1번으로 셋팅되는데, GET 방식과 마찬가지로 BODY에 key-value 쌍으로 데이터를 넣는다. 구분자는 똑같이 &이다.
2번은 BODY에 단순 txt를 넣는다.
3번은 파일전송 할 때 많이 쓰는데, BODY의 데이터를 바이너리 데이터로 넣는다는 걸 알려준다.


GET - 쿼리스트링으로 요청이 왔을 시 처리방법

요청(request)이 GET 쿼리스트링 형식으로 왔을 때 처리하는 방식에 대해서 알아보고자한다.

쿼리스트링

우선 쿼리스트링 이란게 뭔지 알아야한다. 쿼리스트링은 사용자가 입력 데이터를 전달하는 방법 중 하나로, url 주소에 미리 협의된 데이터를 파라미터를 통해 넘기는 것을 말한다.

쿼리스트링 형식

  • 정해진 엔드포인트 주소 이후에 ?를 쓰는 것으로 쿼리스트링의 시작을 알린다.
  • parameter=value 로 필요한 파라미터 값을 적는다
  • 파라미터가 여러개일 경우 & 을 붙여서 구분해준다.
엔드포인트주소/엔드포인트주소?파라미터=값&파라미터&값...

그렇다면 백엔드에서 받는 방법은?

예를 들어서

http://localhost:8000/example?sort_by=price-ascending

라는 형식의 주소가 있다면, key값이 sort_by이며 value값에 price-ascending 를 넣어서 example 이라는 엔드포인트에 요청을 보낸것이다.

여기서 중요한 점은 장고 기능으로 인해 query parameter의 key와 value는 request의 GET 객체로 쿼리 딕셔너리로 담겨서 들어온다는 점이다.

#views.py
class ExamView(View):
	def get(self, request):
    	sort_by = request.GET.get('sort_by', None)
        data = Exam.objects.get(sort=sort_by)
        
        return JsonResponse(...)

그러므로 위와같이 request의 GET객체에서 get메소드를 이용해서 key 값인 sort_by에 해당하는 value를 갖고오면 된다. 없으면 None을 갖고와서 에러를 줄여주자.
그리고서 데이터베이스에서 필요한 데이터를 갖고와서 가공해준 뒤 반환해주면 된다.

request.GET vs request.GET.get()
request.GET은 GET으로 받는 파라미터들을 다 포함하는 딕셔너리 객체이다.
get() 메서드는 key 값이 딕셔너리 안에있으면 value값을 리턴해준다. 키 값이 존재하지 않으면 디폴트값인 None을 리턴한다.
request.GET.get()은 위 두 개념을 합친것으로 GET 요청이 접근할 수 있는 key와 value 값을 이용한다.


GET - Path parameter (url parameter)

요청이 쿼리스트링이 아니라 Path parameter(url parameter)로 들어올수도 있다.
백엔드에서 특정 endpoint를 지정하지 않고, 프론트엔드가 보내는 특정 string이나 int를 path안의 parameter로 받아 view로 보내는 방식이다.

urlpatterns = [
    path('/<str:target_code>', AreaView.as_view())
]

당연히 위에 처럼 urls 파일에도 반영해줘야한다.

class AreaView(View):
    def get(self, request, target_code=''):

view 클래스 함수에서 self와 request외에 다른 인자를 갖게되며 처리해줘야한다. 그 인자값이 endpoint가 된다.

url parameter는 인자가 없으면 안되고, 인자값으로 확실한 구분이 가능한 경우에 주로 사용한다. 그에 반해 query parameter는 여러개의 조건이 결합될 때 주로 사용한다.


POST - JSON 으로 요청이 왔을 시 처리방법

이번에는 POST 메소드로 요청이 왔을 시에는 어떻게 처리하는지 알아보자.
위에서도 알아봤지만 보통 POST 메소드는 HTTP의 BODY에 내용을 담아서 요청(request)을 보낸다.

아래와 같이 프론트엔드에서 바디에 json 객체 형태로 요청(request)를 보내게된다. 여기서 주의할 점은 프론트와 백엔드가 같이 데이터를 어떤식으로 보내고 어떤 식으로 받을지 잘 소통해야된다는 것이다. 객체로 보낼지, 리스트로 보낼 지, 키 값을 어떻게 보낼 지 등등.. 소통이 굉장히 중요하다.

{
	"user_id": "test1",
	"password": "qwer1234!",
	"name": "test",
	"birth_date": "2020-05-01",
	"phone": "010-0000-0000",
	"email": "test@naver.com"
}

위와같이 json 형식으로 요청이 들어왔다고 생각하고 진행할 것이다.

json 형식?
JSON 형식을 자바스크립트의 객체와 마찬가지로 key:value 가 존재할 수 있음, key값이 문자열이면 항상 쌍따옴표를 이용하여 표기해야한다.
객체나 배열 등의 표기를 사용할 수 있다. ({key:value} / [30,40]
또한 null, number, string, array, object, boolean 을 사용할 수 있다.

백엔드는 그럼 저 json 형식의 request를 어떻게 처리해야할까?

#views.py

data = json.loads(request.body)

위 코드의 의미는 request의 바디에 담겨서 오는 내용이 json 형식이면, json 형식의 string을 python dictionary 형식으로 변경해서 data라는 변수에 저장해줘 라는 의미이다.

data = json.loads(request.body)
            if len(data.keys()) < 6:
                return HttpResponse(status=400)
            for value in data.values():
                if value in "":
                    return HttpResponse(status=400)

위 코드는 그룹프로젝트 중에 회원가입 코드의 일부분이다.
변수에 저장한 뒤 data의 key 수가 6개 이하면 에러코드를, 그리고 value에 빈값이 들어왔다면 에러코드를 보내준다는 의미이다.
회원가입 시 필수사항이 6개이기에 혹시라도 그 이하의 값이 들어오거나, 아예 아무런 값도 넣지 않았을 때 에러코드를 반환한다는 의미이다.(지금 보니 고쳐야할 곳이 꽤 많다.)


클라이언트에서 필요한 구조가 어떤지 알고 응답하기

위에서 GET 메소드, POST 메소드 형식으로 요청이 들어왔을 때 어떤식으로 받아들이는지 알아봤다. (저 위 방식이 전부는 당연히 아니다.)
우리는 들어온 데이터를 데이터베이스에 집어넣기도 하고, 필요한 데이터를 데이터베이스에서 꺼내서 가공하고 클라이언트에게 반환해주기도 해야한다.

요청을 받을 때와 마찬가지로 반환을 할 때도 프론트엔드와의 소통이 중요하다.
데이터를 객체로 보낼 지, 리스트로 보낼 지 등을 협의해야하기 때문이다.

area_info = [
                {
                    "area_name" : area.name,
                    "area_code" : area.code,
                    "clickable" : area.branch_set.exists()
                } for area in region.area_set.all()
            ]
            return JsonResponse({"area_info": area_info}, status=200)

위 코드도 그룹프로젝트에서 필자가 작성한 코드 중 일부분이다.
데이터베이스의 데이터들을 빼서 객체 형식으로 저장하면서 리스트에 담고 area_info라는 변수에 저장한 것이다. return으로 status와 함께 반환해준다.
리턴결과는 아래와 같다.

{
    "area_info": [
        {
            "area_name": "전체",
            "area_code": "A00",
            "clickable": true
        },
        {
            "area_name": "강남구",
            "area_code": "A01",
            "clickable": true
        },
...
        {
            "area_name": "금천구",
            "area_code": "A08",
            "clickable": false
        }
    ]
}

어떤식으로 반환할지는 프론트엔드와 잘 협의해서 결정해야한다.

참조 블로그
20191104 TIL - Query String
TIL - Path parameter and Query parameter
django - query/url 파라미터

profile
이제 막 배우기 시작한 개발자입니다.

0개의 댓글