🧩

012 메인화면

 
지금까지 했던 것을 살펴보도록 하겠습니다.
  • 데이터베이스를 만들기
  • 데이터베이스 내용을 admin 페이지에서 확인
  • 사용자 URL에 따른 View를 연결
  • View 함수 기능 정의
  • template을 통해서 실제 웹사이트가 보여지는 것을 구현
  • 발생한 오류들을 수정
 
이러한 과정이 한 싸이클이라고 보시면 됩니다. Django에서는 한 페이지, 한 기능 단위로 제작하면서 디버깅을 자주 하게 됩니다. 이 싸이클을 좀 더 반복하도록 하겠습니다.
 
이번 시간에는 메인 화면을 만들어보겠습니다. 페이스북, 인스타그램 모두 비슷한 형식의 메인 화면을 가지고 있습니다.
 
notion imagenotion image
 

1. post 앱 만들기

서버가 잘 작동된다면 post 라는 이름의 앱을 생성하겠습니다.
(venv)root@goorm:/workspace/instaclone/instaclone# python manage.py startapp post
 
앱을 만들었으면 config/settings.pyINSTALLED_APPS에 추가를 해야합니다.
notion imagenotion image
 
post/models.py를 열도록 하겠습니다.
notion imagenotion image
notion imagenotion image
 
밑줄 그어지는 린트 기능은 원래는 문법에 맞게 코드를 작성하고 디버깅하는데 도움을 주는 기능입니다. 장고 프로젝트에서는 아직 완벽하게 기능을 하지 못하기 때문에 기능을 꺼두는 것이 좋습니다. 구름 IDE에서 '실시간 린트 토글 항목'을 해제하면 됩니다.
notion imagenotion image
 

2. Post 모델 작성

필요한 모듈과 함수를 import 합니다.
 
파일명 : post/models.py
from django.conf import settings from django.db import models from imagekit.models import ProcessedImageField from imagekit.processors import ResizeToFill
 
Post 모델 클래스를 만듭니다.
 
파일명 : post/models.py
... class Post(models.Model): author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) photo = ProcessedImageField(upload_to=photo_path, processors=[ResizeToFill(600, 600)], format='JPEG', options={'quality': 90}) content = models.CharField(max_length=140, help_text="최대 140자 입력 가능") created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ['-created_at'] def __str__(self): return self.content
 
  • 유저 모델을 불러와서 모델의 외래키를 author 에 저장합니다.
  • 사진은 가져온 ProcessedImageField 기능을 활용합니다. 사진이 저장될 경로가 있어야 하는데 이는 뒤에 정의할 photo_path 함수를 사용합니다.
  • 사이즈 조정을 얼마나 할지 ResizeToFill을 이용합니다. 사진 파일 형식과 옵션을 설정하고 photo에 저장합니다.
  • 포스트 내용은 content로 140자 길이 제한을 둡니다.
  • 생성 시각과 수정 시각을 각각 현재 시간을 통해 저장하게 됩니다.
  • 생성 시각을 기준으로 정렬이 되도록 Meta 클래스를 설정합니다.
 
사진 경로는 프로필 사진 경로 만들기와 거의 같습니다. photo_path 함수는 난수를 발생시켜서 문자열에 포함하고 확장자명과 함께 경로를 반환하는 함수입니다.
 
파일명 : post/models.py
... def photo_path(instance, filename): from time import strftime from random import choice import string arr = [choice(string.ascii_letters) for _ in range(8)] pid = ''.join(arr) extension = filename.split('.')[-1] return '{}/{}/{}.{}'.format(strftime('post/%Y/%m/%d/'), instance.author.username, pid, extension) ...
 
완성된 코드는 아래와 같습니다.
 
파일명 : post/models.py
from django.conf import settings from django.db import models from imagekit.models import ProcessedImageField from imagekit.processors import ResizeToFill def photo_path(instance, filename): from time import strftime from random import choice import string arr = [choice(string.ascii_letters) for _ in range(8)] pid = ''.join(arr) extension = filename.split('.')[-1] return '{}/{}/{}.{}'.format(strftime('post/%Y/%m/%d/'), instance.author.username, pid, extension) class Post(models.Model): author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) photo = ProcessedImageField(upload_to=photo_path, processors=[ResizeToFill(600, 600)], format='JPEG', options={'quality': 90}) content = models.CharField(max_length=140, help_text="최대 140자 입력 가능") created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ['-created_at'] def __str__(self): return self.content
 
