본문 바로가기
Django/Django개념

마케터를 위한 CMS만들기

by hyun-am 2021. 12. 31.

CMS를 만든 이유

처음에 장고로 모바일뷰를 마케터가 직접 조작하면서 웹을 생성할수 있는 페이지를 만들 수 있냐는 요구사항이 들어왔었습니다. 처음 들었을 때는 막막했는데 먼저 이런 페이지가 어떤 페이지인지 명칭이 뭔지 찾아봤는데 CMS라는 기술로 웹사이트 웹 저작물들을 발행할 때 매우 수월하게 도와주며 비슷한 규격인 페이지를 쉽게 만들 수 있게 도와주는 서비스입니다.

Django로 만들 수 있는 CMS 서비스들의 특징들

Django CMS

Django CMS는 “기한이 있는 완벽주의자를 위한” 웹 애플리케이션 프레임워크인 Django로 구축된 최신 웹 퍼블리싱 플랫폼입니다.

Django CMS는 CMS에서 기대할 수 있는 일반적인 기능에 대한 기본 지원을 제공하지만 개발자가 쉽게 사용자 정의하고 확장하여 정확한 요구사항에 맞는 사이트를 만들 수도 있습니다.

공식 문서 : https://docs.django-cms.org/en/latest/index.html

Django Wagtail

StreamField

이 필드를 이용하면 , rich, long-form을 만들 수 있고, 간단한 에디터를 사용할 수 있습니다.

  • Classy implementation
    • StreamField를 사용하면 editor에서 콘텐츠 유형의 혼합 시퀀스를 작성할 수 있습니다. 이것은 데이터베이스의 단일 JSON 필드로 직렬화되며 HTML 표현을 사용하여 template에 내용을 출력하는 간단한 방법을 제공합니다.
  • Simple editing
    • 이러한 블록 타입을 equal citizens로 처리함으로써 주로 텍스트용으로 설계된 인터페이스로 이동하지 않고 각 블록에 대한 편집 컨트롤을 필요에 따라 단순하거나 복잡하게 만들 수 있습니다.
  • Content economy
    • 일단 풍부한 텍스트 영역 마인드셋을 벗어나면, 이것을 통해 더 장리할 수 있는 방법을 찾을 수 있습니다.

Localization

  • Entry-level localization
    • 언어 및 국가에 따라 다른 사이트로 로컬라이즈 해줍니다.
    • 직관적인 UI
    • wagtail에서 직접 콘텐츠 번역

Elasticsearch

  • Elasticsearch는 Wagtail과의 간단한 통합과 뛰어난 성능을 갖춘 우수한 오픈 소스 검색 엔진 입니다.
  • 이것을 통해 Wagtail또한 검색 인터페이스를 통해 이루어진 쿼리에 대한 간단한 통계를 수집합니다. 관리 인터페이스 또한 페에지, 이미지 및 문서에 대한 빠르고 원활한 액세스를 제공하기 위해 광범위한 검색을 사용합니다.

Wagtail로 선택한 이유

탄탄한 공식 문서 및 튜토리얼

먼저 wagtail로 선택한 이유는 공식문서가 쉽고 이해하기 편하게 구성되어 있고 장고 지식을 조금만 알아도 쉽게 만들 수 있기 때문입니다.

youtube에 있는 튜토리얼 영상

문서로만 빠른 시간내로 이해하는데는 한계가 있는데 wagtail은 youtube에 친절하게 따라할 수 있는 프로젝트가 있었습니다.

요구사항 처리가능

요구 사항 중에 아래와 같은 내용들이 있었는데 이것을 만족하는 것들이 wagtail이였습니다.

  • url을 마케터가 쉽게 바꿀 수 있나? (o)
  • 다양한 블록을 사용해서 이미지와 버튼을 쉽게 넣을수 있는가? (o)
  • 페이지 끼리 연동이 쉽나? (o)
  • 쉽게 페이지 한개를 생성할 수 있나? (o)
  • 개발자도 1~2주내로 학습해서 쉽게 구현할 수 있는가? (o)

주의사항

wagtail을 선택한 것은 저의 개인적인 생각이므로 django cms랑 비교해서 너무 wagtail만 옳다는 생각하지는 말고 두개를 비교하면서 어느게 본인에게 잘 맞는지 또한 어느것이 자신의 핏과 맞는지 확인하고 선택해서 개발을 진행하시길 바랍니다.😀😀

Django Wagtail 시작하기

먼저 시작하기전에 위에 튜토리얼을 어느정도 시청했다는 전제하에 설명을 진행하겠습니다.

필요한 Blcok 및 App들

