멋사 열한 번째 과제

olhsg·2023년 7월 17일
0

멋쟁이 사자처럼

목록 보기
11/15
post-thumbnail

이번 시간에는 온라인 쇼핑몰 웹 프로젝트를 구현하는 것에 대해 작성할 것이다!
(지독한 오류를 견뎌내고! 드디어 시작)


1 shop

우선 기본 세팅이 완료된 프로젝트 파일에

python manage.py startapp shop

shop 앱을 생성하고, settings.py 파일에서 INSTALLED_APPS 부분에 추가한다!

2. shop/models.py

shop 디렉토리 폴더에서 models.py 파일로 이동하여 해당 코드를 작성한다
우선 카테고리에 대한 모델을 정의한다.

카테고리 이름인 name은 문자열 최대 200길이와, db_index=True 값을 주어 카테고리가 저장되는 테이블에 name으로 인덱스 열을 설정한다

meta_description은 검색엔진에서 찾기 수월하도록 사이트를 개선하는 프로세스이며, slug는 상품명 등을 이용해 URL을 만드는 방식이다. 이때 다른 언어 값도 사용할 수 있도록 allow_unicode=True 값을 준다.

class Meta: 부분은 관리자 페이지에서 보여질 객체가 단수일 경우 'category'로 보여지게 하고, 복수일 경우 'categories'로 보여지게 하는 것을 설정한다.

str(self): return self.name 으로 카테고리 이름을 스스로 리턴하게 만들고, get_absolute_url(self): 로 원하는 모델을 찾을 수 있는 url 함수를 정의한다. 해당 함수의 return 값을 보면 slug를 이용해 해당하는 상품의 카테고리로 이동하도록 한다.

그리고 이제 상품에 대한 모델을 정의한다.
카테고리와 상품의 모델의 관계가 이뤄지도록 외래키 관계값을 주고, 카테고리가 삭제되어도 상품은 삭제되지 않도록 on_delete=models.SET_NULL 값을 준다.
카테고리와 마찬가지로 name, slug에 대한 값을 지정하고,

추가적으로 image, 배민 큐알 이미지인 beaminImage, 상품 설명 description, SEO를 위한 필드인 meta_description, 상품 홈페이지인 url, 상품 가격을 나타내는 price, 상품의 재고를 나타내는 stock, 상품 노출 여부를 체크하는 available_display, 상품 판매 여부를 나타내는 available_order, 상품 등록 날짜를 나타내는 created, 상품 수정 날짜를 나타내는 updated들을 설정한다.

class Meta: 함수로 id, slug 필드를 묶어 index_together 변수에 저장하여 탐색이 가능하도록 합니다. str(self): return self.name으로 스스로 상품 이름을 리턴하도록 만들고, 카테고리와 비슷하게 get_absolute_url 함수를 지정하여 이번엔 해당 상품의 상세 페이지로 이동하도록 합니다.


모델을 다 작성한 뒤엔
python manage.py makemigrations shop
python manage.py migrate shop

코드를 터미널에 작성하여 데이터베이스에 반영한다!

3. shop/views.py

다음은 뷰 작성이다!
product_in_category 함수를 지정하여 카테고리와 상품이 함께 보여지는 메인 화면을 설정한다.

Category.objects.all()을 사용해 관리자 페이지에서 작성한 카테고리 객체들을 모두 불러와 categories 변수에 저장한다.

Product.objects.filter()는 상품의 모든 객체를 불러오는 것이 아닌, 필터가 적용된 즉, 원하는 값만 가져오는 것인데, 이때 괄호 안에 available_display=True 값을 줬으므로 노출 여부가 가능한 객체들만 불러와 products 변수에 저장한다는 의미이다.

조건문 if를 사용하여 카테고리 슬러그가 현재 어느 카테고리를 보여주는지 판단하는데 이때 카테고리가 선택됐다면 current_category 변수에 저장하고, 노출 여부가 가능한 상품들에 필터를 적용해 current_category에 속한 상품들만 products에 저장한다.

