[Python] First Django Tutorial Review

김상웅·2022년 6월 16일
0

[파이썬]

목록 보기
14/17
post-thumbnail

✅ 들어가면서


python 언어의 강력한 프레임워크 중 하나인 Django를 활용하여 간단한 웹페이지의 서버를 구현해보았습니다.

순서는 초기세팅부터 로그인을 성공하였을 때 JWT 토큰을 발행해주는 것까지 진행하였습니다.

conda3를 통해 만든 가상환경에 대해서는 따로 언급하지 않겠습니다.

기존 튜토리얼을 진행하면서 httpie로 로컬 내에서 자체 통신을 진행하였으나,

이번에는 Postman을 활용하여 End to End Test까지 진행하였습니다.

보완사항에 대해서는 해당 튜토리얼을 진행하면서 받은 리뷰를 중심으로 작성하였습니다.

코드는 GitHub Repository - User directory를 확인해주세요.



✅ 초기세팅


가상환경을 활성화한 상태에서 초기세팅을 진행해줍니다.

📌 .gitignore

git과 github의 branch 기능을 통해 효율적인 협업이 가능합니다.

하지만, 해당 프로젝트의 중요한 정보는 환경 변수로 설정하고, github에 올라가지 않도록 지정해주어야 합니다.

서버에 대한 secret_key뿐만 아니라 데이터베이스에 대한 정보가 유출될 수 있기 떄문입니다.

my_settings.py에 필요한 환경변수를 지정하여 주고, .gitignore 에 my_setting.py 파일을 추가하여줍니다.

📌 requirements.txt

프로젝트를 처음부터 같이 시작하는 동료 개발자가 있듯

프로젝트 중간에 투입되는 개발자도 있을 것입니다.

해당 프로젝트에 설치한 모듈과 패키지를 하나씩 알려주는 것은 굉장히 비효율적일 것입니다.

다음 명령어를 입력하면 text 파일을 manage.py와 같은 경로에 설치해줍니다.

pip freeze > requirements.txt

그 다음 프로젝트에 설치한 모듈패키지버전만 남을 수 있도록 정리해주면 됩니다.

다음 명령어를 통해 개발환경을 복구, 세팅할 수 있습니다.

pip install -r requirements.txt

📌 setting.py

해당 튜토리얼의 목적은 배포용 서버가 아닌 개발용 서버를 만드는 것입니다.

차후 공유 wifi를 통해 end to end 통신을 위한 세팅을 해주어야합니다.

ALLOWED_HOST = ["*"]

corsheader
... 등

📌 urls.py

관리자용 페이지와 서버를 따로 만들지 않았습니다.

메인이 되는 장고 프로젝트 디렉토리의 urls.py 파일에서 admin과 관련된 코드를 삭제해줍니다.



✅ 모델링


초기 모델링을 통해 프로젝트를 배포할 때까지 바꾸지 않을 수 있고,
중간에 모델링을 바꿔야할 수도 있습니다.

필요한 데이터베이스 테이블과 관계를 잘 살펴보고 초기에 모델링을 잘 설정해주는 것이 중요하겠습니다.

이번 튜토리얼에서는 회원가입을 통해 회원의 정보를 저장해야했습니다.

📌 기본 모델링

따라서 User라는 테이블에는 다음과 같은 코드를 통해 각각의 필드를 지정하였습니다.

email 필드에 unique 속성을 부여하여 회원 정보 등록 간 중복이 저장되지 않도록 설정하였습니다.

📌 core

회원정보를 저장하는 것은 타인의 정보를 우리가 관리해야 한다는 막중한 책임감이 따를 것입니다.

만약 회원이 비밀번호를 수정하였는데 그 변화를 기록하지 않았고, 그로 인해 발생한 문제점에 대해 개발자에게 책임을 묻는다면 어떻게 대처를 해야할까요?

이런 문제를 해결하기 위해 데이터의 이력을 관리하는 필드를 생성해줍니다.

모델링을 하고 있는 앱의 models 파일에 직접 추가할 수도 있습니다.

이번 튜토리얼에서는 core 앱 생성 후 TimeStampModel이라는 추상화 된 클래스를 상속받도록 지정해주었습니다.

django-admin startapp core

cd core/models.py

cd users/models.py

위의 코드와 같이 작성할 수 있습니다! 차이점이 보이시나요?

