Django,Python 유효성 검사

손성수·2023년 7월 4일
0
post-thumbnail

사용 목적

  • 저장되는 데이터의 유효성 검사
  • 어떠한 경로로 접근한 사용자의 유효성 검사

이해하기

lambda 함수

추상 클래스

  • 클래스의 메서드에 @classmethod를 사용하여
    외부에서 접근시, 별도의 오브젝트 생성 없이 사용할 수 있다.

모듈화


validated.py에서는 데이터 검증에 관한 모듈만을 구성하고
다른 코드와 뒤섞이지 않아, 가독성과 유지 보수성을 높일 수 있었다.



비밀번호 검증

알파벳, 숫자, 특수문자 이외의 값이 있는지 파악하기

lambda element: all(
    x.isdigit() or
    x.islower() or
    x.isupper() or
    (x in ['!', '@', '#', '$', '%', '^', '&', '*', '_'])
    for x in element
)
  • all()
    튜플 내의 요소들 모든 요소가 True라면 True를 반환한다.
all(True,True,True)  >> True
all(True,False,False)  >> False
all(False,False,False) >> False
  • x.isdigit()
    변수 x가 숫자라면 True, 아니라면 False를 반환한다.
"123".isdigit() >> True
# 숫자로 구성
"123!".isdigit() >> False
# 특수문자 포함
"aa123".isdigit() >> False
# 알파벳 포함
  • x.islower()
    변수 x가 대문자가 없다면 True, 아니라면 False를 반환한다.
print("aa".islower())  >> True
print("aaㅁㅁ".islower())  >> True
print("aa123".islower())  >> True
print("aa!".islower())  >> True
print("aA".islower())  >> False
  • x.isupper()
    islower와 반대로 소문자가 없다면 True, 아니라면 False를 반환 한다.
print("AA".isupper()) >> True
print("AAㅁㅁ".isupper()) >> True
print("AA123".isupper()) >> True
print("AA!".isupper()) >> True
print("aA".isupper()) >> False
  • x in [...]
    리스트 안에 있는 요소중, x와 일치하는 것이 있다면 True, 아니라면 False를 반환한다.
print("a" in ['!', '@', '#', '$', '%', '^', '&', '*', '_'])
> False
print("!" in ['!', '@', '#', '$', '%', '^', '&', '*', '_'])
> True
  • 컴프리 핸션
    다양한 기능이 있지만, 위의 코드에서는
    문자열을 하나하나의 요소로 잘라내는 기능을 하게 되었다.
print([i for i in "hello"])
> ["h","e","l","l","o"]
  • 구현 원리
    컴프리 핸션으로 쪼개낸 하나 하나의 요소가
    각각의 조건문에서 단 하나의 요소라도 True라면
    all() 메서드로 인해 True값이 반환된다.

최소 하나의 특수문자가 포함되어있는지 확인하기

lambda element: any(
  x in ['!', '@', '#', '$', '%', '^', '&', '*', '_']
  for x in element
),
  • any()
    요소들중 단 하나라도 True라면 True를, 아니라면 False를 반환한다.

  • 동작 원리
    컴프리 핸션으로 전달받은 값을 하나 하나 쪼개어, 각 요소들에 대하여
    x in [...] 조건문을 검증한다.


    나열된 요소들중 단 하나라도 True값이 있다면 참, 아니라면 거짓
print(any(
    [x in ['!', '@', '#', '$', '%', '^', '&', '*', '_']
    for x in "123!"]
))
> True
print(any(
    [x in ['!', '@', '#', '$', '%', '^', '&', '*', '_']
    for x in "123"]
))
> False

최소 하나의 숫자가 포함되어있는지 확인하기

lambda element: any(
	x.isdigit() for x in element
),
  • 구현 원리
    컴프리 핸션을 통해 쪼개어진 요소들을 하나 하나씩 isdigit을 통해
    숫자가 포함되어 있는지 확인한다.

공백이 포함되어 있는지 확인하기

lambda element: len(element) == len(element.replace(" ", "")),
  • len()
    전달된 문자열의 인자값의 길이를 반환
len("abc") 
> 3
len("12345") 
> 5
  • x.replace(" ","")
    문자열의 값을 튜플에 선언해둔 좌항의 값을, 우항의 값으로 변환
