서버개발자가 되는법 - #8 To-Do 앱 만들기 [4], 공통 처리 영역 만들기

     

목차 - 2020/09/29 - [Study/서버] - 서버개발자가 되는법 - 목차

 

git - tkdlek11112/server_dev at 서버개발자_8 (github.com)

git(화면) - tkdlek11112/todo-list (github.com)

 

유튜브 - 

 

 

 

 

 

들어가기 전에

지난 포스팅에서는 page 처리하는 법과 input으로 올라오는 사용자 ID값에 따라 해당 사용자의 정보만 내려주는 API를 만들었습니다. 이렇게 사용자의 정보를 서버에 알려줘야 해당 사용자의 데이터만 골라서 내려줄 수 있습니다. 하지만 이전 방식과 같이 항상 input data영역에 사용자 id를 올린다면 새로운 API를 만들 때마다 input data 첫 번째 필드는 user_id가 되어야 합니다. 어째꺼나 무조건 사용자 ID는 필요하다고 하면 고정되게 올릴 수 있는 방법은 없을까요?

 

이번 시간에는 http body 영역에 넣어줬던 user_id를 헤더로 올려서 공통부분에서 처리되도록 만들어 봅시다. 추가로 응답 형식도 공통부분으로 만들어서 통일화를 시킵니다.

 

제가 자꾸 "공통부분"이라는 설명을 쓰는데, 아마 처음 접하시는 분들은 이게 뭔가? 생각할 것 같습니다. 저도 처음에 그랬거든요 ㅋㅋ 아니 코드에 공통이 어딨어 ㅋㅋ 공통 아니면 개인인가? ㅋㅋㅋㅋㅋ

 

하지만 이 글을 읽거나 영상을 보면서 따라 하시다 보면 '아~ 이게 공통이구나~' 하실 겁니다. 생각보다 별거 없거든요 ^^

 

 

이런걸 말하는건 아닙니다.

 

화면 맛보기

사실 화면은 지난번 포스팅과 똑같습니다. 다만 API의 I/O, Input Output이 변경되었습니다.

 

 

 

 

 

공통으로 사용할 커스텀 APIView 만들기

 

우리가 만들 장고 프로젝트에서 views.py를 보면 각 클래스들이 class 이름(APIView):라고 되어있습니다. 여기서 괄호 안에 들어가는 APIView가 바로 상위 클래스를 뜻하는데요 자바에서는 implement라고 표현되는 "상속"개념입니다. 파이참에서 컨트롤을 누르고 APIView에 마우스를 가따대보세요.

 

컨트롤을 누르면 타고 갑니다.

 

컨트롤을 누르고 클래스에 마우스를 올리면 링크를 타고 갈 수 있는데요, 바로 APIView가 실제로 어떻게 되어있는지 살펴볼 수 있습니다. 이때 만약 패키지로 제공되는 클래스라면 패키지 내부까지 갈 수 있기 때문에 특정 클래스가 어떻게 만들어졌고 어떻게 동작하는지 자세히 알고 싶다면 무조건 컨트롤 클릭해보는 습관을 기르세요.

 

만약 특정 사용자가 만든 클래스라면 어딘가의 소스로 가게 됩니다. 이게 바로 여러분이 새로운 회사에서 남이 만든 소스를 분석할 때 분석을 어렵게 하는 요소인데요, 누군가가 상위 클래스를 만들어놓고 그것을 상속받아서 자식 클래스를 계속해서 만들어 쓴다면, 상위 클래스를 살펴보지 않는 이상 어떤 기능이 있는지 예측하기 쉽지 않습니다. 

 

지금 당장은 이 말이 이해가 안 될 수도 있는데요, 바로 실습을 하면서 몸소 느껴보겠습니다.

 

 

먼저 프로젝트 경로에 common이란 폴더를 만들고, common.py를 만듭니다.

 

~/common/common.py

 

요렇게 common.py가 만들어졌습니다. 여기다가 APIView를 상속받는 TodoView를 만들어보겠습니다.

 

