03_Web

20_페이지 구성 개선

chuuvelop 2025. 2. 19. 17:23
728x90

현재 포스트 목록 페이지의 문제점

  • 대표 이미지가 없는 포스트가 하나라도 있으면 포스트 목록 페이지에서 오류가 발생
  • 포스트의 본문 전체를 다 보여주는 데 장문의 포스트가 있으면 첫 페이지에서 원하는 게시글을 빠르게 찾기 어려움
    • 본문 앞 부분만 보여주는 미리보기 기능 필요

 

조건에 따라 이미지 보이기

# blog/post_list.html

    {% if p.head_image %}
        <a href="#!">
            <img class="card-img-top" src="{{ p.head_image.url }}" alt="{{ p }} head image" />
        </a>
    {% endif %}

 

  • img 태그의 alt 속성 : 이미지를 보여줄 수 없을 때 이미지 대신 나타나게 하는 텍스트
    • 제대로 웹사이트가 동작하고 있다면 필요없지만 만약의 경우에 대비해야함

 

이미지가 없을 경우 사용할 임의의 이미지 가져오기

# blog/post_list.html

{% if p.head_image %}
    <a href="#!">
        <img class="card-img-top" src="{{ p.head_image.url }}" alt="{{ p }} head image" />
    </a>
{% else %}
    <img class="card-img-top" src="https://picsum.photos/800/200" alt="random_image"/>
{% endif %}

 

 

임의로 나타나는 이미지 고정하기

  • 문제
    • 임의의 이미지의 첫 번째 문제는 웹브라우저를 새로고침 할 때 마다 이미지가 바뀐다는 것
      • 웹사이트를 방문하는 사람 입장에서는 새로운 게시글이 업로드 된 것으로 착각할 수 있음
    • 두 번째 문제는 이미지 없는 게시글이 여러 개 있을 때 모두 똑같은 이미지를 보여준다는 것
  • 해결방법
    • https://picsum.photos/seed/id값/ 을 입력하면 해당 id값을 가지는 위치에 매번 동일한 이미지를 나타냄
    • 각 게시글마다 고유한 pk값을 가지기 때문에 id값 부분에 해당하는 게시글의 pk를 넣으면 고정된 결과를 얻을 수 있음
# blog/post_list.html

    {% if p.head_image %}
        <a href="#!">
            <img class="card-img-top" src="{{ p.head_image.url }}" alt="{{ p }} head image" />
        </a>
    {% else %}
        <img class="card-img-top" src="https://picsum.photos/seed/{{ p.id }}/800/200" alt="random_image"/>
    {% endif %}

 

 

첨부파일이 있는 경우 다운로드 버튼 만들기

  • file_upload 필드를 추가해 파일 첨부 기능을 구현했지만 현재는 관리자페이지에서만 확인 가능함
  • if문을 사용해 첨부파일이 있는 경우에는 다운로드 버튼이 보이도록 템플릿을 수정
# blog/post_detail.html

    <!-- Post content-->
    <section class="mb-5">
        <p class="fs-5 mb-4">{{ post.content }}</p>
        {% if post.file_upload %}
            <a href="{{ post.file_upload.url }}" 
                class="btn btn-outline-dark" 
                role="button" download>Download
            </a>
        {% endif %}
    </section>

 

첨부파일명과 확장자 아이콘 나타내기

  • 지금은 사용자가 어떤 파일을 다운받는지 알 수 없음
  • models.py에서 파일명을 찾아내는 함수와 확장자를 찾아내는 함수를 개발
# blog/models.py

import os

# Create your models here.
class Post(models.Model):
    title = models.CharField(max_length=30)
    content = models.TextField()
    head_image = models.ImageField(upload_to="blog/images/%Y/%m/%d/", blank=True)
    file_upload = models.FileField(upload_to="blog/files/%Y/%m/%d/", blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return f"[{self.pk}]{self.title}"
    
    # 이걸 설정해주면 관리자페이지에 VIEW ON SITE라는 버튼이 생김
    def get_absolute_url(self):
        return f"/blog/{self.pk}/"
    
    def get_file_name(self):
        return os.path.basename(self.file_upload.name)
    
    def get_file_ext(self):
        return self.get_file_name().split(".")[-1]

 

# blog/post_detail.html
    <head>
...
        <script src="https://kit.fontawesome.com/4c4232cd4f.js" crossorigin="anonymous"></script>
    </head>
...
    {% if post.file_upload %}
        <a href="{{ post.file_upload.url }}" 
            class="btn btn-outline-dark" 
            role="button" download>Download:
            {% if post.get_file_ext == "xlsx" or post.get_file_ext == "xls" %}
                <!-- 엑셀 아이콘 -->
                <i class="fa-solid fa-file-excel"></i>
            {% elif post.get_file_ext == "docx" or post.get_file_ext == "doc"  %}
                <!-- 워드 아이콘 -->
                <i class="fa-solid fa-file-word"></i>
            {% else %}
                <!-- 일반 파일 아이콘 -->
                <i class="fa-solid fa-file-arrow-down"></i>
            {% endif %}
            {{ post.get_file_name}}
        </a>
    {% endif %}

 

 

템플릿 필터를 사용해 게시글 미리보기 기능 구현(...)

  • 포스트 목록에서 본문 내용 전체를 보여줄 경우 본문이 너무 길다면 포스트 목록을 한 눈에 살펴보기 어려움
  • 요약문이나 내용의 앞부분 일부만 보여주는 미리보기 기능을 장고에서 제공함
    • truncatewords 또는 truncatechars를 사용
      • truncatewords : 문자열을 단어 수 기준으로 자름
      • truncatechars : 문자열을 글자 수 기준으로 자름
# blog/post_list.html

<div class="card-body">
    <div class="small text-muted">{{ p.created_at }}</div>
    <h2 class="card-title h4">{{ p.title }}</h2>
    <p class="card-text">{{  p.content | truncatewords:45 }}</p>
    <a class="btn btn-primary" href="{{ p.get_absolute_url }}">Read more →</a>
</div>
728x90