x = "12 3".replace(" ","")
print(x)
> "123"
  • 동작 원리
    replace를 사용하여 공백의 값을 제거하지 않은 문자열의 길이와
    원본 데이터의 문자열의 길이를 비교한다.
    이때 길이가 다르다는것은 공백이 제거된 경우가 생긴것으로 판단할 수 있다.

길이가 6자 이상 20자 이하인지 판단하기

lambda element: True if (len(element) > 5 and len(element) < 21) else False,
  • 삼항 연산자
    조건문의 참이라면 반환할 값과
    조건문이 거짓이라면 반환할 값을 지정할 수 있다
print(a if 0 != 1 else b)
> a
  • 동작 원리
    len 메서드를 이용하여 길이값이 충족될경우 True 아니라면 False를 반환한다.

람다함수를 이용하여 비밀번호 데이터 검증

class ValidatedData:
    @classmethod
    def validated_password(cls, password):
        check = [
            lambda element: all(
                x.isdigit() or x.islower() or x.isupper() or (x in ['!', '@', '#', '$', '%', '^', '&', '*', '_']) for x
                in element),
            # 요소 하나 하나를 순환하며 숫자,소문자,대문자,지정된 특수문자 제외한 요소가 있을경우 False
            lambda element: any(x in ['!', '@', '#', '$', '%', '^', '&', '*', '_'] for x in element),
            # 최소 하나 이상의 특수문자 요구
            lambda element: any(x.isdigit() for x in element),
            # 최소 하나 이상의 숫자 요구
            lambda element: len(element) == len(element.replace(" ", "")),
            # 공백이 포함 되어 있을 경우 False
            lambda element: True if (len(element) > 5 and len(element) < 21) else False,
            # 전달된 값의 개수가 5~20 사이일 경우 True
            lambda element: any(x.islower() or x.isupper() for x in element),
            # 요소 하나하나를 순환하며, 요소중 대문자 또는 소문자가 있어야함(숫자로만 가입 불가능)
        ]
        for i in check:
            if not i(password):
                return False
        return True

이해하기

  • check 라는 리스트에 각각의 검증할 람다 함수를 선언

  • for문을 통해 각각의 람다 함수는
    반복자 i에 담기게 되고 i는 함수로써 사용할 수 있다.

  • i의 인자값으로 검증할 데이터를 인자값으로 전달한다.

  • 리턴값이 True가 아니라면 그 즉시 False를 반환한다.

  • 모든 데이터 검증이 통과되면 True를 반환한다.


닉네임 검증

닉네임 검증 로직

    @classmethod
    def validated_nickname(cls, nickname):
        check = [
            lambda element: element is not None,
            # 값이 None이 아닐 경우 True
            lambda element: len(element) == len(element.replace(" ", "")),
            # 공백이 없을 경우 True
            lambda element: True if (1 < len(element) < 20) else False,
            # 2자 이상 20자 이하라면 True
        ]
        for i in check:
            if not i(nickname):
                return False
        return True
  • 비밀번호의 검증에서 사용된 람다 함수와
    비슷한 로직으로 구현 하였다.
  • len 메서드를 이용하여 닉네임 길이 조건 제시
  • len 과 replace를 이용하여 공백 검사


이메일 검증

정규식을 이용한 이메일 검증

import re
class ValidatedData:
    @classmethod
    def validated_email(cls, email):
        """
        이메일 검증
        """
        if email is None:
            return False
        email_pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'
        email_match = re.match(email_pattern, email)
        return bool(email_match)
[\w\.-]
> 알파벳, 숫자, 밑줄,, 그리고 하이픈을 포함한 하나 이상의 문자
@
> 문자 @를 의미
re.match(email_pattern, email)
> email이 선언한 정규식 email_pattern 조건에 일치한다면
True 아니라면 False를 반환한다.


통관번호

통관 번호 검증
통관 번호 양식 나무 위키
통관 번호 양식을 참고하여 vailidation을 작성 하였다.

