Ruby와 Google Indexing API를 이용하여 자동으로 신규URL 등록하기

제가 블로그 글을 작성하고 commit - push 시 많은 작업들이 일어납니다. 대표적인 것들을 이야기 드리면, Jekyll build 및 deploy 과정이 진행되고 이미지 중 compress가 필요한 이미지는 compress 처리하여 용량을 줄이고, notify 등 여러가지 과정들이 github action에 녹아있습니다.

여기서 거의 마지막에는, 그리고 주기적으로 SEO를 위해 각 주요 서비스의 검색 등록 부분에 새로운 글, 수정된 글이 있다고 노티해줍니다. 보통 이러한 과정은 수동으로 하시는 경우가 많을텐데요, 오늘은 해당 내용 중 구글 검색등록을 자동화하는 방법에 대해 글을 작성하려고 합니다. 그럼 시작해보죠 😊

검색등록 서비스

종류야 많이 있게지만 한글 기반으로 포스팅 시 주로 신경쓰시는 부분들이 네이버,다음,구글 정도일거라고 생각됩니다.

ervice Support API Desc
Bing Y 토큰 발급->헤더에 포함하여 요청
Daum N 수동 등록만 지원
Google Y 키 기반 인증->토큰 발급->헤더에 포함하여 요청
Naver Y 수동 등록만 지원(API가 있지만 제휴 업체만 가능함)

대략 정리해보면 이렇습니다. (순서는 알파벳순입니다)

Bing과 Google은 API를 지원하며, 누구나 연동하여 사용할 수 있도록 제공하고 있고 다음과 네이버의 경우 수동만 지원하거나 제휴 기능으로 별도로 제공하는 것으로 알고 있습니다. 물론 headless browser로 처리하면 뭐… 자동화는 가능하겠지만, 엄연히 정책에 어긋나는 부분(특히 네이버는 제휴API가 있어서 민감할듯)이기 때문에 잘 고민해 보시길 바래요.

Bing은 한번 해보시면 굉장히 간단해서 쉬울거고, Google이 약간 복잡한 감이 있어서 Google로 내용 정리해볼게요.

What is Google Indexing API

Google API는 굉장히 많습니다. 검색,지도,메일 등 대다수 서비스에 API를 지원하고 있어서, 원하시는 기능을 API를 통해 별도의 앱에서 처리할 수 있도록 만들 수 있습니다. 다만 이러한 과정에서 사용되는 인증이 단순히 계정에 대한 토큰정보가 아니라, GCP에서 IAM통해 생성한 계정과 토큰 정보라서 GCP에서 미리 세팅이 필요합니다. (GCP 무료 플랜에 기간 없어서, 그냥 간단한 서버 쓰시기에도 괜찮을거에요. 물론 전 아직 DigitalOcean Credit이 남아 있어서.. 물렸습니다..)

Step by Step

Step1. GCP에서 프로젝트 생성하기

우선 GCP 콘솔로 접근합니다.

https://console.cloud.google.com/

기존에 사용하셨었다면 프로젝트가 있을거고, 사용한적이 없으시다면 프로젝트를 하나 생성해야 합니다. 혹시나 다른거 사용하시다가 결제 관련해서 나갈까 걱정이시라면, 결제정보를 등록하지 않는게 제일 좋을 것 같아요.

Step2. IAM에서 계정생성

프로젝트 생성이 완료됬다면, 각 프로젝트 페이지에 진입 후 IAM 서비스로 이동합니다. IAM은 계정관리 서비스로 AWS, Azure 등 다른 Cloud 서비스 사용해보셨다면 아마 익숙하실텐데요, 내 프로젝트 하위에 별도의 로그인 계정을 만들어주신다고 생각하면 됩니다.

프로젝트 > IAM 및 관리자 > 서비스 계정 으로 접근합니다.

https://console.cloud.google.com/iam-admin/serviceaccounts?hl=ko&project=프로젝트이름

이제 해당 페이지의 상단에 서비스 계정 만들기를 눌러줍시다.

계정 생성이 완료됬습니다. 여기서 계정의 메일 주소를 확인할 수 있는데, 뒤에서 사용된 메모해두시거나 복사해두세요.

Step3. App에서의 인증을 위한 키 추가하기

이제 해당 계정을 눌러서 계정 상세 정보에 접근합니다.

다음 키 탭에서 키 추가 > 새 키 만들기를 눌러서 비공개 키를 만들어줍니다. 이 때 Type은 JSON을 사용해줍시다.

생성되면, 키 파일이 다운로드되며, 키 파일은 아래와 같이 개인키를 담은 JSON 파일입니다.

Step4. Indexing API Enable

이제 API 및 서비스에서 Indexing API를 사용할 수 있도록 설정해줘야 합니다. 전체 메뉴에서 API 및 서비스로 이동해주세요.

https://console.cloud.google.com/apis/dashboard?hl=ko&project=프로젝트이름

해당 페이지 접근 후 대시보드 상단에 보면 API 및 사용 서비스 설정 버튼이 있습니다. 눌러주세요. 이후 Indexing API 키워드로 검색하시면 아래와 같이 결과가 나타납니다. Indexing API를 눌러 해당 API의 관리 페이지로 이동해주세요.

접근 시 해당 API를 사용할지 물어보는데, 사용한다고 해주시면 아래와 같이 API 사용 설정됨이 표기됩니다.

Step5. Search console에서 권한 추가하기