DB 적용을 위해 makemigration을 합니다.
(venv)root@goorm:/workspace/instaclone/instaclone# python manage.py makemigrations post
 
만약 오타나 문제가 없으면 이와 같이 출력 됩니다.
notion imagenotion image
 
만약 실패하게 되면 왜 실패했는지 출력 되기 때문에 잘 보고 고치시면 됩니다.
notion imagenotion image
 
예를 들어서 이와 같은 문구가 나왔을 때, module 'django.db.models' has no attribute 'ForeignKe' 부분에서 ForeignKe라는 속성을 importdjango.db.models 모듈에서 찾을 수 없다는 뜻입니다. 대부분 오타로 인한 오류가 많기 때문에 Ctrl + F 를 통해 해당 부분을 찾고 오타가 있는 지 보시면 됩니다. 아니면 구글에 해당 오류를 검색해도 적절한 답을 찾을 수 있습니다.
 
이제 makemigrations를 성공했으면 아래 명령어를 입력합니다.
(venv)root@goorm:/workspace/instaclone/instaclone# python manage.py migrate
 
이와 같이 화면이 보이면, 성공적으로 모델을 적용한 것이라 볼 수 있습니다.
notion imagenotion image
 

3. admin에 Post 모델 등록

post 앱의 admin.py 를 수정하도록 하겠습니다. 프로젝트 폴더에서 post/admin.py 파일을 엽니다.
 
파일명 : post/admin.py
from django.contrib import admin # Register your models here.
 
admin.register 데코레이터를 이용할 것입니다.
  • admin 페이지에서 id, author, nickname, content, created_at을 리스트에 보이도록 하고, author, nickname, content 에만 링크가 걸리도록 할 것입니다.
  • nickname은 사용자가 포스트를 작성할 때 따로 입력하지 않기 때문에 nickname을 가져오는 함수를 만듭니다.
 
파일명 : post/admin.py
from django.contrib import admin from .models import Post @admin.register(Post) class PostAdmin(admin.ModelAdmin): list_display = ['id', 'author', 'nickname', 'content', 'created_at'] list_display_links = ['author', 'nickname', 'content'] def nickname(request, post): return post.author.profile.nickname
 
admin 페이지에서 입력하는 부분을 조금 수정하도록 하겠습니다. PostForm 클래스를 생성합니다. 내용을 입력하는 부분을 Textarea로 지정합니다. 그리고 Meta에서 모델을 포스트로 정합니다.
 
파일명 : post/admin.py
from django import forms from django.contrib import admin from .models import Post class PostForm(forms.ModelForm): content = forms.CharField(widget=forms.Textarea) class Meta: model = Post fields = '__all__' @admin.register(Post) class PostAdmin(admin.ModelAdmin): list_display = ['id', 'author', 'nickname', 'content', 'created_at'] list_display_links = ['author', 'nickname', 'content'] form = PostForm def nickname(request, post): return post.author.profile.nickname
 
여기까지 작성하고 서버를 작동시켜 테스트합니다. 만약, 서버가 작동하지 않는다면 오류 문구를 잘 봐서 문제를 해결하도록 합니다. 서버가 작동하면 https://*.run.goorm.io/admin/ 를 통해서 admin 페이지에 접속합니다. 그리고 예전에 있었던 admin 아이디를 입력합니다. 로그인이 성공하면, Posts 관리 항목이 생긴 것을 볼 수 있습니다.
notion imagenotion image
 
포스트를 하나 작성해보도록 하겠습니다.
notion imagenotion image
 
입력하고 저장하도록 하겠습니다.
notion imagenotion image
 
이와 같이 저장됩니다. 아까 admin.py 에서 list_display를 설정한 바와 같이 id, author, nickname, content, created_at 이렇게 페이지에 나오는 것을 볼 수 있습니다. 그리고 list_display_links를 설정한 바와 같이 author, nickname, content에만 링크가 걸리는 것을 볼 수 있습니다. 링크를 누르면 해당 포스트를 관리할 수 있게 됩니다.
 

