오늘 한 것
팀 과제 기능 구현
팀 과제 기능 테스팅
코딩테스트 풀이
팀 과제 기능 구현: 프로필 조회 및 수정
0. 협업 환경 조성
본격적으로 시작하기 전에 협업을 위한 환경을 조성하기로 하였다.
가상 환경 만들기 및 requirements.txt로 패키지관리
먼저 가상환경(venv)를 생성한 뒤, 가상환경에서 Django를 비롯한 필요한 패키지 들을 설치하였다.
이후 requirements.txt 파일을 이용하여 venv를 깃헙에 올리지 않고도 손쉽게 가상환경 셋팅을 공유할 수 있게 하였다.
pip freeze >requirements.txt # 작성
pip install -r requirements.txt #사용
.gitignore 미리 작성
처음부터 우리 팀원들이 사용중인 개발환경을 고려하여 gitignore.io에서 작성하고 시작하였다.
프로젝트를 생성한 뒤 필요한 app (User_app, Posting_app)들을 만들고 settings.py에 등록.
현재로는 이렇게 두개만 있으면 될 것이다.
DB에 접속하기 위한 시크릿 키를 환경변수로 분리하여 관리
django-dotenv를 설치하고 .env 파일을 생성해 시크릿 키를 잘라 붙여 넣은 뒤, settings.py에 다음과 같이 작성하였다.
import os
SECRET_KEY=os.environ.get("SECRET_KEY")
그 다음 manage.py에 다음과 같이 작성한다.
if __name__ =='__main__':
dotenv.read_dotenv() #추가되는 부분
main
.env 파일은 gitignore에 들어가야하므로, 깃헙의 원격 repo를 클론해 진행할 팀원들은 다음과 같은 명령어를 통해 .env에 새로 저장할 시크릿 키를 생성한다.
python3 -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'
깃헙에 publish 한 후 각 팀원이 fork
이제 팀원들이 각자의 브랜치에서 작업한뒤 PR을 통해 협업한다.
1. Profile 모델 구성(Model)
class Profile(models.Model):
'''
프로필. 거주지역과 생성일, 수정일,
'''
class Meta:
db_table = 'profile'
username = models.OneToOneField(get_user_model(), on_delete=models.CASCADE, to_field="username")
locate = models.CharField(verbose_name='region',max_length=16, default='Not specified')
description = models.TextField(max_length=300,)
created_at = models.DateField(auto_now_add=True)
updated_at = models.DateField(auto_now=True)
# profile_image=models.ImageField(,)
모든 회원가입된 사용자는 자신의 프로필(profile) 데이터를 단 하나 가진다. profile은 유저 모델을 OneToOneField로 참조한다. 이때 유저모델의 자동 생성된 ID가 아닌 고유한 username을 참조한다. 유저가 삭제되면 당연히 프로필도 삭제되어야 하므로 on_delete는 CASCADE로 하였다.
사는 지역(locate)필드는 CharField로, 자기소개(description)필드는 TextField로 정했는데, 전자는 짧고 간결한 값을 입력하길 원하고 후자는 여러줄에 걸친 긴 내용도 처리할 수 있게 하고 싶었다.
2. 백엔드 기능 구현(View)
1. Profile 페이지 조회
모든 사용자는 특정 사용자의 프로필 페이지를 조회할 수 있다. 프로필페이지에는 거주지역과 자기소개, 해당 사용자가 작성한 글들의 목록을 조회할 수 있어야 한다.
def profile_view(request, id: int) -> HttpResponse | HttpResponseRedirect:
'''
모든 사용자가 프로필 페이지를 조회할 수 있습니다. 프로필과 프로필 소유자의 글 목록이 주어집니다.
오직 프로필 소유자 일때만 프로필의 수정을 할 수 있습니다.
'''
# 프로필 가져오기
opened_profile = Profile.objects.get(id=id)
if request.method == 'GET':
# 역참조로 지정된 사용자의 글만 가져오기
posts = opened_profile.username.posting_set.order_by('-created_at')
return render('profile.html', {'profile': opened_profile, 'posts': posts})
...# 이하 프로필 수정 부분
url에 파라미터로 주어진 prifile id를 이용해 프로필 정보를 가져오고, 해당 프로필이 참조하는 유저의 posting들을 역참조하여 가져와 프론트 엔드에 함께 response로 보낸다.
2. Profile 수정
가입된 사용자는 자신의 프로필 정보(거주지역, 자기소개)를 수정할 수 있다. 이외의 사용자나 익명의 사용자는 수정할 수 없다.
...# 이상 프로필 조회 부분
if request.method == 'POST':
if request.user != opened_profile.username:
return redirect('/')
locate = request.POST.get('locate', '')
description = request.POST.get('description', '')
opened_profile.locate = locate
opened_profile.description = description
opened_profile.save()
return redirect('/api/proflie/'+id)
만약 POST요청이 들어오면 현재 사용자가 익명이거나 프로필이 참조하는 사용자가 아니라면 메인페이지로 바로 redirect한다. 만약 사용자가 일치할 경우, opened_profile의 필드들을 수정하여 업데이트한다.
회고: 마주쳤던 오류 및 문제들
1. requirements.txt로 가상환경 설정 중 오류
팀장분이 생성한 requirements.txt로 가상환경에 패키지를 설치하려고 하자 다음과 같은 오류가 발생하였다.
Could not build wheels for backports.zoneinfo, which is required
시도 1. requirements.txt 파일을 이용해 터미널 없이 가상환경설정
시도는 해 보았으나, 해결되지 않았다. pip 자체의 문제이거나, 혹은 패키지 자체의 문제인 것 같았다.
해결: 파이썬 버전 호환
asgiref==3.6.0
backports.zoneinfo==0.2.1
Django==4.2
django-dotenv==1.4.2
sqlparse==0.4.3
tzdata==2023.3
파이썬 버전이 달라 생긴 문제였다. 위의 내용이 3.8.x에서 django, django-dotenv를 사용하기 위한 requirements.txt파일이었는데, backports.zoneinfo=, tzdata는 3.9 이상에서는 지원되지 않는 패키지 였다.
pip는 어떤 패키지를 설치할 때 알아서 해당 패키지가 의존하는 패키지를 찾아 설치해주므로, 버전에 따라 유연하게 설치할 수 있도록 해당 패키지들을 requirements.txt에서 지워주었다. 그랬더니 문제가 해결 되었다.
2. Put 못 쓰는 장고와 Post 못하는(?) Post맨
위의 profile_view함수의 뒷부분을 테스팅하기 위해 처음에는 Postman으로 put 요청을 보냈다.
그러나 아래와 같은 오류를 발생시켰다.
WSGIRequest object has no attribute PUT
시도 1: POST로 대신하기
해결하기 위해 찾아보니, 애초에 장고는 PUT method를 지원하지 않는다고 한다. 그래서 그냥 그래서 위와 같이 POST를 쓰는 코드로 작성하고, 다시 포스트맨으로 데이터를 보내보았더니, 오류가 발생했다. db에 자꾸 제대로 된 입력값이 아닌 빈 문자열이 저장되는 것이다.
포스트맨이 포스트를 못하는 아이러니...
시도 2: request.POST, request.__dict__ 출력해보기
이쯤 되자 이 둘을 출력해서 post가 제대로 입력된 데이터를 백엔드로 전달하고 있는지 부터 확인해 보기로 했다.
print(request.POST) # <QueryDict: {}>
print(request.__dict__) # {..., '_messages': <FallbackStorage: request=<WSGIRequest: POST '/api/profile/2?locate=aassddff&description=aassddffssddffsaa'>>}
전자는 빈 QueryDict를 출력했고, 후자는 key에 POST가 존재하지 않았다. 대체 어떻게 된 걸까?
해결: 택배는 제대로 택배상자에: body에 실어보내기
같이 내배캠에 참여하고 있는 동기들과 상의한 결과, 문제는 Postman에 있는 것이 확실했다. 그리고 동기분중 한분이 문제를 지적해 주셨는데, 지금 내가 url에 파라미터로 데이터를 전달하고 있다는 것이었다.
위와 같이 url에 파라미터로 데이터를 실어보내는건 사실 get이 하는거다.
post는 url의 parameter로 전달하지 않고, body의 form data 등으로 데이터를 전달한다. 아는건데도 급한마음에 튜터님이 하실 때 표 모양만 보고 무지성으로 따라하다가 한참 헤맸다.
오늘 배운 것
1. POST는 requeset body에 데이터를 실어보낸다.
2. 파이썬 버전에 따른 패키지 호환성을 고려하여 requirements.txt를 작성하는 것이 좋고, 가장 좋은 것은 파이썬 인터프리터 버전을 사전에 맞추는 것이다.
3. SECRET_KEY등 민감한정보를 환경변수로 저장해 노출을 피할 수 있다.
4. django는 기본적으로 PUT 요청을 처리하지 않는다. 다른 처리할 수 있는 방법을 알아보자.
레퍼런스
김묭의 자기개발
kmy9810.tistory.com
ERROR: Could not build wheels for backports.zoneinfo, which is required to install pyproject.toml-based projects
The Heroku Build is returning this error when I'm trying to deploy a Django application for the past few days. The Django Code and File Structure are the same as Django's Official Documentation and
stackoverflow.com
'TIL' 카테고리의 다른 글
23.04.13 TIL (0) | 2023.04.13 |
---|---|
23.04.12 TIL (0) | 2023.04.12 |
23.04.10 TIL(팀플 개발일지) (2) | 2023.04.11 |
23.04.07 TIL (0) | 2023.04.08 |
23.04.06 TIL (0) | 2023.04.07 |