
개요: 여행갈 때 듣기 좋은 음악을 추천해주는 SNS 서비스 칼슘(라디오) 무(원문) (이..?)
프로젝트 정보
- 프로젝트: SNS 서비스 CaMu(Django 기본 팀 프로젝트)
- 개발주기 : 2023.04.10 – 2023.04.13 (4일)
- 역할
- 홈페이지
- 완전한 레이아웃
- CSS
- 언어: 파이썬
- 사용한 라이브러리:
- 파이썬
- 장고 (풀 스택 프레임워크)
- 베개 (이미지 업로드)
- ytmusicapi (YouTube API)
- 파이썬
프로젝트 구성
★ 파일 구조
관리 앱 Django_Team_Project, 댓글 앱 댓글, 메인 콘텐츠 앱 트윗, 사용자 관리 앱 사용자로 구성되어 있습니다.
CaMu comment
┣ templates
┃ ┗ comment_test.html
┣ admin.py
┣ apps.py
┣ models.py
┣ tests.py
┣ urls.py
┣ views.py
┗ __init__.py
Django_Team_Project
┣ asgi.py
┣ settings.py
┣ urls.py
┣ wsgi.py
┗ __init__.py
templates
┗ base.html (모든 페이지에 공통적으로 들어가는 상단 nav바)
tweet
┣ migrations
┃ ┗ __init__.py
┣ static
┃ ┗ tweet
┃ ┣ css
┃ ┃ ┗ styles.css
┃ ┗ image
┃ ┣ ad-1.png (이스터에그 광고)
┃ ┣ edit_btn.svg
┃ ┣ logo-camu.png (CaMu 로고)
┃ ┗ user_image.png (유저 기본 이미지)
┣ templates
┃ ┗ tweet
┃ ┣ create_post.html
┃ ┣ home.html
┃ ┣ my_page.html
┃ ┣ post_detail.html
┃ ┣ set_post.html
┃ ┗ set_profile.html
┣ admin.py
┣ apps.py
┣ models.py
┣ oauth.json
┣ tests.py
┣ urls.py
┣ views.py
┗ __init__.py
user
┣ migrations
┃ ┗ __init__.py
┣ templates
┃ ┗ user
┃ ┣ signin.html
┃ ┗ signup.html
┣ admin.py
┣ apps.py
┣ models.py
┣ tests.py
┣ urls.py
┣ views.py
┗ __init__.py
manage.py
requirements.txt
db.sqlite3
★ 화면 구성 및 코드
1. 상단 로고 및 상단 내비게이션 바
여기에서 보기 좋은 화면을 어떻게 보고 싶은지 모르겠습니다.
포토샵으로 간단한 로고를 만들어 봤습니다.
두 개의 텍스트 레이어만으로 제작
카무와..
너의 위에 ..를 올려 웃는 얼굴을 만들고 싶어
이제 상단 navbar html의 로고가 어떻게 배치되는지 봅시다.
<!
-- templates/base.html -->
<div class="nav nav-wrap">
<div class="navbar-container">
<!
-- 상단 로고 -->
<div>
<a class="logo" href="http://zeus2141./"></a>
</div>
(..skip..)
아주 간단하다
나는 nav 및 nav-wrap 클래스를 최상위 div에 제공하고 navbar-container를 내부에 실제 콘텐츠를 포함하는 div에 제공하고 있습니다.
로고가 배경으로 제공되기 때문에 html 파일에는 href=”http://zeus2141./” 속성(클릭 시 홈페이지로 이동)만 추가됩니다.
CSS는 어떻습니까
/* tweet/static/tweet/css/styles.css */
.logo {
display: flex;
margin: 0 auto;
width: 200px;
height: 70px;
background-image: url("../image/logo-camu.png");
background-repeat: no-repeat;
background-size: 100% auto;
background-position: center;
filter: invert(100%);
}
크기에 맞게 배경 이미지와 다양한 옵션이 있는 이미지 파일을 로드합니다.
2. 사용자 드롭다운 메뉴
사용자가 로그인한 후 상단 탐색 모음에 나타나는 드롭다운 메뉴입니다.
사용자 프로필과 함께 쓰기, 프로필 보기, 로그아웃의 세 가지 핵심 기능으로 이동할 수 있습니다.
로그아웃한 경우 위와 같이 로그인/등록하세요.
django 템플릿의 이 부분을 살펴보겠습니다.
<div class="menu">
{% if user.is_authenticated %}
<li class="nav-item dropdown">
<div class="box" href="#" style="background: #BDBDBD; float:left; margin: 0 4px 10px;">
{% if user.image %}
<img class="profile" src={{ user.image }}>
{% elif not user.image %}
<img class="profile" src={% static 'tweet/image/user_image.png' %}>
{% endif %}
</div>
<a class="nav-link dropdown-toggle" href="#" role="button" style="color: white;"
data-bs-toggle="dropdown" aria-expanded="false">
{{ user.username }}
</a>
<ul class="dropdown-menu dropdown-menu" style="padding: 5px;">
<li><a class="dropdown-item" href={% url 'create-post' %}>글쓰기</a></li>
<li><a class="dropdown-item" href={% url 'my-page' user.pk %}>프로필 보기</a></li>
<li><a class="dropdown-item" href={% url 'logout' %}>로그아웃</a></li>
</ul>
</li>
{% else %}
<div>
<p style="line-height: 45px; color: white;">
<a href={% url 'sign-in' %} class="sign-text">로그인하기</a> / <a href={% url 'sign-up' %} class="sign-text">회원가입 </a>
</p>
</div>
{% endif %}
</div>
메뉴 클래스가 있는 div를 사용하면 탐색 모음의 오른쪽 상단 모서리에 콘텐츠를 고정할 수 있습니다.
이제 내부 콘텐츠에서 두 개의 템플릿 if 태그를 사용하여 사용자 인증 여부, 아바타 설정 여부를 결정하고 각 화면을 표시합니다.
user.is_authenticated 즉, 사용자가 로그인한 경우 로그인 인터페이스가 표시되고 사용자가 로그인하지 않은 경우 로그인 인터페이스가 표시됩니다.
그러면 if 문은 로그인한 사용자 중 개인 사진을 설정한 사용자가 있는지 판단하고 개인 사진을 설정한 사용자가 있는지 여부에 따라 사용자 사진 또는 기본 사진을 표시합니다.
회원가입 시 프로필 사진을 업로드 하셔야 합니다.
프로필 사진을 업로드한 경우 프로젝트의 미디어 폴더에 저장하고 로드할 수 있도록 사용자 데이터베이스에 URL을 저장합니다.
회원가입 기능에서 아바타 업로드 기능의 모습을 살펴보겠습니다.
# user/views.py
from .models import UserModel
from django.shortcuts import render, redirect
from django.core.files.storage import FileSystemStorage
import random
# 회원가입 기능
def sign_up_view(request):
if request.method == 'GET':
(..skip..)
elif request.method == 'POST': # POST request일 경우
(..skip..)
# profile-image라는 name을 가진 데이터가 있다면 profile_image변수에 넣어줌
try:
profile_image = request.FILES('profile-image')
# 그렇지 않으면 None을 넣어줌
except:
profile_image = None
(..입력 데이터 판별 코드 skip..)
# 판별이 끝나고 문제가 없다면
else:
# UserModel클래스에 새로운 유저를 만들어 주고 그 유저의 정보를 new_user에 저장
new_user = UserModel.objects.create_user(
username=username, password=password)
# 만약 프로필 사진 데이터가 있다면
if profile_image:
# 프로필사진 파일 이름을 랜덤하게 변경!
user2_19232.png 형태
profile_image.name="user" + str(new_user.user_id) + '_' + str(
random.randint(10000, 100000)) + '.' + str(profile_image.name.split('.')(-1))
# 파일 저장
file_system_storage = FileSystemStorage()
fs = file_system_storage.save(
profile_image.name, profile_image)
# 저장한 파일 url 따기
uploaded_file_url = file_system_storage.url(fs)
# 신규 회원의 id를 다시 검색해 이미지 url 업데이트
check = UserModel.objects.filter(user_id=new_user.user_id)
check.update(image=uploaded_file_url)
return redirect('/sign-in') # 회원가입이 완료되었으므로 로그인 페이지로 이동
입력 태그의 종류가 file인 경우 로컬 파일명은 그대로 저장됩니다.
파일명은 랜덤하게 하고 싶어서 중간에 있는 코드처럼 user(user id)_(10000~100000 사이의 임의의 값).(확장자)로 변경했습니다.
파일 시스템 스토리지()파일 업로드 클래스 등을 사용하려면 settings.py, urls.py도 변경해야 합니다.
settings.py를 살펴보자
# Django_Team_Project/setting.py
import os
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
미디어가 저장될 위치를 설정 파일에 알려줍니다.
Django에서 미디어 파일은 프로젝트 단위로 저장되기 때문에 프로젝트 상단에 지정됩니다.
MEDIA_URL은 최상위 폴더 아래의 미디어 폴더로 지정되며 MEDIA_ROOT는 os.path.join을 사용하여 운영 체제에 따라 최상위 폴더의 미디어 경로를 가리킵니다.
다음으로 urls.py를 살펴봅니다.
# user/urls.py
from django.conf import settings
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns 목록에 정적 함수를 추가합니다.
MEDIA_URL에 대한 요청에 대한 MEDIA_ROOT 경로를 검색합니다.
3-5.주요 콘텐츠, 페이지 번호 및 광고
글 작성시 유튜브 주소를 입력하고, 저장시 유튜브 썸네일 url을 db에 저장합니다.
그래서 위 콘텐츠에 유튜브 썸네일을 표시하기로 했습니다.
아래 별점은 리뷰와 함께 작성되며 소수점 첫째 자리에서 반올림하여 출력됩니다.
하단에는 내비게이션에 사용된 프로필 사진이 재활용되어 작성자의 프로필이 표시되며, 클릭 시 작성자 프로필 화면으로 이동합니다.
이를 보여주는 home.html을 살펴보겠습니다.
<!
-- tweet/home.html -->
<div class="row row-cols-1 row-cols-md-4 g-4">
<!
-- 카드 반복 출력 -->
{% for post in page_obj %}
<div class="col">
<div class="card h-100">
<a href={% url 'post-detail' post.pk %} style="text-decoration-line: none;">
<img src={{ post.youtube_thumbnail }} width=100% height=100% class="card-img-top" alt="...">
<div class="card-body">
<div class="like-wrap">
<div style="">
<p class="like">{{ post.avg_star }}</p>⭐
</div>
</div>
<h5 class="card-title main-text" style="font-size: 1.5rem;">{{ post.title }}</h5>
<p class="card-text main-text" style="font-size: 1rem;">{{ post.comment }}</p>
</a>
<div>
<div class="post-author">
<a href={% url 'my-page' post.owner.pk %} role="button" style="color: black; text-decoration-line: none;">
<div class="box" style="background: #BDBDBD; float:left; margin: 0 4px 10px;">
{% if post.owner.image %}
<img class="profile" src={{ post.owner.image }}>
{% else %}
<img class="profile" src={% static 'tweet/image/user_image.png' %}>
{% endif %}
</div>
{{ post.owner }}
</a>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
<!
-- 카드 반복 출력 end -->
</div>
<!
-- 페이지네이션 -->
<div style="margin: 0 auto; display: flex;">
<nav aria-label="Page navigation example" style="margin: 0 auto;">
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="http://zeus2141./m/?page={{page_obj.previous_page_number}}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% endif %}
{% for p in page_obj.paginator.page_range %}
<li class="page-item"><a class="page-link" href="?page={{p}}">{{p}}</a></li>
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{page_obj.next_page_number}}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% endif %}
</ul>
</div>
</nav>
</div>
<!
-- 페이지네이션 end -->
<!
-- 광고 출력 -->
{% if user.is_authenticated %}
<div class="ad-1">
<img src={% static 'tweet/image/ad-1.png' %} width="80%">
</div>
{% endif %}
<!
-- 광고 end -->
page_obj는 Django 내장 클래스 Paginator()를 사용하여 콘텐츠를 8개의 청크로 슬라이스하고 검색합니다.
그리고 레이블을 템플릿으로 사용하여 출력을 하나씩 반복합니다.
중간에 있는 if 문은 네비바 메뉴와 동일하게 DB에 있으면 작성자 사진이 노출되고 없으면 기본 사진이 노출됩니다.
페이지 매김은 Django의 내장 클래스를 사용하여 생성됩니다.
프로젝트 납품
https://github.com/Churnobyl/Django_Team_Project
GitHub – Churnobyl/Django_Team_Project
GitHub에서 계정을 생성하여 Churnobyl/Django_Team_Project 개발에 기여하십시오.
github.com
프로젝트를 마친 후의 생각
이것은 Git을 사용한 첫 번째 프로젝트입니다.
처음에는 이 방법이 익숙하지 않아서 머지할 때 충돌이 많았는데 프로젝트가 끝나고 나니 자연스럽게 Git을 다룰 수 있었다.
Django로 SNS를 만들었는데 Paginator, FileSystemStorage, template syntax 등 페이지를 만드는 기능이 많아서 만들기가 쉽습니다.
더 많은 기능이 있으니 더 열심히 공부해야겠습니다.