Block

  • AppLinkBlock : 앱 또는 외부 페이지로 가기 위한 Block
  • TitleAndTextBlock : 제목과 부제목 또는 앱에서 빈공백을 위한 Block
  • FAQBlock : 질문을 클릭하면 답변을 쉽게 볼 수 있게 도와주는 Block
  • RichTextBlock : RichText를 사용할 수 있게 도와주는 Block
  • ImageBlock : 이미지를 사용할 수 있게 도와주는 Block
  • ManyImageBlock : carousel image를 사용하기위해 도와주는 Block
  • YoutubeVideoBlock : youtube동영상을 넣을수 있게 도와주는 Block

APP

  • home : 프로젝트 생성시 기본적으로 생성되는 앱입니다
  • flex : 자유로운 페이지를 만들기 위한 앱입니다.
  • notice : 공지사항을 관리하기 위한 앱입니다.
  • streams : streamfield에 들어갈 blocks들을 세팅하기 위한 app

프로젝트 세팅하기(Flex Page까지만)

먼저 wagtail프로젝트를 생성하겠습니다.

pip install wagtail
mkdir cms
cd cms
wagtail start config .

cms라는 디렉터리를 만든 후 그곳에서 작업을 진행하겠습니다.

그러면 다음과 같이 생성 된 것을 확인할 수 있습니다.

그 후 다음과 같은 명령어를 입력하겠습니다.

python manage.py migrate
python manage.py runserver

그러면 장고에서 항상 나오던 로켓이 아닌 무슨 신기한 알이 나오는 것을 확인할 수 있습니다. 그러면 기본 세팅은 끝이 났습니다.

이제 wagtail은 대부분의 페이지들을 admin에서 생성합니다. 따라서 django에서 admin을 생성해줄 때 사용하는 명령어를 통해 관리자 계정을 생성하겠습니다.

python manage.py createsuperuser

그 후 admin페이지에 접속한 후 로그인이 잘 동작되는지 확인하겠습니다.

url : localhost:8000/admin

Flex, Streams 앱 생성하기

django-admin startapp flex
django-admin startapp streams

그러면 flex와 streams디렉터리가 잘 생성된 것을 확인할 수 있습니다. 그 후 django에서 app등록한 것 처럼 config에 있는 base.py에 들어가서 INSTALLED_APPS에 streams와 flex를 추가하겠습니다.

INSTALLED_APPS = [
    "home",
    "search",
    "streams",
    "flex",
    ...
    ...
]

먼저 streamfield에 들어갈 blocks들을 만들어주기위해 streams앱에 blocks.py라는 파일을 생성하겠습니다.

그 후 다음과 같이 코드를 작성하겠습니다.

from wagtail.core import blocks
from wagtail.images.blocks import ImageChooserBlock

class AppLinkBlock(blocks.StructBlock):
    link = blocks.CharBlock(required=True, help_text="앱 링크 작성")
    button_title = blocks.CharBlock(required=False, help_text="링크 타이틀 작성")
    image = ImageChooserBlock(required=False, help_text="버튼 이미지")
    is_fixed = blocks.BooleanBlock(
        required=False,
        help_text="버튼 플로팅 여부",
        default=False,
    )
    is_image = blocks.BooleanBlock(
        required=False, help_text="버튼 이미지 사용 여부", default=False
    )

    class Meta:
        template = "streams/link_block.html"
        icon = "edit"
        label = "app link"

class TitleAndTextBlock(blocks.StructBlock):
    """
    Title and text and nothing else
    """

    title = blocks.CharBlock(required=False, help_text="Add yourt title")
    text = blocks.TextBlock(required=False, help_text="Add yourt text")

    class Meta:
        template = "streams/title_and_text_block.html"
        icon = "edit"
        label = "Title & Text"

class FAQBlock(blocks.StructBlock):
    """
    Title and text and nothing else
    """

    title = blocks.CharBlock(required=False, help_text="Add yourt title")
    text = blocks.RichTextBlock(blank=True)

    class Meta:
        template = "streams/faq_block.html"
        icon = "edit"
        label = "FAQ "

class RichTextblock(blocks.RichTextBlock):
    """ㄱ
    Richtext with all the features
    """

    class Meta:
        template = "streams/richtext_block.html"
        icon = "doc-full"
        label = "Full RichText"

class ImageBlock(blocks.StructBlock):
    image = ImageChooserBlock()

    class Meta:
        template = "streams/image_block.html"
        icon = "placeholder"
        label = "이미지"

