728x90

드디어 나를 수개월간 괴롭혀 왔던 프로젝트 발표가 끝이났다~ 그리고 6개월간의 이스트시큐리티에소의 현장실습이 종료되었다!! 

2주간 진행량 및 2021년 회고

  • GETIT 프로젝트

함께 작업하는 클라이언트 개발자분이 당근마켓에 지원하신다고하셔서 부리나케 일단 기능개발에 초점을 맞춰 현재 만들어진 기능을 다듬는 작업을 진행했다.

 

-검색 기능 개발

최종적으로는 elasticsearch를 사용하여 기능을 개발하려고 했는데 아무래도 처음 다뤄보는 기능이기도 해서 시간이 오래걸릴것 같아 model filtering을 사용하여 간단하게 구현했다.

search = request.GET.get('search')
        common_paginator = WholeBoardCommonPageNumberPagination()
        recruit_paginator = WholeBoardRecruitmentPageNumberPagination()
        free_posts = CommonBoard.objects.filter(Q(title__icontains = search) |
      Q(content__icontains = search),category='free')
        free_posts = common_paginator.paginate_queryset(free_posts, request)
        free_serializer = CommonBoardSerializer(free_posts, many=True)
        question_posts = CommonBoard.objects.filter(Q(title__icontains = search) |
      Q(content__icontains = search),category='question')
        question_posts = common_paginator.paginate_queryset(question_posts, request)
        question_serializer = CommonBoardSerializer(question_posts, many=True)
        recruit_posts = RecruitmentBoard.objects.filter(Q(title__icontains = search) |
      Q(content__icontains = search))
        recruit_posts = recruit_paginator.paginate_queryset(recruit_posts, request)
        recruit_serializer = RecruitmentBoardSerializer(recruit_posts, many=True)
        return JsonResponse({
            "freeboard": free_serializer.data,
            "questionboard": question_serializer.data,
            "recruitboard" : recruit_serializer.data
        })

코드가 굉장히 길다. 반드시 리팩토링이 필요한 부분인것이다. 하하...

url로 넘어온 category파라미터는 말그대로 카테고리 구분에 사용되면 ,title, content기능을 django orm filter기능중 __contains라는 걸 사용해 필터링할 문자열이 속한 필드를 모두 가져오는것으로 구현하였다. 

 

-필터 기능 개발

이부분도... 굉장한 리팩토링이 필요해보이는데 일단은 노가다로 구현했다. 백퍼센트 어디 지원해서 면접볼때 까일것 같긴하지만.. 일단 시간투자해서 해결했다는데 의의를 두고...

		pm = request.GET.get('pm')
        developer = request.GET.get('developer')
        designer = request.GET.get('designer')
        category = request.GET.get('category')
        if category == 'free':
            if pm == 'true':
                if developer == 'true':
                    if designer == 'true':
                        posts = CommonBoard.objects.filter(Q(worker = 1) | Q(worker = 2) | Q(worker = 3), category='free').distinct()
                        paginator = BoardPageNumberPagination()
                        result_page = paginator.paginate_queryset(posts, request)
                        serializer = CommonBoardSerializer(result_page, many=True, context={'request': request})

                        return paginator.get_paginated_response(serializer.data)
                    else:
                        posts = CommonBoard.objects.filter(Q(worker = 1) | Q(worker = 2), category='free').distinct()
                        paginator = BoardPageNumberPagination()
                        result_page = paginator.paginate_queryset(posts, request)
                        serializer = CommonBoardSerializer(result_page, many=True, context={'request': request})

                        return paginator.get_paginated_response(serializer.data)
                else:
                    if designer == 'true':
                        posts = CommonBoard.objects.filter(Q(worker = 3) | Q(worker = 2), category='free').distinct()
                        paginator = BoardPageNumberPagination()
                        result_page = paginator.paginate_queryset(posts, request)
                        serializer = CommonBoardSerializer(result_page, many=True, context={'request': request})

                        return paginator.get_paginated_response(serializer.data)
                    else:
                        posts = CommonBoard.objects.filter(worker=2, category='free').distinct()
                        paginator = BoardPageNumberPagination()
                        result_page = paginator.paginate_queryset(posts, request)
                        serializer = CommonBoardSerializer(result_page, many=True, context={'request': request})

                        return paginator.get_paginated_response(serializer.data)
            elif designer == 'true':
                if developer == 'true':
                    posts = CommonBoard.objects.filter(Q(worker = 1) | Q(worker = 3), category='free').distinct()
                    paginator = BoardPageNumberPagination()
                    result_page = paginator.paginate_queryset(posts, request)
                    serializer = CommonBoardSerializer(result_page, many=True, context={'request': request})

                    return paginator.get_paginated_response(serializer.data)
                else:
                    posts = CommonBoard.objects.filter(worker=3, category='free').distinct()
                    paginator = BoardPageNumberPagination()
                    result_page = paginator.paginate_queryset(posts, request)
                    serializer = CommonBoardSerializer(result_page, many=True, context={'request': request})

                    return paginator.get_paginated_response(serializer.data)
            elif pm == 'false' and designer == 'false' and developer == 'false':
                res = {
                    'count': 0,
                    'next':None,
                    'previous':None,
                    'results': [],
                }
                return JsonResponse(res)
            else:
                posts = CommonBoard.objects.filter(worker=1, category='free').distinct()
                paginator = BoardPageNumberPagination()
                result_page = paginator.paginate_queryset(posts, request)
                serializer = CommonBoardSerializer(result_page, many=True, context={'request': request})

                return paginator.get_paginated_response(serializer.data)