4. DB에 모델 등록 확인

(해당 파트는 postgresql 을 사용하시는 분만 보시면 됩니다.)
데이터베이스에 Post 모델이 잘 적용되는 지 확인해봅니다. 슈퍼 유저로 접속합니다.
root@goorm:/workspace/instaclone/instaclone# su - postgres
 
notion imagenotion image
 
psql에 접속합니다.
postgre@goorm:~$ psql
 
notion imagenotion image
 
데이터베이스 목록을 살펴봅니다.
postgres=# \l
 
notion imagenotion image
 
instaclone 데이터베이스로 접속합니다.
postgres=# \c instaclone
 
notion imagenotion image
 
instaclone 데이터베이스의 table 들을 확인합니다.
instaclone=# \dt
 
notion imagenotion image
 
post_post 테이블이 잘 확인이 됩니다. 장고 프로젝트에서 테이블 이름은 앱이름_모델이름(모두 소문자) 형식으로 생성됩니다. post 앱의 Post 모델이기 때문에 post_post 이름으로 만들어지는 것입니다.
 

5. urls.py 수정

이제 Post 데이터가 있으니 이것을 화면에 뿌리기만 하면 됩니다. 화면에 뿌리기 전에 가장 먼저 수정할 파일은 urls.py 입니다.
 
파일명 : config/urls.py
from django.contrib import admin from django.urls import include, path from django.conf import settings from django.conf.urls.static import static from django.shortcuts import redirect urlpatterns = [ path('admin/', admin.site.urls), path('accounts/', include('accounts.urls')), path('accounts/', include('allauth.urls')), path('post/', include('post.urls', namespace='post')), path('', lambda r: redirect('post:post_list'), name='root') ] urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
 
redirect를 import 하고 'post/' URL 과 post 폴더의 urls.py 를 연결합니다. 그리고 루트 URL로 접근할 때도 post 로 연결되도록 합니다.
이제 post 폴더에 있는 urls.py 를 작성하겠습니다. 파일이 없으면 만들어줍니다.
notion imagenotion image
 
기본적인 기능을 가져오고 views.py의 기능을 가져올 것입니다. 앱 이름을 적어주고 URL은 'post/'views.py에 만들 함수 post_list를 연결하도록 하겠습니다.
 
파일명 : post/urls.py
from django.urls import path from .views import * app_name = 'post' urlpatterns = [ path('', post_list, name='post_list'), ]
 

6. views.py

로그인 기능을 만들 때 했던 것처럼 views.py를 만들도록 하겠습니다.
유저 모델을 import하고, 필요한 기능을 가져오고, 저희가 만든 Post 모델을 가져옵니다.
 
파일명 : post/views.py
from django.contrib.auth import get_user_model from django.shortcuts import get_object_or_404, redirect, render from .models import Post
 
요청이 들어오면, posts 변수에 Post 전부를 저장합니다. 사용자가 로그인 상태일 때, 사용자 이름을 저장하고, 유저 모델 내용을 확인하고, 사용자 프로필을 저장합니다. 그리고 HTML을 렌더링합니다. 유저 프로필과 포스트 리스트를 같이 넘겨줍니다.
만약, 로그인 상태가 아니라면 포스트 내용만 보이도록 할 것입니다.
 
파일명 : post/views.py
from django.contrib.auth import get_user_model from django.shortcuts import get_object_or_404, redirect, render from .models import Post def post_list(request): posts = Post.objects.all() if request.user.is_authenticated: username = request.user user = get_object_or_404(get_user_model(), username=username) user_profile = user.profile return render(request, 'post/post_list.html', { 'user_profile': user_profile, 'posts': posts, }) else: return render(request, 'post/post_list.html', { 'posts': posts, })
 

7. Template 작성

1. layout.html 수정

우선 처음에 작성한 기본 틀 config/templates/layout.html 을 수정하도록 하겠습니다. 백엔드 완성 소스에도 같은 위치에 존재합니다.
 
