2월 20, 2022

[django][westagram] 회원가입 기능 구현 : 이메일, 비밀번호 검증 정규식 / 비밀번호 앞에 @ 붙일시 에러 / django의 exist()

email_regex = '^[a-zA-Z0-9+-\_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$’

이메일 정규식은 다음과 같습니다.

정규식에서 [] 대괄호는 안에 있는 값들 중 하나라도 만족하는 것을 찾을 때 이용합니다.

대괄호 "안에" [^a-z] 이렇게 ^를 붙여주면 대괄호 안에 있는 아이들을 제외한 값을 찾아달란 뜻입니다.

반대로, 정규식 맨 처음의(대괄호 안에 들어있지 않은) ^는, 문장에서 시작하는 아이를 지정한다는 뜻입니다.

특정 문자열 다음의 + 는, 하나 있거나, 그 이상(one or more)을 의미합니다.

종합해서 보자면, 

^[a-zA-Z0-9+-\_.]

는 문장의 맨 앞에 영문 대소문자, 숫자, +-/_. 들 중 만족하는 것 하나라도 있는경우 를 따져라. 라는 뜻입니다

그 이후 @ 가 오고, 같은 패턴의 대괄호가 반복됩니다.

그 이후 \.(역슬래쉬 .) 이 오는데요 바로 . 을 찍으면 그것이 정규식에서 어떠한 기능을 할 수 있기에, 단순히 . 문자 자체가 온다는 것을 표현하기 위해 \ 뒤에 . 을 찍어주었습니다.

마지막 대괄호 뒤에 오는 $는 문장의 끝임을 알려주는 기호입니다


password_regex = (?=.*[A-Za-z])(?=.*\d)(?=.*[$@!%*#?&])[A-Za-z\d$@!%*#?&]{8,}

비밀번호 정규식입니다. 이 친구가 좀 어렵습니다. 


"비밀번호는 8자리 이상의 문자,숫자,특수문자의 복합" 이라는 조건에 대한 정규식입니다.

보이는 대로 해석을 해보면 그룹() 이 세개이고, 대괄호 안에는 영문 대소문자와 \d(숫자), 그리고 특수문자가 있습니다

(?=expr)

이 아이는 positive lookahead입니다.

Positive lookahead assertion. 이것은 포함된 정규표현식(expr)이 현재위치에서 성공적으로 match되면 성공하고, 그렇지 않으면 실패합니다. 그러나, 포함된 정규표현식(expr)이 시도된 이후, matching engine은 advance하지 않습니다; 나머지 패턴은 assertion(참이어야 한다고 생각한 사항을 표현한 논리식)이 시작한 곳에서 시도됩니다(tried). 


정규표현식 문서를 그대로 직역했습니다

. 은 모든 문자이고,

* 는 zero or more(없거나 있거나 많거나) 입니다

정리하자면, positive lookahead는 뒤의 표현식이 어떠한 문자열에 있는지 사전검증 하는 역할이고, 뒤의 패턴은 

어떠한 문자가 오거나 오지 않은 이후, 영문 대소문자가 있는지 확인합니다.

이 lookahead 기능을 여러번 사용하면 정규식의 AND연산을 수행할 수 있습니다.

(?=expr1)(?=expr2)(?=expr3) 이런식으로 

1. positive lookahaed를 통해 표현식에 해당하는 문자들이 있는지 찾습니다

2. 찾았지만 이를 포함하지 않습니다(advance 하지 않습니다)

3. 찾은 이후 뒤의 정규표현식을 실행하는데, 그 뒤의 정규표현식이 전방탐색에서 찾은 문자를 포함하면 이를 저장하고, 그렇지 않으면 저장하지 않습니다.

3. 이런식으로 패턴의 일치를 연속적으로 확인합니다 


따라서 위의 식은,

[A-Za-z\d$@!%*#?&] 

이 대괄호에 해당하는 문자열이, (?=) 안에 있는 패턴 세개를 포함하는지 검증하고, 

문자열의 길이는 8자 이상 {8,} 이어야 한다는 뜻인듯 합니다. 


저도 아직 정확히 이해하지 못했지만, 나름대로 이해한 것을 기술해 보았습니다.


회원가입을 위해 post 요청을 보낼 때, 비밀번호의 맨 앞에 @를 붙일 시 


이 에러가 계속 났습니다.

@ ! 등의 특수문자들은 단순 특수문자가 아니라 파이썬에서 무언가를 실행하는 코드일 수 있습니다.

따라서 이 특수문자들을 사용하고 싶으면. r 또는 \(역슬래쉬)를 사용해야 합니다.

password=r"@dfdf#2df” 이렇게 앞에 r을 붙여주거나

password="\@dfdf#2df” 이렇게 @ 바로 앞에 \(역슬래쉬)를 붙여줘야만 합니다.

그렇지 않으면 어떠한 명령으로 보아 에러가 났던 것이었습니다.


이렇게 .get메소드에 .exists() 메소드로 존재를 확인하여 email의 일치 여부를 확인하려 했는데요, get메소드로 object를 가져오게 되면, User모델의 인스턴스(객체)가 나오게 됩니다. 반면에 .exists() 메소드는 "쿼리셋"의 존재를 확인하게 되기에, 이런식으로 .get에 exists메소드를 써주면 에러가 나게됩니다. 따라서 .filter로 메소드를 변환해줘야 됩니다.


그렇다면 

if User.objects.get(email=email):

이런식으로 exists()가 없이 코드를 작성해도 괜찮지 않을까요?

아닙니다.

Django의 쿼리셋은 기본적으로 lazy evaluation을 지향하기에, 마지막 순간에 가서야만 query(데이터베이스에 정보 요청)을 날리게 됩니다. 

exists()는 레코드가 "1개이상만" 존재하는 지를 확인하기에, QuerySet 전체를 evaluate하지는 않습니다. 

그러나, 위의 코드에서는 if문에 bool()을 사용하는 것이기에 QuerySet 자체를 evaluate하는 셈이 됩니다. 따라서 lazy evaluation을 지키기 힘들어집니다