장고(django)의 뷰(View)

2023-03-16 hit count image

웹 페이지를 위해 장고(django)의 뷰(View)를 사용하는 방법에 대해서 알아보겠습니다.

개요

지금까지 데이터를 저장할 데이터베이스와의 연동과 데이터를 생성하고 읽고 갱신하고 삭제(CRUD - Create Read Update Delete)하는 방법, 그리고 사용자가 접속할 URL을 생성하는 방법 등 장고(django)로 웹 서비스를 하기 위한 많은 작업을 진행하였습니다. 이번 블로그에서는 사용자에게 보여줄 웹 페이지를 만들기 위해 장고(django)의 뷰(View)를 사용하는 방법에 대해서 알아보겠습니다.

이 블로그는 시리즈로 작성되어 있으며, 아래에 링크를 통해 시리즈의 다른 글을 확인할 수 있습니다.

또한 이 블로그 시리즈에서 다룬 소스는 github에 공개되어 있습니다. 아래에 링크를 통해 확인 가능합니다.

URL 확인

이전 블로그(장고(django)의 라우팅(Routing))에서 우리는 사용자가 접속할 URL과 정적 HTML을 연결하는 방법에 대해서 알아보았습니다. 아래에 장고(django) 명령어를 실행하여 테스트 서버를 시작한 후, http://127.0.0.1:8000/에 접속하면 Hello World가 표시되는 웹 페이지를 확인할 수 있습니다.

# source venv/bin/activate
# cd django_exercise/
python manage.py runserver

데이터 전달

이제 이전 블로그(장고(django)의 ORM)에서 생성한 데이터를 웹 페이지에서 보기 위해 뷰(View)에 데이터를 전달해 보도록 하겠습니다. blog/views.py를 열고 아래와 같이 수정합니다.

from django.shortcuts import render
from .models import Post

def posts(request):
    posts = Post.objects.filter(published_at__isnull=False).order_by('-published_at')
    return render(request, 'blog/posts.html', {'posts': posts})
  • from .models import Post: 우리가 만든 Post 모델(Models)을 불러옵니다.
  • posts = Post.objects.filter(published_at__isnull=False).order_by('-published_at')
    • published_at__isnull=False: published_at 필드가 null이 아닌 경우(__isnull=False)에 데이터를 가져옵니다
    • order_by('-published_at'): 데이터를 published_at 필드로 내림차순 정렬합니다.
  • return render(request, 'blog/posts.html', {'posts': posts}): 가져온 데이터를 blog/posts.html 페이지에 {'posts': posts} posts란 변수명으로 전달합니다.

데이터 표시

이제 위에서 전달한 데이터를 화면에 표시해 봅시다. blog/templates/blog/posts.html 파일을 열고 아래와 같이 수정합니다.


<html>
  <head><title>Hello World</title></head>
  <body>
    {% for post in posts %}
    <div>
      <h1>
        {{ post.title }}<small>(published: {{ post.published_at }})</small>
      </h1>
      <p>{{ post.content | linebreaksbr }}</p>
    </div>
    {% endfor %}
  </body>
</html>

  • {% for post in posts %}...{% endfor %}: 위에서 blog/views.py에서 전달한 posts 데이터 리스트에서 post로 데이터를 하나씩 가져와 반복문을 돌립니다.
  • {{ post.title }}<small>(published: {{ post.published_at }})</small>: post 데이터의 title / published_at 필드값을 표시합니다.
  • <p>{{ post.content | linebreaksbr }}</p>: 우리가 가지고 있는 post 데이터의 content를 가져와 줄바꿈(\n)을 줄바꿈 태그(<br/>)로 변환하여 표시합니다.

위와 같이 코드를 수정하고 http://127.0.0.1:8000/에 접속하면 아래와 같이 데이터가 표시되는 것을 확인할 수 있습니다.

장고(django)의 뷰(View)를 활용한 데이터 표시

조금 더 연습을 하기 위해 상세 페이지를 제작해 봅시다. 위에서 만든 blog/templates/blog/posts.html 파일을 아래와 같이 수정합니다.


<html>
  <head><title>Hello World</title></head>
  <body>
    {% for post in posts %}
    <a href="{% url 'post_detail' id=post.id %}"> <!-- <<<<<<<<<<<<<<<<<<<<< here -->
      <h1>
        {{ post.title }}<small>(published: {{ post.published_at }})</small>
      </h1>
      <p>{{ post.content | linebreaksbr }}</p>
    </a> <!-- <<<<<<<<<<<<<<<<<<<<< here -->
    {% endfor %}
  </body>