다음은 product_detail 함수를 지정해 상품의 상세 페이지가 보여지는 화면을 설정한다. 상품 객체에서 id와 slug 값을 읽어와 product에 저장한다. 앞서 카테고리에서 설정한 것과 비슷하게 URL로부터 슬러그 값을 받아오고, 해당 슬러그 값을 이용해 상품을 노출한다고 생각하면 된다.

4. shop/urls.py

이제 url을 설정할 차례이다!

제일 첫 번째는 메인 화면이므로 ''값을 주고, product_in_category 뷰 함수를 지정한다. 이때 name은 product_all로 하는데 이러한 이유는 메인 화면에서 카테고리 선택 없이 전체 상품을 보여주기 위함이다.

다음 두 번째는 카테고리가 선택된 화면으로 url에는 categroy_slug 값을 주고, 똑같이 product_in_category 뷰 함수를 지정한다. name으로는 카테고리가 선택된 것이므로 product_in_category로 설정한다.

세 번째는 상품의 상세 화면으로 url에는 int:id로 해당 상품의 id를 숫자로 나타내고, product_slug로 상품의 슬러그를 나타낸다. product_detail 뷰 함수를 지정한 뒤 name으로는 product_detail을 설정한다.

shop의 url을 다 작성했다면 해당 url을 연결해주기 위해 config/urls.py 파일로 이동한 뒤

path('', include('shop.urls')),

코드를 추가해 준다!

5. 템플릿 확장

온라인 쇼핑몰에서의 각각의 다른 카테고리를 선택하면 각각의 카테고리에 맞는 상품들이 나오게 된다. 그런데 이때 카테고리는 페이지가 바뀌어도 계속 중복되어 있을 것인데 이를 모든 html 파일에 작성하기는 번거롭기 때문에 이런 중복되는 템플릿이 있을 경우 기준이 되는 템플릿을 만들어 이를 확장하는 것을 템플릿 확장이라 한다!

프로젝트 루트에 templates 폴더를 생성하고, base.html (기준 템플릿)을 생성한 뒤 conflg/settings.py 파일에서 TEMPLATES 부분에서

'DIRS'=[os.path.join(BASE_DIR, 'templates')]

코드를 수정한다. 이는 base.html 파일을 찾을 수 있도록 경로를 설정해주는 것이다.

shop/template/shop/list.html 파일을 생성하고

{% extends 'base.html' %}
{% block title %}Category Page{% endblock %}
{% block content %}
<br>
<div class="row">
    <div class="col-2"> 
        <div class="list-group">
            <a href="/" class="list-group-item list-group-item-action list-group-item-secondary
{% if not current_category%}active{% endif %}">All</a>
            {%for c in categories %}
            <a href="{{c.get_absolute_url}}" class="list-group-item list-group-item-action list-group-item-secondary
{% if current_category.slug == c.slug %}active{% endif %}">
                {{c.name}}</a>
            {% endfor %}
        </div>
    </div>
    <div class="col">
        <div class="alert alert-danger" role="alert">
            {% if current_category %}{{current_category.name}}를/을 판매하고 있는 스토어입니다.
            {% else %} 모든 스토어 {% endif %}
        </div>
        <div class="row">
            {% for product in products %}
            <div class="col-sm-4">
                <div class="card">
                    <img class="card-img-top" src="{{product.image.url}}" alt="Product image" width="466px" height="270">
                    <div class="card-body" style="text-align:center">
                        <h5 class="card-title">{{product.name}}</h5>
                        {% load humanize %}
                        <img class="card-img" src="{{product.baeminImage.url}}" alt="QRcode image" style="width:70px; height:70px; text-align:center;"><p/>
                            <h4 class="badge badge-light">
