포트폴리오 페이지 생성¶
포트폴리오 페이지는 이력서 또는 CV(Curriculum Vitae)가 있는 웹 페이지입니다. 이 페이지는 잠재 고용주에게 작업 경험을 검토할 기회를 제공합니다.
이 튜토리얼에서는 Wagtail StreamField를 사용하여 포트폴리오 사이트에 포트폴리오 페이지를 추가하는 방법을 보여줍니다.
먼저 StreamField가 무엇인지 설명하겠습니다.
StreamField란?¶
StreamField는 개발자가 잘 구조화된 데이터를 가질 필요성과 콘텐츠 제작자가 콘텐츠를 생성하고 구성하는 방식에 편집의 유연성을 가질 필요성 사이의 균형을 맞추기 위해 생성된 기능입니다.
기존 콘텐츠 관리 시스템에서는 구조화된 콘텐츠와 편집자에게 유연한 레이아웃을 만들 자유를 부여하는 것 사이에 종종 타협이 있습니다. 일반적으로 리치 텍스트 필드는 콘텐츠 제작자에게 유연하고 다재다능한 콘텐츠를 만드는 데 필요한 도구를 제공하는 데 사용됩니다. 리치 텍스트 필드는 서식 지정을 위한 WYSIWYG 편집기를 제공할 수 있습니다. 그러나 리치 텍스트 필드에는 제한 사항이 있습니다.
리치 텍스트 필드의 한계 중 하나는 의미론적 가치의 손실입니다. 콘텐츠의 의미론적 가치는 콘텐츠의 구조와 마크업에 의해 전달되는 기본 의미 또는 정보를 나타냅니다. 콘텐츠에 의미론적 가치가 없으면 의도된 의미나 목적을 파악하기가 더 어려워집니다. 예를 들어, 편집자가 리치 텍스트 필드를 사용하여 텍스트 스타일을 지정하거나 멀티미디어를 삽입할 때 콘텐츠가 의미론적으로 그렇게 표시되지 않을 수 있습니다.
따라서 StreamField는 편집자에게 더 많은 유연성을 제공하고 리치 텍스트 필드의 한계를 해결합니다. StreamField는 콘텐츠를 블록 시퀀스로 처리하는 다재다능한 콘텐츠 관리 솔루션입니다. 각 블록은 단락, 이미지, 지도와 같은 다양한 콘텐츠 유형을 나타냅니다. 편집자는 이러한 블록을 배열하고 사용자 정의하여 복잡하고 유연한 레이아웃을 만들 수 있습니다. 또한 StreamField는 다양한 콘텐츠 유형의 의미론적 의미를 캡처할 수 있습니다.
재사용 가능한 사용자 지정 블록 생성¶
이제 StreamField가 무엇인지 알았으니, 이를 사용하여 포트폴리오 사이트에 포트폴리오 페이지를 추가하는 방법을 안내해 드리겠습니다.
다음 명령을 실행하여 포트폴리오 사이트에 새 앱을 추가하여 시작합니다:
python manage.py startapp portfolio
새 포트폴리오 앱을 설치하려면 mysite/settings/base.py 파일의 INSTALLED_APPS 목록에 _”portfolio”_를 추가합니다.
이제 base/blocks.py 파일을 생성하고 다음 코드 줄을 추가합니다:
from wagtail.blocks import (
CharBlock,
ChoiceBlock,
RichTextBlock,
StreamBlock,
StructBlock,
)
from wagtail.embeds.blocks import EmbedBlock
from wagtail.images.blocks import ImageBlock
class CaptionedImageBlock(StructBlock):
image = ImageBlock(required=True)
caption = CharBlock(required=False)
attribution = CharBlock(required=False)
class Meta:
icon = "image"
template = "base/blocks/captioned_image_block.html"
class HeadingBlock(StructBlock):
heading_text = CharBlock(classname="title", required=True)
size = ChoiceBlock(
choices=[
("", "제목 크기 선택"),
("h2", "H2"),
("h3", "H3"),
("h4", "H4"),
],
blank=True,
required=False,
)
class Meta:
icon = "title"
template = "base/blocks/heading_block.html"
class BaseStreamBlock(StreamBlock):
heading_block = HeadingBlock()
paragraph_block = RichTextBlock(icon="pilcrow")
image_block = CaptionedImageBlock()
embed_block = EmbedBlock(
help_text="임베드할 URL을 삽입합니다. 예: https://www.youtube.com/watch?v=SGJFWirQ3ks",
icon="media",
)
이전 코드에서 일반적인 목적의 앱에 다양한 콘텐츠 유형에 대한 재사용 가능한 Wagtail 사용자 지정 블록을 생성했습니다. 이러한 블록은 사이트 전체에서 어떤 순서로든 사용할 수 있습니다. 각 블록을 자세히 살펴보겠습니다.
먼저 CaptionedImageBlock 은 편집자가 StreamField 섹션에 이미지를 추가하는 데 사용할 수 있는 블록입니다.
class CaptionedImageBlock(StructBlock):
image = ImageBlock(required=True)
caption = CharBlock(required=False)
attribution = CharBlock(required=False)
class Meta:
icon = "image"
template = "base/blocks/captioned_image_block.html"
CaptionedImageBlock 은 StructBlock 에서 상속됩니다. StructBlock 을 사용하면 여러 자식 블록을 단일 부모 블록 아래에 그룹화할 수 있습니다. CaptionedImageBlock 에는 세 개의 자식 블록이 있습니다. 첫 번째 자식 블록인 Image 는 ImageBlock 필드 블록 유형을 사용합니다. ImageBlock 을 사용하면 편집자가 기존 이미지를 선택하거나 새 이미지를 업로드할 수 있습니다. required 인수의 값은 true 이므로 블록이 작동하려면 이미지를 제공해야 합니다. caption 및 attribution 자식 블록은 CharBlock 필드 블록 유형을 사용하며, 이미지에 캡션과 속성을 추가하기 위한 단일 줄 텍스트 입력을 제공합니다. caption 및 attribution 자식 블록은 required 속성이 false 로 설정되어 있습니다. 즉, 관리 인터페이스에서 비워둘 수 있습니다.
CaptionedImageBlock 과 마찬가지로 HeadingBlock 도 StructBlock 에서 상속됩니다. 두 개의 자식 블록이 있습니다. 살펴보겠습니다.
class HeadingBlock(StructBlock):
heading_text = CharBlock(classname="title", required=True)
size = ChoiceBlock(
choices=[
("", "제목 크기 선택"),
("h2", "H2"),
("h3", "H3"),
("h4", "H4"),
],
blank=True,
required=False,
)
class Meta:
icon = "title"
template = "base/blocks/heading_block.html"
첫 번째 자식 블록인 heading_text 는 제목 텍스트를 지정하기 위해 CharBlock 을 사용하며 필수입니다. 두 번째 자식 블록인 size 는 제목 크기를 선택하기 위해 ChoiceBlock 을 사용합니다. h2, h3, h4 옵션을 제공합니다. blank=True 와 required=False 는 관리 인터페이스에서 제목 텍스트를 선택 사항으로 만듭니다.
BaseStreamBlock 클래스는 StreamBlock 에서 상속됩니다. StreamBlock 은 프로젝트 전체의 모든 StreamField 섹션에 포함하려는 자식 블록 유형 집합을 정의합니다. 이 클래스는 StreamField를 사용하는 모든 다른 페이지 유형에 대해 재사용하고 사용자 정의할 수 있는 공통 블록의 기준 컬렉션을 제공합니다. 예를 들어, 편집자가 모든 페이지에 이미지와 단락 텍스트를 추가할 수 있도록 하는 것은 확실히 원할 것이지만, 블로그 페이지에서만 사용되는 특별한 인용구 블록을 만들고 싶을 수도 있습니다.
class BaseStreamBlock(StreamBlock):
heading_block = HeadingBlock()
paragraph_block = RichTextBlock(icon="pilcrow")
image_block = CaptionedImageBlock()
embed_block = EmbedBlock(
help_text="임베드할 URL을 삽입합니다. 예: https://www.youtube.com/watch?v=SGJFWirQ3ks",
icon="media",
)
BaseStreamBlock 에는 네 개의 자식 블록이 있습니다. heading_block 은 이전에 정의된 HeadingBlock 을 사용합니다. paragraph_block 은 서식 있는 텍스트를 만들기 위한 WYSIWYG 편집기를 제공하는 RichTextBlock 을 사용합니다. image_block 은 이전에 정의된 CaptionedImageBlock 클래스를 사용합니다. embed_block 은 비디오와 같은 외부 콘텐츠를 임베드하기 위한 블록입니다. Wagtail EmbedBlock 을 사용합니다. 사용할 수 있는 더 많은 필드 블록 유형을 찾으려면 필드 블록 유형에 대한 문서를 읽어보십시오.
또한 CaptionedImageBlock 및 HeadingBlock 블록의 Meta 클래스 내에 Meta 클래스를 정의했습니다. Meta 클래스는 블록에 대한 메타데이터를 제공하며, 관리 인터페이스에서 시각적으로 블록을 나타내는 아이콘을 포함합니다. Meta 클래스에는 CaptionedImageBlock 및 HeadingBlock 블록을 렌더링하기 위한 사용자 지정 템플릿도 포함됩니다.
참고
Wagtail은 각 블록을 렌더링하기 위한 내장 템플릿을 제공합니다. 그러나 내장 템플릿을 사용자 지정 템플릿으로 재정의할 수 있습니다.
마지막으로 CaptionedImageBlock 및 HeadingBlock 블록의 Meta 클래스에 정의한 사용자 지정 템플릿을 추가해야 합니다.
CaptionedImageBlock 의 사용자 지정 템플릿을 추가하려면 base/templates/base/blocks/captioned_image_block.html 파일을 생성하고 다음을 추가합니다:
{% load wagtailimages_tags %}
<figure>
{% image self.image fill-600x338 loading="lazy" %}
<figcaption>{{ self.caption }} - {{ self.attribution }}</figcaption>
</figure>
HeadingBlock 블록의 사용자 지정 템플릿을 추가하려면 base/templates/base/blocks/heading_block.html 파일을 생성하고 다음을 추가합니다:
{% if self.size == 'h2' %}
<h2>{{ self.heading_text }}</h2>
{% elif self.size == 'h3' %}
<h3>{{ self.heading_text }}</h3>
{% elif self.size == 'h4' %}
<h4>{{ self.heading_text }}</h4>
{% endif %}
참고
자식 블록에 대한 사용자 지정 템플릿을 만들 수도 있습니다. 예를 들어, embed_block 에 대한 사용자 지정 템플릿을 만들려면 base/templates/base/blocks/embed_block.html 파일을 생성하고 다음을 추가합니다:
{{ self }}
포트폴리오 앱에서 생성한 블록 사용¶
일반적인 목적의 base 앱에서 생성한 재사용 가능한 사용자 지정 블록을 사이트 전체에서 사용할 수 있습니다. 그러나 사용하려는 앱의 blocks.py 파일에 사용하려는 블록을 정의하는 것이 일반적입니다. 그런 다음 앱의 blocks.py 파일에서 블록을 가져와 models.py 파일에서 사용할 수 있습니다.
이제 portfolio/blocks.py 파일을 생성하고 사용하려는 블록을 다음과 같이 가져옵니다:
from base.blocks import BaseStreamBlock
class PortfolioStreamBlock(BaseStreamBlock):
pass
이전 코드는 BaseStreamBlock 에서 상속되는 PortfolioStreamBlock 이라는 사용자 지정 블록을 정의합니다. pass 문은 시작점을 나타냅니다. 튜토리얼 뒷부분에서 PortfolioStreamBlock 에 사용자 지정 블록 정의 및 구성을 추가할 것입니다.
이제 portfolio/models.py 파일에 다음을 추가합니다:
from wagtail.models import Page
from wagtail.fields import StreamField
from wagtail.admin.panels import FieldPanel
from portfolio.blocks import PortfolioStreamBlock
class PortfolioPage(Page):
parent_page_types = ["home.HomePage"]
body = StreamField(
PortfolioStreamBlock(),
blank=True,
use_json_field=True,
help_text="이 섹션을 사용하여 프로젝트 및 기술을 나열하십시오.",
)
content_panels = Page.content_panels + [
FieldPanel("body"),
]
이전 코드에서 PortfolioPage 라는 Wagtail Page 를 정의했습니다. parent_page_types = ["home.HomePage"] 는 포트폴리오 페이지가 홈페이지의 자식 페이지로만 존재할 수 있음을 지정합니다. body 필드는 StreamField 이며, portfolio/blocks.py 파일에서 가져온 PortfolioStreamBlock 사용자 지정 블록을 사용합니다. blank=True 는 관리 인터페이스에서 이 필드를 비워둘 수 있음을 나타냅니다. help_text 는 편집자를 안내하기 위한 필드에 대한 간략한 설명을 제공합니다.
다음 단계는 PortfolioPage 에 대한 템플릿을 만드는 것입니다. 이를 위해 portfolio/templates/portfolio/portfolio_page.html 파일을 생성하고 다음을 추가합니다:
{% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags %}
{% block body_class %}template-portfolio{% endblock %}
{% block content %}
<h1>{{ page.title }}</h1>
{{ page.body }}
{% endblock %}
이제 python manage.py makemigrations 를 실행한 다음 python manage.py migrate 를 실행하여 데이터베이스를 마이그레이션합니다.
더 많은 사용자 지정 블록 추가¶
PortfolioPage 의 본문에 더 많은 사용자 지정 블록을 추가하려면 portfolio/blocks.py 파일을 수정합니다:
# CharBlock, ListBlock, PageChooserBlock, PageChooserBlock, RichTextBlock, StructBlock 가져오기:
from wagtail.blocks import (
CharBlock,
ListBlock,
PageChooserBlock,
RichTextBlock,
StructBlock,
)
# ImageBlock 가져오기:
from wagtail.images.blocks import ImageBlock
from base.blocks import BaseStreamBlock
# CardBlock 추가:
class CardBlock(StructBlock):
heading = CharBlock()
text = RichTextBlock(features=["bold", "italic", "link"])
image = ImageBlock(required=False)
class Meta:
icon = "form"
template = "portfolio/blocks/card_block.html"
# FeaturedPostsBlock 추가:
class FeaturedPostsBlock(StructBlock):
heading = CharBlock()
text = RichTextBlock(features=["bold", "italic", "link"], required=False)
posts = ListBlock(PageChooserBlock(page_type="blog.BlogPage"))
class Meta:
icon = "folder-open-inverse"
template = "portfolio/blocks/featured_posts_block.html"
class PortfolioStreamBlock(BaseStreamBlock):
# pass 문 삭제
card = CardBlock(group="섹션")
featured_posts = FeaturedPostsBlock(group="섹션")
이전 코드에서 CardBlock 에는 heading, text, image 세 개의 자식 블록이 있습니다. 자식 페이지에서 사용되는 필드 블록 유형에 이미 익숙할 것입니다.
그러나 FeaturedPostsBlock 에서 자식 블록 중 하나인 posts 는 ListBlock 을 사용합니다. ListBlock 은 동일한 유형의 여러 하위 블록에 사용할 수 있는 구조적 블록 유형입니다. PageChooserBlock 과 함께 사용하여 블로그 페이지 유형 페이지만 선택했습니다. 구조적 블록 유형을 더 잘 이해하려면 구조적 블록 유형 문서를 읽어보십시오.
또한 icon = "form" 및 icon = "folder-open-inverse" 는 관리 인터페이스에서 블록을 구별하기 위한 사용자 지정 블록 아이콘을 정의합니다. 블록 아이콘에 대한 자세한 내용은 블록 아이콘에 대한 문서를 읽어보십시오.
card = CardBlock(group="Sections") 및 featured_posts = FeaturedPostsBlock(group="Sections") 에서 group="Sections" 를 사용하여 card 및 featured_posts 자식 블록을 section 이라는 범주 내에 함께 분류했습니다.
다음 단계가 무엇인지 아실 것입니다. CardBlock 및 FeaturedPostsBlock 에 대한 템플릿을 생성해야 합니다.
CardBlock 에 대한 템플릿을 만들려면 portfolio/templates/portfolio/blocks/card_block.html 파일을 생성하고 다음을 추가합니다:
{% load wagtailcore_tags wagtailimages_tags %}
<div class="card">
<h3>{{ self.heading }}</h3>
<div>{{ self.text|richtext }}</div>
{% if self.image %}
{% image self.image width-480 %}
{% endif %}
</div>
featured_posts_block 에 대한 템플릿을 만들려면 portfolio/templates/portfolio/blocks/featured_posts_block.html 파일을 생성하고 다음을 추가합니다:
{% load wagtailcore_tags %}
<div>
<h2>{{ self.heading }}</h2>
{% if self.text %}
<p>{{ self.text|richtext }}</p>
{% endif %}
<div class="grid">
{% for page in self.posts %}
<div class="card">
<p><a href="{% pageurl page %}">{{ page.title }}</a></p>
<p>{{ page.specific.date }}</p>
</div>
{% endfor %}
</div>
</div>
마지막으로 python manage.py makemigrations 를 실행한 다음 python manage.py migrate 를 실행하여 변경 사항을 마이그레이션합니다.
이력서 추가¶
포트폴리오 사이트에 이력서를 추가하려면 다음 단계를 따르십시오:
다음 단계를 따라 홈의 자식 페이지로 포트폴리오 페이지를 생성합니다:
a. 서버를 다시 시작합니다. b. 관리 인터페이스로 이동합니다. c. 사이드바에서
페이지를 클릭합니다. d.홈을 클릭합니다. e. 결과 페이지 상단의+아이콘(자식 페이지 추가)을 클릭합니다. f.포트폴리오 페이지를 클릭합니다.다음 단계를 따라 이력서 데이터를 추가합니다: a. 페이지 제목으로 “이력서”를 사용합니다. b. 본문 섹션을 확장하려면 **+**를 클릭합니다. c. 단락 블록을 클릭합니다. d. 새 단락 블록에 다음 텍스트를 복사하여 붙여넣습니다:
저는 복잡한 웹 애플리케이션을 개발하고 유지 관리한 입증된 실적을 가진 Wagtail 개발자입니다. Wagtail 애플리케이션을 확장하기 위한 사용자 지정 코드를 작성하고, 다른 개발자와 협력하며, 타사 서비스 및 API를 통합한 경험이 있습니다.
e. 이전 단락 블록 아래의 **+**를 클릭한 다음 단락 블록을 클릭하여 새 단락 블록을 추가합니다. f. 새 단락 블록의 입력 필드에 “/”를 입력한 다음 H2 제목 2를 클릭합니다. g. 제목 2로 “경력”을 사용합니다. h. 제목 2 아래에 “/”를 입력하고 H3 제목 3을 클릭합니다. i. 다음과 같이 제목 3을 사용합니다:
영국 Birdwatchers Inc.의 Wagtail 개발자
j. 제목 3 뒤에 다음을 입력합니다:
2022년 1월 ~ 2023년 11월 - Wagtail을 사용하여 복잡한 웹 애플리케이션을 개발하고 유지 관리하여 첫 해에 사용자 참여율 25% 증가 및 수익 20% 증가를 달성했습니다. - Wagtail 애플리케이션을 확장하기 위한 사용자 지정 코드를 작성하여 개발 시간 30% 단축 및 전반적인 코드 품질 15% 향상을 달성했습니다. - 다른 개발자, 디자이너 및 이해 관계자와 협력하여 타사 서비스 및 API를 통합하여 애플리케이션 기능 및 사용자 만족도 40% 증가를 달성했습니다. - 기술 문서를 작성하고 코드 검토에 참여하여 다른 개발자에게 피드백을 제공하고 전반적인 코드 품질을 20% 향상시켰습니다.
참고
문장을 “-“로 시작하면 작업 경험을 글머리 기호 목록으로 작성하는 것입니다. 단락 블록의 입력 필드에 “/”를 입력한 다음 글머리 기호 목록을 클릭하여 동일한 결과를 얻을 수 있습니다.
k. 경력 아래의 **+**를 클릭합니다. l. 단락 블록을 클릭하여 다른 단락 블록을 추가합니다. m. 새 단락 블록의 입력 필드에 “/”를 입력한 다음 H2 제목 2를 클릭합니다. n. 새 단락 블록의 제목 2로 “기술”을 사용합니다. o. 제목 2 뒤에 다음을 복사하여 붙여넣습니다:
Python, Django, Wagtail, HTML, CSS, Markdown, 오픈 소스 관리, Trello, Git, GitHub
포트폴리오 페이지를 게시합니다.
축하합니다! 🎉 이제 Wagtail StreamField로 복잡하고 유연한 레이아웃을 만드는 방법을 이해했습니다. 이 튜토리얼의 다음 섹션에서는 사이트에 검색 기능을 추가하는 방법을 배웁니다.