매 조건 절마다 return한건 테스트용도로 해둔건데 마지막에만 당연히 리턴문 돌려주면되니 알아서 필터링해주세요ㅎㅎ

 

- member 기능 개발

import json

from django.http import JsonResponse
from django.shortcuts import render

# Create your views here.
from profiles.models import TeamProfile
from requests import Response
from rest_framework.generics import GenericAPIView

from .models import Member, WaitingForMember
from .serializers import WaitingMemberSerializer


class MemberAddView(GenericAPIView):

    def post(self, request):
        """
        팀원 추가(POST)
        ---
                {
                    'teamprofile': 2,
                    'member': 3
                }
        """
        requestData = json.loads(request.body)
        teamprofile_id = requestData['teamprofile']
        member_id = requestData['member']
        print(requestData)
        profile = TeamProfile.objects.get(id=teamprofile_id)
        member = Member.objects.create(member=member_id)
        wait_member = WaitingForMember.objects.get(teammember__id=teamprofile_id, waitmember=member.member)

        wait_member.delete()
        profile.members.add(member)
        res = {
            "message": "success"
        }
        return JsonResponse(res)


class MemberWaitingView(GenericAPIView):

    def get(self, request, teamprofile):
        """
                프로젝트 지원 대기 멤버 조회(GET)
                ---
                        {
                            'teamprofile': 2,
                            'waiting_member': 3
                        }
                """
        teamprofile_id = request.GET.get('teamprofile')
        teamprofile = TeamProfile.objects.get(id=teamprofile_id)
        wating_member = teamprofile.waitingmember
        serializer = WaitingMemberSerializer(wating_member, many=True, context={'request': request})
        return Response(serializer.data)

    def post(self, request):
        """
        프로젝트 지원 대기 멤버 추가(POST)
        ---
                {
                    'teamprofile': 2,
                    'waiting_member': 3
                }
        """
        requestData = json.loads(request.body)
        teamprofile_id = requestData['teamprofile']
        waiting_member = requestData['waiting_member']

        try:
            waiting_member = WaitingForMember.objects.get(teammember__id=teamprofile_id, waitmember=waiting_member)
            print(waiting_member)
            teamprofile = TeamProfile.objects.get(id=teamprofile_id, waiting_members=waiting_member)
            print(teamprofile)
            if teamprofile is None:
                raise Exception
            res = {
                "message": "fail"
            }
        except:
            teamprofile = TeamProfile.objects.get(id=teamprofile_id)
            waiting_members = WaitingForMember.objects.create()
            waiting_members.waitmember = waiting_member
            waiting_members.save()
            teamprofile.waiting_members.add(waiting_members)
            res = {
                "message": "success",
                'teamprofile': teamprofile_id,
                'waiting_member': waiting_member
            }

        return JsonResponse(res)