&#8361;{{product.price | floatformat:'0' | intcomma}}</h4><p/>
                        <a href="{{product.get_absolute_url}}" class="btn btn-outline-info">View Detail</a>
                    </div>
                </div>
            </div>
            {% endfor %}
        </div>
    </div>
</div>
{% endblock %}

해당 코드를 작성한다 (html 코드에 대해서는 잘 몰라 준비되어 있는 html 파일을 저장했다)
여기서 중요하게 볼 것은 템플릿 태그이다.

{% extends 'base.html' %}
{% block title %}Category Page{% endblock %}
{% block content %}

맨 윗 부분의 템플릿 태그는 base.html 파일을 확장한다는 의미이며, 해당 페이지의 타이틀을 Category page로 설정한다는 블록 타이틀 태그 값을 지정했다. 그리고 블록 컨텐트 태그를 통해 해당 페이지에서 콘텐트의 시작점을 알린다. 이때 꼭 endblock 태그 값을 지정해야 한다.
조건문, 반복문 역시 템플릿 태그를 사용하여 값을 주고, 추가적으로 해당 페이지에서는
{% load humanize %} 라는 값을 줬다.

humanize에서 가격을 정수형으로 변환하는 필터를 적용하기 위함인데 humanize를 사용하려면 config/settings.py 파일에다가

django.contrib.humanize

코드를 추가하여 humanize를 사용할 앱으로 등록한다.

{{product.price | floatformat:'0' | intcomma}}

정수형으로 출력된 가격은 천 단위로 구분이 되는 쉼표를 포함하여 출력되도록 해당 코드로 설정한다!


다음은 detail.html 파일이다!
{% extends 'base.html' %}
{% block title %}Product Detail{% endblock %}
{% block content %}
<br>
<div class="container">
    <div class="row">
        <div class="col-4">
            <img src="{{product.image.url}}" width="300px" height="300px">
        </div>
        <div class="col">
            <h2 class="display-6">{{product.name}}</h2>
            <p><span class="badge badge-light">가격</span>
                {% load humanize %}
                &#8361;{{product.price | floatformat:'0' | intcomma}}</p>
            <form action="{% url 'cart:product_add' product.id %}" method="post">
                {{add_to_cart}}
                {% csrf_token %}
                <input type="submit" class="btn btn-outline-info btn-sm" value="장바구니에 담기">
            </form>
            <br>
            <h5><span class="badge badge-light"><a href="{{product.url}}">홈페이지 바로가기</a></span></h5>
            <br>
            <h5><span class="badge badge-secondary">설명</span>{{product.description|linebreaks}}</h5>
        </div>
    </div>
    <br><br>
    {% load disqus_tags %}
    {% disqus_show_comments %}
</div>
{% endblock %}

해당 파일도 html 파일이므로 저장된 코드를 붙여넣은 것이다. list.html과 detail.html을 작성한 뒤 runserver를 통해 화면이 잘 뜨는지 확인하고, 에러 없이 화면이 잘 뜬다면 관리자 페이지에서 카테고리, 상품을 등록하여 카테고리별로 상품이 잘 나오는지도 확인한다!

6. shop/admin.py

해당 파일로 이동하여 카테고리와 상품의 옵션 클래스를 작성한다.
카테고리 클래스에서 prepopulated_fields를 살펴보면 name 필드 값으로 slug 필드 값이 자동으로 설정되고 지정한다. 그리고 카테고리와 제품 전부 관리자 페이지에 등록되도록 한다.

상품 클래스에서도 prepopulated_fields 값을 똑같이 설정하고, list_editable는 목록에서 주요 값들을 바로 변경할 수 있도록 값을 지정한다.


이제 카테고리, 상품을 등록한 뒤 페이지를 다시 런서버하면

위와 같은 화면이 나오게 된다. 그렇다면 성공!


7. 소셜 로그인