파일명 : config/templates/layout.html
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta name="mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="default"> <!-- Facebook Meta Tags / 페이스북 오픈 그래프 --> <meta property="og:url" content="http://kindtiger.dothome.co.kr/insta"> <meta property="og:type" content="website"> <meta property="og:title" content="instagram"> <meta property="og:description" content="instagram clone"> <meta property="og:image" content="http://kindtiger.dothome.co.kr/insta/imgs/instagram.jpeg"> <!-- Twitter Meta Tags / 트위터 --> <meta name="twitter:card" content="instagram clone"> <meta name="twitter:title" content="instagram"> <meta name="twitter:description" content="instagram clone"> <meta name="twitter:image" content="http://kindtiger.dothome.co.kr/insta/imgs/instagram.jpeg"> <!-- Google / Search Engine Tags / 구글 검색 엔진 --> <meta itemprop="name" content="instagram"> <meta itemprop="description" content="instagram clone"> <meta itemprop="image" content="http://kindtiger.dothome.co.kr/insta/imgs/instagram.jpeg"> <title>instagram</title> <link rel="stylesheet" href="{% static 'css/reset.css' %}"> <link rel="stylesheet" href="{% static 'css/common.css' %}"> <link rel="shortcut icon" href="{% static 'imgs/instagram.png' %}"> <link rel="stylesheet" href="{% static 'css/style.css' %}"> {% block head %} {% endblock %} </head> <body> <section id="container"> <header id="header"> <section class="h_inner"> <h1 class="logo"> <a href="{% url 'post:post_list' %}"> <div class="sprite_insta_icon"></div> <div> <div class="sprite_write_logo"></div> </div> </a> </h1> <div class="search_field"> <input type="text" placeholder="검색" tabindex="0"> <div class="fake_field"> <span class=sprite_small_search_icon></span> <span>검색</span> </div> </div> <div class="right_icons"> <div class="sprite_camera_icon"> <a href="#"></a> </div> <div class="sprite_compass_icon"><a href="#"></a></div> <div class="sprite_heart_icon_outline"><a href="#"></a></div> {% if user.is_authenticated %} <div class="sprite_user_icon_outline"> <a href="#"></a> </div> {% else %} <div class="sprite_user_icon_outline"> <a href="{% url 'accounts:login' %}"></a> </div> {% endif %} </div> </section> </header> {% block content %} {% endblock %} </section> <!-- jQuery (부트스트랩의 자바스크립트 플러그인을 위해 필요합니다.) --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"> </script> <!-- <script src="{% static 'js/insta.js' %}"></script> --> {% block js %} {% endblock %} </body> </html>
 

8. post_list.html 작성

이제 메인 페이지에 넣을 템플릿을 만들겠습니다.

1. layout 설정

post 폴더 아래에 templates라는 폴더를 생성합니다. post/templates 폴더 아래에 post 폴더를 생성합니다.
notion imagenotion image
notion imagenotion image
 
post/templates/post 폴더 아래에 layout.html 을 생성합니다.
notion imagenotion image
 
아래의 소스코드는 config/templates/layout.html 을 확장해서 가져온다는 의미입니다.
 
파일명 : post/templates/post/layout.html
{% extends "layout.html" %}
 
그리고 post/templates/post 폴더 아래 post_list.html 파일을 새로 생성합니다.
notion imagenotion image
 
아래의 코드를 입력합니다.
 
파일명 : post/templates/post/post_list.html
{% extends "post/layout.html" %} {% load static %} {% block head %} {% endblock %} {% block content %} {% endblock %}
 