아래의 사진은 두개의 필드가 추가된 데이터베이스의 모습입니다.

차후 데이터를 수정했을 때 어떤 결과가 있는지 보고 반영해보겠습니다.



✅ 회원가입


많은 웹 서비스를 이용하기 위해 회원가입과 로그인은 필수적인 요소가 되었습니다.

이메일에는 @가 포함되어야 하며, 비밀번호는 8글자 이상 혹은 특수문자를 포함해달라는 요구사항을 많이 보셨을 것입니다.

이렇게 이메일과 비밀번호에 특정 표현의 유무는 정규표현식을 기반으로 판단이 되는데요.

코드를 통해 알아보겠습니다.

📌 유효성 검사

POST HTTP 메서드를 통해 회원의 정보를 등록해달라는 요청을 받으면, users/views 파일에서는 다음의 유효성 검사를 거칩니다.

이메일

EMAIL_CHECK = "^[a-zA-Z0-9+_.]+@[a-zA-Z0-9-.]+$"

^ 첫부분
[a-zA-Z0-9+_.] 영소문자 / 대문자 / 숫자 / _
+@[a-zA-Z0-9-.] @ 영소문자 / 대문자 / 숫자 / .
+ 최상위 root 도메인 포함 여부
$ 마지막 부분

비밀번호

최소 8자 + 최소 한개의 영문자 + 최소 한개의 숫자

"^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$"

최소 8자 + 최소 한개의 영문자 + 최소 한개의 숫자 + 최소 한개의 특수 문자

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

최소 8자 + 최소 한개의 소문자 + 최소 한개의 대문자 + 최소 한개의 숫자

"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$"

최소 8자 + 최소 한개의 소문자 + 최소 한개의 대문자 + 최소 한개의 숫자 + 최소 한개의 특수 문자

"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$"

최소 8자 + 최대 10자 + 최소 한개의 소문자 + 최소 한개의 대문자 + 최소 한개의 숫자 + 최소 한개의 특수 문자

"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,10}$"

📌 중복검사

위의 정규식을 통과하면 해당 정보를 받아들이게 됩니다.

이후 DB 쿼리문을 통해 유저 정보가 등록된 부분이 있는 지 없는 지 확인을 하는 단계를 거칩니다.

없다면 성공적으로 유저 정보를 DB에 저장하게 되고, 중복이 있다면 그에 맞는 에러를 반환해줍니다.

📌 Postman 회원가입

포스트맨을 이용하여 서버를 가동하고 회원가입에 대한 요청과 응답 메시지를 아래 사진에서 볼 수 있습니다:

클라이언트는 요청 데이터를 bodyJSON 형식으로 담아 서버에게 회원가입을 요청합니다.

서버는 이 요청 데이터들의 유효성 검사와 중복 검사를 통해 데이터베이스에 저장했다는 메시지를 보여줍니다.

만약 유효성 검사가 실패하거나 중복이 되는 이메일이 있다면 그에 상응하는 에러를 반환하게 됩니다.



✅ 로그인


📌 유저정보 확인 쿼리 vs if문

쿼리문

view 로직으로 쿼리문을 작성하여 데이터베이스에 접근할 수 있습니다.

만약 해당하는 유저 정보가 없다면 반환하는 데이터가 없어 User.DoesNotExist라는 에러를 반환합니다

해당하는 유저 정보가 있다면 user 변수를 통해 해당 유저 객체의 다양한 key값으로 데이터를 얻을 수 있겠습니다.
(예시: user.id, user.email 등)

if문

조건문을 통해 해당 유저 정보의 유무는 다음과 같이 판단할 수 있습니다.

exists() 메서드는 정보 유무에 따라 True / False를 반환해줍니다.

동일한 결과를 반환하기 때문에 두 구문을 같이 사용하면 불필요한 로직을 처리하여 비효율적일 것입니다.

📌 비밀번호 확인

유저 정보가 있다면! 비밀번호만 확인해주면 되겠습니다.

지금까지 단계에서는 비밀번호를 암호화하고 저장하지 않았기 때문에 저장된 회원정보의 password 값에 직접 접근하여 요청된 비밀번호와 비교하여 줍니다!



✅ 암호화


이번 튜토리얼에서는 bcrypt를 활용하여 회원 정보의 비밀번호를 암호화하였습니다.

다음 명령어를 통해 bcrypt를 설치해줍니다.

