refactor: [structure] sam/ 하위 문서를 docs 루트로 재배치

- .gitignore를 sam/ 기반에서 루트 기반으로 변경
- sam/docs/ 하위 문서를 루트로 이동 (contracts, features, guides, plans 등)
- sam/ 폴더 삭제 (docker, coocon 포함)
This commit is contained in:
2026-03-09 22:53:07 +09:00
parent cc38b00c11
commit 7a969b9d57
64 changed files with 18723 additions and 15 deletions

View File

@@ -0,0 +1,119 @@
# Gemini 모델 업그레이드: 2.0-flash → 2.5-flash
**날짜:** 2026-03-03
**작업자:** Claude Code
---
## 변경 개요
Google이 2026년 6월 1일부로 Gemini 2.0 Flash 모델 서비스를 종료한다는 통보를 받아, SAM 시스템 전체의 Gemini 모델을 `gemini-2.0-flash``gemini-2.5-flash`로 마이그레이션했다.
---
## 변경 사유
- Google의 공식 메일 통보: Gemini 2.0 Flash / 2.0 Flash-Lite → 2026-06-01 강제 종료
- 마이그레이션 경로: `gemini-2.0-flash``gemini-2.5-flash`
- API 키, Base URL 변경 없음 (모델명만 변경)
---
## 수정된 파일
### API 프로젝트 (`/home/aweso/sam/api`)
| 파일 | 변경 내용 |
|------|----------|
| `.env` | `GEMINI_MODEL=gemini-2.0-flash``gemini-2.5-flash` |
| `config/services.php` | fallback 기본값 `gemini-2.0-flash``gemini-2.5-flash` |
| `app/Services/AiReportService.php` | fallback 기본값 변경 |
### MNG 프로젝트 (`/home/aweso/sam/mng`)
| 파일 | 변경 내용 |
|------|----------|
| `.env` | `GEMINI_MODEL=gemini-2.0-flash``gemini-2.5-flash` |
| `config/services.php` | fallback 기본값 변경 |
| `app/Models/System/AiConfig.php` | `DEFAULT_MODELS['gemini']` 상수 + `getActiveGemini()` fallback 변경 |
| `app/Services/NotionService.php` | fallback 기본값 변경 |
| `resources/views/system/ai-config/index.blade.php` | UI placeholder, 기본값, JS defaultModels 변경 |
| `resources/views/google-cloud/ai-guide/index.blade.php` | 서비스 현황 테이블 모델명 7곳 변경 |
| `resources/views/academy/env-management.blade.php` | 환경변수 예시 테이블 변경 |
### 문서 (`/home/aweso/sam/docs`)
| 파일 | 변경 내용 |
|------|----------|
| `guides/ai-config-settings.md` | 기본 모델명 업데이트, 최종 업데이트 날짜 변경 |
| `guides/ai-management.md` | **신규** — AI 관리 종합 가이드 (아키텍처, 버전 이력, 온보딩) |
| `guides/ai-model-update-workflow.md` | **신규** — 모델 업데이트 표준 절차 (7단계 워크플로우) |
| `changes/20260303_gemini_model_upgrade.md` | **신규** — 이 변경 이력 문서 |
### 수정하지 않은 파일 (의도적)
| 파일 | 이유 |
|------|------|
| `api/database/migrations/2026_01_27_*.php` | 이미 실행된 마이그레이션 — 변경 시 DB 무결성 문제 |
| `api/database/migrations/2026_02_07_*.php` | 동일 |
| `api/database/migrations/2026_02_09_*.php` | 동일 |
| `mng/views/google-cloud/cloud-api-pricing/index.blade.php` | `2.0 → 2.5` 마이그레이션 안내 UI — 이전 모델명이 의도적 잔존 |
---
## 서버 .env 수정 필요 (배포 후)
| 환경 | 파일 | 변수 | 담당 |
|------|------|------|------|
| 개발서버 | `/home/webservice/api/.env` | `GEMINI_MODEL=gemini-2.5-flash` | SSH 접속 수정 |
| 개발서버 | `/home/webservice/mng/.env` | `GEMINI_MODEL=gemini-2.5-flash` | SSH 접속 수정 |
| 운영서버 | `/home/webservice/api/.env` | `GEMINI_MODEL=gemini-2.5-flash` | 개발팀장 직접 |
| 운영서버 | `/home/webservice/mng/.env` | `GEMINI_MODEL=gemini-2.5-flash` | 개발팀장 직접 |
수정 후 반드시 실행:
```bash
php artisan config:clear
```
---
## DB 단가 설정 필요
MNG `/system/ai-token-usage` → 단가 설정에서:
- 기존 `gemini-2.0-flash` 단가 → 비활성화
- 신규 `gemini-2.5-flash` 단가 추가:
- `input_price_per_million`: 0.15
- `output_price_per_million`: 0.60
- `exchange_rate`: 현재 환율
---
## 테스트 체크리스트
- [x] 로컬 .env 수정 완료
- [x] 코드 fallback 전체 변경 완료
- [ ] 로컬 연결 테스트 (MNG `/system/ai-config`)
- [ ] 개발서버 .env 수정 + config:clear
- [ ] 개발서버 연결 테스트
- [ ] 운영서버 .env 수정 + config:clear
- [ ] DB 단가 설정 (gemini-2.5-flash)
- [ ] 토큰 사용량 로그 확인 (새 모델명)
---
## 롤백 절차
문제 발생 시 `.env`만 되돌리면 즉시 복구:
```bash
# 모든 환경의 .env에서
GEMINI_MODEL=gemini-2.0-flash
php artisan config:clear
```
---
## 관련 문서
- [AI 관리 종합 가이드](../guides/ai-management.md)
- [모델 업데이트 워크플로우](../guides/ai-model-update-workflow.md)
- [AI 설정 기술문서](../guides/ai-config-settings.md)