content 블록은 프론트엔드 틀을 가져올 것입니다. 중복되는 요소(article 태그)들을 제거했습니다.
 
 
파일명 : post/templates/post/post_list.html
{% extends "post/layout.html" %} {% load static %} {% block head %} {% endblock %} {% block content %} <section id="main_container"> <div class="inner"> <div class="contents_box"> <article class="contents"> <header class="top"> <div class="user_container"> <div class="profile_img"> <img src="imgs/thumb.jpeg" alt="프로필이미지"> </div> <div class="user_name"> <div class="nick_name m_text">KindTiger</div> <div class="country s_text">Seoul, South Korea</div> </div> </div> <div class="sprite_more_icon" data-name="more"> <ul class="toggle_box"> <li><input type="submit" class="follow" value="팔로우" data-name="follow"></li> <li>수정</li> <li>삭제</li> </ul> </div> </header> <div class="img_section"> <div class="trans_inner"> <div><img src="imgs/img_section/img01.jpg" alt="visual01"></div> </div> </div> <div class="bottom_icons"> <div class="left_icons"> <div class="heart_btn"> <div class="sprite_heart_icon_outline" name="39" data-name="heartbeat"></div> </div> <div class="sprite_bubble_icon"></div> <div class="sprite_share_icon" data-name="share"></div> </div> <div class="right_icon"> <div class="sprite_bookmark_outline" data-name="bookmark"></div> </div> </div> <div class="likes m_text"> 좋아요 <span id="like-count-39">2,346</span> <span id="bookmark-count-39"></span> 개 </div> <div class="comment_container"> <div class="comment" id="comment-list-ajax-post37"> <div class="comment-detail"> <div class="nick_name m_text">dongdong2</div> <div>강아지가 너무 귀여워요~!</div> </div> </div> <div class="small_heart"> <div class="sprite_small_heart_icon_outline"></div> </div> </div> <div class="timer">1시간 전</div> <div class="comment_field" id="add-comment-post37"> <input type="text" placeholder="댓글달기..."> <div class="upload_btn m_text" data-name="comment">게시</div> </div> </article> </div> <input type="hidden" id="page" value="1"> <div class="side_box"> <div class="user_profile"> <div class="profile_thumb"> <img src="imgs/thumb.jpeg" alt="프로필사진"> </div> <div class="detail"> <div class="id m_text">KindTiger</div> <div class="ko_name">심선범</div> </div> </div> <article class="story"> <header class="story_header"> <div>스토리</div> <div class="more">모두 보기</div> </header> <div class="scroll_inner"> <div class="thumb_user"> <div class="profile_thumb"> <img src="imgs/thumb02.jpg" alt="프로필사진"> </div> <div class="detail"> <div class="id">kind_tigerrrr</div> <div class="time">1시간 전</div> </div> </div> </div> </article> <article class="recommend"> <header class="reco_header"> <div>회원님을 위한 추천</div> <div class="more">모두 보기</div> </header> <div class="thumb_user"> <div class="profile_thumb"> <img src="imgs/thumb02.jpg" alt="프로필사진"> </div> <div class="detail"> <div class="id">kind_tigerrrr</div> <div class="time">1시간 전</div> </div> </div> </article> </div> </div> </section> {% endblock %}
 
서버를 동작시키면 다음과 같이 나타납니다. 지금은 post_list.html 에 적힌 대로 출력이 되고 있습니다.
notion imagenotion image
 
이제부터는 화면에 내용을 하나씩 DB에 저장된 값이 나타나게 고쳐보겠습니다. 먼저, 메인 화면에는 포스트가 존재하는 만큼 출력이 됩니다. 아래와 같은 포스트들이 반복적으로 나타나는 것입니다.
notion imagenotion image
 
post_list.html 에서 div.contents_box 태그의 내용을 {% for post in posts %} 태그로 감쌉니다. 끝은 {% endfor %}로 감쌉니다.
 
파일명 : post/templates/post/post_list.html
... <div class="contents_box"> {% for post in posts %} <article class="contents"> <header class="top"> <div class="user_container"> ... </div> </header> </article> {% endfor %} </div> ...
 
구름 IDE에서 HTML을 작성하실 때, 태그 단위로 코드를 접을 수 있습니다.
notion imagenotion image
 
표시한 화살표를 누르면 해당하는 태그의 내용이 접혀서 숨겨집니다. article 태그를 반복할 것이기 때문에 endfor 태그를 마지막에 넣어줍니다.
notion imagenotion image
 
이제 article 태그 내부를 수정하겠습니다. div.profile_img 태그는 프로필 사진을 서버에서 가져오는 부분입니다. 프로필 사진은 선택 사항 이었기 때문에 없을 수도 있습니다. 따라서 if 문을 사용합니다.
 