pip install bcrypt

사용하기 위해서는 import bcrypt를 불러와 사용해주면 됩니다.

encode("인코딩방식") 문자열의 비밀번호를 바이트 형식으로 변환해줍니다.
decode("인코딩방식") 바이트 형식의 데이터를 문자열로 변환해줍니다.
hashpw(인코딩된 비밀번호, bcrypt.gensalt()) 인코딩된 비밀번호에 salt 값을 더해 해싱값을 반환합니다.
chekcpw(인코딩된 비밀번호, 디코딩된 DB상의 비밀번호)

회원등록 과정:

  1. 받은 비밀번호를 인코딩한다.
  2. hashpw()를 통해 비밀번호를 해싱한다.
  3. 그 값을 데이터베이스에 넣어준다.

📌 데이터베이스 암호화

회원가입 단계에서 7개의 회원정보 중 비밀번호를 모두 동일하게 입력하였습니다.

하지만 데이터베이스에 저장된 비밀번호는 다음과 같이 암호화되어 저장됩니다.



✅ JWT


가장 많이 활용하는 JWT (Json Web Token)을 활용하여 로그인에 성공하면 토큰을 발행하였습니다.

토큰을 발행하기 위해 필요한 정보는 유저에 대한 정보, 서버의 secret_key, algorithm 세가지입니다.

유저에 대한 정보는 주요 정보가 아닌 어느 데이터베이스에 있는 id 값을 활용합니다.

secret_key, algorithm 은 환경변수로 지정하여 사용해주면 되겠습니다.

토큰은 로그인을 하면서 발행되기 때문에 로그인 로직이 통과된 이후에 발급하도록 코드를 설계해야합니다.

로그인 과정:

  1. 받은 비밀번호를 인코딩한다.
  2. 해당 유저 정보가 있는지 확인하고 있다면 데이터베이스에 등록된 비밀번호를 꺼내온다.
  3. 등록된 비밀번호를 인코딩한다.
  4. checkpw()를 통해 1번과 3번이 일치하는지 확인한다.

위 과정을 거치면 해당 access_token을 응답메시지로 반환해주면 됩니다.

📌 Postman

이번에는 Postman을 통해 로그인 요청을 해보겠습니다.

서버에서는 로그인을 하기 위해 accountpassword를 요구하여 데이터베이스의 데이터와 비교합니다.

아래 응답메시지를 보면 access_token이 같이 반환되었는데요.

클라이언트는 이 토큰을 로그인 이후의 요청을 할 때마다 요청메시지 body에 담아 보내고

서버는 그 토큰의 권한을 확인합니다.



📌 보완해야할/보완했던 사항


1. Validation

지금은 간단한 회원가입과 로그인만 구현하였기 때문에 같은 views 파일 내부에서 유효성을 검사하는 로직을 작성하였습니다.

좀 더 복잡한 기능을 구현하기 위해 validation 모듈을 작성하여 필요할 때 import하여 사용할 수 있겠습니다.

2. PEP8

지금은 WECODE라는 회사의 코드 컨벤션을 따른다고 생각하고 코드를 작성하고 컨벤션을 지키려고 노력 중입니다.

PEP8이라는 코드 컨벤션을 더 알아보고 코드 작성 간 가독성이 좋은 코드를 작성할 수 있어야겠습니다.

3. wsl 포트포워딩

처음으로 End to End 테스트를 하면서 첫 시도만에 될 것이라는 기대감은 두 프론트엔드 개발자 분을 오래 서있게 만들었습니다. 젠틀하고 여유롭게 기다려주신 덕분에 서버와의 통신을 성공하였고 성공적으로 두분께 토큰을 발급해드렸습니다

공통된 wifi에 접속하여 IP4 주소로 HTTP 통신을 요청해야 했습니다.

Postman을 통해서 확인한 통신 결과는 서버 자체에 접속이 되지 않았습니다.

윈도우를 사용하고 있기 때문에 사전에 포트포워딩을 마친 상태였고, 문제점은 보안이 강한 개별 wifi를 사용했기 때문입니다.

Port Forwarding
wsl은 os자체를 linux로 바꾸는 것이 아니기 때문에 linux 서버에 연결을 해주어야 합니다.

4. 게시글, 댓글, 좋아요 등 다양한 서비스 적용

profile
누구나 이해할 수 있도록

0개의 댓글