- google/auth 패키지 추가 (OAuth2 Service Account 인증) - FcmSender: FCM HTTP v1 API 발송 서비스 - FcmResponse: 응답 DTO (성공/실패, 토큰 유효성 체크) - FcmException: FCM 전용 예외 클래스 - fcm:test artisan 명령어 (테스트 발송) - PushNotificationService에 FcmSender 연동 - config/fcm.php 설정 파일 추가 - 알림 유형별 채널 분리 (push_default, push_urgent)
134 lines
4.4 KiB
PHP
134 lines
4.4 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Services\Fcm\FcmSender;
|
|
use Illuminate\Console\Command;
|
|
|
|
class FcmTestCommand extends Command
|
|
{
|
|
/**
|
|
* The name and signature of the console command.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $signature = 'fcm:test
|
|
{--token= : FCM 디바이스 토큰 (필수)}
|
|
{--channel=push_default : 알림 채널 ID (push_default, push_urgent)}
|
|
{--title=테스트 알림 : 알림 제목}
|
|
{--body=FCM 테스트 메시지입니다. : 알림 내용}
|
|
{--data= : 추가 데이터 (JSON 형식)}';
|
|
|
|
/**
|
|
* The console command description.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $description = 'FCM 푸시 알림 테스트 발송';
|
|
|
|
/**
|
|
* Execute the console command.
|
|
*/
|
|
public function handle(): int
|
|
{
|
|
$token = $this->option('token');
|
|
|
|
if (empty($token)) {
|
|
$this->error('--token 옵션은 필수입니다.');
|
|
$this->line('');
|
|
$this->line('사용법:');
|
|
$this->line(' php artisan fcm:test --token=YOUR_FCM_TOKEN');
|
|
$this->line(' php artisan fcm:test --token=YOUR_FCM_TOKEN --channel=push_urgent --title="긴급 알림"');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$channel = $this->option('channel');
|
|
$title = $this->option('title');
|
|
$body = $this->option('body');
|
|
$dataJson = $this->option('data');
|
|
|
|
// 추가 데이터 파싱
|
|
$data = [];
|
|
if ($dataJson) {
|
|
$data = json_decode($dataJson, true);
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
$this->error('--data 옵션은 유효한 JSON이어야 합니다.');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
}
|
|
|
|
// 설정 확인
|
|
$this->info('FCM 테스트 발송 시작...');
|
|
$this->line('');
|
|
$this->table(
|
|
['항목', '값'],
|
|
[
|
|
['Project ID', config('fcm.project_id') ?: '(미설정)'],
|
|
['Token', substr($token, 0, 30).'...'],
|
|
['Channel', $channel],
|
|
['Title', $title],
|
|
['Body', mb_substr($body, 0, 50).(mb_strlen($body) > 50 ? '...' : '')],
|
|
['Data', $data ? json_encode($data, JSON_UNESCAPED_UNICODE) : '(없음)'],
|
|
]
|
|
);
|
|
$this->line('');
|
|
|
|
// Project ID 확인
|
|
if (empty(config('fcm.project_id'))) {
|
|
$this->error('FCM_PROJECT_ID가 설정되지 않았습니다.');
|
|
$this->line('.env 파일에 FCM_PROJECT_ID를 설정해주세요.');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
// Service Account 파일 확인
|
|
$saPath = config('fcm.service_account_path');
|
|
$fullPath = str_starts_with($saPath, '/') ? $saPath : storage_path($saPath);
|
|
|
|
if (! file_exists($fullPath)) {
|
|
$this->error("Service Account 파일을 찾을 수 없습니다: {$fullPath}");
|
|
$this->line('.env 파일에 FCM_SA_PATH를 확인해주세요.');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$this->info('Service Account 파일 확인됨: '.basename($fullPath));
|
|
|
|
// FCM 발송
|
|
try {
|
|
$sender = new FcmSender;
|
|
$response = $sender->sendToToken($token, $title, $body, $channel, $data);
|
|
|
|
$this->line('');
|
|
|
|
if ($response->success) {
|
|
$this->info('✅ FCM 발송 성공!');
|
|
$this->line("Message ID: {$response->messageId}");
|
|
$this->line("Status Code: {$response->statusCode}");
|
|
} else {
|
|
$this->error('❌ FCM 발송 실패');
|
|
$this->line("Error: {$response->error}");
|
|
$this->line("Status Code: {$response->statusCode}");
|
|
|
|
if ($response->isInvalidToken()) {
|
|
$this->warn('토큰이 유효하지 않습니다. 디바이스에서 새 토큰을 발급받아야 합니다.');
|
|
}
|
|
|
|
$this->line('');
|
|
$this->line('Response:');
|
|
$this->line(json_encode($response->rawResponse, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
} catch (\Exception $e) {
|
|
$this->error('FCM 발송 중 예외 발생: '.$e->getMessage());
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
} |