class ManyImageBlock(blocks.StructBlock):
    """
    cards with image and text and button(s).
    """

    title = blocks.CharBlock(required=True, help_text="add your title")

    images = blocks.ListBlock(
        blocks.StructBlock(
            [
                ("image", ImageChooserBlock(required=True)),
            ]
        )
    )

    class Meta:
        template = "streams/many_image_block.html"
        icon = "placeholder"
        label = "ManyImage"

class YoutubeVideoBlock(blocks.StructBlock):
    """
    cards with image and text and button(s).
    """

    youtube_id = blocks.CharBlock(required=True, help_text="유튜브 video id 입력")

    class Meta:
        template = "streams/youtube_block.html"
        icon = "edit"
        label = "youtube video"

그 후 templates폴더 아래에 streams를 생성한 다음 필요한 템플릿들을 생성해주면 되겠습니다. 여기서 한개만 예시를 들겠습니다.

streams/templates/streams/link_block.html

{% load wagtailimages_tags %}
{% if self.is_fixed %}
<style>
  #under {
    position: fixed;
    bottom: 0;
    font-weight: bold;
    width: 100%;
  }
</style>
{% endif %}

<div class="d-grid gap-2" {% if self.is_fixed %}id="under"{% endif %}>
  {% if self.is_image %}
  {% image self.image original as img %}
  <a href="{{ self.link }}">
    <img src="{{ img.url }}" class="d-block mh-50 w-100" alt="{{ img.alt }}">
  </a>
  {% else %}
  <a class="btn btn-primary" href="{{ self.link }}" role="button" style="background-color:#393f7b;border-color:#393f7b;">{{ self.button_title }}</a>
  {% endif %}
</div>

이렇게 self를 통해 block에 선언해 준 것들을 불러올 수 있습니다. 이제 이런식으로 만들어진 block들을 flex에서 불러오겠습니다.

그리고 wagtail은 views.py가 아닌 models.py에서 대부분 작업을 진행합니다. 마찬가지로 flex앱에서도 models.py에서 진행하겠습니다.

from django.db import models
from wagtail.core.fields import StreamField
from wagtail.admin.edit_handlers import FieldPanel, StreamFieldPanel
from wagtail.core.models import Page
from wagtail.images.blocks import ImageChooserBlock

from streams import blocks

# Create your models here.

class FlexPage(Page):
    template = "flex/flex_page.html"
    subtitle = models.CharField(max_length=100, null=True, blank=True)

    content = StreamField(
        [
            ("title_and_text", blocks.TitleAndTextBlock()),
            ("full_richtext", blocks.RichTextblock()),
            ("app_button", blocks.AppLinkBlock()),
            ("image", blocks.ImageBlock()),
            ("youtube_id", blocks.YoutubeVideoBlock()),
            ("images", blocks.ManyImageBlock()),
						("faq", blocks.FAQBlock()),
        ],
        null=True,
        blank=True,
    )

    content_panels = Page.content_panels + [
        FieldPanel("subtitle"),
        StreamFieldPanel("content"),
    ]

    class Meta:
        verbose_name = "Flex Page"
        verbose_name_plural = "Flex Pages"

그 후 flex도 마찬가지로 template에 html을 생성해줘야하는데 block을 자유자재로 사용하게 다음과 같이 생성하면 되겠습니다.

flex/templates/flex/flex_page.html

{% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags %}

{% block content %}
    {% if self.subtitle %}
    <div>
        {{ self.subtitle }}
    </div>
    {% endif %}
    {% for block in page.content %}
        {% include_block block %} 
    {% endfor %}

{% endblock %}

이제 makemigrations와 migrate를 통해 적용하겠습니다.

python manage.py makemigrations flex
python manage.py migrate flex

페이지 생성하기

 

위에 그림과 같이 따라하면 다음과 같이 Flex Page가 생긴 것을 확인할 수 있습니다. 여기서 FlexPage에 들어가면 다음과 같이 편집할 수 있는 공간이 나오는 것을 확인할 수 있습니다.

이런식으로 streamsfields에서 생성한 것들로 자유롭게 편집할 수 있습니다. Content에 있는 항목들은 원하는 만큼 계속 생성할 수 있습니다.

또한 PROMOTE탭에 들어가서 페이지에 링크도 직접 수정할 수 있습니다.

Django Wagtail 결과물

'Django > Django개념' 카테고리의 다른 글

Django multiple DB 문서정리  (0) 2022.12.19
django transaction(장고 트랜잭션)  (0) 2022.08.25
Django Channel Tutorial Part 1  (0) 2021.07.28
왜 Django에서 PostgreSQL을 DB로 사용할까?  (0) 2020.12.01
models.py 살펴보기  (0) 2020.11.18

댓글