# ~/common/common.py
from rest_framework.views import APIView


class TodoView(APIView):
    # APIView를 상속받은 TodoView
    # 아직은 아무것도 하지 않는다.
    user_id = "이건 헤더에 있는 user_id"
​

 

 

아주 간단히 만들어졌습니다. 현재 TodoView는 APIView를 상속받은 APIView와 동일한 기능을 하는 class입니다. class 내부에는 user_id값이 있는데 과연 이 값을 어떻게 사용할지 지켜보세요.

 

일단 이 TodoView를 테스트하기 위해 ~/todo/views.py로 가봅시다.

거기 저희가 예전에 만들어놓은 test api가 있습니다.

 

# Create your views here.
class Test(APIView):
    def post(self, request):
        return Response(status=400)

 

여기에서 APIView를 TodoView로 바꿔봅시다. 물론 import가 필요합니다.

 

from common.common import TodoView


# Create your views here.
class Test(TodoView):
    def post(self, request):
        return Response(status=400)

 

아무 에러가 안나죠? 이제 Test라는 class는 TodoView를 상속받았기 때문에 아까 TodoView class에서 선언한 user_id를 쓸 수 있습니다.

 

자동완성까지 됩니다.
출력하게 만듭니다.

 

자 이제 출력하게 만들었으니 테스트 툴을 이용해서 Test를 호출해봅시다. urls.py에 등록돼있는 거를 확인하고 insomnia로 날려봅시다.

 

잘 찍히쥬?

로그에는 잘 찍힙니다. 응답에도 잘 찍히는지 다음과 같이 코드를 수정하고 테스트해봅시다.

 

 

from common.common import TodoView


# Create your views here.
class Test(TodoView):
    def post(self, request):
        print(self.user_id)
        return Response(status=200, data=dict(user_id=self.user_id))

 

insomnia로 호출

 

결과가 잘 나옵니다. 

 

자 그럼 이제 이걸로 뭘 할 수 있을까요?

 

 

 

 

body 영역에 있던 user_id 필드를 헤더로 올리기

 

이전 시간에는 http 요청에 body영역에 user_id 필드를 채워서 통신했습니다. 대부분의 api에서 user_id가 필요하다면, 공통영역에서 user_id를 처리해서 서비스를 새로 만들 때마다 매번 user_id 필드를 추가하지 않아도 되도록 변경한다면 개발 편의성이 올라갈 것입니다. 

 

그럼 먼저 common에 있는 TodoView에서 헤더에 있는 데이터를 꺼내와서 저장하도록 바꿔보겠습니다.

 

from rest_framework.views import APIView


class TodoView(APIView):
    # APIView를 상속받은 TodoView
    user_id = ''

    # dispatch는 클라이언트로 들어온 요청이 어떤 요청(get or post)인지 구분해서 처리하도록 분기해주는 녀석
    def dispatch(self, request, *args, **kwargs):
        self.user_id = request.headers.get('id', False)

        return super(TodoView, self).dispatch(request, *args, **kwargs)

 

dispatch 함수가 추가되었습니다. drf에서 dispatch는 특정 요청이 들어왔을 때 어떤 요청인지 분기 태워주는 역할을 합니다. 이렇게 모르는 내장 함수가 튀어나왔으면 공식문서를 찾아봐주는 연습을 하면 도움이 많이 됩니다.

 

출처 - Views - Django REST framework (django-rest-framework.org)

 

 

TodoView의 dispatch는 APIView의 dispatch가 실행되기 전에 실행돼서 헤더에 있는 키값 'id'로 세팅된 값을 받아와서 user_id에 넣고 super method를 사용해서 상위 클래스의 dispatch를 실행합니다. 즉 그냥 APIView를 사용했을 때 dispatch가 돌기 전에 우리가 정의한 작업을 하는 dispatch가 실행되는 구조입니다. 

 

자 이제 헤더에 있는 id값을 받아서 user_id에 세팅하는 것을 만들었으니, 테스트 툴로 요청을 올려봅시다.

 