팀 프로젝트의 멤버 추가기능을 개발하는데에 있어 신청을 했을때 받는 멤버와 팀장이 수락하여 멤버가된 모델을 분리하였고 역참조의 개념을 한번 익히는데 도움이 되었다. 사실 이 역참조라는걸 생각안하고 항상 구현하다보니 코드가 지져분해졌었는데 나중에 다끝나고 리팩토링할때 많이 쓰일것 같은? 기능이다. foreignkey의 related_name을 설정하여 외래키로 참조된 모델에서 역으로 참조한 모델을 참조할때를 역참조라고한다. 시간이 좀많이 걸렸다.

 

Get IT

 

getit.best

이젠 어느정도 명함을 내밀수 있게 완성이 되었다. 알림과 채팅만 남았다!!!! 현재 기능으로도 나쁘지 않다고 생각한다~

 

-KOTLUCK

이제 제대로 시작했다. 한국형 파티문화를 만들겠다! 라는 멋진 포부로 POTLUCK의 이름을따 KOTLUCK으로 지었는데 여러 회의를 거쳐 보라(BORA)라고 결정지었다!!!! 무엇보다 기획자 포지션이 정말 중요한데 너무 좋은분이 오신것같아 나도 함께 열심히 하고 있는중이다. 개발에 있어선 아직 세부기능이 확정이 안되서 일단 FCM을 활용하여 채팅, 알림기능 개발중이다. 이제 시간이 넘쳐나는 백수가 되었으니 졸업전 창업이라는 목표로 열심히 다시 불태워봐야겠다~~

 

 

-인턴

할말이 참많다.. 일단 엄청난 스트레스였던 패치랩 시연 및 발표가 끝이났다. 한20여분이 구글미트에 들어오셔서 보셨는데 많이좀 떨었고 개발을하며 ppt만들일은 없을줄알았는데 ppt까지 만들어서 발표하게 되었다. 리드님이랑 사수님이 질문때 많은 지원사격을 해주셨다는 생각이 들었고 두분은 모르시겠지만 사실나는 정말  감사했다ㅎㅎ 그리고 무엇보다 6개월간의 인턴 즉 현장실습이 종료되었다! 맨처음 글을올렸던 올해 6월말에 업무에 있어 조금이라도 도움이 되는 인턴이되자!라고 마음 먹었는데 다른 팀원분들도 그렇게 느끼셨으려나 모르겠다..ㅎ... 자주 실수도 하고 모르는것도 많아서 질문 투성이였는데 화한번 내지않고 모두 친절하게 알려주신 팀원분들께 그저 감사할 따름이다. 지금와서 생각해보면 업무가 정말 많다고 혼자서 불평도 했었고 초반에 엄청난 열정에 비해 조금은 게을러지고 했던것 같기만 하다. 사실 6개월간 많은 일이 있었고 처음으로 자취라는 것도 경험을 해서 후회되진 않는시간이었다. 무엇보다도 6개월전 무급이라도 개발자분들과 일을 함께 해볼수 있는 기회가 있었으면 좋겠다라고 생각도 했었고 불합 통지를 받았지만 추후 면접을 보지 않겠냐고 선뜻 다시 물어봐주신 pms-mpi셀 팀원분들한테 너무 고맙기만 한것같다. 자취라는 꿈과 개발일을하며 돈을 벌 첫기회를 준 기업이다. 사실, 일을 하기전 직급에 따라 불합리한 상황이 벌어지는것이 조금은 당연한거라 생각했는데 이스트시큐리티에서 일을 하며 정말 내가 잘못생각했구나!라는걸 느꼈다. 나에게도 중요한 업무를 할당해주시고 내의견을 조금이라도 무시하는 경향이 전혀없었으며 직급이 높다고 해서 불합리한 상황이란게 전혀없었다. 모든분들이 의견을 낼수 있으며 피드백을 바로바로주시고 정말 자유로운 분위기의 회사였다. 물론 기술스택이나 목표로 잡았던 기업이아니라 당장은 지원하진 않겠지만 회사생활을 해보면 이스트소프트라는 기업 정말 좋은 회사인걸알수 밖에없다. 개발자에서 대표가 되신 대표님부터 시작해서 현장실습생까지 모두가 동등한 위치에서 자신의 능력을 한껏 뽐낼수 있는 회사라고 생각한다. 많이 힘들었기도 했지만 얻은것도 많았던 터라 이제 막 나왔는데 아련한 기분이 든다ㅎㅎ.. 다만 걸리는건 중간에 이슈가 발생해서 업무를 다못끝냈다는거..? 많이 못끝내고나와 아쉽고 죄송하기만 하다....

 

