ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [WebPage] 실전 웹 프로젝트 구축 !!(Server Part 2) - 웹 페이지 제작 강좌
    Web/WebPage 2015. 7. 9. 11:37

    이전 장에 이어서 계속해서 api를 구현하도록 하겠습니다.




    연결된 강좌이므로 아래 링크대로 따라주시면 이해가 더 쉬우실 것 입니다.

    ===========================================


    1. 개발환경설정

    2. CSS & Bootstrap 

    3. CSS 선택자(Selector)

    4. CSS 요소(Element)

    5. Bootstrap Part. 1

    6. Bootstrap Part. 2

    7. 실전 예제 - 개인용 포트폴리오 페이지 제작

    8. Python & Django

    9. Django

    10. 장고(Django) 프로젝트

    11. JavaScript & jQuery

    12. 실전 웹 프로젝트(예제)


    13. 실전 웹 프로젝트(Timeline) Server Part.1


    ===========================================








    17. 다음으로는 사용자 목록을 가져오는 기능을 구현하도록 하겠습니다.





     - 사용자 목록 가져오기: api/user/list/


     URI

     Method 

     api/user/list/

     GET 

     가입된 사용자 목록을 보여주는 API 입니다.

     Input Parameter

     None

     Output

     회원 프로필 리스트

     


    urls.py에 코드를 추가합니다.



    1
        url(r'^api/user/(?P<method>list)/$', user_view),
    cs





    user_view에 추가해주도록 합니다. 생성 부분과 수정 부분의 소스는 생략했습니다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    def user_view(request, method):
        if method == 'create' and request.method == 'POST':
            skip this source
        elif method == 'update' and request.method == 'POST':
            skip this source
        elif method == 'list':
            users = UserProfile.objects.all()
        else:
            return HttpResponse('bad request', status=400)
    cs




    views.py를 위와 같이 작성해 주시고, models.py를 작성해야합니다. 


    models.py의 class UserProfile 부분을 아래와 같이 수정해줍니다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    class UserProfile(models.Model):
        user = models.OneToOneField(User)
        nickname = models.CharField(max_length=128)
        comment = models.TextField()
        country = models.CharField(max_length=128, blank=True)
        url = models.CharField(max_length=128, blank=True)
        ignores = models.ManyToManyField(User, related_name='ignore_set', blank=True, null=True)
        
        def __unicode__(self):
            return "%s" % (self.user,)
        
        def serialize(self):
            data = {
                'user': self.user_id,
                'username': self.user.username,
                'nickname': self.nickname,
                'comment': self.comment,
                'country': self.country,
                'url': self.url,
                'ignores': [],
            }
            return data
        
    cs




    다시 views.py로 돌아와 아래 코드를 elif list 부분에 추가해줍니다.



    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import json
     
    def user_view(request, method):
        if method == 'create' and request.method == 'POST':
            skip this source
        elif method == 'update' and request.method == 'POST':
            skip this source
        
        elif method == 'list':
            users = UserProfile.objects.all()
            serialized = []
            for u in users:
                serialized.append(u.serialize())
            j = json.dumps(serialized, ensure_ascil=False)
            return HttpResponse(j, content_type='application/json; charset=utf-8')
            
        else:
            return HttpResponse('bad request', status=400)
    cs






    이제 사용자 목록을 확인 할 수 있게 되었습니다. 그런데 뭔가 복잡해 보이기 때문에 모듈화를 해줘야 할 것 같습니다.

    views.py 하단에 아래 코드를 추가합니다.


    1
    2
    3
    4
    5
    6
    7
    8
     
    def serialize(objs):
        return map(lambda x:x.serialize(), objs)
     
    def toJSON(objs, status=200):
        j = json.dumps(objs, encoding="utf-8", ensure_ascii=False)
        return HttpResponse(j, status=status, content_type='application/json; charset=utf-8')
     
    cs


    그 다음 user_view 부분을 아래와 같이 수정해줍니다.



    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    import json
     
    def user_view(request, method):
        if method == 'create' and request.method == 'POST':
            try:
                username = request.POST.get('username')
                password = request.POST.get('password')
                if User.objects.filter(username__exact=username).count():
                    return HttpResponse('duplicate id'400)
                user = User.objects.create_user(username, password=password)
                user.first_name = request.POST.get('name''')
                user.save()
                profile = UserProfile()
                profile.user = user
                profile.save()
                return toJSON({'status':'create Success'})
            except:
                return toJSON({'status':'create failed'}, 400)
        elif method == 'update' and request.method == 'POST':
            try:
                username = request.POST.get('username')
                password = request.POST.get('password')
                newpassword = request.POST.get('newpassword')
                user = User.objects.get(username__exact = username)
                if user.check_password(password) is False:
                    return toJSON({'status':'wrong password'}, 400)
                else:
                    user.set_password(newpassword)
                    user.first_name = request.POST.get('name', user.first_name)
                    user.save()
            except:
                return toJSON({'status':'bad request'}, 400)
            return toJSON({'status':'updated'})
        
        elif method == 'list':
            users = UserProfile.objects.all()
            return toJSON(serialize(users))
            
        else:
            return toJSON({'status':'bad request'}, 400)
     
    cs









    18. 사용자에 대한 api 구현은 끝이 났습니다. 다음으로는 타임라인 API를 작성하도록 하겠습니다.






     - 타임라인 목록 가져오기: api/timeline/


     URI

     Method 

     api/timeline/

     GET 

     타임라인 목록을 가져오는 API(로그인 필요)

     Input Parameter

     page - 가져오고 싶은 페이지

     per_page - 한 페이지에 몇 개씩 볼 것인가


     Output

     {

        'total_page': 전체 페이지 수,

        'total_count': 전체 메시지 수,

        'messages': [{

            메시지 정보

        }]

      }







    먼저 UserProfile과 마찬가지로 Message 모델serialize 함수를 추가해 줍니다. models.py를 열어서 추가합니다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    class Message(models.Model):
        user = models.ForeignKey(User)
        message = models.CharField(max_length=128)
        created = models.DateTimeField(auto_now_add=True)
     
        def serialize(self):
            data = {
                'id': self.id,
                'user': self.user_id,
                'username': self.user.username,
                'liked': self.like_set.count(),
                'message': self.message,
                'created': self.created.ctime()
            }
        
            return data
     
    cs






    다음으로 timeline_view를 수정해줍니다.



    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     
    from django.core.paginator import Paginator
     
    @need_auth
    def timeline_view(request):
        messages = Message.objects.order_by('-created').all()
        
        try:
            tweet_per_page = int(request.GET.get('per_page'10))
            page_num = int(request.GET.get('page'1))
            
            pages = Paginator(messages, tweet_per_page)
            
            resp = {
                'total_page': pages.num_pages,
                'total_count': pages.count,
                'messages': serialize(pages.page(page_num).object_list)
            }
            
            return toJSON(resp)
        
        except:
            resp = {
                'status''pagination error'
            }
            return toJSON(resp, 400)
     
    cs








    19. 다음으로 타임라인에 메시지 남기기 기능을 구현하겠습니다.





     - 타임라인에 메시지 남기기: api/timeline/create/


     URI

     Method 

     api/timeline/create/

     POST 

     타임라인에 메시지를 작성하는 API(로그인 필요)

     Input Parameter

     

      message - 가져 오고 싶은 페이지


     Output

      - 성공시

        {'status': 'create success'}


      - 실패시

        {'status': 'bad request'}







    urls.py에 아래 코드를 추가합니다.



    1
    2
     
        url(r'^api/timeline/create/$', message_create_view),
    cs




    다음으로 views.py에 아래 코드를 추가합니다.



    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    @need_auth
    def message_create_view(request):
        if request.method != 'POST':
            return toJSON({'status''bad request'}, 400)
        
        message = Message()
        
        try:
            message.user = request.user
            message.message = request.POST.get('message''')
            message.save()
            
            return toJSON({'status''create success'})
            
        except:
            return toJSON({'status''bad request'}, 400)
    cs




     타임라인 글 생성이 잘 되는지 확인하기 위해 간단히 Rest Console로 테스트 해보겠습니다.


     Request URI를 아래와 같이 설정하고 


      http://localhost:8000/api/timeline/create/



     파라미터를 아래와 같이 설정해줍니다.






    설정 뒤 Send 를 눌러서 Response Body 탭을 눌러 아래와 같이 떴는지 확인합니다.














    20. 특정 메시지 조회 기능을 구현하도록 하겠습니다.






     - 타임라인에서 특정 메시지 조회: api/timeline/메시지ID/


     URI

     Method 

     api/timeline/메시지ID/

     GET 

     타임라인에 있는 메시지를 조회하는 API(로그인 필요)

     Input Parameter


     None


     Output

     - 성공시

        메시지 출력

     

     - 실패시

       {'status': 'not found'}







    urls.py에 아래와 같은 코드를 입력합니다.



    1
    2
       url(r'^api/timeline/(?P<num>\d+)/$', message_view),
     
    cs



    views.py에서 아래 코드를 추가해줍니다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @need_auth
    def message_view(request, num):
        try:
            message = Message.objects.get(id = num)
            
            return toJSON(message.serialize())
            
        except:
            return toJSON({'status''not found'}, 400)
        
        return HttpResponse(None)
    cs




    아까 작성한 메시지를 조회하기 위해 Rest Console을 통해 조회합니다.


    http://localhost:8000/api/timeline/1/ 를 URI로 입력하여 파라미터를 지워준 뒤, Send 해서 아래와 같이 뜨는 지 확인합니다.


    똑같이 뜨는게 아니라 자신의 아이디나 올린 날짜등은 다를 수 있습니다. JSON으로 내뱉는 게 확실히 나오는 지 확인하기 위함 입니다.













    21. 특정 메시지 삭제 기능을 구현하도록 하겠습니다.







     - 타임라인에서 특정 메시지 삭제: api/timeline/메시지ID/delete/


     URI

     Method 

     api/timeline/메시지ID/delete/

     POST 

     타임라인에 메시지를 삭제하는 API(로그인 필요)

     Input Parameter


     None


     Output

     - 성공시

        {'status': 'deleted'}

     

     - 실패시

       {'status': 'forbidden'}

       {'status': 'not found'}






    urls.py에 아래와 같은 코드를 입력합니다.


    1
    2
        url(r'^api/timeline/(?P<num>\d+)/delete/$', message_delete_view),
     
    cs

     


    views.py에서 아래 코드를 추가해줍니다.



    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    @need_auth
    def message_delete_view(request, num):
        try:
            message = Message.objects.get(id = num)
            if message.user == request.user:
                message.delete()
                return toJSON({'status''deleted'})
            else:
                return toJSON({'status''forbidden'}, 401)
            
        except:
            return toJSON({'status''not found'}, 400)
     
    cs





    Rest Console로 생성하였던 타임라인 메시지를 삭제합니다. 삭제 기능이 잘 되는지 반응을 살펴보시기 바랍니다. 일일이 확인하는 작업을 올리진 않겠습니다. 후에 클라이언트 페이지를 만들면서도 확인 가능하나 구현 후 바로 확인해보시길 바랍니다.









    22. 특정 메시지에 Like 표시하는 기능을 구현하도록 하겠습니다.







     - 타임라인에서 특정 메시지 Like 표시: api/timeline/메시지ID/like/


     URI

     Method 

     api/timeline/메시지ID/like/

     POST 

     특정 메시지에 Like 표시하는 API(로그인 필요)

     Input Parameter


     None


     Output

     - 성공시

        {'status': 'created'}

     

     - 실패시

       {'status': 'bad request'}




    urls.py에 아래와 같은 코드를 입력합니다.

       


    1
    2
        url(r'^api/timeline/(?P<num>\d+)/like/$', like_view),
     
    cs



    views.py에서 아래 코드를 추가해줍니다.



    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    @need_auth
    def like_view(request, num):
        try:
            message = Message.objects.get(id=num)
            like = Like()
            like.user = request.user
            like.message = message
            like.save()
            
        except:
            return toJSON({'status''bad request'}, 400)
        
        return toJSON({'status''created'})
     
     
    cs











    23. 타임라인에서 검색하기 기능을 구현하도록 하겠습니다.







     - 타임라인에서 검색하기: api/timeline/find/


     URI

     Method 

     api/timeline/find/

     GET 

     타임라인에 메시지를 검색하는 API(로그인 필요)

     Input Parameter


     query - 검색하고 싶은 검색어


     Output

     - 성공시: 검색한 단어가 포함된 메시지 

     - 실패시: {'status': 'no'}





    urls.py에 아래와 같은 코드를 입력합니다.

       


    1
    2
     
        url(r'^api/timeline/find/$', find_view),
    cs

     


    views.py에서 아래 코드를 추가해줍니다.



    1
    2
    3
    4
    5
    6
    7
    8
    @need_auth
    def find_view(request):
        query = request.GET.get('query''')
        
        result = Message.objects.filter(Q(message__contains=query)|Q(user__userprofile__nickname__contains=query))
     
        return toJSON(serialize(result))
     
    cs








    24. 다음으로 사용자의 이름 수정 기능을 구현하도록 하겠습니다.





     - 사용자 이름 변경 or 수정하기 : api/user/name/


     URI

     Method 

     api/user/name/

     GET, POST

     현재 로그인한 사용자의 이름을 변경하거나 가져오는 API (로그인 필요)

     Input Parameter

    POST: 이름 수정

     name - 바꾸고자 하는 이름


    GET: 이름 가져오기

     None


     Output

     1. 이름 수정 시(POST)

      - 성공시: {'status': 'updated'}

      - 실패시: {'status': 'bad request'}


     2. 이름 가져올 때(GET)

      {'name': 이름}






    urls.py에 아래와 같은 코드를 입력합니다.

       

    1
    2
     
        url(r'^api/user/name/$', name_view),
    cs




    views.py에서 아래 코드를 추가해줍니다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    @need_auth
    def name_view(request):
        if request.method == 'GET':
            data = {
                'name': request.user.first_name,
            }
            
            return toJSON(data)
        
        if request.method == 'POST':
            try:
                name = request.POST.get('name')
                request.user.first_name = name
                request.user.save()
                
                return toJSON({'status''updated'})
                
            except:
                return toJSON({'status''bad request'}, 400)
    cs










    25. 암호를 확인하고 설정하는 API를 구현하도록 하겠습니다.






     - 암호를 확인하고 설정하기 : api/user/checkpassword/, api/user/setpassword/


     URI

     Method 

     api/user/checkpassword/

     POST

     현재 로그인 한 사용자의 비밀번호가 맞는지 확인하는 API (로그인 필요)

     Input Parameter


     password - 비밀번호


     Output

     

     비밀번호 맞을 때: {'status': 'ok'}


     비밀번호 틀릴 때: {'status': 'no'}





     URI

     Method 

     api/user/setpassword/

     POST

     현재 로그인 한 사용자의 비밀번호를 변경하는 API (로그인 필요)

     Input Parameter


     password - 비밀번호


     Output

     

     비밀번호 변경 성공 시: {'status': 'ok'}






    urls.py에 아래와 같은 코드를 입력합니다.



    1
    2
    3
        url(r'^api/user/checkpassword/$', checkpassword_view),
        url(r'^api/user/setpassword/$', setpassword_view),
        
    cs



    views.py에서 아래 코드를 추가해줍니다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
            
    @need_auth
    def checkpassword_view(request):
        try:
            password = request.POST.get('password')
            if request.user.check_password(password):
                return toJSON({'status''OK'})
            
        except:
            pass
        return toJSON({'status''no'})
     
    @need_auth
    def setpassword_view(request):
        try:
            password = request.POST.get('password')
            if password:
                request.user.set_password(password)
                request.user.save()
                return toJSON({'status''OK'})
            
        except:
            pass
        
        return toJSON({'status''no'})
    cs







    26. Profile을 조회하는 API를 구현하도록 하겠습니다.






     - Profile 조회하기 : api/profile/사용자아이디/


     URI

     Method 

     api/profile/사용자아이디/

     GET

     URI에 명시된 사용자의 프로필을 가져오는 API

     Input Parameter


     None


     Output

     

     URI에 명시된 사용자의 프로필





    urls.py에 아래와 같은 코드를 입력합니다.

     

    1
    2
        url(r'^api/profile/(?P<username>\w+)/$', profile_view),
     
    cs




    views.py에서 아래 코드를 추가해줍니다.



    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
     
    @need_auth
    def profile_view(request, username):
        if request.method == 'GET':
            try:
                userprofile = User.objects.get(username=username).userprofile
                return toJSON(userprofile.serialize())
                
            except:
                return toJSON({'status''not found'}, 400)
            
     
    cs










    27. 현재 로그인한 사용자의 프로필을 가져오거나 수정하는 API를 구현하도록 하겠습니다.






    urls.py에 아래와 같은 코드를 입력합니다.

     

    1
    2
    3
    4
        url(r'^api/profile/$', profile_view),
     
     
     
    cs




    views.py에서 아래 코드를 추가해줍니다. 기존의 profile_view에 username 파라미터가 없을 때, POST 형식으로 받았을 때를 추가하겠습니다.



    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
     
     
    @need_auth
    def profile_view(request, username=None):
        if username == None:
            username = request.user
        
        if request.method == 'GET':
            try:
                userprofile = User.objects.get(username=username).userprofile
                return toJSON(userprofile.serialize())
                
            except:
                return toJSON({'status''not found'}, 400)
            
        elif request.method == 'POST':
            profile = request.user.userprofile
            profile.nickname = request.POST.get('nickname', profile.nickname)
            profile.comment = request.POST.get('comment', profile.comment)
            profile.country = request.POST.get('country', profile.country)
            profile.url = request.POST.get('url', profile.url)
            ignores = request.POST.get('ignore', None)
            if ignores:
                ignores = json.loads(ignores)
                profile.set_ignorelist(ignores)
            
            profile.save()
            
            return toJSON({'status''updated'})
            
    cs





     다음으로 ignore 속성에 대한 설정이 없었으므로, models.py를 수정하도록 하겠습니다.



    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    class UserProfile(models.Model):
        user = models.OneToOneField(User)
        nickname = models.CharField(max_length=128)
        comment = models.TextField()
        country = models.CharField(max_length=128, blank=True)
        url = models.CharField(max_length=128, blank=True)
        ignores = models.ManyToManyField(User, related_name='ignore_set', blank=True, null=True)
        
        def __unicode__(self):
            return "%s" % (self.user,)
        
        def get_ignorelist(self):
            ignores = []
            for k in self.ignores.all():
                ignores.append(k.id)
            return ignores
        
        def set_ignorelist(self, ignores):
            self.ignores = []
            for k in ignores:
                try:
                    ignore = User.objects.get(id = k)
                    self.ignores.add(ignore)
                    self.save()
                    
                except:
                    pass
        def serialize(self):
            data = {
                'user': self.user_id,
                'username': self.user.username,
                'nickname': self.nickname,
                'comment': self.comment,
                'country': self.country,
                'url': self.url,
                'ignores': self.get_ignorelist(),
            }
            return data
     
     
     
    cs





    마지막으로 timeline_view를 수정하여서 api 구현을 완료합니다.




    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     
    @need_auth
    def timeline_view(request):
        messages = Message.objects.order_by('-created').all()
        ignore = request.user.userprofile.get_ignorelist()
        messages = messages.exclude(user__id__in = ignore)
        
        try:
            tweet_per_page = int(request.GET.get('per_page'10))
            page_num = int(request.GET.get('page'1))
            
            pages = Paginator(messages, tweet_per_page)
            
            resp = {
                'total_page': pages.num_pages,
                'total_count': pages.count,
                'messages': serialize(pages.page(page_num).object_list)
            }
            
            return toJSON(resp)
        
        except:
            resp = {
                'status''pagination error'
            }
            return toJSON(resp, 400)
     
    cs










    28. 정상적으로 로그인 되었는지 확인하는 API를 구현하도록 하겠습니다.





     - 로그인 확인하기 : api/login/


     URI

     Method 

     api/login/

     GET

     로그인 테스트에 사용할 API

     Input Parameter


     None


     Output

     

     {'status': 'ok', ,'user': 로그인 한 사용자의 프로필 }




    urls.py에 아래와 같은 코드를 입력합니다.

     

    1
    2
     
        url(r'^api/login/$', login_view),
    cs




    views.py에서 아래 코드를 추가해줍니다.


    1
    2
    3
    4
    @need_auth
    def login_view(request):
        return toJSON({'status''OK''user':request.user.userprofile.serialize()})
     
    cs









    28. 마지막으로 Template Page를 설정하겠습니다.






     지금까지 우리는 Django를 이용해 API를 개발하였습니다. 여기에는 템플릿을 사용하지도 않았고 HTML을 쓰지도 않았습니다.


     하지만 실제 서비스를 운영하려면 API만 가지고는 불가능 합니다. 아래 방법을 통해 html 페이지를 제공하기 위한 방법을 확인하면서 서버 부분을 마치겠습니다.



    urls.py에 아래와 같은 코드를 입력합니다.

     


    1
    2
     
        url(r'^home/(?P<page>\w+).html$', serve_html),
    cs





    views.py에서 아래 코드를 추가해줍니다.



    1
    2
    3
    4
    5
    from django.shortcuts import render_to_response
     
    def serve_html(reqeust, page):
        return render_to_response(page+'.html')
     
    cs





    여기까지 작성으로 Server 딴에서의 작업은 끝이 났습니다.



    모든 기능에 이상이 없는지 체크하시고 다음 장에서 뵙도록 하겠습니다.




    질문 사항은 댓글/ DISQUS를 통해 달아주세요.



    * 본 포스팅은 이재근 등 4명 저 "Fast Web Service Build up: 웹 서비스를 쉽고 빠르게 구축하는 기술" 저서를 참고하여 작성하였습니다


    댓글

Designed by Tistory.