# AMP 지원 사이트를 구축하는 방법 이 문서는 [AMP](https://amp.dev/) 버전의 Wagtail 사이트를 만들고 URL 접두사를 사용하여 사이트의 나머지 부분과 별도로 호스팅하는 방법을 설명합니다. 또한 사용자가 사이트의 AMP 버전에서 페이지를 방문할 때 Wagtail이 `` 태그로 이미지를 렌더링하도록 하는 방법도 설명합니다. ## 개요 다음 섹션에서는 Wagtail의 내부 `serve()` 뷰를 가리키는 새로운 URL 항목을 추가하여 `/amp` 접두사 아래에서 전체 사이트를 다시 렌더링하는 효과를 얻을 것입니다. 그런 다음, 요청 객체 없이도 현재 요청이 사이트의 `/amp` 접두사 버전에 있는지 추적할 수 있는 유틸리티를 추가할 것입니다. 그 후, 템플릿에서 사이트의 어떤 버전이 렌더링되고 있는지 확인할 수 있는 템플릿 컨텍스트 프로세서를 추가할 것입니다. 마지막으로, 사이트의 AMP 버전을 렌더링할 때 `{% image %}` 태그의 동작을 수정하여 `` 태그를 렌더링하도록 할 것입니다. ## 두 번째 페이지 트리 생성하기 프로젝트 `urls.py` 파일에서 Wagtail URL을 복제하고 접두사를 부여하여 다른 접두사에서 전체 사이트를 렌더링할 수 있습니다. 이는 Wagtail의 기본 URL 앞에 있어야 합니다. 그렇지 않으면 페이지로 `/amp` 를 찾으려고 시도할 것입니다: ```python # /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 모드가 활성화되어 있음을 알려야 합니다. ```{note} 왜 스레드-로컬인가? (이 부분에 관심이 없다면 건너뛰어도 됩니다) `request` 객체를 수정하는 것이 가장 일반적인 방법일 것입니다. 그러나 이미지 태그 렌더링은 요청에 접근할 수 없는 Wagtail의 일부에서 수행됩니다. 스레드-로컬은 각 실행 스레드마다 다른 값을 가질 수 있는 전역 변수입니다. 각 스레드는 한 번에 하나의 요청만 처리하므로, 요청 객체를 모든 곳에 전달하지 않고도 해당 요청에 특정한 데이터를 전달하는 방법으로 사용할 수 있습니다. Django는 내부적으로 스레드-로컬을 사용하여 요청에 대해 현재 활성화된 언어를 추적합니다. Python은 `threading.local` 클래스를 통해 스레드-로컬 데이터를 구현하지만, Django 3.x부터는 여러 요청이 단일 스레드에서 처리될 수 있으므로 스레드-로컬은 더 이상 단일 요청에 고유하지 않습니다. 따라서 Django는 대체품으로 `asgiref.Local` 을 제공합니다. ``` 이제 스레드-로컬과 이와 상호 작용하는 몇 가지 유틸리티 함수를 만들어 보겠습니다. 이 모듈을 프로젝트의 앱에 `amp_utils.py` 로 저장하세요: ```python # /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` 컨텍스트 매니저를 호출하는 뷰를 정의해야 합니다: ```python # /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` 파일을 만들어야 합니다: ```python # /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 파일을 사용해야 합니다: ```python # /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` 파일을 앱에 추가하세요: ```python # /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']` 키에 추가하세요: ```python # /settings.py 또는 /settings/base.py TEMPLATES = [ { ... 'OPTIONS': { 'context_processors': [ ... # 다른 컨텍스트 프로세서 뒤에 이것을 추가하세요 'myapp.amp_context_processors.amp', ], }, }, ] ``` 이제 템플릿에서 `amp_mode_active` 변수를 사용할 수 있어야 합니다. 예를 들면: ```html+django {% if amp_mode_active %} AMP 모드가 활성화되었습니다! {% endif %} ``` ## AMP 모드가 활성화되었을 때 다른 페이지 템플릿 사용하기 AMP 사이트에서 일반 웹 사이트와 동일한 템플릿을 사용하고 싶지 않을 것입니다. AMP가 활성화된 상태에서 페이지가 제공될 때마다 Wagtail이 별도의 템플릿을 사용하도록 하는 로직을 추가해 보겠습니다. 다양한 페이지 유형에서 로직을 재사용할 수 있는 믹스인을 사용할 수 있습니다. 다음 내용을 앞서 만든 amp_utils.py 파일의 맨 아래에 추가하세요: ```python # /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) ``` 이제 이 믹스인을 모든 페이지 모델에 추가하세요, 예를 들면: ```python # /models.py from .amp_utils import PageAMPTemplateMixin class MyPageModel(PageAMPTemplateMixin, Page): ... ``` AMP 모드가 활성화되면, 기본 템플릿 대신 `app_label/mypagemodel_amp.html` 템플릿이 사용됩니다. 다른 명명 규칙이 있다면, 모델에서 `amp_template` 속성을 재정의할 수 있습니다. 예를 들면: ```python # /models.py from .amp_utils import PageAMPTemplateMixin class MyPageModel(PageAMPTemplateMixin, Page): amp_template = 'my_custom_amp_template.html' ``` ## `` 태그를 출력하도록 `{% image %}` 태그 재정의하기 마지막으로, Wagtail의 `{% image %}` 태그를 변경하여 AMP가 활성화된 상태에서 페이지를 렌더링할 때 `` 태그를 렌더링하도록 해보겠습니다. 이 변경을 `Rendition` 모델 자체에 적용하면 `{% image %}` 태그로 렌더링된 이미지와 리치 텍스트 필드에 렌더링된 이미지 모두에 적용됩니다. [사용자 정의 이미지 모델](custom_image_model)을 사용하면 더 쉽게 할 수 있습니다. 사용자 정의 `Rendition` 모델의 `img_tag` 메서드를 재정의하여 다른 태그를 반환할 수 있습니다. 예를 들면: ```python 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(''.format(flatatt(attrs))) else: return mark_safe(''.format(flatatt(attrs))) ... ``` 사용자 정의 이미지 모델 없이는 내장 `Rendition` 모델을 몽키패치해야 합니다. 이 코드를 시작 시 가져올 프로젝트의 아무 곳에나 추가하세요: ```python 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 모드가 켜져 있을 때, 태그 대신 태그를 반환합니다 """ attrs = rendition.attrs_dict.copy() attrs.update(extra_attributes) if amp_mode_active(): return mark_safe(''.format(flatatt(attrs))) else: return mark_safe(''.format(flatatt(attrs))) Rendition.img_tag = img_tag ```