그냥 요청하기

 

그냥 요청하게 되면 아까와 다르게 빈 값이 오게 됩니다. 헤더에 id값이 세팅되어있지 않기 때문이죠. url 아래 Header메뉴를 눌러서 Header값을 세팅해봅시다.

 

헤더에 id값을 넣어보자

 

자 이렇게 헤더에 id값을 넣었더니 user_id값이 헤더의 값과 같게 나오게 되었습니다. 이제 user_id를 헤더로 올렸으니 기존에 만들었던 api에서 user_id를 지워도 됩니다!!

 

class TaskCreate(TodoView):
    def post(self, request):
        # user_id = request.data.get('user_id', None) 
        user_id = self.user_id
        todo_id = request.data.get('todo_id', None)
        name = request.data.get('name', None)

        # 이전버전 호환을 위해 todo_id가 들어오고 안들어오고로 분기
        if todo_id:
            task = Task.objects.create(id=todo_id, user_id=user_id, name=name)
        else:
            task = Task.objects.create(user_id=user_id, name=name)

        return Response(data=dict(id=task.id))

 

원래는 user_id = request.data.get('user_id', None)으로 받아왔지만, TodoView를 사용한다면 self.user_id를 사용하면 됩니다. 클라이언트 개발자 역시 일일이 body영역에 user_id 필드를 만들 필요 없이 헤더에 user_id만 세팅하면 되므로 개발이 편해집니다.

 

새로 만들 때는 위 코드처럼 만들면 편하겠지만, 지난 시간에 언급했던 것처럼 헤더에 user_id를 올리지 않는 옛날 버전에 API에 대해서도 지원을 해줘야 합니다. 따라서 아래와 같이 코드를 바꿉니다.

 

class TaskCreate(TodoView):
    def post(self, request):
        # 이전버전 호환을 위해 헤더먼저 검사하고 body로 내려감.
        if self.user_id:
            user_id = self.user_id
        else:
            user_id = request.data.get('user_id', None)
        todo_id = request.data.get('todo_id', None)
        name = request.data.get('name', None)

        # 이전버전 호환을 위해 todo_id가 들어오고 안들어오고로 분기
        if todo_id:
            task = Task.objects.create(id=todo_id, user_id=user_id, name=name)
        else:
            task = Task.objects.create(user_id=user_id, name=name)

        return Response(data=dict(id=task.id))

 

추가로 select에서도 user_id를 쓰기 때문에 아래와 같이 수정합니다.

 

class TaskSelect(TodoView):
    def post(self, request):
        # 헤더에 id가 있으면 헤더의 id를 사용하고 없으면 body의 id를 사용
        if self.user_id:
            user_id = self.user_id
        else:
            user_id = request.data.get('user_id', None)
        page_number = request.data.get('page_number', None)

        is_last_page = True

        # user_id를 올리는 경우
        if user_id == "":
            tasks = []
        elif user_id:
            tasks = Task.objects.filter(user_id=user_id)
        else:
            tasks = Task.objects.all()

        if len(tasks) > 0:
            if page_number is not None and page_number >= 0:
                # print('총 todo 수 : ', tasks.count())
                if tasks.count() <= 10:
                    pass
                elif tasks.count() <= (1 + page_number) * 10:
                    tasks = tasks[page_number * 10:]
                else:
                    tasks = tasks[page_number * 10: (1 + page_number) * 10]
                    is_last_page = False

            # page_number가 없는경우.. 이전 버전 api이거나 실수로 못올렸거나
            # 그냥 0으로 생각하고 응답줄지 아니면 에러 응답할지 선택해야함.
            else:
                pass

        task_list = []
        for task in tasks:
            task_list.append(dict(id=task.id,
                                  userId=task.user_id,
                                  name=task.name,
                                  done=task.done))

        return Response(dict(tasks=task_list, is_last_page=is_last_page))

 

