서론
최근 팀장님께 조언을 들은겸, 게임 하나를 간단하게 만들어 보려고 한다. 특정 페이지의 키워드를 기준으로 단어를 맞추는 게임을 react로 만드려고 하는데, 단어를 맞추기 앞서 단어의 정의 및 사전적 의미를 구해야 하기 때문에 관련하여 NAVER에서 제공하는 OPEN API 중 하나, Search API를 활용하여 네이버 백과사전 검색 데이터를 받아오는 API를 라라벨로 구현해 보자.
NAVER Developers 애플리케이션 등록
아래 공식 페이지에서 내 애플리케이션 등록을 통해 "검색" 애플리케이션을 만든 후 Client ID, Client Secret 두 가지 Key를 확보한다.
Laravel Route 설정
나는 티스토리 API를 만들었던 프로젝트에 이어서 Naver API를 구현하기로 하였다. 티스토리 API가 궁금하다면 아래 포스팅에서 확인 가능하다.
// routes/api.php
Route::prefix('v1')->group(function () {
Route::prefix('tistory')->group(function (){
Route::get('/',[\App\Http\Controllers\Api\TistoryController::class,'index'])->name('tistory.index');
Route::get('/accessToken',[\App\Http\Controllers\Api\TistoryController::class,'accessToken'])->name('tistory.accessToken');
});
Route::prefix('naver')->group(function (){
Route::get('/{keyword?}',[\App\Http\Controllers\Api\NaverController::class,'index'])->name('naver.index');
});
});
이제 localhost:8000/api/v1/naver/{keyword} URL을 통해서 백과사전 검색 정보를 받아오도록 만들었다.
NaverController 생성
Laravel에서는 artisan command 를 사용하여 쉽게 컨트롤러를 생성할 수 있다.
php artisan make:controller Api/NaverController
나는 Controller와 Service로직을 분리해서 개발하는 것을 좋아해서 Controller의 코드는 최대한 간결하게 작성했다.
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Library\NaverLibrary;
class NaverController extends Controller
{
public function index(?string $keyword)
{
if (!is_null($keyword)) {
$naverLibrary = new NaverLibrary();
$result = $naverLibrary->getSearchDictionary(keyword: $keyword);
return response()->json($result);
}
return response()->json(['message'=>'we can not find search keyword. you must insert search keyword']);
}
}
한 눈에 봐도 어떤 기능을 담당하는지 알게 작성하는 것이 클린코드라고 생각한다. 때문에 위 코드를 보면, NaverLibrary 객체의 getSearchDictionary()를 통해서 키워드에 관련된 검색 데이터를 받아와서 응답을 json으로 return한다.
Naver API 로직 관련 Service 객체 생성
Serivce, Library 등 문구는 본인의 마음대로 정해도 되지만, 나는 이번에는 Library라는 단어를 사용하여 NaverLibrary 클래스를 만들었다. 그 전에 TistoryLibrary에서 정의되어있던 makeJsonFile() 메서드와 initApiLibrary() 메서드를 공통으로 사용하기 위한 Class로 빼서 정의해줬다.
<?php
namespace App\Library;
use MingyuKim\PhpKafka\Libraries\ApiLibrary;
abstract class BaseLibrary
{
/**
* @param array $data
* @param string|null $filePath
* @param string $fileName
* @return void
* @description 지정한 filePath 및 fileName 장소에 data를 json형태로 저장한 후 파일 저장 경로를 반환한다.
*/
protected function makeJsonFile(array $data, ?string $filePath, string $fileName): string
{
try {
$fileFullPath = !is_null($filePath) ? $filePath . $fileName : $fileName;
// 디렉토리가 존재하는지 확인하고, 없으면 생성
if (!is_null($filePath) && !file_exists($filePath)) {
mkdir($filePath, 0777, true); // true는 중첩된 디렉토리 생성을 허용
}
$jsonData = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
file_put_contents($fileFullPath, $jsonData);
// 파일의 전체 경로 반환
return $fileFullPath;
} catch (\Exception $exception) {
die("makeJsonFile ERROR :: " . $exception->getMessage());
}
}
protected function checkAlreadyExist(?string $filePath, string $fileName): bool
{
$fileFullPath = !is_null($filePath) ? $filePath . $fileName : $fileName;
if (file_exists($fileFullPath)) {
return true;
}
return false;
}
protected function initApiLibrary(string $method, string $url, mixed $data): void
{
$this->apiLibrary = ApiLibrary::getInstance();
$this->apiLibrary->setMethod($method);
$this->apiLibrary->setApiUrl($url);
$this->apiLibrary->setRequestData($data);
}
}
이제 위 추상 클래스를 상속받는 NaverLibrary 클래스를 생성한다.
<?php
namespace App\Library;
use MingyuKim\PhpKafka\Libraries\ApiLibrary;
class NaverLibrary extends BaseLibrary
{
protected ApiLibrary $apiLibrary;
protected string $outputDataPath;
private string $client_id;
private string $client_secret;
public function __construct()
{
$this->client_id = config('naver.client_id');
$this->client_secret = config('naver.client_secret');
$this->outputDataPath = config('naver.output_data_path') ?? "";
}
/**
* @param string $keyword
* @return array
* @description 백과사전 서치 정보 가져오기
*/
public function getSearchDictionary(string $keyword): array
{
if (!$this->checkAlreadyExist(filePath: $this->outputDataPath, fileName: $keyword . ".json")) {
$requestData = [
'query' => $keyword
];
$this->initApiLibrary(method: 'GET', url: 'https://openapi.naver.com/v1/search/encyc', data: $requestData);
$result = $this->apiLibrary->callAPI();
if (!$result || empty($result)) {
die("getSearchDictionary :: result not found");
}
$result = json_decode($result, true);
if (json_last_error() !== JSON_ERROR_NONE) {
die("getSearchDictionary JSON decoding error: " . json_last_error_msg());
}
$this->makeDetailDictionary(keyword: $keyword, data: $result);
} else {
$result = file_get_contents($this->outputDataPath.$keyword.'.json');
$result = json_decode($result,true);
}
return $result;
}
public function makeDetailDictionary(string $keyword, array $data)
{
try {
$returnData = array();
$returnData['outFileFullPath'][] = $this->makeJsonFile(data: $data, filePath: $this->outputDataPath, fileName: $keyword . ".json");
} catch (\Exception $exception) {
die("makeDetailCategory ERROR :: " . $exception->getMessage());
}
return $returnData;
}
protected function initApiLibrary(string $method, string $url, mixed $data): void
{
parent::initApiLibrary($method, $url, $data); // TODO: Change the autogenerated stub
$header = [
'Content-Type: application/json',
'X-Naver-Client-Id: ' . $this->client_id,
'X-Naver-Client-Secret: ' . $this->client_secret
];
$this->apiLibrary->setHeader($header);
}
}
Naver API 호출시에는 Request Header에 'X-Naver-Client-Id' 및 'X-Naver-Client-Secret' 값이 필요하다. 때문에 상속 받은 메서드를 오버라이딩 하여 header값을 변경해주는 로직을 추가로 작성하였다.
결과물
정상적으로 키워드 '구조체'에 관련된 백과사전 검색 데이터가 아래와 같이 응답 및 정상적으로 파일로 저장되는 것을 확인할 수 있다.
'Laravel' 카테고리의 다른 글
Laravel로 JWT 기반 API 구현 방법 (0) | 2023.11.23 |
---|---|
티스토리 OPEN API 사용해서 블로그 데이터 추출하기 (0) | 2023.11.10 |
Laravel 프레임웍에서 Controller를 Controller, Service, Repository 패턴으로 확장하기 (1) | 2023.11.08 |
Laravel 데이터 caching 처리 하기 (1) | 2023.11.08 |
Laravel Authentication Session Life Time 종료시 강제로 로그아웃 로그 쌓기 (0) | 2023.11.08 |