-2021 회고

앞에 주저리 주저리 썻지만 요약하자면

  • 6개월간의 자취생활과 꿈에 그리던 인턴을 해냈다!
  • 한번은 제대로 완성해보고 싶었던 getit 프로젝트를 완성해냈다!

-2022 목표

  • 창업 : BORA로 창업이 목표다. 당장 어떻게 하는진 모르겠다. 일단 앱개발해서 대회도 나가보고하고싶다.
  • 졸업: 졸업이다. 코스모스 졸업인데 일단 정처기따면 졸업일테니 정처기가 목표인가?ㅎ..
  • 취업: 학생이지만 내생각엔 취준생이 맞다 좋은회사가서 발전하고 싶다. 좋은회사 갈꺼다
    • cs, 알고리즘 공부좀 개학전까지 끝내두기!
    • django이외에 다른 무기하나더 장착해두기!
  • GETIT 광고달기: 에드센스 달고 싶다. 알림, 채팅기능달면 다른 스터디 모집엔 비교할수 없는 최고라고 자신한다. 광고비 받아서 서버비용이라도 충당하고 싶다ㅎㅎ...
  • 여행: 사실 생각보다 돈좀 많이 모은것 같아 1달간 돈다쓰고올꺼다. 제주도가서 앞으론 쉬고싶지 않을정도로 좀 쉬다와야겠다.

필수적인것만 적었는데 다이뤘음 좋겠다!! 2022년도 화이팅!

그리고 2021년은 너무 그냥 고마운 한해였다!!

-끝-

 

+)사실 WIL컨텐츠는 아무래도 잠시 멈추려한다... 한번에 몰아쓰는 경향이 있는것 같아서 뭔가 제대로 매일 공부할만한 것이 생기면 그때 진행할꺼다ㅎㅎ....... 일단 남은기간동안 연합동아리 지원, 정보처리기사 공부, 프로젝트 진행이 대다수 일듯해서?? 일단 잠시는 멈춘다.. 다음에 시작하면 WIL이 아닌 TIL로 복귀하길...ㅎ

728x90
728x90

인턴 110일차


일단 지난번에 밤을새며 고생했던 일은 결국 기한을 지키는데 성공을 하였다. 하지만 또다시 난관... 서버망이전 작업에 있어 인프라팀과 대화를 많이 나누고 있는데 분명 방화벽 포트 개방까지 했지만 원격접속이 안되고 있어 난항을 겪는중이다. VSPHERE 환경에서 인턴기간동안 프로젝트 하나를 개발하게 되었는데 장고를 사용하여 웹서버를 만들기회가 생겨 설레는 맘으로 임하고 있지만 역시나 기획할때 술술 풀어내던것들이 막상 개발하면 탁탁막힌다 막혀.. 이번 회사 프로젝트를 완성하게 되면 엄청난 경험이 될것이라 믿어의심치 않으며 진행할것이다~

TIL 유형 변화


공부한걸 적어내는 TIL은 아무래도 쓰기전부터 양이 워낙많다보니 부담이 되는부분이있어 계속해서 미루게 된다. 그래서 앞으로 TIL은 하루의 시작전 오늘내가할 분량을 미리 적고 간단하게 한줄평식으로 리뷰하는 유형으로 변화시키려한다. 인턴 업무의 경우, 보안상 적기 힘든부분이 있으니 진행하는 프로젝트에서 목표량을 적을것이다.

목표


GETIT 프로젝트

  • 댓글 -> 대댓글 기능 구현
  • 이미지 -> 다중이미지로 기능 구현