self.user_id를 검사해서 헤더에 user_id가 있으면 사용하고 없으면 body field에 있는 user_id값을 사용합니다. ㅎㅎ 코딩을 할 때마다 i/o가 바뀌니까 번거로운 수정이 많습니다. 그래서 실제 업무에서는 개발에 들어가기 전에 확실하게 input output값을 정하고 들어가야 고생 안 해요~

 

추가로 아래 if len(tasks) > 0: 부분이 추가됐는데 tasks가 0개일 때는. count()를 쓰면 에러가 나서 체크하도록 바꿨습니다. count대신 처음부터 len() 쓸 걸 그랬네요.

 

 

공통으로 빼는 게 무조건 좋을까?

 

저희는 지금 Todo 앱 프로젝트를 처음부터 쭈~욱 따라왔습니다. 근데 과연 이 프로젝트를 처음 보는 사람이 아래 코드를 보면 어떻게 생각할까요?

class TaskCreate(TodoView):
    def post(self, request):
        user_id = self.user_id
        todo_id = request.data.get('todo_id', None)
        name = request.data.get('name', None)

        # 이전버전 호환을 위해 todo_id가 들어오고 안들어오고로 분기
        if todo_id:
            task = Task.objects.create(id=todo_id, user_id=user_id, name=name)
        else:
            task = Task.objects.create(user_id=user_id, name=name)

        return Response(data=dict(id=task.id))

 

어? todo_id는 클라이언트에서 올라오는 request에서 뽑아서 쓰는데 user_id는 self.user_id네.

이상하네 user_id를 선언해놓은 곳이 없는데 어떻게 user_id가 들어오지?!

라고 생각하지 않을까요? ㅋㅋㅋ

 

이렇게 공통 영역으로 기능들을 빼놓으면, 상속만 받아서 재활용해서 개발 속도가 올라가고 편리하겠지만, 다른 사람에게 인수인계해주거나 이 프로젝트에 익숙하지 않은 사람이 본다면 코드 가독성이 떨어집니다. 하지만 경험이 많은 사람의 경우 아 뭐 공통영역에서 처리해주나 보다~ 생각하고 바로 넘어갈 수 있지요.

 

이제 여러분들도 한번 경험을 했으니, 남에 코드를 보는 눈이 조금 넓어졌습니다. 대~~ 충 내가 값을 넣어준 게 없는 데 사용되는 게 있으면 공통영역을 의심하면 됩니다 ㅋㅋㅋ

 

 

 

공통 응답 만들기

 

출력필드
tasks (Array) : [
id (int): To-Do 고유 아이디
name (string): To-Do 이름
userId (string): 사용자 id
done (boolean): To-Do 완료 여부
]
isLastPage (boolean): 마지막 페이지 여부

출력필드
id (int) : 생성된 To-Do의 ID

출력필드
없음

현재 출력 필드를 보면 API별로 전부 따로 놀고 있습니다. 필드가 아무것도 없는 것도 있고, Array가 있는 것도 있고, 그냥 필드만 몇 개 있는 것도 있죠. 

 

클라이언트는 대부분 공통된 형식의 출력 값을 원합니다. 그냥 공통이라고 말하면 뭔지 감이 잘 안 오는데 예를 들어서

 

{
  "result_code": 0,
  "result_msg": "success",
  "data":{
    "some_data":"some_value"
  }
}

 

요런 식입니다. 

 

어떤 API든 위에 형식에 맞춰 온다면 클라이언트 입장에서 출력 값을 사용하기 상당히 쉬울 겁니다. 위에서 result_code는 결과 코드(에러코드), result_msg는 결과 메시지(에러 메시지), data는 api 처리 후 데이터가 들어가는 부분입니다. 물론 회사마다, 팀마다 상요하는 공통 형식이 다를 것이고, 이미 공통된 형식으로 만들어진 곳이 많을 겁니다.

 

위처럼 공통화된다면 좋은 점이 에러가 나거나 data가 없는 api들도 통일화가 된다는 겁니다. 기존에 응답이 없던 api들도 아래와 같이 바꿀 수 있습니다.

 