View File

@@ -0,0 +1,165 @@
# 계좌 입출금내역 부분 월 조회 시 무한루프 크래시 수정
**날짜:** 2026-03-04
**작업자:** Claude Code
---
## 변경 개요
계좌 입출금내역 페이지에서 **날짜를 수동 입력**하여 조회 시 500 에러가 발생하는 문제를 수정했다.
편의 버튼(이번달, 지난달 등)은 항상 전체 월(1일~말일)을 사용하여 문제가 없었으나,
수동으로 날짜를 입력하면 **부분 월**(예: 12/01~12/18)이 되어 무한루프가 발생했다.
---
## 근본 원인
### `splitDateRangeMonthly()` 함수의 cursor 이동 버그
긴 기간 조회 시 바로빌 SOAP API의 한계로 인해 기간을 **월별 청크**로 분할하는 함수에서,
endDate가 **월 중간**일 때 cursor가 **같은 달 1일로 되돌아가** 무한루프가 발생했다.
```php
// ❌ 버그 코드 — endDate가 월 중간이면 무한루프
$cursor = $chunkEnd->copy()->addDay()->startOfMonth();
// 예시: endDate = 20251218
// chunkEnd = 20251218
// → addDay() = 20251219
// → startOfMonth() = 20251201 ← 같은 달 1일로 되돌아감!
// → while($cursor <= $end) 조건 여전히 true → 무한 반복
```
```php
// ✅ 수정 코드 — chunkStart 기준으로 다음 월로 이동
$cursor = $chunkStart->copy()->addMonth()->startOfMonth();
// 예시: startDate = 20251201
// chunkStart = 20251201
// → addMonth() = 20260101
// → startOfMonth() = 20260101 ← 다음 달로 정상 이동
// → while($cursor <= $end) 조건 false → 루프 종료
```
### 재현 조건
| 조건 | 결과 |
|------|------|
| 전체 월 (12/01~12/31) | 정상 — `addDay()` = 01/01 → `startOfMonth()` = 01/01 |
| 부분 월 (12/01~12/18) | **무한루프**`addDay()` = 12/19 → `startOfMonth()` = 12/01 |
| 다중 월 (12/01~02/18) | **무한루프** — 마지막 월이 부분 월이면 동일 증상 |
### 증상
- PHP 프로세스가 메모리 한도(256M/512M)에 도달하여 **Fatal Error로 크래시**
- Laravel 로그에 에러 기록 없음 (try-catch 밖에서 프로세스가 종료)
- 프론트엔드에 `서버 응답 오류 (500):` (빈 응답 본문)
---
## 수정된 파일
| 파일 | 변경 내용 |
|------|----------|
| `app/Http/Controllers/Barobill/EaccountController.php` | `splitDateRangeMonthly()` cursor 이동 로직 수정 |
---
## 검증 결과
tinker에서 수정 전후 비교 테스트:
```
=== 수정 전 (버그): 20251201~20251218 ===
→ 같은 청크 무한 반복 (10회 제한으로 강제 중단)
=== 수정 후: 20251201~20251218 ===
→ [{start: 20251201, end: 20251218}] ← 1개 청크, 정상
=== 수정 후: 20251201~20260218 (다중 월) ===
→ [{20251201~20251231}, {20260101~20260131}, {20260201~20260218}] ← 3개 청크, 정상
=== 수정 후: 20251215~20251231 ===
→ [{start: 20251215, end: 20251231}] ← 1개 청크, 정상
```
---
## 동일 패턴 코드베이스 점검 결과
`sam/mng` 전체를 검색하여 유사 패턴을 점검했다:
| 파일 | 함수 | 패턴 | 위험도 |
|------|------|------|--------|
| `EaccountController.php` | `splitDateRangeMonthly()` | 월별 청크 분할 | ✅ 수정 완료 |
| `DashboardStatService.php` | `generateDateRange()` | `addDay()` 단순 증가 | 안전 |
| `InspectionCycle.php` | `getHolidayDates()` | `addDay()` 단순 증가 | 안전 |
| `CorporateCardController.php` | `getNextBusinessDay()` | `addDay()` 단순 증가 | 안전 |
| `PartitionManagementService.php` | `addPartitions()` | `for` 루프 (고정 횟수) | 안전 |
> **결론**: `EaccountController` 외에 동일 버그 패턴 없음.
> 다른 코드들은 모두 `addDay()` 단순 증가 패턴을 사용하여 무한루프 위험 없음.
---
## 교훈 및 방지 규칙
### R1. 날짜 cursor 이동 시 `chunkEnd` 기반 이동 금지
```php
// ❌ 위험: chunkEnd가 월 중간이면 startOfMonth()가 같은 달로 되돌림
$cursor = $chunkEnd->copy()->addDay()->startOfMonth();
// ✅ 안전: chunkStart 기준으로 항상 다음 월로 이동
$cursor = $chunkStart->copy()->addMonth()->startOfMonth();
```
### R2. 날짜 루프에 안전장치(max iterations) 추가 권장
```php
$maxIterations = 120; // 10년 = 120개월
$iterations = 0;
while ($cursor->lte($end) && $iterations < $maxIterations) {
// ... 청크 처리 ...
$iterations++;
}
if ($iterations >= $maxIterations) {
Log::error('날짜 분할 루프 안전장치 작동', compact('startDate', 'endDate'));
}
```
### R3. 부분 월 테스트 필수
날짜 범위를 분할하는 코드 작성/수정 시 반드시 다음 케이스를 테스트:
- [ ] 전체 월 (01일~말일)
- [ ] 부분 월 — 시작 (01일~중간)
- [ ] 부분 월 — 끝 (중간~말일)
- [ ] 다중 월 (마지막 월이 부분 월)
- [ ] 같은 날 (시작일 = 종료일)
---
## 부수 개선 사항
이 문제 조사 과정에서 추가로 발견/수정된 항목:
| 항목 | 내용 |
|------|------|
| WSDL 캐싱 | `WSDL_CACHE_NONE``WSDL_CACHE_BOTH` (4개 바로빌 컨트롤러 전체) |
| 소켓 타임아웃 | `default_socket_timeout` 60→120초 연장 |
| Shutdown handler | PHP Fatal Error 감지 시 Laravel 로그에 기록 |
| SOAP 호출 로깅 | 호출 시작/완료 시간 + 소요시간(ms) 기록 |
---
## 관련 문서
- `app/Http/Controllers/Barobill/EaccountController.php` — 바로빌 계좌 입출금내역
---
**최종 업데이트**: 2026-03-04

