검색 엔진 최적화는 프로덕트에서 굉장히 큰 역할을 한다고 생각합니다.
마케팅을 통해 사용자들을 유입시킬 수도 있지만, SEO를 잘 해놓으면 값을 지불하지 않고도 사용자들을 유입시킬 수 있습니다. 공짜 마케팅 역할을 할 수 있으니, 우리의 타켓 사용자들의 키워드들을 잘 파악하여 해당 키워드를 검색했을 때 우리의 프로덕트가 노출될 수 있도록 하는 것이 좋습니다.
SEO를 하는 방법은 생각보다 단순한 것도 있고, 컨텐츠적인 측면도 있어서 개발자 혼자서 작업하기 어려운 부분도 있습니다.
이 글에서는 SEO의 테크니컬적인 측면에 집중하고, 최적화를 진행하기 전에 어떤 개념을 알고 있으면 좋을지를 소개합니다.
또한 검색엔진마다 최적화하는 방법이 다를 수 있는데, 구글을 기준으로 작성된 글임을 알려드립니다!
글에 있는 오류는 댓글로 알려주시면 너무나 감사하겠습니다!! 🙏🙏🙏
SEO란?
검색엔진의 검색결과에 자신의 웹 페이지가 더 많이 노출되도록 최적화된 컨텐츠를 만드는 것
구글봇의 기본 동작 순서
1. 크롤링
- 구글은 막대한 수의 컴퓨터로 웹 페이지들을 크롤링하며, 이를 서치엔진봇, 크롤러, 구글봇이라고 함
- 이 문서에서 크롤러, 구글봇을 혼용해서 사용하는데, 구글에 한정하여 같은 대상을 가리킴
- 크롤링 대상 URL
- 구글에 제출된 sitemap.xml
- 크롤러가 진입한 페이지의 링크로 등록된 URL
<a href="https://www.example.com">이 링크에 크롤러가 접근합니다.</a>
- 크롤링에는 최신 버전의 크롬이 사용됨
- 크롤러는 HTML 문서를 파싱하고 자바스크립트가 있으면 렌더링 대기열에 페이지를 추가
크롤링에서 특정 페이지를 제외하는 방법
- Robots.txt에 크롤링에서 제외하고자 하는 URL을 넣어놔도, 다른 페이지에 링크로 들어가 있으면 크롤링 대상이 됨
- 로그인 후 사용할 수 있는 페이지는 크롤링 불가
- 구글봇이 로그인을 하지는 않으므로 크롤러가 접근할 수 없음
noindex
를 적용하여 크롤링 대상에서 제외
head
태그 내에 meta
태그로 noindex
를 설정하면 크롤러가 크롤링을 하지 않고, 다시 방문하지 않게 됨
<meta name="robots" content="noindex">
<meta name="googlebot" content="noindex">
a
태그에 rel="nofollow"
, rel="sponsored"
를 적용하여 링크 제외
- 크롤러는 페이지의 링크가 발견되면 그 페이지도 크롤링을 진행한다.
- 댓글, 포스트 등에 입력된 스팸 URL이 우리 사이트에 의해 크롤링되면 우리 사이트 SEO에 악영향
<a rel="nofollow" href="https://smap.com">저희 사이트도 방문해주세요 ^^</a>
<a rel="sponsored" href="https://sponsored.com">최저가 9,900원!!</a>
2. 렌더링
- 구글봇은 헤드리스 크로미움 기반으로 자바스크립트를 실행하고 렌더링을 진행
- 렌더링을 하여 HTML 문서를 완성해야 정확한 페이지 정보를 파악할 수 있음
자바스크립트 페이지 제약 사항
- 구글봇이 지원하는 수준의 자바스크립트 문법을 사용해야 함
- 구글봇은 리소스 효율을 위해 장기간동안 캐싱을 하고, WRS(Web Rendering Service)는 캐싱 헤더를 무시할 수 있으므로 강제로 캐싱을 깨줘야 함
index-2s9djce23asdas.js
이렇게 파일/폴더명에 해시값, 날짜 등의 값을 넣어서 캐싱이 깨질 수 있도록 설계
- 프레그먼트 대신 히스토리 API 사용
- 구글봇이 페이지에서 링크를 찾을때
a href
만을 링크로 인식
- 프레그먼트 방식으로 페이지 이동을 구현하면 크롤러가 링크를 인식하지 못함
- History API를 사용하여 URL이 크롤러에게 인식될 수 있도록 함
<a href="#/list">상품 목록</a>
<a href="/list">상품 목록</a>
SSR이 CSR보다 SEO가 좋은 이유?
- 모든 구글봇이 자바스크립트를 실행할 수 없음
- 서버 사이드 렌더링이 더 빠르게 콘텐츠를 제공함
모두 구글봇의 리소스와 관련된 이야기! 아래 크롤러 관리 파트에서 자세히 소개
3. 인덱싱
페이지가 어떤 페이지인지 파악하고, 검색 결과로 노출될 수 있도록 페이지 정보를 저장하는 것
- Meta 태그, Title, HTML 콘텐츠, 이미지, 비디오 등의 페이지 정보를 분석하여 페이지를 파악
- 구글 데이터베이스에 페이지 정보를 저장
Document 개념
- Document - 완전히 동일한 컨텐츠를 갖는 여러 개의 웹 페이지 모음
https://www.example.com/page/1
https://www.example.com?page=1
http://www.example.com?page=1
- 위 주소 모두 동일한 HTML 콘텐츠를 갖는 페이지라고 가정하면, 1개의 문서에 3개의 페이지가 있다고 할 수 있음
- 하나의 문서를 대표하는 페이지의 URL을 Canonical URL
- Canonical URL이 아닌 페이지들의 URL을 Duplicates URL (Alternates URL)
다시 인덱싱으로
- 인덱싱 과정에서 이 페이지가 중복이 되는지 검사
- 중복되면 Document에 Duplicates URL이 추가되며, 동일한 페이지를 중복으로 크롤링했다고 판단 → 해당 사이트에 대한 크롤링 빈도가 감소할 수 있음
link
태그의 “rel=canonical”
속성을 설정
- 크롤러에게 Canonical URL이 이미 존재하고 이 페이지는 크롤링할 필요가 없음을 명시
- 중복 크롤링을 방지할 수 있음
<link rel="canonical" href="https://example.com/page/1" />
<link rel="canonical" href="https://example.com/page/1" />
noindex
태그 사용
- 중복되는 페이지에 noindex를 선언하여 크롤링하지 않겠다고 명시하고, 중복 크롤링을 방지
4. 검색결과 게재
- 검색어에 대한 가장 높은 품질의 결과를 반환
- 사용자의 위치, 언어, 기기, 이전 검색어 등을 고려
- 페이지의 컨텐츠, 속도, 모바일 호환성 등을 고려
Sitemap
사이트에 있는 페이지, 동영상, 파일 등의 정보를 제공하는 파일
- 검색 엔진에 제출하여 더 효율적으로 크롤링될 수 있도록 하는 역할
- Sitemap은 어떤 페이지가 중요한지 알려주는 일종의 제안으로, Sitemap에 등록된 URL이 모두 인덱싱될 것이라는 보장은 없음
Sitemap에 포함할 내용
- 내 사이트에서 검색 결과로 노출시키고 싶은 페이지의 URL
- 동영상의 영상 길이, 카테고리, 연령 등급
- 이미지의 유형, 라이센스
- 뉴스의 제목, 게시 날짜 등
Sitemap 제약조건
- 용량 50MB 이하, URL 5만개 이하 → 초과시 분할
- Sitemap은 Sitemap의 상위 디렉토리의 하위 디렉토리에만 영향을 줌
- 따라서 대부분의 Sitemap은 루트 디렉토리에 위치
www.example.com/sitemap.xml
-> www.example.com 하위 모든 URL에 대해 적용
www.example.com/some/path/sitemap.xml
-> www.example.com/some/path 하위에만 적용되며, www.example.com에는 미적용
Django Sitemap Generator
- Sitemap에 URL을 하나씩 입력하기에는 너무 많기 때문에 동적으로 Sitemap을 만들어주는 라이브러리들이 존재
- Django에서는
django.contrib.sitemaps
를 사용하면 쉽게 만들 수 있음
- 모든 Post, Interview 객체의
get_absolute_url
을 Sitemap에 등록해줌
urlpatterns
에 만들어진 sitemaps
딕셔너리를 넣어줌
from django.contrib.sitemaps import GenericSitemap
from django.contrib.sitemaps.views import sitemap
sitemaps = {
"post": GenericSitemap({"queryset": Post.objects.all() }, priority=1),
"interview": GenericSitemap({"queryset": Interview.objects.all() }, priority=1),
}
urlpatterns = [
path("sitemap.xml/", sitemap, {"sitemaps": sitemaps}, name="sitemap"),
]
Robots.txt
크롤러가 사이트에 접근할 수 있는 URL, 접근 거부한 URL을 알려주는 파일
- 검색엔진마다 Robots.txt를 대하는 구현이 다르므로, 검색엔진마다 다르게 적용될 수 있음
Robots.txt 형식
- User-agent : 어떤 검색엔진에 대한 규칙인지 선언
- Disallow : 해당 URL은 크롤링을 거부하겠다는 선언
- Allow : 해당 URL은 크롤링을 허용하겠다는 선언 (기본적으로 모든 URL은 허용 상태)
- Sitemap : Sitemap의 위치
User-agent: BaiDuSpider
Disallow: /
User-agent: BadBot
Disallow: /
User-agent: *
Disallow: /api
Allow: /
Sitemap: http://www.example.com/sitemap.xml
- BaiDuSpider, BadBot 검색엔진 크롤러에 대해서는 모든 페이지를 허용하지 않음
- 위 두 크롤러를 제외한 모든 크롤러에 대해
/api
로 시작하는 URL은 크롤링은 거부하고, 나머지 모든 주소에 대한 크롤링은 허용함
Robots.txt 에 접근 거부를 했음에도 계속 인덱싱이 되는 이유
- Robots.txt에서 Disallow 했음에도 불구하고, 크롤러가 제외한 URL에 접근하는 경우가 있음
→ 크롤러가 방문하지 않을 것이라 생각하고 개발된 페이지면 크롤러 접근시 에러가 발생할 수 있는데, 에러가 발생하면 사이트에 대한 크롤링 빈도가 낮아질 수 있음
- Robots.txt는 페이지를 숨기기 위한 목적이 아니고, 사이트에 부하가 덜 생기게 하기 위한 목적
→ 크롤러가 과도한 접근으로 인해 서버에 과부하가 걸리지 않도록, Robots.txt에 페이지 접근을 허용하지 않겠다 선언하면 크롤러 접근이 줄어듬
- 크롤러는 Robots.txt에 접근 거부가 되어있으면 그 페이지는 방문하지 않지만, 다른 페이지에 링크로 존재하는 경우에는 크롤링을 진행
noindex
, nofollow
설정을 하여 페이지에서 인덱싱 거절
크롤링 자원 관리
크롤링 예산 (Crawl Budget)
보통 사이트의 경우, 크롤링 예산을 신경써야하는 일은 극히 드물다고 함
구글 기준 "100만개의 페이지가 일주일에 한번 변경되는 수준" or "1만개 페이지가 하루에 한번 변경되는 수준"이 아니면 예산에 영향이 없음
→ 유저로부터 받은 콘텐츠를 SEO 페이지로 등록되어, 기하급수적으로 페이지가 생기는 프로덕트라면 신경써야할 가능성이 있다. (Dynamic SEO)
- 크롤러의 개수는 한정적인데에 반해, 웹 페이지의 수는 계속해서 늘어남
- 이미 인덱싱된 페이지라 할 지라도 최신 내용을 유지하기 위해 다시 크롤링이 필요하기도 함
- 구글은 효율적으로 크롤러를 운용하기 위해서, 하나의 사이트에 크롤링할 시간과 크롤러의 수를 제한하고 이를 Crawl Budget (크롤링 예산) 이라고 함
- 크롤링 예산은 “크롤러가 얼만큼 서버에 부하를 주는가”와 “얼만큼 크롤링이 필요한 사이트인가”에 의해 결정
크롤링 용량 한도 (Crawl Capacity Limit)
크롤러가 얼만큼 서버에 부하를 주는가? (== 크롤러가 얼만큼 크롤링을 해도 되는가?)
- 크롤링 상태
- 크롤러가 페이지에 접근하면 당연하게도 서버에 요청이 보내짐
- 요청에 대한 응답 Status가 OK가 아니라면 크롤링 빈도를 낮춤
- 응답속도가 빠를수록 크롤링을 많이 시도하고, 느릴수록 크롤링 적게 시도함
- 구글의 크롤링 한도
- 구글봇의 수가 한정되어 있기 때문에 우리 사이트에 많은 구글봇을 배정해주지 못 할수도 있음
- 사이트 소유자의 크롤링 한도 설정
- 크롤러의 진입이 서버 부하로 이어지면, 사이트 소유자는 구글 서치 콘솔에서 크롤링 제한을 직접 걸 수 있음
- 결론적으로 아래 상황을 유지해야 우리 사이트가 크롤링을 더 많이 받을 수 있다.
- 서버의 응답속도를 빠르게 유지하고, 400대, 500대 응답코드가 나오는 페이지를 크롤링 대상에서 제외한다.
- 서버 부하에 영향이 크지 않다면 크롤링 한도를 제한하지 않는다.
크롤링 수요 (Crawl Demand)
얼만큼 크롤링이 필요한 사이트인가?
-
구글은 사이트의 품질, 업데이트 빈도, 관련성 등을 고려하여 사이트에 크롤링 시간은 분배 (한 사이트에 계속 시간을 쓸 수 없으므로)
-
주어진 시간에 효율적으로 크롤링되도록 해야함
-
아래 요소에 의해 얼마나 크롤링이 필요한지 결정됨
- 인식된 크롤링 대상 URL
- 사용자는 크롤러에게 Sitemap과 Robots.txt, noindex 설정 등으로 크롤링 대상 페이지의 정보를 알려줄 수 있음
- 사용자가 크롤링하지 않겠다고 명시하지 않으면, 크롤러는 기본적으로 사이트의 모든 URL을 크롤링
- 크롤링하고 싶지 않은 페이지가 크롤링되면, 그만큼 다른 페이지가 크롤링될 수 없음
- 크롤링 제외 방법
Canonical
설정으로 중복 크롤링 방지
- 크롤링하고 싶지 않은 페이지에
noindex
설정
- 크롤링하고 싶지 않은 페이지
robots.txt
에서 Disallow 시키기
- 인기도
- 페이지의 인기가 높을수록 구글은 해당 페이지의 콘텐츠를 최신화하기 위해 자주 크롤링을 시도
Crawl Budget 결론
- 크롤링이 서버 부하에 영향을 주지 않을수록 좋다.
- 크롤러가 더 많은 페이지를 크롤링할 수 있도록, 중복 페이지를 차단시켜주는 것이 좋다.
- 인기가 많고 콘텐츠가 좋을수록, 최신 정보를 유지하기 위한 크롤링이 더 자주 진행된다.
한정된 Crawl Budget에 의해 생기는 일
1. 중복된 페이지 크롤링시 해당 사이트 크롤링 빈도 감소
- 중복으로 크롤링하면 크롤러의 시간을 낭비한 것으로 간주
- 해당 사이트에 대한 크롤링 빈도가 감소하게 됨
2. CSR(Client Side Rendering)보다 SSR(Server Side Rendering)이 SEO에서 더 유리
- CSR의 경우 콘텐츠가 완성될 때까지의 시간이 오래걸림
- 첫 시점에 콘텐츠가 없음
- 자바스크립트를 실행하며, 렌더링이 진행되어야 함
- 반면 SSR은 이미 완성된 HTML 컨텐츠를 크롤러에게 바로 줄 수 있으므로, 더 많이 크롤링되고 속도가 빠른 것으로 인식됨
- 크롤러는 시간이 한정되어 있으므로, 더 효율적인 크롤링을 할 수 있으므로 SSR이 더 유리할 수 밖에 없음
3. Dynamic Rendering 개념 등장
- 웹 서버에서 크롤러를 식별하여
- 크롤러에게는 완성된 Static HTML 문서를 반환
- 일반 클라이언트에게는 원래대로 클라이언트 사이드 렌더링을 진행
- 크롤러는 자바스크립트를 실행할 필요없이 콘텐츠를 받을 수 있으므로, 더 효율적으로 크롤링이 진행됨
- 크롤러에게 제공하는 HTML 문서와 클라이언트에서 렌더링된 HTML 문서가 유사하면 클로킹으로 취급되지 않음
클로킹이란, 크롤러에게 보여주는 페이지와 일반 클라이언트에게 보여주는 페이지를 다르게 하는 SEO 어뷰징의 일종
- 다이나믹 렌더링을 사용할 때에는 클로킹으로 오해받지 않도록 Static HTML 문서와 클라이언트 사이드에서 렌더링된 문서가 유사하도록 신경써야함
마무리
SEO(Search Engine Optimization)에 대한 개념적 이해를 도움이 될 수 있는 내용을 쓰려고 노력
- 검색 엔진, 특히 구글이 어떤 방식으로 동작하는지
- Sitemap, Robots.txt가 의미하는바가 무엇인지
- 크롤러는 효율적으로 움직이고자 하는데, 여기서 우리는 어떤 시도를 할 수 있는지
그래서 최적화는 어떻게 하는건데?
SEO 스코어를 측정해주는 사이트를 사용하면 좋다.
SEO Checker | Test your website for free with Seobility
- 특정 페이지의 URL을 입력하면 SEO가 얼만큼 잘 되어 있는지, 보완해야할 점은 무엇이 있는지 추천해줌
- 실질적인 개선사항을 알려주는데, 이 개선작업을 왜 하는지 이해하고 보면 작업이 더 즐거워질 수 있다!
References
Advanced SEO Techniques and Strategies | Google Search Central | Google Developers
우왕 , 마침 찾고 있던 내용인데 이렇게 잘 설명해주시다 .
감사합니다.