{
  "result_code": 0,
  "result_msg": "success",
}

 

data만 없고 result_code랑 result_msg 필드만 살려서 api가 성공했는지 실패했는지, 실패했으면 왜 실패했는지를 전달할 수 있습니다.

 

 

공통 응답을 만드는 방법은 그냥 return 할 때 field를 일일이 같은 형식으로 지정해주는 방법이 있습니다. 근데 이렇게 하면 매번 API를 만들 때마다 응답 값을 설정해줘야 하는 번거로움이 있습니다.

 

class TestOutput(TodoView):
    def post(self, request):
        return Response(
            status=200,
            data=dict(
                result_code=0,
                result_msg="success",
                data=dict(
                    some_data="some_value"
                )
            )
        )

 

위 코드처럼 일일이 응답에다가 형식을 맞춰줘서 해야 하는데, 이러면 귀찮잖아요?

common.py에 아래와 같은 CommonResponse를 만들어줍니다.

 

# ~/common/common.py

from rest_framework.views import APIView
from rest_framework.response import Response


class TodoView(APIView):
    # APIView를 상속받은 TodoView
    user_id = ''

    # dispatch는 클라이언트로 들어온 요청이 어떤 요청(get or post)인지 구분해서 처리하도록 분기해주는 녀석
    def dispatch(self, request, *args, **kwargs):
        self.user_id = request.headers.get('id', False)

        return super(TodoView, self).dispatch(request, *args, **kwargs)


def CommonResponse(result_code, result_msg, data):
    return Response(status=200,
                    data=dict(
                        result_code=result_code,
                        result_msg=result_msg,
                        data=data
                        )
                    )

 

위 코드처럼 일일이 응답에다가 형식을 맞춰줘서 해야 하는데, 이러면 귀찮잖아요?

common.py에 아래와 같은 CommonResponse를 만들어줍니다.

 

from common.common import CommonResponse

# Create your views here.
class Test(TodoView):
    def post(self, request):
        print(self.user_id)
        return CommonResponse(0, "success", dict(some_data="some_value"))

 

위보다 깔끔해졌죠?

아래 TaskToggle도 바꿔봅시다.

class TaskToggle(TodoView):
    def post(self, request):
        todo_id = request.data.get('todo_id', "")
        task = Task.objects.get(id=todo_id)
        task.done = False if task.done else True
        task.save()
        return Response()

위에 응답이 없는 Response()를 아래와 같이

class TaskToggle(TodoView):
    def post(self, request):
        todo_id = request.data.get('todo_id', "")
        task = Task.objects.get(id=todo_id)
        task.done = False if task.done else True
        task.save()
        return CommonResponse(0, "success", "")

CommonResponse를 통해 return 하게 만듭니다. 

 

근데 result_code랑 result_msg를 넣는 것조차 귀찮지 않나요? data도 없는데 구지 "" 값 넣어야 되니까 ㅋㅋ

그래서 성공 응답이랑 실패 응답이랑 나눕시다.

 

# ~/common/common.py

def SuccessResponse():
    return Response(status=200,
                    data=dict(
                        result_code=0,
                        result_msg="success"
                    ))


def SuccessResponseWithData(data):
    return Response(status=200,
                    data=dict(
                        result_code=0,
                        result_msg="success",
                        data=data
                    ))


def ErrorResponse():
    return Response(status=200,
                    data=dict(
                        result_code=999,
                        result_msg="error!!!"
                    ))

 

요롷게 나눠놓고 맘에 드는 걸 사용합니다 ㅋㅋ

 

# ~/todo/views.py
from common.common import CommonResponse, SuccessResponse, SuccessResponseWithData, ErrorResponse

class TaskToggle(TodoView):
    def post(self, request):
        todo_id = request.data.get('todo_id', "")
        task = Task.objects.get(id=todo_id)
        task.done = False if task.done else True
        task.save()
        return SuccessResponse()