class ValidatedData:
    @classmethod
    def validated_customs_code(cls, customs_clearance_number):
        number = customs_clearance_number.lower()
        # 알파벳 요소를 소문자로 변경
        check = [
            lambda element: element is not None,
            # 값이 비어 있는지 확인
            lambda element: len(element) == len(element.replace(" ", "")),
            # 값에 공백이 있는지 확인
            lambda element: True if (10 < len(element) < 13) else False,
            # 10자 이상, 11자 이하의 조건인지 확인
            lambda element: element[1:].isdigit()
            # 첫 알파벳 을 제외한 요소들이 숫자로 이루어 졌는지 확인
        ]
        for i in check:
            if not i(number):
                return False
        return True
  • len 메서드를 이용하여 길이에 대한 조건 요구
  • len과 replace를 이용하여 공백 유무 검사
  • 슬라이스를 이용하여 첫 번째에 들어올 알파벳을 제외하고
    나머지 요소들이 숫자로 이루어졌는지 검사


우편 번호 검증

우편 번호 양식

    @classmethod
    def validated_postal_code(cls, postal_code):
        check = [
            lambda element: element is not None,
            # 값이 비어 있는지 확인
            lambda element: len(element) == len(element.replace(" ", "")),
            # 공백이 포함되어 있는지 확인
            lambda element: True if len(element) == 5 else False,
            # 길이가 정확히 5 인지 확인
            lambda element: element.isdigit(),
            # 숫자를 제외한 값이 있는지 확인
        ]
        for i in check:
            if not i(postal_code):
                return False
        return True
  • len 메서드를 이용하여 길이에 대한 조건 요구
  • len과 replace를 이용하여 공백 유무 확인
  • isdigit() 메서드를 이용하여 숫자로 이루어 졌는지 확인


핸드폰 번호 검증

    @classmethod
    def validated_phone_number(cls, numbers):
        """
        핸드폰 번호 검증
        """
        check = [
            lambda element: element != None,
            # None값이 아니라면 True
            lambda element: len(element) == len(element.replace(" ", "")),
            # 공백이 포함 되어 있을 경우 False
            lambda element: len(element) == 11,
            # 11자리가 아니라면 False
            lambda element: element.isdigit()
            # 숫자가 아닌 값이 들어 있다면 False
        ]
        for i in check:
            if not i(numbers):
                return False
        return True
  • len 메서드를 이용하여 길이에 대한 조건 확인
  • len과 replace를 이용하여 공백 유무 확인
  • isdigit()를 이용하여 숫자로 이루어 졌는지 확인


주소지 검증

다음 주소지 불러오기 API
사용자 편의성을 위하여 간단한 주소지 정보를 입력하면
그에 관한 상세한 주소 정보를 불러오는 API이다.
사용자 편의성 뿐만 아니라 개발에 있어서도 복잡한 로직 없이 구현할 수 있었다.

  • 전달 받은 데이터는 프론트 데이터 이므로 조작이 가능하다는 점을 인지하고 간단한 벨리데이션을 추가 하였다.

  • 검증 대상
    주소지 정보, 수령인 정보

  • 검증 제외 대상
    우편 번호 : 별도의 validation이 존재
    상세 주소 : 입력하지 않아도 될 경우가 생길 수 있음

  • 검증 조건
    공백을 제거하고난 문자열의 길이가 0일경우 False
    문자열의 길이가 0일 경우 False
False if len(element.replace(" ", "")) == 0 else True 
# len과 삼항 연산자를 이용하여 조건식 구성
  • 전체 코드
    @classmethod
    def address_information_verification(cls, **information):
        check_list = [
            information.get('address'),
            information.get('recipient'),
        ]
        try:
            return all([False if len(element.replace(" ", "")) == 0 else True for element in check_list])
        except AttributeError:
            return False

이해하기

  • check_list에 검증할 데이터를 저장
  • 컴프리 핸션으로 check_list의 요소들을 하나씩 반환
  • 반환 받은 데이터를 삼항 연산자를 이용하여 데이터 검증
  • all 메서드를 이용하여 모든 데이터가 True가 아닐경우 False를 반환


    예외 처리
  • replace를 이용하여 문자열 연산시
    문자열이 아니거나, 또는 값이 없을 경우 속성에러가 발생할 수 있음
profile
더 노력하겠습니다

0개의 댓글