75 장고 검색엔진최적화 - json-ld 적용
75.1 구글검색 갤러리
구글 검색엔진에게 내 사이트의 특성에 맞는 구조화된 데이터를 제공함으로써 구글 검색 결과를 다른 글들과 다르게 표현할 수 있게 됩니다.
구글검색 갤러리에서 어떤 데이터를 제공하면 어떤 결과가 나오는지 미리 확인 할 수 있으며, 기능가이드를 통해 요구하는 데이터를 확인 할 수 있습니다.
검색갤러리 : https://developers.google.com/search/docs/appearance/structured-data/search-gallery
내 글이 남들과 다른 구글검색 결과화면을 원하신다면 잘 살펴보시면 좋습니다.
75.2 구조화된 데이터 기능가이드 - HowTo
제 블로그는 step by step 형식으로 내용을 작성하였기에 방법(howto type) 형식이 맞는거 같습니다.
json-ld, 마이크로데이터 2가지중 하나로 제공하면 된다고 가이드에 나와있습니다.
기능가이드>방법 : https://developers.google.com/search/docs/appearance/structured-data/how-to
저는 json-ld로 장고에 만들겠습니다.
75.3 장고 django-json-ld 모듈 설치 - base.py
파이썬에 django-json-ld 모듈을 설치합니다.
pip install django-json-ld
모듈 설치 완료 후 Setting 파일인 base.py에 django_json_ld app을 추가하고 JSON_LD_EMPTY_INPUT_RENDERING 등의 옵션을 설정합니다.
django_json_ld 모듈 옵션
옵션 | 설명 |
---|---|
JSON_LD_CONTEXT_ATTRIBUTE | CBV에서 context에 등록되는 이름입니다. 기본 sd |
JSON_LD_MODEL_ATTRIBUTE | JsonLdDetailView 사용시 모델 프라퍼티 이름입니다. 기본 sd |
JSON_LD_DEFAULT_CONTEXT | CBV에서 기본 컨텍스트 입니다. 기본은 https://schema.org/ |
JSON_LD_INDENT | 디버그 모드에서 사용되며, json을 들여쓰기하여 보기좋게 표시해줍니다. |
JSON_LD_DEFAULT_TYPE | CBV 사용시 기본유형입니다. 기본 Thing |
JSON_LD_GENERATE_URL | CBV 를 사용할 때 json-ld의 url을 생성합니다. 기본 True |
JSON_LD_EMPTY_INPUT_RENDERING | json-ld가 없으면 아무것도 렌더링 하지 않는 옵션입니다. strict기본적으로 TemplateSyntaxError를 발생시킵니다. silent아무것도 렌더링하지 않습니다. generate_thing현재 페이지의 URL로 객체를 생성합니다 |
pypi.org에서 설치 방법과 사용방법을 확인할 수 있습니다.
75.4 template에 render_json_ld 추가 - base.html
75.5 JsonLdContextMixin - view.py 구현
pypi.org 가이드에서는 JsonLdContextMixin, JsonLdDetailView의 사용법을 알려주고 있습니다.
pyp django-json-ld : https://pypi.org/project/django-json-ld/
테이블 1개만 사용했으면 가이드의 Class-Based View example 처럼 사용해도 되겠지만
저는 2개의 테이블을 조인해서 사용하고 있어 그대로 사용할 수 가 없습니다.
view 구현시 query_set을 내용을 get_structured_data()에 담아 json-ld를 만들어야 되는데
get_structured_data()가 get_context_data()보다 더 빨리 처리되어
model에서 가져온 데이터를 담을 수 없었습니다.
그래서 get_context_data에서 get_structured_data()을 가져와 처리하는 방식으로 변경하였습니다.
#myapp/blog/views.py
from django.shortcuts import render
from django.views import generic
from django_json_ld.views import JsonLdContextMixin
# from django_json_ld.views import JsonLdDetailView
from .models import PyBlog
from .models import PyBlogDetail
#sidebar
from myapp.common.common_views import MenuMixin
import time
class BlogDetail(MenuMixin, JsonLdContextMixin, generic.DetailView):
model = PyBlogDetail
template_name = "blog/blogDetail.html"
structured_data = {"@type": "HowTo",
"estimatedCost": {"@type": "MonetaryAmount","currency": "USD","value": "0"},
"supply": [{"@type": "HowToSupply","name": "server"}, ],
"tool": [{"@type": "HowToTool","name": "visual studio code"}, {"@type": "HowToTool","name": "python"}, ],
"totalTime": "P1D",
}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
query_set = PyBlog.objects.filter(id=self.kwargs['pk'], use_yn = 'Y')
context['pageInfo'] = query_set[0].get_page_info()
page_title = query_set[0].title
query_set = query_set.values('id','title','update_dt','regist_dt',
'pb_detail__id',
'pb_detail__detail_id',
'pb_detail__sub_title','pb_detail__img_url','pb_detail__img_size','pb_detail__new_img_url',
'pb_detail__content_body'
).order_by('pb_detail__sub_title')
# print("query_set[0].pb_detail__content_body : ", query_set[0]['pb_detail__content_body'])
page_img_url = query_set[0]['pb_detail__img_url']
page_desc = query_set[0]['pb_detail__content_body'].replace('```','').replace('"','')[:140]
context['dataList'] = query_set
context['pageInfo'].update({ "title": page_title, "get_descript":' '.join(page_desc.split()), "img_url":page_img_url})
context.update(self.getMenuList)
#json-ld 생성
self.setJsonLD(query_set, page_title, context)
return context
def replaceImg(self, new_img, org_img):
if new_img != None and new_img !='':
return new_img.replace('/image/upload/','/image/upload/h_306,w_406,q_auto:best/')
if org_img == None or org_img =='':
return ''
return org_img
def replaceText(self, text):
return text.replace("```"," ").replace("<br/>"," ").replace("<br>"," ").replace("\r","").replace("\t","").replace("\n"," ").replace("<","").replace(">","")
def setJsonLD(self, query_set, page_title, context):
structured_data = super().get_structured_data()
structured_data.update({'name':page_title, 'image':{ "@type": "ImageObject","height": "306","width": "406",
"url": self.replaceImg(query_set[0]['pb_detail__new_img_url'],query_set[0]['pb_detail__img_url']),
},
})
stepList = []
for i in query_set:
stepList.append({
"@type": "HowToStep",
"url": f"{structured_data['url']}#{i['pb_detail__id']}",
"name": f"{i['pb_detail__sub_title']}",
"itemListElement":[{"@type": "HowToDirection", "text": f"{self.replaceText(i['pb_detail__content_body'][:400])}"}],
"image": {"@type": "ImageObject",
"url": f"{self.replaceImg(i['pb_detail__new_img_url'],i['pb_detail__img_url'])}",
"height": "306",
"width": "406"}
})
structured_data.update({'step':stepList})
context.update({'sd':structured_data})
75.6 구조화 데이터 테스트
작업한 모든 결과를 서버에 반영 후에
구글에서 제공하는 스키마 마크업 테스트 도구를 통해 잘 적용되 었는지 확인합니다.
구조화 데이터 테스트 도구로 이동하여
구조화 데이터가 적용된 페이지의 URL을 입력합니다.
테스트도구 URL : https://developers.google.com/search/docs/appearance/structured-data
75.7 구조화 데이터 테스트 확인
에러 없이 잘 적용되었다면
구글에 사이트맵을 다시 읽도록 재요청 해주시면 됩니다.
구글 크롤링 재요청: https://www.google.com/ping?sitemap=https://pythonblog.co.kr/sitemap.xml
제가원하는대로 구글검색결과에서 남들과 다르게 나왔으면 좋겠습니다.
75.8 구글웹마스터도구 - 서치콘솔 확인
구조화가 적용된 페이지를 구글에서 다시 크롤링하면
구글 웹마스터 도구 - Google Search Console 에서
위 그림은 제가 운영하는 다른사이트에 리뷰스니펫을 적용했을때 구조화데이터에 관련된 섹션이
나온 내용입니다.
몇일 후에 모바일에서 확인해보니 위와 같이
구글 검색엔진에 howTo가 적용되어 검색 결과에 반영 된 모습니다.
75.9 구조화데이터 관련 링크 모음
json-ld 관련 | 링크 |
---|---|
구글검색엔진 검색갤러리 | https://developers.google.com/search/docs/appearance/structured-data/search-gallery |
PYPI django-json-ld | https://pypi.org/project/django-json-ld/ |
GITHUB django-json-ld | https://github.com/hiimdoublej/django-json-ld |
구조화 데이터 테스터 도구 | https://developers.google.com/search/docs/appearance/structured-data |
구글서치콘솔 | https://search.google.com/search-console |
json-ld 모듈 | https://json-ld.org/ - json-ld 구현을 위한 각 언어별로 제공되는 모듈 및 명세서 등을 확인 할 수 있습니다. |