인턴

  • SQLITE - PYTHON DB 연동 테스트
  • 시작 프로그램 - script.bat 구현
728x90
728x90

새롭게 프로젝트를 시작하며 개발전 1주일간 3파트로 프로젝트 사전 준비를 하였다.

(1) 프론트, 백엔드 통신방법

(2) 기술스택 선정, 협업 노하우

(3) token, django user, 소셜로그인

위의 3가지를 사전지식으로 먼저 조사하고 시작하기로 하였다.

RESTAPI


API는 데이터베이스와 사용자 인터페이스 즉, 프론트엔드 영역 사이에서 중간다리 역할을 해준다고 생각하면 된다. 백엔드단에서 만든 JSON,XML과 같은 형식의 데이터를 URI에 나타내어 프론트엔드는 URI에 있는 데이터를 가져다 쓰는 역할을 하는것이다. RESTAPI는 HTTP Method(GET, POST, PUT, DELETE)로 표현된 행위와 /accounts/2와 같은 주소를 합쳐 GET accounts/2와 같이 나타내어 프론트엔드가 데이터를 쓰도록 도와준다. 그리고 HTTP응답을 상태코드로 나타내는데

상태 코드 의미
200 클라이언트의 요청을 정상적으로 수행함
201 클라이언트가 어떠한 리소스 생성을 요청, 해당 리소스가 성공적으로 생성됨을 의미
400 클라이언트의 요청이 부적절할때
401 클라이언트가 인증되지 않은 상태에서 리소스를 요청할때
403 유저인증상태와 관계없이 응답하고 싶지 않은 리소스를 요청할때
405 클라이언트가 사용불가능한 리소스를 요청할때(ex.계정, 인증관련)
301 클라이언트가 요청한 리소스에 대한 URI가 변경되었을때
500 서버에 문제가 있을경우

위와 같은 상태코드로 나타낸다.

즉, RESTAPI는 

1.HTTP METHOD

2.데이터 주소

3.상태코드

크게 3가지를 통해 데이터를 표시하고 프론트가 적절하게 사용하게끔 도와주는 역할을 한다.

DjangoRESTFramework가 바로 RESTAPI인것이다.

 

 

프론트와 백엔드 연동방식(REACT-DRF)


-연동원리

1. 장고와 데이터베이스를 연결합니다.

2. 장고 ORM을 통해 데이터를 받습니다.

3. 받아온 데이터를 DjangoRESTFramework를 활용해 json객체로 만들어줍니다.

4. json객체를 특정 url에 뿌려줍니다.

5. React에서 fetch를 통해 특정 url로부터 json객체를 받아옵니다.

6. 받아온 데이터를 리액트식 문법으로 가공/사용하여 프론트엔드단을 꾸밉니다.

 

-연동방법

이를 실습으로 연결하는것은 실제 프로젝트를 진행하며 포스팅하기로 하고 8000포트의 django서버, 3000포트의 react서버를 사용하는 두프레임워크를 연결하기위한 사전준비에 대해 다뤄보려한다. 크게 2가지인것 같다.

1. django

- cors 문제를 해결하기 위해 django-cors-header 설치

