개발
Laravel 개발 일지

라라벨에서 IndexNow 사용법

212 views as of October 31, 2024.
이제는 더 이상 검색엔진이 알아서 긁어가는 크롤링 방식이 아닌, 검색엔진에게 수집해달라고 요청하는 프로토콜인 IndexNow 라는 것이 출시되었다.

말그대로 내 웹사이트에 변화가 생기면 각종 검색엔진에 내 사이트에 새 글이 올라오거나 글이 변경했어요! 를 능동적으로 알리는 것이라고 생각하면된다.

MS에서 시작해서 사용하기 시작한 이 프로토콜은 지금 Bing, Naver, Google 에서도 모두 지원하는 프로토콜이 되었다.

그래서 나도 한번 적용해볼까 해서 여러 관련 자료를 검색해보았는데, 생각보다 이미 만들어져있는 플랫폼(워드프레스, 블로그 등)에서 설정하는 법만있지 직접 구축하는 방법은 없었다.

더불어 나는 Laravel 환경에서 작업을 하다보니 이에 관해 사용법을 여기 적어보자 글을 시작하려 한다.


IndexNow 키 발행

API 방식의 프로토콜을 이용하다보니 나라는것을 고유하게 특정하기위해 API 키를 발급해야한다.
근데 이 발급방식이 어디가서 발급하는게 아니라 내가 마음대로 원하는 규정에 따라 발급하면 되는거라고 한다.

키 생성 규칙 

- UTF-8
- 16진수 문자만 사용 (a-f, A-F, 0-9, -)
- 최소 8자, 최대 128자

어디서 많이 본 조건이라 생각했는데 내가 줄곧 라라벨 환경에서 고유 uid 발급을 위해 사용하는 Str::uuid()와 비슷하다고 생각했다.

그래서 좀 더 알아보니 uuid도 위의 발행조건 내의 값을 배출한다는 것을 알게되었다.

그럼 일단 이 기능을 이용해서 한 번 만들어보자 생각이들었다. (물론 그냥 위의 조건대로 임의로 만들어도된다.)

라라벨 Str객체의 uuid를 이용한 키 생성 방법

Str::uuid()로 키값을 얻기위해 라라벨 프로젝트가 설치되어있는 루트에서 아래와 같이 명렁어를 입력해보자.


php artisan tinker
> use Illuminate\Support\Str;
> Str::uuid()->toString();Copy

그러면 아래에 초록색 텍스트로 uuid() 실행결과가 나오게된다.
이는 IndexNow의 API Key 규칙안의 텍스트이므로 나는 이 데이터를 기반으로 향후의 작업을 시작하려고한다.


IndexNow 키 배치

위에서 만든 키와 같은 이름의 txt 파일을 만든 후 이 파일을 웹 루트에 업로드한다.

라라벨의 경우에는 public 폴더에 위에서 발급받은 키로
52a314e3-2785-446c-a749-4bed3141d7dd.txt
라는 파일을 만들고 그 파일 내부에 마찬가지로
52a314e3-2785-446c-a749-4bed3141d7dd
을 적어 위치시키면 된다.

다시 한번 당부하지만 위의 키는 본인이 직접 고유하게 생성해야하는 키파일이다. 위에꺼 그대로 쓰면안되요~


필수 패키지 설치

이어서 라라벨 컨트롤러 내부에서 HTTP 요청을 보내야하므로 guzzle 패키지가 필수로 필요하다.

composer require guzzlehttp/guzzleCopy

컴포저 패키지에 위 패키지가 없다면 설치해주자.


서비스 생성

이어서 IndexNow에 Http 요청을 보내는 서비스 클래스를 아래와 같이 생성해주자.
나는 App\Services 폴더를 만들어 여기에 직접 만든 서비스 클래스들을 모아두고있다.

<?php
 
namespace App\Services;
 
use GuzzleHttp\Client;
 
class IndexNowService
{
    protected $apiUrl = 'https://api.indexnow.org/indexnow';
    protected $client;
 
    public function __construct()
    {
        $this->client = new Client();
    }
 
    public function submitUrl(string $url)
    {
        $key = '52a314e3-2785-446c-a749-4bed3141d7dd'; // public/indexnow_key.txt에 넣은 키 값과 일치해야 함
        $host = parse_url($url, PHP_URL_HOST);
 
        $response = $this->client->post($this->apiUrl, [
            'json' => [
                'host' => $host,
                'key' => $key,
                'urlList' => [$url],
            ]
        ]);
 
        return $response->getStatusCode() === 200;
    }
}Copy

본 클래스가 생성되면 guzzle Client 객체를 초기화하고 이후에 submitUrl을 통해 요청을 JSON 형식에 맞게 구조화해서 Post 로 쏘는 역할을 한다.


