AMP 지원 사이트를 구축하는 방법

이 문서는 AMP 버전의 Wagtail 사이트를 만들고 URL 접두사를 사용하여 사이트의 나머지 부분과 별도로 호스팅하는 방법을 설명합니다. 또한 사용자가 사이트의 AMP 버전에서 페이지를 방문할 때 Wagtail이 <amp-img> 태그로 이미지를 렌더링하도록 하는 방법도 설명합니다.

개요

다음 섹션에서는 Wagtail의 내부 serve() 뷰를 가리키는 새로운 URL 항목을 추가하여 /amp 접두사 아래에서 전체 사이트를 다시 렌더링하는 효과를 얻을 것입니다.

그런 다음, 요청 객체 없이도 현재 요청이 사이트의 /amp 접두사 버전에 있는지 추적할 수 있는 유틸리티를 추가할 것입니다.

그 후, 템플릿에서 사이트의 어떤 버전이 렌더링되고 있는지 확인할 수 있는 템플릿 컨텍스트 프로세서를 추가할 것입니다.

마지막으로, 사이트의 AMP 버전을 렌더링할 때 {% image %} 태그의 동작을 수정하여 <amp-img> 태그를 렌더링하도록 할 것입니다.

두 번째 페이지 트리 생성하기

프로젝트 urls.py 파일에서 Wagtail URL을 복제하고 접두사를 부여하여 다른 접두사에서 전체 사이트를 렌더링할 수 있습니다. 이는 Wagtail의 기본 URL 앞에 있어야 합니다. 그렇지 않으면 페이지로 /amp 를 찾으려고 시도할 것입니다:

# <project>/urls.py

urlpatterns += [
    # 기본 ``include(wagtail_urls)`` 라인 바로 앞에 이 줄을 추가하세요
    path('amp/', include(wagtail_urls)),

    path('', include(wagtail_urls)),
]

이제 브라우저에서 http://localhost:8000/amp/ 를 열면 홈페이지가 표시될 것입니다.

페이지가 “AMP 모드”를 인식하도록 만들기

이제 모든 페이지가 /amp 접두사 아래에서 렌더링되지만, 현재 AMP 버전과 일반 버전 사이에는 차이가 없습니다.

변경하려면 페이지를 렌더링하는 데 사용된 URL을 감지하는 방법을 추가해야 합니다. 이를 위해 Wagtail의 serve() 뷰를 래핑하고 스레드-로컬을 설정하여 모든 다운스트림 코드에 AMP 모드가 활성화되어 있음을 알려야 합니다.

참고

왜 스레드-로컬인가?

(이 부분에 관심이 없다면 건너뛰어도 됩니다)

request 객체를 수정하는 것이 가장 일반적인 방법일 것입니다. 그러나 이미지 태그 렌더링은 요청에 접근할 수 없는 Wagtail의 일부에서 수행됩니다.

스레드-로컬은 각 실행 스레드마다 다른 값을 가질 수 있는 전역 변수입니다. 각 스레드는 한 번에 하나의 요청만 처리하므로, 요청 객체를 모든 곳에 전달하지 않고도 해당 요청에 특정한 데이터를 전달하는 방법으로 사용할 수 있습니다.

Django는 내부적으로 스레드-로컬을 사용하여 요청에 대해 현재 활성화된 언어를 추적합니다.

Python은 threading.local 클래스를 통해 스레드-로컬 데이터를 구현하지만, Django 3.x부터는 여러 요청이 단일 스레드에서 처리될 수 있으므로 스레드-로컬은 더 이상 단일 요청에 고유하지 않습니다. 따라서 Django는 대체품으로 asgiref.Local 을 제공합니다.

이제 스레드-로컬과 이와 상호 작용하는 몇 가지 유틸리티 함수를 만들어 보겠습니다. 이 모듈을 프로젝트의 앱에 amp_utils.py 로 저장하세요:

# <app>/amp_utils.py

from contextlib import contextmanager
from asgiref.local import Local

_amp_mode_active = Local()

@contextmanager
def activate_amp_mode():
    """
    AMP 모드를 활성화하는 컨텍스트 매니저
    """
    _amp_mode_active.value = True
    try:
        yield
    finally:
        del _amp_mode_active.value

def amp_mode_active():
    """
    AMP 모드가 현재 활성화되어 있으면 True를 반환합니다
    """
    return hasattr(_amp_mode_active, 'value')

이 모듈은 두 가지 함수를 정의합니다:

  • activate_amp_mode 는 Python의 with 구문을 사용하여 호출할 수 있는 컨텍스트 매니저입니다. with 문의 본문에서는 AMP 모드가 활성화됩니다.

  • amp_mode_active 는 AMP 모드가 활성화되었을 때 True 를 반환하는 함수입니다.

다음으로, Wagtail의 내장 serve 뷰를 래핑하고 activate_amp_mode 컨텍스트 매니저를 호출하는 뷰를 정의해야 합니다:

# <app>/amp_views.py

from django.template.response import SimpleTemplateResponse
from wagtail.views import serve as wagtail_serve

from .amp_utils import activate_amp_mode

def serve(request, path):
    with activate_amp_mode():
        response = wagtail_serve(request, path)

        # AMP 모드가 여전히 활성화된 상태에서 템플릿 응답을 렌더링합니다
        if isinstance(response, SimpleTemplateResponse):
            response.render()

        return response

그런 다음 같은 앱에 amp_urls.py 파일을 만들어야 합니다:

# <app>/amp_urls.py

from django.urls import re_path
from wagtail.urls import serve_pattern

from . import amp_views