파일명 : post/templates/post/post_list.html
... <div class="profile_img"> <!--포스트 저자의 프로필 사진이 있다면--> {% if post.author.profile.picture %} <!-- 이미지 주소는 포스트 저자 프로필 사진의 URL입니다. --> <img src="{{ post.author.profile.picture.url }}" alt="프로필이미지"> <!--프로필 사진이 없다면 --> {% else %} <!-- 기본 이미지를 가져옵니다.--> <img src="{% static 'imgs/thumb.jpeg'%}" alt="프로필이미지"> {% endif %} </div> ...
 
div.user_name 부분에서 사용자 닉네임이 보이는 부분을 수정하겠습니다.
 
파일명 : post/templates/post/post_list.html
... <div class="user_name"> <div class="nick_name m_text"> <!--포스트 저자 프로필의 닉네임--> {{ post.author.profile.nickname }} </div> <div class="country s_text">Seoul, South Korea</div> </div> ...
 
잘 작동하는지 테스트를 할 것입니다. 테스트를 위해서 포스트를 작성합니다. 슈퍼 유저로 접속한 후 post 추가하면 됩니다. 저는 2개의 유저로 작성을 했습니다. 잘 작동합니다.
notion imagenotion image
 
구름IDE 에디터에서 내용 영역별로 코드를 접으면 다음과 같이 됩니다. 이때까지 header.top 부분을 수정한 것입니다. 이렇게 영역을 나누어서 수정하면 훨씬 보기 편합니다.
notion imagenotion image
 
다음 수정할 부분은 div.img_section 입니다. 포스트에 첨부된 사진이 나오게 할 것입니다. 포스트의 포토는 필수 사항이어서 따로 if-else 를 사용할 필요가 없습니다.
 
파일명 : post/templates/post/post_list.html
... <div class="img_section"> <div class="trans_inner"> <div><img src="{{ post.photo.url }}" alt="visual01"></div> </div> </div> ...
 
수정하게 되면 이와 같이 잘 나옵니다. 아래에 나오는 사진들은 포스트를 생성할 때 추가했던 사진들입니다.
notion imagenotion image
 
포스트 생성 시간도 '1시간 전'에서 실제 생성 시간 계산으로 교체해보겠습니다.
notion imagenotion image
 
div.timer 부분을 수정합니다.
 
파일명 : post/templates/post/post_list.html
... <div class="timer">{{ post.updated_at | timesince }}</div> ...
 
notion imagenotion image
 
이제 포스트에서 다음으로 수정할 부분들은 전부 기능을 추가해야 하는 것들입니다. 좋아요, 팔로우, 댓글 등과 같은 기능들은 추후에 추가하겠습니다.
 
다음으로 수정할 것은 사이드 박스입니다.
notion imagenotion image
 
notion imagenotion image
 
사용자 프로필이 있다면 관련 정보를 출력해줄 것입니다. 프로필 관련 정보들을 연결해줍니다. 프로필 사진은 선택 사항이기 때문에 아까와 같이 if else로 나눕니다. 프로필이 없는 경우는 프로필 사진을 기본 사진으로 연결하고 detail 부분은 일단 없애도록 하겠습니다.
 
파일명 : post/templates/post/post_list.html
... <div class="side_box"> <div class="user_profile"> <div class="profile_thumb"> {% if user_profile and user_profile.picture %} <img src="{{ user_profile.picture.url }}" alt="프로필이미지"> {% else %} <img src="{% static 'imgs/thumb.jpeg'%}" alt="프로필이미지"> {% endif %} </div> <div class="detail"> {% if user_profile %} <div class="id m_text">{{ user_profile.user }}</div> <div class="ko_name">{{ user_profile.nickname }}</div> {% endif %} </div> </div> ...
 
작동하는 것을 보면 usernamenickname이 잘 나오는 것을 볼 수 있습니다.
notion imagenotion image
 
프로필 사진은 지금 추가를 안 한 상태여서 기본 사진이 나오게 됩니다. 한 번 추가해보겠습니다. admin 페이지를 가서 추가를 하겠습니다.
notion imagenotion image
 