</html>

  • {% url 'post_detail' id=post.id %}: 우리가 정의한 django_exercise/urls.pyblog/urls.py에서 post_detail이라는 이름(name)을 찾고 해당 URL로 변경해 줍니다. 이때 id라는 파라메터를 만들고 Post의 id를 넣어줍니다.

아직은 post_detail과 매칭되는 URL을 만들지 않았습니다. 이제 post_detail과 대응하는 URL과 뷰(View)를 제작해 봅시다. blog/templates/blog/post_detail.html을 생성하고 아래와 같이 수정합니다.


<html>
  <head><title>Hello World</title></head>
  <body>
    <a href="{% url 'posts' %}">
      Post List
    </a>
    <h1>
      {{ post.title }}
    </h1>
    <p>created: {{ post.created_at }}</p>
    <p>updated: {{ post.updated_at }}</p>
    <p>published: {{ post.published_at }}</p>
    <p>author: {{ post.author.username }}</p>
    <p>{{ post.content | linebreaksbr }}</p>
  </body>
</html>

  • {% url 'posts' %}: URL 파일에서 posts라는 이름(name)을 갖는 URL을 찾고 그 URL로 대체합니다.
  • author: {{ post.author.username }}: 우리는 우리가 만든 Post 모델과 장고(django)에서 기본적으로 제공하는 auth.User과 연결하여 작성자(author) 필드를 만들었습니다. 이렇게 연결한 모델(Models)에서 username을 가져와 표시하였습니다.

이제 화면에 표시할 페이지는 준비되었습니다. blog/urls.py를 열고 아래와 같이 수정합니다.

from django.urls import path
from . import views

urlpatterns = [
    path('', views.posts, name='posts'),
    path('post/<int:id>/', views.post_detail, name='post_detail'), # <<<<<<<<<<<< here
]
  • post/<int:id>/: URL에 파라메터로 넘어온 숫자형 데이터 id를 표시합니다.
  • name='post_detail': URL의 이름은 post_detail이며 views.post_detail와 매핑되어있습니다.

이로써 {% url 'post_detail' id=post.id %}을 사용하여 post/<int:id>/ URL을 표시하고 views.post_detail과 매핑 시킬 준비가 되었습니다. 이제 blog/views.py 파일을 열고 아래와 같이 수정합니다.

from django.shortcuts import render, get_object_or_404 # <<<<<<<<<<<< here
from .models import Post


def posts(request):
    posts = Post.objects.filter(
        published_at__isnull=False).order_by('-published_at')
    return render(request, 'blog/posts.html', {'posts': posts})

# <<<<<<<<<<<< here
def post_detail(request, id):
    post = get_object_or_404(Post, id=id)
    return render(request, 'blog/post_detail.html', {'post': post})
  • get_object_or_404: 장고에서 기본적으로 제공하는 함수를 추가하였습니다. 이 함수는 해당 객체(Object)에서 데이터를 가져오는데 데이터가 없으면 404 에러를 발생시키는 함수입니다.
  • def post_detail(request, id):: 새롭게 추가한 URL과 매핑되는 함수입니다. 파라메터로 넘겨준 id를 별도의 변수로 전달받을 수 있습니다.

이것으로 상세 페이지를 제작해 보았습니다. http://127.0.0.1:8000으로 접속하면 이전과는 다르게 아래와 같이 링크가 걸린 페이지를 볼 수 있습니다.

장고(django)의 모델(Models) 상세 페이지 링크