해당 페이지에서의 로그인을 설정할 것이다. 그런데 요즘은 타 사이트의 회원 정보를 끌고 와 가입하는 경우가 많기 때문에 기존 앱을 다운로드해 소셜 로그인을 할 수 있또록 할 것이다!
우선 터미널 창에

pip install django-allauth

파일을 작성한 뒤 config/settings.py 파일에
'django.contrib.sites','allauth', 'allauth.account'.
'allauth.socialaccount', 'allauth.socialaccount.providers.naver',을 등록한다
(이때 카카오 계정으로 로그인하는 것이 이번 과제이므로~! allauth.socialaccount.providers.kakao'를 등록한다!

관리자 페이지에서 로그인을 할 때의 방식을 추가하기 위해서 conflig/settings.py 파일에서

AUTHENTICATION_BACKENDS = (
    'django.contrib.auth.backends.ModelBackend',
    'allauth.account.auth_backends.AuthenticationBackend',
)

코드를 작성한다. 이때 ModelBacked는 장고 기본인 사용자명으로 로그인할 수 있는 것이고,
AuthenticationBackend는 allauth의 이메일 사용으로 로그인할 수 있는 것이다.

그리고 config/urls.py 파일에서

    path('accounts/', include('allauth.urls')),

해당 url 경로를 추가한다!

여기까지 끝났다면 새로 추가한 앱을 데이터베이스에 반영하기 위해 터미널에 python manage.py migrate 코드를 실행한다.

8. API 키 발급

네이버에서 키를 발급받은 것은 동아리 활동 때 진행했으므로 벨로그에서는 카카오에서 키를 발급 받는 걸 설명하겠다! (네이버 키 발급 참고 사이트)

우선 카카오 개발자 사이트로 접속한다!
https://developers.kakao.com/

로그인을 한 뒤 내 애플리케이션을 선택해서 애플리케이션 추가를 클릭한다.
앱 이름과 사업자명을 입력한다! 앱 아이콘은 생략하겠다
그 후 생성된 애플리케이션을 클릭하면

네이티브 앱 키, REST API키, JavaScript키, Admin키 정보들을 확인할 수 있는데,
REST API 키를 기억해두자! 그럼 왼쪽에 카카오 로그인을 선택한다.

활성화 버튼을 눌러 활성화를 시키고, Redirect URI를 등록한다.
이때 allauth 문서를 참조하는 것이 가장 좋으며
나는 'http://127.0.0.1:8000/accounts/kakao/login/callback' 으로 설정했다.
플랫폼으로 이동해 Web에서 사이트 도메인을 설정한다
나는 도메인이 없으니까 프로젝트를 런서버했을 때 뜨는 해당 페이지 url을 작성했다.


그리고 보안에서 client Secret 발급을 받는다!
이제 관리자 페이지로 들어간 뒤

소셜 애플리케이션에 +add 를 눌러 카카오 로그인을 추가한다.
클라이언트 아이디에는 카카오 개발자 페이지에서 발급 받은 REST API를 붙여넣고,
시크릿 키에는 맨 마지막에 발급 받은 클라이언트 시크릿을 붙여넣은 뒤 사이트에서 example.com을 오른쪽으로 이동한 뒤 save를 눌러 저장한다.

해당 서버를 런서버한 뒤 카카오로 로그인을 클릭!

그럼 위와 같이 카카오 로그인창이 뜨고
본인의 카카오 계정으로 로그인을 하면 로그인이 된다!

해당 계정이 관리자 페이지에 저장됐는지 확인하기 위해 관리자 페이지로 이동하면

Social accounts 부분에 kakao 계정이 추가된 것을 확인할 수 있다!
이상으로 이번 주 과제는 끝이다~ ^,^

profile
누구보다 밝게 코딩하기♡

2개의 댓글

comment-user-thumbnail
2023년 7월 17일

잘 봤습니다. 좋은 글 감사합니다.

답글 달기
comment-user-thumbnail
2023년 7월 17일

잘 봤습니다. 좋은 글 감사합니다.

답글 달기