class TaskDelete(TodoView):
    def post(self, request):
        todo_id = request.data.get('todo_id', "")
        task = Task.objects.get(id=todo_id)
        if task:
            task.delete()

        return SuccessResponse()

응답 데이터가 없는 API는 이렇게 SuccessResponse로 통일합니다.

 

 

 

헤더에 버전 값 추가하기

 

공통 응답으로 바꾸니까 또 다른 문제가 발생했는데요, 기존에는 출력되는 data가 제일 상위에 위치했는데, 이제 제일 상위에는 result_msg, result_code, data 3가지 필드가 있고 기존에 data가 한 단계 아래로 내려갔습니다. 따라서 지금 전부를 공통 응답으로 바꾸게 되면 클라이언트가 작동하지 않을 거예요. (제가 바꾸기 전까지는)

 

위에서도 이전 버전을 위해 이것저것 예외처리를 많이 해줬는데 이것도 귀찮고,,, 해서 헤더에 버전 값을 넣어보도록 하겠습니다. 

 

실제로 애플리케이션을 서비스하는 많은 회사들이 헤더에 버전 값들을 많이 올리는데, 앱의 경우 마켓 심사를 받아야 업로드가 되고, 실제로 사용자가 앱 업데이트를 하지 않을 수도 있기 때문에 서버와 버전이 항상 일치할 수가 없습니다. 따라서 헤더에 내가 지름 올리는 앱의 버전을 올려서 서버가 대처할 수 있게 알려주는 거죠.

 

헤더에 추가하는 건 user_id 추가하는 것과 같습니다. 클라이언트 개발자한테 헤더에 "version"으로 올려주세요~ 해놓으면 됩니다. ㅎㅎ

 

class TodoView(APIView):
    # APIView를 상속받은 TodoView
    user_id = ''
    version = ''

    # dispatch는 클라이언트로 들어온 요청이 어떤 요청(get or post)인지 구분해서 처리하도록 분기해주는 녀석
    def dispatch(self, request, *args, **kwargs):
        self.user_id = request.headers.get('id', False)
        self.version = request.headers.get('version', '1.0')

        return super(TodoView, self).dispatch(request, *args, **kwargs)
 

 

self.user_id 말고 self.version을 추가했습니다. 이제 각 API에서 version을 체크해서 로직 분기를 할 수 있습니다.

 

class TaskToggle(TodoView):
    def post(self, request):
        todo_id = request.data.get('todo_id', "")
        task = Task.objects.get(id=todo_id)
        task.done = False if task.done else True
        task.save()

        if self.version < '1.1':
            return Response()
        else:
            return SuccessResponse()


class TaskDelete(TodoView):
    def post(self, request):
        todo_id = request.data.get('todo_id', "")
        task = Task.objects.get(id=todo_id)
        if task:
            task.delete()

        if self.version < '1.1':
            return Response()
        else:
            return SuccessResponse()

 

실습 4는 버전 "1.1"이라고 정의하고 실습 3 이하는 버전 "1.0"이라고 제가 정의했습니다 ㅋㅋ 이건 뭐 따로 정해진 건 없고 클라이언트랑 잘 협의하면 됩니다.

 

class TaskCreate(TodoView):
    def post(self, request):
        # 이전버전 호환을 위해 헤더먼저 검사하고 body로 내려감.
        if self.user_id:
            user_id = self.user_id
        else:
            user_id = request.data.get('user_id', None)
        todo_id = request.data.get('todo_id', None)
        name = request.data.get('name', None)

        # 이전버전 호환을 위해 todo_id가 들어오고 안들어오고로 분기
        if todo_id:
            task = Task.objects.create(id=todo_id, user_id=user_id, name=name)
        else:
            task = Task.objects.create(user_id=user_id, name=name)

        if self.version < '1.1':
            return Response(data=dict(id=task.id))
        else:
            return SuccessResponseWithData(dict(id=task.id))