해당 링크를 누르면 상세페이지 URL(http://127.0.0.1:8000/post/2/)로 이동하며 아래와 같이 Post의 상세 페이지를 확인할 수 있습니다.

장고(django)의 모델(Models) 상세 페이지

템플릿 파일

이제 뷰(View)와 URL, 그리고 데이터를 표시하는 방법은 어느정도 익숙해진거 같습니다. 하지만 HTML 파일에 중복되는 코드가 많습니다. 아직 css, jsmeta 태그를 넣지 않았지만, 이런 태그들이 많아지면 점점 중복되는 코드들이 많아지기 시작합니다. 이런 중복되는 부분을 새로운 파일로 만들고 가져오는 방법에 대해서 알아보도록 하겠습니다.

일단 정적 파일(css, js 등)을 추가하는 방법에 대해서 알아보겠습니다. blog/static/css/main.css 파일을 생성하고 아래와 같이 수정합니다.

h1 {
  color: red;
}

여기에서는 디자인에 관해서는 다루지 않겠습니다. 더 이쁘게 꾸미고 싶으신 분들은 이 파일을 수정하시기 바랍니다. 장고(django)는 static이라는 이름의 폴더를 자동으로 찾아 등록하기 때문에 추가적인 등록 과정은 필요하지 않습니다.

이제 blog/templates/blog/layout.html 파일을 생성하고 아래와 같이 수정합니다.


{% load static %}
<html>
  <head>
    <title>Hello World</title>
    <link rel="stylesheet" href="{% static 'css/main.css' %}" />
  </head>
  <body>
    {% block content %} {% endblock %}
  </body>
</html>

  • {% load static %}: 이 파일에서 static 폴더를 사용할 수 있도록 static 폴더를 불러옵니다.
  • <link rel="stylesheet" href="{% static 'css/main.css' %}" />: 우리가 불러온 static 폴더에서 css/main.css를 찾아 URL로 변경합니다.
  • {% block content %} {% endblock %}: 이 레이아웃(layout) 파일에 필요한 데이터(content)를 표시할 위치를 설정합니다. 이렇게 표시하고 싶은 부분을 block [name]으로 설정하고 해당 블록(block)을 대체할 부분을 [name]을 사용하여 대체합니다.

이제 레이아웃(layout) 파일을 활용하기 위해 blog/templates/blog/posts.html 파일을 열고 아래와 같이 수정합니다.


{% extends 'blog/layout.html' %}

{% block content %}
  {% for post in posts %}
    <a href="{% url 'post_detail' id=post.id %}">
      <h1>
        {{ post.title }}<small>(published: {{ post.published_at }})</small>
      </h1>
      <p>{{ post.content | linebreaksbr }}</p>
    </a>
  {% endfor %}
{% endblock %}

  • {% extends 'blog/layout.html' %}: 이 파일(posts.html)은 우리가 만든 레이아웃(layout.html) 파일을 활용합니다.
  • {% block content %}...{% endblock %}: 우리가 레이아웃(layout.html) 파일에 만든 블록(block), 그중에 이름이 content인 블록(block)을 여기에 작성한 내용으로 대체합니다.

그리고 blog/templates/blog/post_detail.html 파일을 열고 아래와 같이 수정합니다.


{% extends 'blog/layout.html' %}

{% block content %}
  <a href="{% url 'posts' %}">
    Post List
  </a>
  <h1>
    {{ post.title }}
  </h1>
  <p>created: {{ post.created_at }}</p>
  <p>updated: {{ post.updated_at }}</p>
  <p>published: {{ post.published_at }}</p>
  <p>author: {{ post.author.username }}</p>
  <p>{{ post.content | linebreaksbr }}</p>
{% endblock %}

위에서 설명한 내용과 동일하게 레이아웃(layout.html) 파일을 활용하고 이름이 content인 블록(block)을 여기에 작성한 내용으로 대체합니다.

확인

실제 작업한 내용을 확인하기 위해 http://127.0.0.1:8000/에 접속해 봅니다. 그러면 우리가 작성한 css가 적용된 화면을 아래와 같이 볼 수 있습니다.(화면이 제대로 표시되지 않는 다면 테스트 서버를 재실행 해 보세요.)

장고(django)의 템플릿 활용 - 메인 페이지

링크를 눌러 상세 페이지(http://127.0.0.1:8000/post/2/)에 이동해도 아래와 같이 css가 적용된 것을 확인할 수 있습니다.

장고(django)의 템플릿 활용 - 상세 페이지

이것으로 우리는 장고(django)의 템플릿을 활용하여 정적 파일(css, js 등)을 가져오는 방법과 HTML의 중복 줄이기 위한 방법에 대해서 알아보았습니다.

완료

이번 블로그에서는 장고(django)의 뷰(View)를 활용하는 전박적인 방법에 대해서 살펴보았습니다. 데이터를 가져와 화면에 출력하고, 출력하는 화면을 잘 관리하기 위한 장고(django)의 템플릿도 확인해 보았습니다. 또한 css와 js같은 정적 파일(static)을 가져오는 방법에 대해서도 알아 보았습니다. 이제 우리는 이것들을 활용하여 일반적인 웹 서비스 사이트를 제작할 수 있게 되었습니다!

제 블로그가 도움이 되셨나요? 하단의 댓글을 달아주시면 저에게 큰 힘이 됩니다!

앱 홍보

책 홍보

블로그를 운영하면서 좋은 기회가 생겨 책을 출판하게 되었습니다.

아래 링크를 통해 제가 쓴 책을 구매하실 수 있습니다.
많은 분들에게 도움이 되면 좋겠네요.

스무디 한 잔 마시며 끝내는 React Native, 비제이퍼블릭
스무디 한 잔 마시며 끝내는 리액트 + TDD, 비제이퍼블릭
[심통]현장에서 바로 써먹는 리액트 with 타입스크립트 : 리액트와 스토리북으로 배우는 컴포넌트 주도 개발, 심통
Posts