From ab042cb13270a6a4b8693ac4e3ab1261a5dcc6cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Mon, 2 Mar 2026 10:41:50 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[claude-code]=20Claude=20Code=20?= =?UTF-8?q?=EB=89=B4=EC=8A=A4=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GitHub Releases API 연동 서비스 (1시간 캐싱) - 뉴스 컨트롤러 + Blade 뷰 (릴리즈 카드 목록) - /claude-code/news 라우트 그룹 등록 --- .../Controllers/ClaudeCode/NewsController.php | 42 +++++ app/Services/ClaudeCodeNewsService.php | 74 ++++++++ .../views/claude-code/news/index.blade.php | 166 ++++++++++++++++++ routes/web.php | 13 ++ 4 files changed, 295 insertions(+) create mode 100644 app/Http/Controllers/ClaudeCode/NewsController.php create mode 100644 app/Services/ClaudeCodeNewsService.php create mode 100644 resources/views/claude-code/news/index.blade.php diff --git a/app/Http/Controllers/ClaudeCode/NewsController.php b/app/Http/Controllers/ClaudeCode/NewsController.php new file mode 100644 index 00000000..ea7818f2 --- /dev/null +++ b/app/Http/Controllers/ClaudeCode/NewsController.php @@ -0,0 +1,42 @@ +header('HX-Request')) { + return response('', 200)->header('HX-Redirect', route('claude-code.news.index')); + } + + $releases = $this->newsService->getReleases(); + + return view('claude-code.news.index', compact('releases')); + } + + /** + * 캐시 새로고침 + */ + public function refreshCache(): RedirectResponse + { + $this->newsService->clearCache(); + + return redirect()->route('claude-code.news.index') + ->with('success', '캐시가 새로고침되었습니다.'); + } +} diff --git a/app/Services/ClaudeCodeNewsService.php b/app/Services/ClaudeCodeNewsService.php new file mode 100644 index 00000000..1bb4be4b --- /dev/null +++ b/app/Services/ClaudeCodeNewsService.php @@ -0,0 +1,74 @@ +fetchFromGitHub($perPage); + }); + } + + /** + * 캐시 클리어 + */ + public function clearCache(): void + { + Cache::forget(self::CACHE_KEY); + } + + /** + * GitHub API에서 릴리즈 정보 가져오기 + */ + private function fetchFromGitHub(int $perPage): array + { + try { + $response = Http::withHeaders([ + 'Accept' => 'application/vnd.github.v3+json', + 'User-Agent' => 'SAM-MNG-App', + ])->timeout(10)->get(self::API_URL, [ + 'per_page' => $perPage, + ]); + + if (! $response->successful()) { + return []; + } + + return collect($response->json()) + ->map(function ($release) { + return [ + 'id' => $release['id'], + 'tag_name' => $release['tag_name'], + 'name' => $release['name'] ?? $release['tag_name'], + 'body_html' => Str::markdown($release['body'] ?? ''), + 'published_at' => $release['published_at'], + 'author' => $release['author']['login'] ?? 'unknown', + 'author_avatar' => $release['author']['avatar_url'] ?? '', + 'html_url' => $release['html_url'], + 'prerelease' => $release['prerelease'] ?? false, + 'draft' => $release['draft'] ?? false, + ]; + }) + ->toArray(); + } catch (\Exception $e) { + report($e); + + return []; + } + } +} diff --git a/resources/views/claude-code/news/index.blade.php b/resources/views/claude-code/news/index.blade.php new file mode 100644 index 00000000..2403e5a2 --- /dev/null +++ b/resources/views/claude-code/news/index.blade.php @@ -0,0 +1,166 @@ +@extends('layouts.app') + +@section('title', 'Claude Code 뉴스') + +@push('styles') + +@endpush + +@section('content') +
+ {{-- 페이지 헤더 --}} +
+
+

Claude Code 뉴스

+

GitHub Releases에서 최신 업데이트를 확인합니다.

+
+
+
+ @csrf + +
+ + + GitHub + +
+
+ + {{-- 성공 메시지 --}} + @if(session('success')) +
+ {{ session('success') }} +
+ @endif + + @if(count($releases) > 0) + {{-- 요약 카드 --}} +
+
+
최신 버전
+
{{ $releases[0]['tag_name'] }}
+
+
+
총 릴리즈
+
{{ count($releases) }}건
+
+
+
최근 릴리즈
+
+ {{ \Carbon\Carbon::parse($releases[0]['published_at'])->format('Y-m-d') }} +
+
+
+ + {{-- 릴리즈 목록 --}} +
+ @foreach($releases as $index => $release) +
+ {{-- 헤더 --}} +
+
+ + {{ $release['tag_name'] }} + + {{ $release['name'] }} + @if($release['prerelease']) + + Pre-release + + @endif +
+
+
+ @if($release['author_avatar']) + + @endif + {{ $release['author'] }} +
+ + {{ \Carbon\Carbon::parse($release['published_at'])->format('Y-m-d H:i') }} + + + + + +
+
+ + {{-- 본문 (첫번째만 열림) --}} +
+
+ {!! $release['body_html'] !!} +
+
+
+ @endforeach +
+ @else + {{-- 빈 상태 --}} +
+ + + +

릴리즈 정보를 불러올 수 없습니다

+

GitHub API에 접속할 수 없거나 릴리즈가 없습니다.

+
+
+ @csrf + +
+
+
+ @endif +
+ + +@endsection diff --git a/routes/web.php b/routes/web.php index 2d2f51d7..b6696e33 100644 --- a/routes/web.php +++ b/routes/web.php @@ -32,6 +32,7 @@ use App\Http\Controllers\Juil\ConstructionSitePhotoController; use App\Http\Controllers\Juil\MeetingMinuteController; use App\Http\Controllers\Juil\PlanningController; +use App\Http\Controllers\ClaudeCode\NewsController as ClaudeCodeNewsController; use App\Http\Controllers\Lab\StrategyController; use App\Http\Controllers\MenuController; use App\Http\Controllers\MenuSyncController; @@ -712,6 +713,18 @@ return redirect()->route('dashboard'); }); + /* + |-------------------------------------------------------------------------- + | Claude Code Routes + |-------------------------------------------------------------------------- + */ + Route::prefix('claude-code')->name('claude-code.')->group(function () { + Route::prefix('news')->name('news.')->group(function () { + Route::get('/', [ClaudeCodeNewsController::class, 'index'])->name('index'); + Route::post('/refresh', [ClaudeCodeNewsController::class, 'refreshCache'])->name('refresh-cache'); + }); + }); + /* |-------------------------------------------------------------------------- | R&D Labs Routes (5130 마이그레이션)