IndexNow 에서 받는 요청은 위의 구조처럼 받는다.


글 최초 게시시 호출

마지막으로 위 서비스 클래스를 실제로 구동시키는 쪽에서 작업하는것만 남았다.

public function add(Request $request)
{
    ... $post add process
    $post->refresh();
 
    $indexNow = new IndexNowService();
    $indexNow->submitUrl(url('/' . $post->id));
 
    return redirect('/' . $post->id);
}Copy

이부분은 각자에 따라 다 달라질 수 있는 부분인데 요는 위에서 만든 서비스 클래스를 생성한 후 url을 보내면 된다는 것이다.

중요한거는 여기서 submitUrl에 보내는 url은 축약 URL(/post)이 아닌 전문(https://domain.com/post)을 보내야한다.

라라벨에선 url() 매직 메소드가 그 역할을 해준다.

이렇게 글 작성시 IndexNow에 해당 글의 URL을 넘기는것으로 작업을 요청 할 수 있다.

요청 응답 확인 방법

만약에 제대로 요청이 갔는지 결과값을 저장하고 싶을때는 IndexNowService 의 SubmitUrl() 의 리턴값을
return $response->getStatusCode();
로 바꾸고, IndexNow 요청값을 객체에 저장하는 방식으로 변경 구현하면 결과값을 저장해둘 수도 있을것이다.

$indexNow = new IndexNowService();
$post->index_now = $indexNow->submitUrl(url('/' . $post->id));
$post->save();
Copy


그럼 위에 처럼 indexNow에 http request가 된 이후에 받은 response status code 값이 해당 컬럼에 적히게 될것이다.

응답 코드 값


응답 코드값은 위에 처럼 전송이 되고 일반적으로 200이면 문제없음, 400 이면 잘못된 구조로 요청, 422면 키값이 다름으로 해석할 수 있겠다.

결론 적으로 보자면 IndexNow 란 사이트 주가 능동적으로 검색엔진에 크롤링, 수집 요청을 하는 것이고 이는 내가 임의로 정한 키값을 웹루트에 배치함으로써 사이트 소유자임을 증명하며 요청 형식에 맞는 Post 요청을 indexnow.org 에 보내는 것으로 각 검색엔진에 이를 전파할 수 있음을 뜻하는거라고 볼 수 있다.


여러 검색엔진에 요청하기

위의 단일 엔진(IndexNow)에 보내는 것이 어느정도 문제없이 되었다면 여러 검색엔진에 해당 요청을 보내고 싶을것이다.

이럴땐 위에서 만든 IndexNowService 클래스를 아래와 같이 확장할 수 있다.

<?php
 
namespace App\Services;
 
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
 
class IndexNowService
{
    protected $endpoints = [
        'https://api.indexnow.org/indexnow',
        'https://www.bing.com/indexnow',
        'https://searchadvisor.naver.com/indexnow',
        'https://search.seznam.cz/indexnow',
        'https://yandex.com/indexnow',
        'https://indexnow.yep.com/indexnow',
    ];
 
    protected $client;
 
    public function __construct()
    {
        $this->client = new Client();
    }
 
    public function submitUrl(string $url)
    {
        $key = '52a314e3-2785-446c-a749-4bed3141d7dd';
        $statusCodes = []; // 각 요청의 상태 코드를 저장할 배열
        $host = parse_url($url, PHP_URL_HOST);
 
        foreach($this->endpoints as $endpoint) {
            try {
                $response = $this->client->post($endpoint, [
                    'json' => [
                        'host' => $host,
                        'key' => $key,
                        'urlList' => [$url],
                    ]
                ]);
 
                // 상태 코드를 배열에 저장
                $statusCodes[$endpoint] = $response->getStatusCode();
            } catch (RequestException $e) {
                // 요청 실패 시 예외 처리 (예: 네트워크 문제)
                $statusCodes[$endpoint] = $e->getMessage();
            }
        }
 
        return json_encode($statusCodes);
    }
}Copy

최초의 클래스와 달라진점은 $api_url이 $endpoints로 바뀌고 이 모든 url에 요청을 보내도록 처리한 후 그 결과를 반환한다는 점이다.

해당 엔드포인트는 IndexNow FAQ 에서 확인할 수 있다.
자기가 필요없는 엔드포인트는 배열에서 지우면 된다. (예를들어 Yandex는 러시아, Seznam은 체코, Yep은 미국 서비스)

위에 메소드 실행결과는 아래와같이 출력되어 디비에 저장된다.


#개발 #Laravel #라라벨 #IndexNow #index now #검색 엔진
0 개의 댓글
×