- 마케터로 일할 때 플랫폼 사용에 대한 한계로 아쉬웠던 경험이 많아 쇼핑몰을 첫 프로젝트로서 만들게 되었습니다.
- 또한 회원가입, 인증, 결제 및 CRUD 등 기초적이지만 중요한 기술들을 연습하기에 좋다고 생각했습니다.
- 개발 기간 : 2023.05 ~
- 개발 인원 : 1명 (개인 프로젝트)
- 사이트 바로가기 : 👉 https://yohanyohan.com/
- Version 1 : 장고 템플릿을 이용해 fullstack 개발
- Version 2 :
-
- 기존 사이트는 유지하면서 DRF를 이용해 백엔드와 프론트 엔드 분리 (개발중)
-
- 유닛/통합 테스트 진행
-
- [Version 1 실행 방법]
- [Version 1 소개] -> 장고 만으로 쇼핑몰 구축
- [Version 2 소개] -> REST-API 도 함께 동작 가능하도록 구축 (유닛테스트 & 통합테스트) (개발중)
개발환경 | - |
---|---|
언어 | Python - 3.11 |
프레임워크 | Django - 4.2.2 |
DB | PostgreSQL - 15.3 |
API | 카카오페이, PayPal, Daum 주소 |
Devops | AWS - Elastic Beanstalk, S3, RDS, Route53, VPC, IAM |
├─ accounts
│ ├─ admin.py
│ ├─ apps.py
│ ├─ forms.py
│ ├─ helpers.py
│ ├─ models.py
│ ├─ serializers.py
│ ├─ tests.py
│ ├─ urls.py
│ ├─ views.py
│ └─ views_api.py
├─ carts
│ ├─ admin.py
│ ├─ apps.py
│ ├─ context_processors.py
│ ├─ models.py
│ ├─ tests.py
│ ├─ urls.py
│ └─ views.py
├─ category
│ ├─ admin.py
│ ├─ apps.py
│ ├─ context_processors.py
│ ├─ models.py
│ ├─ tests.py
│ ├─ views.py
│ └─ __init__.py
├─ greatkart
│ ├─ asgi.py
│ ├─ settings.py
│ ├─ static
│ ├─ tests
│ │ ├─ conftest.py
│ │ ├─ factories.py
│ │ ├─ test_accounts
│ │ │ ├─ test_endpoints.py
│ │ │ ├─ test_forms.py
│ │ │ ├─ test_models.py
│ │ │ ├─ test_views.py
│ │ │ └─ __init__.py
│ │ └─ __init__.py
│ ├─ urls.py
│ ├─ views.py
│ ├─ wsgi.py
│ └─ __init__.py
├─ manage.py
├─ orders
│ ├─ admin.py
│ ├─ apps.py
│ ├─ forms.py
│ ├─ models.py
│ ├─ tests.py
│ ├─ urls.py
│ ├─ views.py
│ └─ __init__.py
├─ pytest.ini
├─ README.md
├─ requirements.txt
├─ static
├─ store
│ ├─ admin.py
│ ├─ apps.py
│ ├─ forms.py
│ ├─ models.py
│ ├─ tests.py
│ ├─ urls.py
│ ├─ views.py
│ └─ __init__.py
└─ templates
- Version 1 처음 목차 돌아가기
- User
- 로그인 / 로그아웃
- 회원가입
- 이메일 토큰 링크를 통한 본인인증
- 대시보드
- 프로필, 마이페이지, 주문조회
- Review
- 각 리뷰 평균 및 카운팅
- 회원 및 구매한 이력이 있을 경우만 댓글 달기 가능
- Search
- 쿼리에 걸리는 아이템 갯수 카운팅
- 검색 기능
- Payment
- SDK 와 REST API 두 가지 방법으로 개발
- SDK 는 PayPal
- REST API 는 카카오 페이
- SDK 와 REST API 두 가지 방법으로 개발
- Paginator
- Paginator 내장 함수로 구현
- Cart
- 장바구니에서 아이템 추가 및 삭제
- 세션을 통해 로근인 했을 때 중복된 상품이 있을 경우 상품 증가
- 그렇지 않은 경우 장바구니에 새로 추가
- 주소 찾기는 Daum API로 구현
- Sort by
- 카테고리 및 필터기능 적용
- 폼 형식으로 랜더링
- 카테고리 및 필터기능 적용
- 장고의 기본 BaseUserManager, AbstractBaseUser 를 이용해서 회원가입 모델을 구현했습니다.
- 핸드폰 번호의 유효성 검사의 경우
PhoneNumberField
라이브러리를 사용해 구현했습니다.- 회원가입을 할 때 가입한 이메일로 토큰을 보내고 해당 링크로 접속했을 때의 pk와 토큰이 일치할 경우에만 본인인증이 확인되어 계정이 활성화 되도록 구현했습니다.
- 비밀번호 일치 및 핸드폰, 이메일 유효성 검사를 구현했습니다.
- 회원가입을 했을 경우 본인인증된 이메일을 통해서만 계정이 활성화 됩니다.
- 회원 가입시 기재한 이메일 주소로 토큰과 uid와 대조하여 본인 인증을 진행합니다.
- 가입한 이메일 주소가 존재할 경우 해당 이메일이 전송됩니다.
- 회원가입과 같은 방식으로 본인인증이 진행되며 본인인증이 완료되면 새로운 비밀번호를 설정 할합니다.
- 새로운 비밀번호로 로그인에 성공하면 계정이 다시 활성화됩니다.
- 회원가입 때 기본으로 생성된 프로필이 변경 가능하며 비밀번호도 변경이 가능합니다.
Review 기능은 크게 두 가지로 나눠서 살펴볼 수 있습니다.
- 회원과 비회원 그리고 구매자와 비 구매자를 각각 나눠서 유저의 경로가 달라집니다.
- 아이템마다 각각 달리는 리뷰 개수 와 총 별점의 평균을 나타냅니다.
- 로그인이 되어있지 않은 경우 로그인 페이지가 나옵니다.
- 로그인이 되어있는 상태이지만 물건을 구매한 적이 없다면 리뷰를 달 수 없습니다.
- 회원일 경우 리뷰를 남길 수 있으며 리뷰를 남김과 동시에 제품에 총 리뷰 개수가 카운팅 되며 별점은 전체 별점 총 평균에 반영됩니다.
검색 기능은 판매자가 상품을 등록할 때 설명이나 제품명이 키워드에 걸리면 반영해 주는 쿼리를 반영합니다. 해당 쿼리에 걸리는 상품 개수를 카운팅 합니다.
결제 방식은 SDK 와 REST API 두 가지 방법을 사용했고 SDK 방식은 페이팔, REST API 방식은 카카오 페이를 선택했습니다.
장고에서 제공하는 Paginator를 사용하여 페이지 단위를 구현했습니다.
- 장바구니에서 아이템 추가 및 삭제를 구현했습니다.
- 세션을 활용하여 비로그인 상태에서 장바구니에 물건을 담았다가 로그인을 했을 때 중복된 상품이 있을 경우는 해당 상품의 개수가 늘어나고 그렇지 않은 경우에는 새로 장바구니에 추가되도록 구현했습니다.
- 주소 찾기는 DAUM API를 이용했습니다.
상품을 필터링할 때 다음과 같은 알고리즘으로 만들었습니다.
( 개발할 당시에는 자료구조에 대해 잘 몰라서 if문으로만 구현했지만 이번에 자료구조를 공부하면서 더 좋은 방법으로 만들 수 있을 것 같아 DRF 버전을 개발할 때 적용하려고 합니다!)
- 깃 레파지토리 선택 ->
Version_1_only_Django_Local
- 파일 다운로드
- 로컬 환경에 파이썬이 설치되어 있다면 (3.11) 터미널에서 requirements.txt 가 있는 경로로 이동해 줍니다. 해당 경로에서 가상환경을 만들어줍니다.
python -m venv venv
- 가상환경 활성화
- Windows
source venv/Scripts/activate
- Mac
source venv/bin/activate
- 올바르게 실행 되었다면 터미널에 (venv)라고 터미널창에서 확인 가능합니다.
- 가상환경이 활성화 되어있다면 현재 라이브러리 목록을 확인해줍니다.
pip list
- 새로운 환경이므로 Package 리스트에 pip, setuptools 만 보이면 정상입니다.
- 필요한 라이브러리를 설치
pip install -r requirements.txt
- 설치시 몇 분 걸릴 수 있습니다.
- 다시 pip list를 통해 requirements.txt에 있는 목록과 일치하는지 확인해줍니다.
- superuser
ID : test@naver.com
PASS : anwkrdnlqlalfqjsgh
- Run server
- 마이그레이션이 끝났다면 아래의 명령어를 실행해서 서버를 실행!
python manage.py runserver
- Admin page
- http://127.0.0.1:8000/admin/ 은 fake 어드민 페이지입니다.
- 위 주소로 로그인 시도를 하면 IP가 남도록 되어있고 해당 IP를 차단시켜 접속을 제한할 수 있습니다.
- admin 페이지를 확인하시려면 밑의 주소로 접속하시면 됩니다.
- http://127.0.0.1:8000/securelogin/ <- 진짜 어드민 페이지
-
API_KEY 설정
- 로컬테스트에서 이메일 본인인증, 카카오페이, 페이팔 기능은 settings.py에서 따로 설정하셔야 합니다.
- 로컬 환경에서 API_KEY를 설정 했을 경우 시범 영상 :
- 동영상 👉👉👉 https://drive.google.com/file/d/16uyTOVPtCR6d_NeIkZWtBG7iAFALtgHX/view?usp=drive_link
- 관리자 페이지 아이디와 비밀번호
id : test@naver.com
pass : anwkrdnlqlalfqjsgh
├─ accounts
│ ├─ admin.py
│ ├─ apps.py
│ ├─ forms.py
│ ├─ helpers.py
│ ├─ models.py
│ ├─ serializers.py
│ ├─ tests.py
│ ├─ urls.py
│ ├─ views.py
│ └─ views_api.py
- accounts 앱에서는 회원가입,로그인,로그아웃,대시보드,비밀번호 변경,재설정의 기능을 다루고 있습니다.
-
admin.py
- AccountAdmin
- list_display : 관리자 페이지에서 사용자 프로필 목록에 표시할 정보 설정
- list_display_links : 사용자 목록에서 특정 열을 클릭해서 사용자 상세 페이지로 이동할 수 있는 링크 설정
- readonly_fields : 읽기 전용 필드로 사용자 계정에 대한 마지막 로그인 시간과 가입 날짜를 변경하지 못하도록 설정
- ordering : 사용자 목록을 가입 날짜를 기준으로 내림차순으로 정렬
- UserProfileAdmin
- thumnail : 사용자 프로필 사진을 원형으로 표시하는 썸네일 이미지를 생성
- list_display : 관리자 페이지에서 사용자 프로필 목록에 표시할 정보 설정
- AccountAdmin
-
apps.py
- AccountsConfig
- default_auto_field : 모델 클래스에 대한 기본 자동 필드 타입을 지정
- name : 앱의 이름을 지정, 해당 앱의 모든 기능과 리소스에 접근할 때 사용
- AccountsConfig
-
forms.py
- RegistrationForm
- 사용자의 회원가입을 위한 폼 정의
- 비밀번호와 확인 비밀번호 필드를 비교해 일치하는지 검사
- 사용자의 이름, 성, 전화번호, 이메일, 비밀번호를 입력받음
- 각 필드에 대한 플레이스 홀더와 클래스를 추가하고 필드들을 form-control 클래스로 스타일링
- UserForm
- 사용자 프로필 수정을 위한 폼 정의
- 사용자의 이름과 성, 젆화번호를 수정할 수 있는 필드를 제공
- 필드들을 form-control 클래스로 스타일링
- RegistrationForm
-
models.py
- MyAccountManager
- create_user() : 사용자 생성을 담당하는 메서드
- create_superuser() : 관리자 권한과 함께 슈퍼유저를 생성하고 저장
- Account (AbstractBaseUser 상속)
- 사용자 계정에 대한 정보 및 권한을 관리, AbstractBaseUser 를 상속받아 원하는 필드와 메서드를 추가로 정의 가능
- 사용자 계정과 관련된 필드들과 권한 설정, 그리고 사용자 프로필과의 일대일 관계를 설정
- UserProfile
- 사용자의 프로필 정보, 프로필 사진, 주소등의 사용자 정보를 관리
- MyAccountManager
-
urls.py
- 클라이언트 요청을 처리하는 뷰에 있는 함수들과 연결
-
views.py
register
: 사용자 회원가입을 처리하는 뷰, POST 요청을 통해 회원가입 정보를 받아 사용자 계정을 생성하고 이메일 인증 메일을 보내는 기능을 구현login
: 사용자 로그인을 처리하는 뷰. POST 요청을 통해 사용자의 이메일과 비밀번호를 받아 인증하고, 장바구니 정보를 사용자에게 연결하는 등의 기능도 포함logout
: 사용자 로그아웃을 처리하는 뷰. 인증된 사용자를 로그아웃시키고, 메시지를 표시activate
: 사용자 계정 활성화를 처리하는 뷰. 이메일 인증 링크를 통해 사용자 계정을 활성화하는 기능을 구현dashboard
: 사용자 대시보드 페이지를 보여주는 뷰. 사용자의 주문 목록과 프로필 정보를 표시forgotPassword
: 비밀번호 재설정을 위한 이메일 전송을 처리하는 뷰. 입력된 이메일로 비밀번호 재설정 링크를 보내는 기능을 구현resetpassword_validate
: 비밀번호 재설정을 위한 유효성 검사를 처리하는 뷰. 재설정을 위한 링크의 유효성을 검증하고 세션에 사용자 ID를 저장resetPassword
: 비밀번호를 재설정하는 뷰. 새로운 비밀번호를 입력받아 사용자의 비밀번호를 변경my_orders
: 사용자의 주문 목록을 보여주는 뷰edit_profile
: 사용자 프로필 정보를 수정하는 뷰. 사용자 정보와 프로필 정보를 입력받아 업데이트change_password
: 사용자의 비밀번호를 변경하는 뷰.order_detail
: 주문 상세 정보를 보여주는 뷰. 주문에 대한 상세 정보와 총 가격을 표시.
-
├─ carts
│ ├─ admin.py
│ ├─ apps.py
│ ├─ context_processors.py
│ ├─ models.py
│ ├─ tests.py
│ ├─ urls.py
│ └─ views.py
-
carts 앱에서는 장바구니 기능을 다루고 있습니다.
- context_processors.py
- 장바구니 아이템 수를 계산해서 웹 페이지에 표시하는 기능을 구현
cart_count
변수를 초기화- 현재 요청의 경로(
request.path
)에 "admin"이 포함되어 있으면 빈 딕셔너리를 반환하여 관리자 페이지에서는 아무 작업도 수행 하지 않음. - 그렇지 않은 경우, 사용자의 장바구니를 확인하고 장바구니에 담긴 아이템 수를 계산
- 사용자가 인증되어 있다면 해당 사용자와 연관된 장바구니 아이템을 검색하고, 그렇지 않으면 세션에 연결된 장바구니에서 아이템을 가져옴
- 검색된 장바구니 아이템의 수량(
quantity
)을 더하여cart_count
를 계산 - 장바구니가 없는 경우(
Cart.DoesNotExist
예외)cart_count
를 0으로 설정 - 마지막으로,
cart_count
를 딕셔너리 형태로 반환하여 템플릿에서 장바구니 아이템 수를 표시
- 장바구니 아이템 수를 계산해서 웹 페이지에 표시하는 기능을 구현
- models.py
- Cart
cart_id
: 최대 250자까지의 문자열을 저장하는 필드로, 장바구니를 식별하는 고유한 식별자를 저장.date_added
: 아이템을 장바구니에 추가한 날짜를 자동으로 기록하는 필드로,auto_now_add=True
속성이 있다. 즉, 아이템을 추가할 때마다 현재 날짜 저장__str__(self)
: 객체를 문자열로 표현할 때 사용되며, 이 메서드를 통해 장바구니 객체를 식별하는 데 사용되는cart_id
를 반환
- CartItem
user
:Account
모델과의 외래 키 관계로, 사용자와 장바구니 아이템 간의 관계 ->> 사용자가 인증되어 있지 않은 경우null=True
로 설정product
:Product
모델과의 외래 키 관계로, 장바구니에 추가된 상품을 가져옴variations
:Variation
모델과의 Many-to-Many 관계로, 상품의 옵션 값(variation)을 나타낸다. 여러 옵션이 선택될 수 있으므로blank=True
로 설정cart
:Cart
모델과의 외래 키 관계로, 장바구니 아이템이 어떤 장바구니에 속하는지 나타냄. 사용자가 인증되어 있지 않은 경우null=True
로 설정quantity
: 장바구니에 추가된 특정 상품의 수량을 저장하는 필드로, 정수형으로 설정is_active
: 장바구니 아이템의 활성 여부를 나타내는 필드로, 기본값은True
로 설정sub_total(self)
: 장바구니 아이템의 총 가격을 계산하여 반환하는 메서드로, 상품의 가격(product.price
)과 수량(quantity
)을 곱한 값을 반환
- Cart
- views.py
_cart_id(request)
:- 현재 세션에서 장바구니 식별자(
cart_id
)를 검색하거나 생성하여 반환
- 현재 세션에서 장바구니 식별자(
add_cart(request, product_id)
:- 특정 상품을 장바구니에 추가
- 인증된 사용자와 비인증된 사용자 각각을 고려하여 상품과 상품 변형(variation)을 처리
- 이미 장바구니에 있는 상품인 경우 수량을 증가시키고, 그렇지 않은 경우 장바구니에 새로운 아이템을 추가
remove_cart(request, product_id, cart_item_id)
:- 장바구니에서 특정 상품의 수량을 1만큼 감소
- 수량이 1인 경우 해당 아이템을 장바구니에서 제거
remove_cart_item(request, product_id, cart_item_id)
:- 장바구니에서 특정 상품 아이템을 완전히 제거
cart(request, total=0, quantity=0, cart_items=None)
:- 장바구니 페이지를 렌더링하고 장바구니에 있는 상품들을 표시
- 인증된 사용자와 비인증된 사용자 각각을 고려하여 장바구니 내역을 가져옴
- 상품 가격과 수량을 계산하여 총 가격 및 부가가치세를 계산
checkout(request, total=0, quantity=0, cart_items=None)
:- 결제 페이지를 렌더링하고 장바구니에 있는 상품들을 표시
- 인증된 사용자와 비인증된 사용자 각각을 고려하여 장바구니 내역을 가져옴
- 상품 가격과 수량을 계산하여 총 가격 및 부가가치세를 계산
- 로그인된 사용자만 결제 페이지에 액세스할 수 있도록
@login_required
데코레이터를 사용
- context_processors.py
├─ category
│ ├─ admin.py
│ ├─ apps.py
│ ├─ context_processors.py
│ ├─ models.py
│ ├─ tests.py
│ ├─ views.py
│ └─ __init__.py
- category 앱에서는 상품 목록을 관리하는 기능을 다루고 있습니다.
- context_processors.py
Category
모델을 이용하여 모든 카테고리 목록을links
변수에 저장links
변수는 템플릿 컨텍스트에 추가되어, 웹 페이지에서 이 함수를 호출할 때 카테고리 목록을 사용
- models.py
category_name
:- 최대 50자까지의 문자열을 저장하는 필드로, 카테고리의 이름을 나타냄
slug
:- 최대 100자까지의 Slug 문자열을 저장하는 필드로, 카테고리의 URL을 나타내고
allow_unicode=True
설정으로 유니코드 문자도 허용
- 최대 100자까지의 Slug 문자열을 저장하는 필드로, 카테고리의 URL을 나타내고
description
:- 최대 255자까지의 텍스트를 저장하는 필드로, 카테고리에 대한 간단한 설명을 나타냄
cat_image
:- 카테고리에 대한 이미지를 업로드하는 필드로, 이미지는 "photos/categories" 디렉토리에 저장
get_url(self)
:- 카테고리의 URL을 반환하는 메서드로,
reverse
함수를 사용하여 "products_by_category" 뷰와 연결된 URL을 생성
- 카테고리의 URL을 반환하는 메서드로,
__str__(self)
:- 모델 객체를 문자열로 표현할 때 사용되며, 카테고리의 이름을 반환
save(self, force_insert=False, force_update=False, using=None, update_fields=None)
:- 모델 객체가 저장될 때 호출되는 메서드로, 카테고리 이름을 Slug로 변환하여 저장, Slugify 함수는 유니코드 문자도 허용하며, 부모 클래스의
save
메서드를 호출하여 저장 작업을 완료
- 모델 객체가 저장될 때 호출되는 메서드로, 카테고리 이름을 Slug로 변환하여 저장, Slugify 함수는 유니코드 문자도 허용하며, 부모 클래스의
- context_processors.py
├─ orders
│ ├─ admin.py
│ ├─ apps.py
│ ├─ forms.py
│ ├─ models.py
│ ├─ tests.py
│ ├─ urls.py
│ ├─ views.py
│ └─ __init__.py
- orders 앱에서는 주문,결제와 관련된 기능을 다루고 있습니다.
- models.py
- Payment
user
: 주문을 한 사용자와의 외래 키 관계, 주문을 한 사용자를 참조payment_id
: 최대 100자까지의 문자열을 저장하는 필드로, 결제 식별자 기록payment_method
: 최대 100자까지의 문자열을 저장하는 필드로, 결제 방법amount_paid
: 최대 100자까지의 문자열을 저장하는 필드로, 결제한 총 금액status
: 최대 100자까지의 문자열을 저장하는 필드로, 결제 상태를 나타냄created_at
: 결제 정보가 생성된 날짜와 시간을 자동으로 기록하는 필드__str__(self)
: 객체를 문자열로 표현할 때 사용되며, 결제 식별자인payment_id
를 반환
- OrderProduct
order
: 주문과 상품 간의 관계를 나타내는 외래 키, 해당 주문을 참조payment
: 주문과 결제 정보 간의 외래 키 관계, 해당 주문과 관련된 결제를 참조user
: 주문한 사용자와의 외래 키 관계, 주문한 사용자를 참조product
: 주문한 상품과의 외래 키 관계로, 주문에 포함된 상품을 참조variations
: 상품의 옵션을 나타내는 Many-to-Many 관계로, 여러 변형 선택 가능quantity
: 주문한 상품의 수량을 나타내는 필드product_price
: 상품의 가격을 나타내는 필드ordered
: 주문이 완료된 상태인지 여부를 나타내는 불리언 필드created_at
: 주문 상품이 생성된 날짜와 시간을 자동으로 기록하는 필드updated_at
: 주문 상품 정보가 업데이트된 날짜와 시간을 자동으로 기록하는 필드
- Payment
- views.py
kakao_pay
:- POST 요청을 처리하여 KaKao Pay 결제를 시작
- 현재 사용자의 카트 아이템을 검사하여, 카트에 상품이 있는지 확인
- 결제할 상품의 총 가격을 계산하고 KaKao Pay 결제를 위한 정보를 준비
- KaKao Pay API를 사용하여 결제 요청을 보내고, 결제 준비 URL로 리디렉션
kakao_pay_approval
:- KaKao Pay 결제가 완료된 후, KaKao Pay에서 리디렉션되어 이 뷰를 호출
- KaKao Pay API를 통해 결제를 승인하고, 결제 정보를 저장
- 주문을 완료 상태로 변경하고, 카트 아이템을 주문 상품으로 이동하고, 상품 재고를 감소
- 결제가 완료된 주문 정보를 템플릿으로 렌더링하여 사용자에게 표시
kakao_pay_cancel
- KaKao Pay 결제가 취소된 경우를 처리하는 뷰
- 결제 실패 페이지를 렌더링하여 사용자에게 표시
payments
:- 주문이 완료된 후, PayPal 및 KaKao Pay 결제 정보를 처리하는 뷰
- 주문 정보와 결제 정보를 저장하고, 카트 아이템을 주문 상품으로 이동하고, 상품 재고를 감소
- 주문 완료 이메일을 사용자에게 보내고, 주문 번호 및 트랜잭션 ID를 JSON 응답으로 반환.
place_order
:- 주문을 생성하는 뷰
- 주문 폼을 통해 제출된 데이터를 처리하고, 주문 정보를 저장
- 주문 번호를 생성하고, 주문 정보를 렌더링하여 사용자에게 표시
order_complete
:- 주문이 완료된 후, 주문 완료 페이지를 렌더링하는 뷰
- 주문 및 결제 정보를 템플릿으로 렌더링하여 사용자에게 표시
- models.py
├─ store
│ ├─ admin.py
│ ├─ apps.py
│ ├─ forms.py
│ ├─ models.py
│ ├─ tests.py
│ ├─ urls.py
│ ├─ views.py
│ └─ __init__.py
└─ templates
- store 앱에서는 상품 페이지에서 수행하는 것과 관련된 기능을 다루고 있습니다 (검색, 상품 상세페이지, 리뷰)
- models.py
-
Product
Product
모델은 스토어에서 판매되는 제품 정보를 나타냄 , 제품 이름, 설명, 가격, 이미지, 재고량, 사용 가능 여부 등의 필드를 갖고 있음category
필드는 외래 키(ForeignKey)로 카테고리 모델과 연결되어 해당 제품이 어떤 카테고리에 속하는지를 나타냄get_url
메서드는 제품의 상세 페이지 URL을 생성averageReview
메서드는 해당 제품의 리뷰 평균 평점을 계산countReview
메서드는 해당 제품에 대한 리뷰 수를 계산
-
Variation
Variation
모델은 제품의 다양한 변형(색상, 크기 등)을 나타냄product
필드는 외래 키로 해당 변형이 어떤 제품에 속하는지를 나타냄variation_category
필드는 변형의 카테고리(색상 또는 크기)를 나타냄variation_value
필드는 변형의 값(예: "Red" 또는 "Large")을 저장
-
ReviewRating
ReviewRating
모델은 제품에 대한 리뷰 및 평점 정보를 저장product
필드는 외래 키로 어떤 제품에 대한 리뷰인지를 연결user
필드는 외래 키로 리뷰를 작성한 사용자를 연결rating
필드는 리뷰의 평점을 저장status
필드는 리뷰의 상태를 나타냄(기본값은 True로 설정되어 있음)
-
ProductGallery
ProductGallery
모델은 제품의 갤러리 이미지를 관리product
필드는 외래 키로 어떤 제품의 갤러리 이미지인지를 연결image
필드는 이미지 파일을 저장
-
- views.py
store
:- 온라인 스토어의 제품 목록을 표시, 카테고리별로 필터링되며, 카테고리 슬러그와 함께 URL을 통해 전달 및 상품 목록을 페이지별로 표시하고, 옵션(색상 및 크기)을 수집하여 필터링 옵션을 제공
update_results
:- 제품 목록을 필터링하고 정렬하는 기능을 제공, 이 함수는 AJAX를 통해 호출되며, 선택한 옵션과 정렬 기준에 따라 결과를 업데이트
product_detail
:- 특정 제품의 상세 정보 페이지를 표시. 제품 카테고리 슬러그와 제품 슬러그를 사용하여 제품을 식별. 장바구니에 제품을 추가할 수 있는 옵션과 사용자 리뷰 및 제품 갤러리를 표시
search
:- 제품 검색을 처리하고 키워드에 따라 제품을 검색하여 결과를 표시
submit_review
:- 사용자 리뷰를 제출하거나 업데이트하는 기능을 제공 제품 페이지에서 리뷰를 작성하고 제출할 때 호출
- models.py
-
프로젝트를 완료하면서 가장 크게 배운 점은 어떤 문제라도 해결할 수 있다는 것입니다.
-
처음에는 어디서 시작해야 할지, 필요한 것이 무엇인지조차 몰랐습니다. 하지만 이런 상황에서 문제를 잘게 나누고, 가능한 한 작은 부분부터 해결하려고 노력했습니다. 그래도 해결이 어려운 경우에는 문제를 더 작게 쪼개어 해결책을 찾아 나갔습니다.
-
이러한 접근 방식을 통해 혼자서도 문제 해결 능력을 크게 향상시킬 수 있었습니다.
-
모든 부분이 다 어려웠지만 AWS에 서버를 올리는 게 가장 어려웠습니다.
-
환경 설정, 클라우드 플랫폼인 AWS를 다루는 것, 심지어 에러 메시지와 로그 확인조차 오래 걸렸습니다.
-
이해가 갈 때까지 구글링을 하고 가설을 세우고 실험하는 과정에서 즐거움과 성취감을 느낄 수 있었습니다.
예를 들어서 AWS에서 PostgresSQL로 db를 변경했을 당시 이런 식으로 가설을 세웠습니다.
- 장고에서는 개발 초기에 간편하고 가벼운 SQLite3를 사용하고, PostgreSQL과 같은 강력한 데이터베이스로 전환하는 경우가 많다.
- 장고는 SQLite3 와 PostgresSQL 가 여러모로 호환성이 좋다.
- 보통 백엔드와 프론트엔드는 API를 통해 json으로 데이터를 주고받고 json으로 받은 내용들은 db에 저장된다.
이렇게 두 가지 데이터베이스 시스템이 장고와 호환성이 좋다면, 초기에 개발한 SQLite3 데이터를 PostgreSQL로 안전하게 옮길 수 있는 방법이 있을 것이고 그 방법은 JSON으로 데이터를 일괄로 추출하고 JSON 데이터를 일괄로 이식하는 방법일 것이다.
해결 과정:
- SQLite3에서 데이터를 PostgreSQL로 이동시키기 위해 데이터베이스의 내용을 JSON 형식으로 추출한다.
- 추출한 JSON 데이터를 로컬 환경에서 AWS 데이터베이스의 엔드포인트를 연결해서 데이터를 적용한다.
- 이러한 작업을 통해 AWS 클라우드에 배포된 서버에서도 적용된 데이터를 사용할 수 있을 것이다.
이러한 방식을 통해 SQLite3에서 PostgreSQL로의 데이터 마이그레이션을 해결했습니다.
그로스 마케터로 일 할 당시 자사몰이 없어서 불편했었던 점이 많았고 그래서 자사몰을 직접 만들어보자는 생각으로 시작했습니다.
처음에는 템플릿 부분을 프론트 쪽에서 만들고 나머지를 백엔드 쪽에서 작업하는 줄 알았을 정도로 아는 게 없었습니다.
-
현재는 DRF(Django Rest Framework)를 알게 되어 백엔드에서 API 개발에 초점을 맞추려고 합니다.
-
쇼핑몰의 두 번째 버전은 기존 템플릿을 유지하면서 API로 기능을 구현하고, 효율적인 자료구조와 필터링을 개선하고자 노력하고 있습니다.
- 예를 들어서 필터링의 경우 당시에는 자료구조의 개념을 아는 게 없어서 if 문으로만 해결을 했는데 두 번째 버전에서는 해시맵 혹은 더 효율적인 자료구조를 적용해가면서 만들려고 합니다.
-
TDD(Test-Driven Development) 개념을 도입하여 pytest와 factory-boy 등을 사용하여 대규모 트래픽을 고려한 테스트 코드를 작성하고 있습니다.
-
DRF를 적용하면서 현재 쇼핑몰을 유지려면 URL path가 복잡해지는데 ViewSet을 이용하면 좀 더 간편하게 관리가 가능하다고 생각해서 ViewSet으로 만들고 있습니다.
-
drf-spectacular를 사용해서 API 문서 자동화 및 스웨거를 적용하고 있습니다.
-
또한 현재 진행하고 있는 팀 프로젝트에서 비동기로 채팅 기능을 구현하고 있는데 상담원 연결 등 배운 내용을 접목하려고 계획 중입니다.