-setting.py

  • INSTALLED_APPS에 ‘corsheaders’ 추가
  • MIDDLEWARE에 ‘corsheaders.middleware.CorsMiddleware’ 추가(가장 위에 놓으라는 말도 있고 CommonMiddleware 위나 밑에 놓으라는 말도 있다.)
  • CORS_ORIGIN_WHITELIST = [‘http://127.0.0.1:3000’ ,’http://localhost:8000’] 추가

2. react

app/package.json 하단에 proxy를 사용해 localhost:8000의 /api에 접속할수 있도록 해준다.

"proxy": {
	"/api": {
		"target": "http://localhost:8000"
}

위의 2가지 방식중 한가지로 설정을 하면 3000포트에서 8000포트에 있는 데이터를 자유자재로 가져올수 있다.

 

이후부터는 프론트단에서 axios통신을 사용해 fetch로 데이터를 가져와 행위에 맞게 데이터를 사용하면된다.

(프로젝트를 진행하며 계속 추가하도록 할 예정입니다.)

 

 

 

참고

 

REST API 제대로 알고 사용하기 : NHN Cloud Meetup

REST API 제대로 알고 사용하기

meetup.toast.com

 

 

프로젝트 지식(1) - Django React 연동

웹개발과 데이터 사이언스를 공부하는 공부일지

kgw7401.github.io

 

728x90

'프로젝트' 카테고리의 다른 글

프로젝트 협업시 준비  (0) 2022.01.09
프로젝트(2)기술스택 선정(feat. NginX, Redis...)  (0) 2021.09.04
협업에 관하여  (0) 2021.05.15
728x90

미리보는 프로젝트 완성코드 -->https://github.com/leeceo97/python_community

 

이번 챕터에서 다룰 내용은 기존의 유저 로그인 코드를 장고내의 form기능을 활용하여 리팩토링 하는 과정입니다.

글을 작성하기전 느꼈던 건데 생각보다 이해하기 어려워서 최대한 자세히 작성을 할테니 글이 길어지긴 할텐데 이해한번 해보고 넘어갑시다!! 잘알면 유용한 기능입니다~~

 

1. forms.py 생성및 기존 코드 삭제및 수정

login.html

{% extends "base.html" %}
{% block contents %}
        <div class="row mt-5">
            <div class="col-12 text-center">
                <h1>로그인</h1>
            </div>
        </div>
        <div class="row mt-5">
            <div class="col-12">
                {{ error }}
            </div>
        </div>
        <div class="row mt-5">
            <div class="col-12">
                <form method="POST" action=".">
                    {% csrf_token %}
                    {% for field in form %}
                    <div class="form-group">
                        <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                        <input type="{{ field.field.widget.input_type }}" class="form-control" id="{{ field.id_for_label }}" placeholder="{{ field.label }}" name="{{ field.name }}" />
                    </div>
                    {% if field.errors %}
                    <span style="color: red">{{ field.errors }}</span>
                    {% endif %}
                    {% endfor %}
                    <button type="submit" class="btn btn-primary">로그인</button>
                </form>
            </div>
        </div>
{% endblock %}

accounts/forms.py(accounts폴더내에 forms.py란 파일 생성후 코드기입해주세요.)

from django import forms
from .models import User
from django.contrib.auth.hashers import check_password

class LoginForm(forms.Form):
    useremail = forms.EmailField(
        error_messages={
            'required': '아이디를 입력해주세요.'
        }, max_length=64, label="사용자 이메일")
    password = forms.CharField(
        error_messages={
            'required': '비밀번호를 입력해주세요.'
        },widget=forms.PasswordInput, label = "비밀번호")

    def clean(self):
        cleaned_data = super().clean()
        useremail = cleaned_data.get('useremail')
        password = cleaned_data.get('password')

        if useremail and password:
            user = User.objects.get(useremail=useremail)
            if not check_password(password, user.password):
                self.add_error('password', '비밀번호가 틀립니다.')
            else:
                self.user_id = user.id

accounts/views.py

from .forms import LoginForm

def login(request):
    if request.method == 'POST':
        form = LoginForm(request.POST)
        if form.is_valid():
            request.session['user'] = form.user_id
            return redirect('/')
    else:
        form = LoginForm()
    return render(request, 'login.html', {'form': form})

하나하나 짚고 넘어가면 일단 forms.py의 기능은 장고 내에 있는 html에서 쓰이는 폼기능들을 백엔드 개발자가 쓰기 편하게 미리 만들어논 기능이라고 보시면 됩니다. 쉽게말해 미리 로그인이나 회원가입에서 필요한 예외처리 패스워드 처리등을 미리 기술 개발을 통해 제공한 것입니다. 

 

-html

html 파일 먼저 보시면 form을 사용하기전에 하나하나 라벨등과 같이 일일이 쳐줬던것과는 달리 장고 템플릿 변수를 사용해 label, widget, error등을 몇줄에 걸쳐 다표현한것을 알수 있습니다. 장고 템플릿 변수에대해 자세히 알고 싶으시면 

아래의 사이트에 방문해 알아보시는것도 좋을것 같습니다. html 같은경우는 코드를 다작성하고 나면 알게 되므로 간단히 생략하고 넘어가도록 하겠습니다.

 

[Django] 템플릿 언어

템플릿 언어를 사용하면 HTML 작업을 훨씬 수월하게 할 수 있다.

velog.io

 

-forms.py

useremail = forms.EmailField(
        error_messages={
            'required': '아이디를 입력해주세요.'
        }, max_length=64, label="사용자 이메일")
password = forms.CharField(
    error_messages={
        'required': '비밀번호를 입력해주세요.'
    },widget=forms.PasswordInput, label = "비밀번호")

이부분을 먼저 살펴보면 html에서 유효성 검사와 에러메시지등 받아올 데이터들에 대해 useremail, password 필드를 따로 만들어준것이다. 이후 모든 유효성 검사가 끝난 데이터들을 accounts 모델에 저장시키는 것이다. 나머지는 이해 되겠지만 password의 widget같은 경우는 CharField에 위젯을 PasswordInput으로 설정해줌으로써 유저가 비밀번호를 입력할때 가려진 상태로 비밀번호가 입력되게 된다. 

 

    def clean(self):
        cleaned_data = super().clean()
        useremail = cleaned_data.get('useremail')
        password = cleaned_data.get('password')

        if useremail and password:
            user = User.objects.get(useremail=useremail)
            if not check_password(password, user.password):
                self.add_error('password', '비밀번호가 틀립니다.')
            else:
                self.user_id = user.id

이부분을 이해하려면 일단 super()가 무엇인지 알아야하는데 이건 오버라이딩의 개념을 알고 있어야 합니다. 아래의 사이트를 참고하시면 됩니다.

 

[Python] 파이썬 super 기초 개념 및 예제

먼저 super를 사용하기전 상속, 오버라이딩 의 개념이 잡혀있어야 이해하기 쉽습니다. (상속, 오버라이딩 클릭시 페이지 이동) 개념 super() - 자식 클래스에서 부모클래스의 내용을 사용하고 싶을

rednooby.tistory.com

clean함수는 다음과 같은 기능을 합니다. 

-User모델에서 기존의 테이블을 불러와 폼에 들어온 데이터와 비교작업

-각종 예외처리에 대한 에러작업

form의 코드를 보면 기존의 view에서 사용하던 기능들을 가져와 처리해줌으로써 view의 코드들이 직관적으로 비즈니스 기능들만 수행하도록 하는 역할을 한다는걸 알수 있습니다.

 

-views.py

from .forms import LoginForm

def login(request):
    if request.method == 'POST':
        form = LoginForm(request.POST)
        if form.is_valid():
            request.session['user'] = form.user_id
            return redirect('/')
    else:
        form = LoginForm()
    return render(request, 'login.html', {'form': form})

기존의 views.py의 코드와는 다르게 많이 간단해진걸 알수가 있습니다. 여기서 궁금한것은 is_valid()였습니다. 

    def is_valid(self):
        """Return True if the form has no errors, or False otherwise."""
        return self.is_bound and not self.errors

is_valid함수를 찾아보니 위와 같이 에러가 없을경우 참값을 반환한다는 걸 알수 있습니다. 즉, forms.py의 유효성 검사를 모두 거쳐야지 다음의 조건을 수행하도록 해준다는 함수임을 알수 있습니다. 

 

2. 마무리 멘트

이번 form은 사실 너무 이해하기가 힘든 챕터였습니다. 오버라이딩의 개념도 제대로 이해하지 못했고 is_valid와 clean()등 이게 뭘뜻하는지 내장된 함수인지 조차 잘모르겠어서 난해했는데 하나하나 파고 들어보니 이해가 갔고 다시한번 파이썬 기본문법의 중요성을 알수있는 챕터였다고 느껴집니다. 한가지 팁을 알려드리면 이게 장고에 내장되어있는 기능인지 확인해보는 방법은 아래의 이미지와 같이 헷갈리는 변수에 오른쪽 클릭을하고 Go to Definition을 클릭하면 그함수 또는 클래스가 어떻게 이루어져있는지 보여주니 보고 참고하셔서 개발하시면 될겁니다!! 감사합니다:)

728x90

+ Recent posts