urlpatterns = [
    re_path(serve_pattern, amp_views.serve, name='wagtail_amp_serve')
]

마지막으로, 프로젝트의 메인 urls.py 를 업데이트하여 /amp 접두사에 이 새로운 URLs 파일을 사용해야 합니다:

# <project>/urls.py

from myapp import amp_urls as wagtail_amp_urls

urlpatterns += [
    # 이 줄을 Wagtail의 urls 대신 amp_urls를 가리키도록 변경하세요
    path('amp/', include(wagtail_amp_urls)),

    re_path(r'', include(wagtail_urls)),
]

이 후에는 사이트의 AMP 버전에 눈에 띄는 차이가 없어야 합니다.

템플릿에서 AMP 상태를 확인할 수 있는 템플릿 컨텍스트 프로세서 작성하기

이는 선택 사항이지만, 지금까지 모든 것이 제대로 작동하는지 확인하기 위해 수행할 가치가 있습니다.

다음 내용이 포함된 amp_context_processors.py 파일을 앱에 추가하세요:

# <app>/amp_context_processors.py

from .amp_utils import amp_mode_active

def amp(request):
    return {
        'amp_mode_active': amp_mode_active(),
    }

이제 이 컨텍스트 프로세서의 경로를 TEMPLATES 설정의 ['OPTIONS']['context_processors'] 키에 추가하세요:

# <project>/settings.py 또는 <project>/settings/base.py

TEMPLATES = [
    {
        ...

        'OPTIONS': {
            'context_processors': [
                ...
                # 다른 컨텍스트 프로세서 뒤에 이것을 추가하세요
                'myapp.amp_context_processors.amp',
            ],
        },
    },
]

이제 템플릿에서 amp_mode_active 변수를 사용할 수 있어야 합니다. 예를 들면:

{% if amp_mode_active %}
    AMP 모드가 활성화되었습니다!
{% endif %}

AMP 모드가 활성화되었을 때 다른 페이지 템플릿 사용하기

AMP 사이트에서 일반 웹 사이트와 동일한 템플릿을 사용하고 싶지 않을 것입니다. AMP가 활성화된 상태에서 페이지가 제공될 때마다 Wagtail이 별도의 템플릿을 사용하도록 하는 로직을 추가해 보겠습니다.

다양한 페이지 유형에서 로직을 재사용할 수 있는 믹스인을 사용할 수 있습니다. 다음 내용을 앞서 만든 amp_utils.py 파일의 맨 아래에 추가하세요:

# <app>/amp_utils.py

import os.path

...

class PageAMPTemplateMixin:

    @property
    def amp_template(self):
        # 기본 템플릿 이름을 가져와서 확장자 앞에 `_amp` 를 삽입합니다
        name, ext = os.path.splitext(self.template)
        return name + '_amp' + ext

    def get_template(self, request):
        if amp_mode_active():
            return self.amp_template

        return super().get_template(request)

이제 이 믹스인을 모든 페이지 모델에 추가하세요, 예를 들면:

# <app>/models.py

from .amp_utils import PageAMPTemplateMixin

class MyPageModel(PageAMPTemplateMixin, Page):
    ...

AMP 모드가 활성화되면, 기본 템플릿 대신 app_label/mypagemodel_amp.html 템플릿이 사용됩니다.

다른 명명 규칙이 있다면, 모델에서 amp_template 속성을 재정의할 수 있습니다. 예를 들면:

# <app>/models.py

from .amp_utils import PageAMPTemplateMixin

class MyPageModel(PageAMPTemplateMixin, Page):
    amp_template = 'my_custom_amp_template.html'

<amp-img> 태그를 출력하도록 {% image %} 태그 재정의하기

마지막으로, Wagtail의 {% image %} 태그를 변경하여 AMP가 활성화된 상태에서 페이지를 렌더링할 때 <amp-img> 태그를 렌더링하도록 해보겠습니다. 이 변경을 Rendition 모델 자체에 적용하면 {% image %} 태그로 렌더링된 이미지와 리치 텍스트 필드에 렌더링된 이미지 모두에 적용됩니다.

사용자 정의 이미지 모델을 사용하면 더 쉽게 할 수 있습니다. 사용자 정의 Rendition 모델의 img_tag 메서드를 재정의하여 다른 태그를 반환할 수 있습니다.

예를 들면:

from django.forms.utils import flatatt
from django.utils.safestring import mark_safe

from wagtail.images.models import AbstractRendition

...

class CustomRendition(AbstractRendition):
    def img_tag(self, extra_attributes):
        attrs = self.attrs_dict.copy()
        attrs.update(extra_attributes)

        if amp_mode_active():
            return mark_safe('<amp-img{}>'.format(flatatt(attrs)))
        else:
            return mark_safe('<img{}>'.format(flatatt(attrs)))

    ...

사용자 정의 이미지 모델 없이는 내장 Rendition 모델을 몽키패치해야 합니다. 이 코드를 시작 시 가져올 프로젝트의 아무 곳에나 추가하세요:

from django.forms.utils import flatatt
from django.utils.safestring import mark_safe

from wagtail.images.models import Rendition

def img_tag(rendition, extra_attributes={}):
    """
    Rendition.img_tag의 대체 구현

    AMP 모드가 켜져 있을 때, <img> 태그 대신 <amp-img> 태그를 반환합니다
    """
    attrs = rendition.attrs_dict.copy()
    attrs.update(extra_attributes)

    if amp_mode_active():
        return mark_safe('<amp-img{}>'.format(flatatt(attrs)))
    else:
        return mark_safe('<img{}>'.format(flatatt(attrs)))

Rendition.img_tag = img_tag