검색 엔진 최적화는 프로덕트에서 굉장히 큰 역할을 한다고 생각합니다.
마케팅을 통해 사용자들을 유입시킬 수도 있지만, SEO를 잘 해놓으면 값을 지불하지 않고도 사용자들을 유입시킬 수 있습니다. 공짜 마케팅 역할을 할 수 있으니, 우리의 타켓 사용자들의 키워드들을 잘 파악하여 해당 키워드를 검색했을 때 우리의 프로덕트가 노출될 수 있도록 하는 것이 좋습니다.
SEO를 하는 방법은 생각보다 단순한 것도 있고, 컨텐츠적인 측면도 있어서 개발자 혼자서 작업하기 어려운 부분도 있습니다.
이 글에서는 SEO의 테크니컬적인 측면에 집중하고, 최적화를 진행하기 전에 어떤 개념을 알고 있으면 좋을지를 소개합니다.
또한 검색엔진마다 최적화하는 방법이 다를 수 있는데, 구글을 기준으로 작성된 글임을 알려드립니다!
글에 있는 오류는 댓글로 알려주시면 너무나 감사하겠습니다!! 🙏🙏🙏
SEO란?
검색엔진의 검색결과에 자신의 웹 페이지가 더 많이 노출되도록 최적화된 컨텐츠를 만드는 것
구글봇의 기본 동작 순서
.png)
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
사이트에 있는 페이지, 동영상, 파일 등의 정보를 제공하는 파일
.png)
- 검색 엔진에 제출하여 더 효율적으로 크롤링될 수 있도록 하는 역할
- 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 개념 등장
.png)
- 웹 서버에서 크롤러를 식별하여
- 크롤러에게는 완성된 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
우왕 , 마침 찾고 있던 내용인데 이렇게 잘 설명해주시다 .
감사합니다.