웹 브라우저에서 새로고침을 하면, 이와 같이 잘 적용됩니다. 화면을 구성하는 프론트엔드 소스를 가져오고, 서버에 값이 나오도록 template을 작성해보았습니다.
notion imagenotion image
 
파일명 : post/templates/post/post_list.html
{% extends "post/layout.html" %} {% load static %} {% block head %} {% endblock %} {% block content %} <section id="main_container"> <div class="inner"> <div class="contents_box"> {% for post in posts %} <article class="contents"> <header class="top"> <div class="user_container"> <div class="profile_img"> <!--포스트 저자의 프로필 사진이 있다면--> {% if post.author.profile.picture %} <!-- 이미지 주소는 포스트 저자 프로필 사진의 URL입니다. --> <img src="{{ post.author.profile.picture.url }}" alt="프로필이미지"> <!--프로필 사진이 없다면 --> {% else %} <!-- 기본 이미지를 가져옵니다.--> <img src="{% static 'imgs/thumb.jpeg'%}" alt="프로필이미지"> {% endif %} </div> <div class="user_name"> <div class="nick_name m_text"> <!--포스트 저자 프로필의 닉네임--> {{ post.author.profile.nickname }} </div> <div class="country s_text">Seoul, South Korea</div> </div> </div> <div class="sprite_more_icon" data-name="more"> <ul class="toggle_box"> <li><input type="submit" class="follow" value="팔로우" data-name="follow"></li> <li>수정</li> <li>삭제</li> </ul> </div> </header> <div class="img_section"> <div class="trans_inner"> <div><img src="{{ post.photo.url }}" alt="visual01"></div> </div> </div> <div class="bottom_icons"> <div class="left_icons"> <div class="heart_btn"> <div class="sprite_heart_icon_outline" name="39" data-name="heartbeat"></div> </div> <div class="sprite_bubble_icon"></div> <div class="sprite_share_icon" data-name="share"></div> </div> <div class="right_icon"> <div class="sprite_bookmark_outline" data-name="bookmark"></div> </div> </div> <div class="likes m_text"> 좋아요 <span id="like-count-39">2,346</span> <span id="bookmark-count-39"></span> 개 </div> <div class="comment_container"> <div class="comment" id="comment-list-ajax-post37"> <div class="comment-detail"> <div class="nick_name m_text">dongdong2</div> <div>강아지가 너무 귀여워요~!</div> </div> </div> <div class="small_heart"> <div class="sprite_small_heart_icon_outline"></div> </div> </div> <div class="timer">{{ post.updated_at | timesince }}</div> <div class="comment_field" id="add-comment-post37"> <input type="text" placeholder="댓글달기..."> <div class="upload_btn m_text" data-name="comment">게시</div> </div> </article> {% endfor %} </div> <input type="hidden" id="page" value="1"> <div class="side_box"> <div class="user_profile"> <div class="profile_thumb"> {% if user_profile and user_profile.picture %} <img src="{{ user_profile.picture.url }}" alt="프로필이미지"> {% else %} <img src="{% static 'imgs/thumb.jpeg'%}" alt="프로필이미지"> {% endif %} </div> <div class="detail"> {% if user_profile %} <div class="id m_text">{{ user_profile.user }}</div> <div class="ko_name">{{ user_profile.nickname }}</div> {% endif %} </div> </div> <article class="story"> <header class="story_header"> <div>스토리</div> <div class="more">모두 보기</div> </header> <div class="scroll_inner"> <div class="thumb_user"> <div class="profile_thumb"> <img src="imgs/thumb02.jpg" alt="프로필사진"> </div> <div class="detail"> <div class="id">kind_tigerrrr</div> <div class="time">1시간 전</div> </div> </div> </div> </article> <article class="recommend"> <header class="reco_header"> <div>회원님을 위한 추천</div> <div class="more">모두 보기</div> </header> <div class="thumb_user"> <div class="profile_thumb"> <img src="imgs/thumb02.jpg" alt="프로필사진"> </div> <div class="detail"> <div class="id">kind_tigerrrr</div> <div class="time">1시간 전</div> </div> </div> </article> </div> </div> </section> {% endblock %}