class TaskSelect(TodoView):
    def post(self, request):
        # 헤더에 id가 있으면 헤더의 id를 사용하고 없으면 body의 id를 사용
        if self.user_id:
            user_id = self.user_id
        else:
            user_id = request.data.get('user_id', None)
        page_number = request.data.get('page_number', None)

        is_last_page = True

        # user_id를 올리는 경우
        if user_id == "":
            tasks = []
        elif user_id:
            tasks = Task.objects.filter(user_id=user_id)
        else:
            tasks = Task.objects.all()

        if len(tasks) > 0:
            if page_number is not None and page_number >= 0:
                # print('총 todo 수 : ', tasks.count())
                if tasks.count() <= 10:
                    pass
                elif tasks.count() <= (1 + page_number) * 10:
                    tasks = tasks[page_number * 10:]
                else:
                    tasks = tasks[page_number * 10: (1 + page_number) * 10]
                    is_last_page = False

            # page_number가 없는경우.. 이전 버전 api이거나 실수로 못올렸거나
            # 그냥 0으로 생각하고 응답줄지 아니면 에러 응답할지 선택해야함.
            else:
                pass

        task_list = []
        for task in tasks:
            task_list.append(dict(id=task.id,
                                  userId=task.user_id,
                                  name=task.name,
                                  done=task.done))

        if self.version < '1.1':
            return Response(dict(
                tasks=task_list, 
                is_last_page=is_last_page
            ))
        else:
            return SuccessResponseWithData(dict(
                tasks=task_list,
                is_last_page=is_last_page
            ))


class TaskToggle(TodoView):
    def post(self, request):
        todo_id = request.data.get('todo_id', "")
        task = Task.objects.get(id=todo_id)
        task.done = False if task.done else True
        task.save()

        if self.version < '1.1':
            return Response()
        else:
            return SuccessResponse()


class TaskDelete(TodoView):
    def post(self, request):
        todo_id = request.data.get('todo_id', "")
        task = Task.objects.get(id=todo_id)
        if task:
            task.delete()

        if self.version < '1.1':
            return Response()
        else:
            return SuccessResponse()

 

위처럼 코드를 다 바꿨습니다. 클라이언트에서 헤더에 버전 값을 1.1 이상으로 올리면 공통 응답 형식으로 나가고, 아니면 일반 버전으로 나갑니다.

 

실제로 "실습 4"화면에서 헤더 값이 잘 올라오는지 확인해보려면 print를 넣어보세요.

 

class TaskCreate(TodoView):
    def post(self, request):
        # 이전버전 호환을 위해 헤더먼저 검사하고 body로 내려감.

        print('헤더 id', self.user_id,'헤더 version', self.version)
        if self.user_id:
            user_id = self.user_id

 

id와 version 확인하기

 

 

 

 

 

정리하면서

 

원래 이번에 간단하게 끝내려고 했는데 ㅋㅋㅋ 자꾸 하나씩 기능 추가하다 보니까 길어졌네요.

 

이번 시간에는 공통 헤더와 공통 응답을 만들어봤습니다. "공통"이라는 말이 생소할 수 있는데, 회사에 가시면 아마 가장 많이 듣고, 가장 많이 사용하게 될 겁니다. ㅎㅎㅎ 공통에 정의된 함수나 기능들을 이용하려면 상속을 받거나 호출을 해야 하는데 서비스단에 코드에서는 잘 안보이기 때문에 코드를 분석하는데 어려을 겪을 겁니다. 저도 첫회사에 취직하고 좀 고생했는데 일부러 첫 1년은 밤 10시까지 남아가며 코드를 분석했던 기억이 납니다. 

 

여러분들도 남의 코드를 보는 연습을 많이 해두시면 첫 취직을 하고 나서도, 이직을 하고 나서도 빠르게 적응할 수 있는 개발자가 될 수 있습니다 :)

 

다음 시간에는 로그에 대해서 한번 다뤄보겠습니다. 고생하셨습니다~ :)

 

 

 

 

 

 

 

 

 

 

반응형

댓글

Designed by JB FACTORY