자 거의 다 왔습니다. 이제 Search console에서 권한을 추가해줘야 하는데요, Step4 까지 진행 후 앱을 만드셔도 되지만, 실제 요청 시 Permission 에러(Permission denied. Failed to verify the URL ownership.)가 발생합니다. 이는 Indexing API를 사용한다고 해도, 허용된 권한의 도메인만 컨트롤 할 수 있도록 구글에서 제한하기 떄문인데요, 아래와 같이 서치 콘솔(https://search.google.com)로 들어갑니다.

https://search.google.com/search-console/users?resource_id=sc-domain%3A도메인이름 위 링크에서 도메인이름으로 기입한 부분만 바꿔주셔도 될 것 같네요.

서치 콘솔 홈에서 접근하셨다면, 각 도메인에 설정 > 사용자 및 권한 으로 진입해주세요.

이제 우측 상단을 보면 사용자 추가가 있는데, 이를 눌러주신 후 아까 생성한 계정의 메일 주소를 넣어줍니다. 권한은 전체 권한이 필요합니다. 계정의 메일 포맷은 계정이름@프로젝트.iam.gserviceaccount.com 이며, 혹시 메모해두시지 않았다면 GCP의 IAM에서 가셔서 다시 확인하셔도 됩니다.

여기까지 진행하고 API 테스트 시 잘 호출된다면 문제없겠지만, Permission 에러가 발생한다면 속성 관리가 추가를 진행해야합니다.

Permission denied. Failed to verify the URL ownership.

그 다음 방금 추가한 계정에 대해 오너 권한을 할당해줘야 하는데요, 아래와 같이 소유자 행의 메뉴(점점점)를 누르고 속성 소유자 관리를 눌러 웹마스터 센터(https://www.google.com/webmasters/verification/home?hl=ko)를 열어줍니다. 도메인이 여러개인 경우 사용하시는 도메인을 눌러주시면. 아래와 같이 웹 마스터 센터가 열리며, 여기서 확인된 관리자를 등록해주시면 정상적으로 API 호출이 가능해집니다.

Step6. 코드 구현 및 테스트

저는 최근에 golang을 사용하지만, 자동화 관련 작업들이 예전부터 짜던게 많아서 다 ruby로 되어 있어 이번에는 ruby로 구성했습니다.

우선 google-api-client gem을 설치해줍니다. 해당 gem은 불편한 인증 과정을 쉽게 처리할 수 있도록 구글이 제공하는 Interface화 된 라이브러리 입니다.

gem install google-api-client

온라인에 예시가 없어서.. 테스트했던 풀 코드로 공유드려요. Google::Apis::IndexingV3::IndexingService 클래스로 Instance를 만들어주고, Google::Auth::ServiceAccountCredentials.make_creds에 인자값으로 아까 다운로드 받은 .json 파일을 넘겨주어, authorization 헤더 정보를 받습니다. 다행히 IndexingService에 authorization 멤버변수로 넣어주면 이후 요청에서 자동으로 붙어서 사용됩니다.

그 다음 Google::Apis::IndexingV3::UrlNotification 으로 신규/수정 URL을 등록할 정보 JSON을 만들어준 후 publish_url_notification 로 쏴주면 됩니다.

require 'google/apis/indexing_v3'
service = Google::Apis::IndexingV3::IndexingService.new
authorization = Google::Auth::ServiceAccountCredentials.make_creds(
      json_key_io: File.open('hahwul-93d2543c7b95.json'),
      scope: 'https://www.googleapis.com/auth/indexing'
   )
service.authorization = authorization
urlObject = Google::Apis::IndexingV3::UrlNotification.new
urlObject.url="https://www.hahwul.com"
urlObject.type="URL_UPDATED"
a = service.publish_url_notification(urlObject)
puts a

간단히 테스트해보면..

ruby 1.rb
#<Google::Apis::IndexingV3::PublishUrlNotificationResponse:0x000055d31104ca88>

네 잘 됩니다! 여기서 urlObject.type 으로 넘겨준 값은 2개의 타입이 있습니다. 추가/수정 등을 의미하는 URL_UPDATED, URL 삭제를 의미하는 URL_DELETED가 있습니다. 실제로 해당 메소드로 API가 호출되면, 아래와 비슷한 요청이 발생하겠네요.

POST https://indexing.googleapis.com/v3/urlNotifications:publish HTTP/1.1
Host: indexing.googleapis.com
...snip...

{
  "url": "https://www.hahwul.com",
  "type": "URL_UPDATED"
}

몇가지 참고했던 링크 공유드릴게요.

Github action?

네 아까 위에서 썼던 코드를 Github action에서 바로 사용하기에는 문제가 있습니다. 파일을 미리 넣어두기 좀 불편한건데요, 그냥 간단한 팁으로 공유드리면 Github Secret에 base64로 json 파일을 인코딩해서 올려둔 후 실제 처리 시 해당 값을 decode 하여 로컬에 file write 하고, 다시 그 파일을 읽어서 인증 처리하면 됩니다.

require 'google/apis/indexing_v3'
ttt = ENV['GOOGLE_TOKEN']
kdat = Base64.decode64 ttt

File.open("hahwul.json", "w") {|f| f.write(kdat) }
service = Google::Apis::IndexingV3::IndexingService.new
authorization = Google::Auth::ServiceAccountCredentials.make_creds(
      json_key_io: File.open('hahwul.json'),
      scope: 'https://www.googleapis.com/auth/indexing'
   )
service.authorization = authorization
urlObject = Google::Apis::IndexingV3::UrlNotification.new
urlObject.url="https://www.hahwul.com/"
urlObject.type="URL_UPDATED"
a = service.publish_url_notification(urlObject)
puts a

물론.. 전 약간 다른 방식을 택하고 있어서 위 방식은 아니지만, 이 정도면.. github action에 녹여서 원하는 event(push,pull,schedule)에 원하시는 URL이 추가/업데이트 됬다고 요청할 수 있겠네요 :D

고생했어 올라프

References