View File

@@ -0,0 +1,69 @@
# 품의서 지급방법 UI 개선
**날짜:** 2026-03-06
**작업자:** Claude Code
## 변경 개요
품의서 2종(구매품의서, 비용정산품의서)에 지급방법 선택 기능을 추가/개선하였다.
## 변경 내용
### 1. 구매품의서 (pr_purchase) - 지급방법 추가
- 납품 정보(납품업체/납품예정일/납품장소) 아래에 **지급방법 radio** 추가
- 옵션: `법인카드` / `계좌이체`
- **일괄 선택** 방식 (전체 구매건에 하나의 지급방법)
### 2. 비용정산품의서 (pr_settlement) - 지급방법 행별 변경
- 기존: 테이블 아래에 일괄 radio (법인카드/개인선지출)
- 변경: 각 내역행에 **지급방법 select** 컬럼 추가
- 테이블 하단에 **지급방법별 합계표** 추가 (법인카드 합계 / 개인선지출 합계)
- 이유: 하나의 정산서에 법인카드/개인선지출 내역이 혼재할 수 있음
### 3. 지출품의서 (pr_expense) - 라벨 변경
- `사용일자` -> `지출일자` 라벨 변경 (폼 + 조회 화면)
## 수정된 파일
| 파일 | 변경 내용 |
|------|----------|
| `mng/resources/views/approvals/partials/_purchase-request-form.blade.php` | 구매품의서 지급방법 radio 추가, 비용정산품의서 행별 select + 합계표, 지출일자 라벨 변경 |
| `mng/resources/views/approvals/partials/_purchase-request-show.blade.php` | 구매품의서/비용정산품의서 조회 화면 동기화 |
## Alpine.js 데이터 변경
### 구매품의서
```javascript
// formData에 추가
payment_method: initialData?.payment_method || '',
// getFormData()에 포함
{ ...base, ..., payment_method: this.formData.payment_method }
```
### 비용정산품의서
```javascript
// makeItem()에 추가
payment_method: data?.payment_method || '',
// computed 속성 추가
get corporateCardTotal() { /* corporate_card 행만 합산 */ },
get personalAdvanceTotal() { /* personal_advance 행만 합산 */ },
// getFormData() 변경
// 기존: payment_method: this.formData.payment_method (일괄)
// 변경: 각 item.payment_method (행별) + corporate_card_total, personal_advance_total
```
## 관련 문서
- [결재 양식 기술 명세](../features/approvals/form-types.md) - 섹션 12, 14 업데이트
---
**최종 업데이트**: 2026-03-06