326 lines
7.8 KiB
Markdown
326 lines
7.8 KiB
Markdown
|
|
# AI 및 스토리지 설정 기술문서
|
||
|
|
|
||
|
|
> 최종 업데이트: 2026-01-29
|
||
|
|
|
||
|
|
## 개요
|
||
|
|
|
||
|
|
SAM MNG 시스템의 AI API 및 클라우드 스토리지(GCS) 설정을 관리하는 기능입니다.
|
||
|
|
관리자 UI에서 설정하거나, `.env` 환경변수로 설정할 수 있습니다.
|
||
|
|
|
||
|
|
**접근 경로**: 시스템 관리 > AI 설정 (`/system/ai-config`)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 지원 Provider
|
||
|
|
|
||
|
|
### AI Provider
|
||
|
|
| Provider | 용도 | 기본 모델 |
|
||
|
|
|----------|------|----------|
|
||
|
|
| `gemini` | Google Gemini (명함 OCR, AI 어시스턴트) | gemini-2.0-flash |
|
||
|
|
| `claude` | Anthropic Claude | claude-sonnet-4-20250514 |
|
||
|
|
| `openai` | OpenAI GPT | gpt-4o |
|
||
|
|
|
||
|
|
### Storage Provider
|
||
|
|
| Provider | 용도 |
|
||
|
|
|----------|------|
|
||
|
|
| `gcs` | Google Cloud Storage (음성 녹음 백업) |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 데이터베이스 구조
|
||
|
|
|
||
|
|
### 테이블: `ai_configs`
|
||
|
|
|
||
|
|
```sql
|
||
|
|
CREATE TABLE ai_configs (
|
||
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||
|
|
name VARCHAR(50) NOT NULL, -- 설정 이름 (예: "Production Gemini")
|
||
|
|
provider VARCHAR(20) NOT NULL, -- gemini, claude, openai, gcs
|
||
|
|
api_key VARCHAR(255) NOT NULL, -- API 키 (GCS는 'gcs_service_account')
|
||
|
|
model VARCHAR(100) NOT NULL, -- 모델명 (GCS는 '-')
|
||
|
|
base_url VARCHAR(255) NULL, -- 커스텀 Base URL
|
||
|
|
description TEXT NULL, -- 설명
|
||
|
|
is_active BOOLEAN DEFAULT FALSE, -- 활성화 여부 (provider당 1개만)
|
||
|
|
options JSON NULL, -- 추가 옵션 (아래 참조)
|
||
|
|
created_at TIMESTAMP,
|
||
|
|
updated_at TIMESTAMP,
|
||
|
|
deleted_at TIMESTAMP NULL -- Soft Delete
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
### options JSON 구조
|
||
|
|
|
||
|
|
**AI Provider (Gemini Vertex AI)**:
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"auth_type": "vertex_ai",
|
||
|
|
"project_id": "my-gcp-project",
|
||
|
|
"region": "us-central1",
|
||
|
|
"service_account_path": "/var/www/sales/apikey/google_service_account.json"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**AI Provider (API Key)**:
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"auth_type": "api_key"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**GCS Provider**:
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"bucket_name": "my-bucket-name",
|
||
|
|
"service_account_path": "/var/www/sales/apikey/google_service_account.json",
|
||
|
|
"service_account_json": { ... } // 또는 JSON 직접 입력
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 설정 우선순위
|
||
|
|
|
||
|
|
### GCS 설정 우선순위
|
||
|
|
|
||
|
|
```
|
||
|
|
1. DB 설정 (ai_configs 테이블의 활성화된 gcs provider)
|
||
|
|
↓ 없으면
|
||
|
|
2. 환경변수 (.env의 GCS_BUCKET_NAME, GCS_SERVICE_ACCOUNT_PATH)
|
||
|
|
↓ 없으면
|
||
|
|
3. 레거시 파일 (/sales/apikey/gcs_config.txt, google_service_account.json)
|
||
|
|
```
|
||
|
|
|
||
|
|
### AI 설정 우선순위
|
||
|
|
|
||
|
|
```
|
||
|
|
1. DB 설정 (ai_configs 테이블의 활성화된 provider)
|
||
|
|
↓ 없으면
|
||
|
|
2. 환경변수 (.env의 GEMINI_API_KEY 등)
|
||
|
|
↓ 없으면
|
||
|
|
3. 레거시 파일
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 환경변수 설정 (.env)
|
||
|
|
|
||
|
|
### GCS 설정
|
||
|
|
```env
|
||
|
|
# Google Cloud Storage (음성 녹음 백업)
|
||
|
|
GCS_BUCKET_NAME=your-bucket-name
|
||
|
|
GCS_SERVICE_ACCOUNT_PATH=/var/www/sales/apikey/google_service_account.json
|
||
|
|
GCS_USE_DB_CONFIG=true # false면 DB 설정 무시, .env만 사용
|
||
|
|
```
|
||
|
|
|
||
|
|
### AI 설정 (참고)
|
||
|
|
```env
|
||
|
|
# Google Gemini API
|
||
|
|
GEMINI_API_KEY=your-api-key
|
||
|
|
GEMINI_PROJECT_ID=your-project-id
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 관련 파일 목록
|
||
|
|
|
||
|
|
### 모델
|
||
|
|
| 파일 | 설명 |
|
||
|
|
|------|------|
|
||
|
|
| `app/Models/System/AiConfig.php` | AI 설정 Eloquent 모델 |
|
||
|
|
|
||
|
|
### 컨트롤러
|
||
|
|
| 파일 | 설명 |
|
||
|
|
|------|------|
|
||
|
|
| `app/Http/Controllers/System/AiConfigController.php` | CRUD + 연결 테스트 |
|
||
|
|
|
||
|
|
### 서비스
|
||
|
|
| 파일 | 설명 |
|
||
|
|
|------|------|
|
||
|
|
| `app/Services/GoogleCloudStorageService.php` | GCS 업로드/다운로드/삭제 |
|
||
|
|
| `app/Services/GeminiService.php` | Gemini API 호출 (명함 OCR 등) |
|
||
|
|
|
||
|
|
### 설정
|
||
|
|
| 파일 | 설명 |
|
||
|
|
|------|------|
|
||
|
|
| `config/gcs.php` | GCS 환경변수 설정 |
|
||
|
|
|
||
|
|
### 뷰
|
||
|
|
| 파일 | 설명 |
|
||
|
|
|------|------|
|
||
|
|
| `resources/views/system/ai-config/index.blade.php` | AI 설정 관리 페이지 |
|
||
|
|
|
||
|
|
### 라우트
|
||
|
|
```php
|
||
|
|
// routes/web.php
|
||
|
|
Route::prefix('system')->name('system.')->group(function () {
|
||
|
|
Route::resource('ai-config', AiConfigController::class)->except(['show', 'create', 'edit']);
|
||
|
|
Route::post('ai-config/{id}/toggle', [AiConfigController::class, 'toggle'])->name('ai-config.toggle');
|
||
|
|
Route::post('ai-config/test', [AiConfigController::class, 'test'])->name('ai-config.test');
|
||
|
|
Route::post('ai-config/test-gcs', [AiConfigController::class, 'testGcs'])->name('ai-config.test-gcs');
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 주요 메서드
|
||
|
|
|
||
|
|
### AiConfig 모델
|
||
|
|
|
||
|
|
```php
|
||
|
|
// Provider별 활성 설정 조회
|
||
|
|
AiConfig::getActiveGemini(); // ?AiConfig
|
||
|
|
AiConfig::getActiveClaude(); // ?AiConfig
|
||
|
|
AiConfig::getActiveGcs(); // ?AiConfig
|
||
|
|
AiConfig::getActive('openai'); // ?AiConfig
|
||
|
|
|
||
|
|
// GCS 전용 메서드
|
||
|
|
$config->getBucketName(); // ?string
|
||
|
|
$config->getServiceAccountJson(); // ?array
|
||
|
|
$config->getServiceAccountPath(); // ?string
|
||
|
|
$config->isGcs(); // bool
|
||
|
|
|
||
|
|
// Vertex AI 전용 메서드
|
||
|
|
$config->isVertexAi(); // bool
|
||
|
|
$config->getProjectId(); // ?string
|
||
|
|
$config->getRegion(); // string (기본: us-central1)
|
||
|
|
```
|
||
|
|
|
||
|
|
### GoogleCloudStorageService
|
||
|
|
|
||
|
|
```php
|
||
|
|
$gcs = new GoogleCloudStorageService();
|
||
|
|
|
||
|
|
// 사용 가능 여부
|
||
|
|
$gcs->isAvailable(); // bool
|
||
|
|
|
||
|
|
// 설정 소스 확인
|
||
|
|
$gcs->getConfigSource(); // 'db' | 'env' | 'legacy' | 'none'
|
||
|
|
|
||
|
|
// 파일 업로드
|
||
|
|
$gcsUri = $gcs->upload($localPath, $objectName); // 'gs://bucket/object' | null
|
||
|
|
|
||
|
|
// 서명된 다운로드 URL (60분 유효)
|
||
|
|
$url = $gcs->getSignedUrl($objectName, 60); // string | null
|
||
|
|
|
||
|
|
// 파일 삭제
|
||
|
|
$gcs->delete($objectName); // bool
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## UI 구조
|
||
|
|
|
||
|
|
### 탭 구성
|
||
|
|
- **AI 설정 탭**: Gemini, Claude, OpenAI 설정 관리
|
||
|
|
- **스토리지 설정 탭**: GCS 설정 관리
|
||
|
|
|
||
|
|
### 기능
|
||
|
|
- 설정 추가/수정/삭제
|
||
|
|
- 활성화/비활성화 토글 (provider당 1개만 활성화)
|
||
|
|
- 연결 테스트
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 사용 예시
|
||
|
|
|
||
|
|
### GCS 업로드 (ConsultationController)
|
||
|
|
|
||
|
|
```php
|
||
|
|
use App\Services\GoogleCloudStorageService;
|
||
|
|
|
||
|
|
public function uploadAudio(Request $request)
|
||
|
|
{
|
||
|
|
// 파일 저장
|
||
|
|
$path = $file->store("tenant/consultations/{$tenantId}");
|
||
|
|
$fullPath = storage_path('app/' . $path);
|
||
|
|
|
||
|
|
// 10MB 이상이면 GCS에도 업로드
|
||
|
|
if ($file->getSize() > 10 * 1024 * 1024) {
|
||
|
|
$gcs = new GoogleCloudStorageService();
|
||
|
|
if ($gcs->isAvailable()) {
|
||
|
|
$gcsUri = $gcs->upload($fullPath, "consultations/{$tenantId}/" . basename($path));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 명함 OCR (GeminiService)
|
||
|
|
|
||
|
|
```php
|
||
|
|
use App\Services\GeminiService;
|
||
|
|
|
||
|
|
$gemini = new GeminiService();
|
||
|
|
$result = $gemini->extractBusinessCard($imagePath);
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 배포 가이드
|
||
|
|
|
||
|
|
### 서버 최초 설정
|
||
|
|
|
||
|
|
1. `.env` 파일에 GCS 설정 추가:
|
||
|
|
```env
|
||
|
|
GCS_BUCKET_NAME=production-bucket
|
||
|
|
GCS_SERVICE_ACCOUNT_PATH=/var/www/sales/apikey/google_service_account.json
|
||
|
|
```
|
||
|
|
|
||
|
|
2. 서비스 계정 JSON 파일 배치:
|
||
|
|
```
|
||
|
|
/var/www/sales/apikey/google_service_account.json
|
||
|
|
```
|
||
|
|
|
||
|
|
3. 설정 캐시 갱신:
|
||
|
|
```bash
|
||
|
|
docker exec sam-mng-1 php artisan config:cache
|
||
|
|
```
|
||
|
|
|
||
|
|
### 이후 배포
|
||
|
|
- 코드 push만으로 동작 (설정 변경 불필요)
|
||
|
|
- UI에서 오버라이드하고 싶을 때만 DB 설정 사용
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 트러블슈팅
|
||
|
|
|
||
|
|
### GCS 업로드 실패
|
||
|
|
|
||
|
|
1. **설정 확인**:
|
||
|
|
```php
|
||
|
|
$gcs = new GoogleCloudStorageService();
|
||
|
|
dd($gcs->isAvailable(), $gcs->getConfigSource(), $gcs->getBucketName());
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **로그 확인**:
|
||
|
|
```bash
|
||
|
|
docker exec sam-mng-1 tail -f storage/logs/laravel.log | grep GCS
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **일반적인 원인**:
|
||
|
|
- 서비스 계정 파일 경로 오류
|
||
|
|
- 서비스 계정에 Storage 권한 없음
|
||
|
|
- 버킷 이름 오타
|
||
|
|
|
||
|
|
### AI API 연결 실패
|
||
|
|
|
||
|
|
1. **API 키 확인**: UI에서 "테스트" 버튼 클릭
|
||
|
|
2. **모델명 확인**: provider별 지원 모델 확인
|
||
|
|
3. **할당량 확인**: Google Cloud Console에서 API 할당량 확인
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 레거시 파일 위치 (참고)
|
||
|
|
|
||
|
|
Docker 컨테이너 내부 경로:
|
||
|
|
```
|
||
|
|
/var/www/sales/apikey/
|
||
|
|
├── gcs_config.txt # bucket_name=xxx
|
||
|
|
├── google_service_account.json # GCP 서비스 계정 키
|
||
|
|
└── gemini_api_key.txt # Gemini API 키 (레거시)
|
||
|
|
```
|
||
|
|
|
||
|
|
호스트 경로 (mng 기준):
|
||
|
|
```
|
||
|
|
../sales/apikey/
|
||
|
|
```
|