docs: [changes] 절곡품 API 연동 수정 이력 보완 (화이트리스트, 안내메시지 추가)
This commit is contained in:
@@ -1,92 +1,117 @@
|
||||
# Docker 환경 MNG→API 호출 internal_url 미적용 500 에러 수정
|
||||
# 절곡품 관리 MNG→API 연동 오류 수정
|
||||
|
||||
**날짜:** 2026-03-21
|
||||
**작업자:** Claude Code (R&D실)
|
||||
|
||||
## 변경 개요
|
||||
|
||||
다른 개발자가 작성한 절곡품(Bending) 관련 컨트롤러 4개에서 Docker 환경의 MNG→API 내부 통신 규칙(`API_INTERNAL_URL`)을 적용하지 않아 로컬 Docker에서 500 에러가 발생하던 문제를 수정했다.
|
||||
다른 개발자가 작성한 절곡품(Bending) 관련 컨트롤러에서 3가지 문제를 수정했다:
|
||||
|
||||
## 원인
|
||||
1. Docker 환경에서 `API_INTERNAL_URL` 미적용으로 500 에러 발생
|
||||
2. API 화이트리스트 미등록으로 Bearer 토큰 없이 호출 시 401 에러 발생
|
||||
3. API 연결 실패 시 사용자에게 안내 메시지 없이 "데이터가 없습니다"만 표시
|
||||
|
||||
MNG에서 API를 호출할 때 환경별로 다른 URL을 사용해야 한다:
|
||||
## 수정 1: internal_url 미적용 (MNG)
|
||||
|
||||
### 원인
|
||||
|
||||
MNG에서 API 호출 시 `config('services.api.base_url')` (`https://api.sam.kr`)만 사용하고, Docker 내부 통신용 `internal_url` 분기 처리가 없었다.
|
||||
|
||||
| 환경 | 사용할 URL | 이유 |
|
||||
|------|-----------|------|
|
||||
| Docker (로컬) | `API_INTERNAL_URL` (`https://nginx`) | 컨테이너 간 내부 통신, `api.sam.kr` DNS 해석 불가 |
|
||||
| 서버 (개발/운영) | `API_BASE_URL` | 직접 도메인 접근 가능 |
|
||||
|
||||
문제 컨트롤러들은 `config('services.api.base_url')` (`https://api.sam.kr`)만 사용하고, `internal_url` 분기 처리가 없었다.
|
||||
|
||||
**에러 로그:**
|
||||
```
|
||||
cURL error 7: Failed to connect to api.sam.kr port 443 after 4 ms: Could not connect to server
|
||||
```
|
||||
|
||||
## 수정된 파일
|
||||
### 수정 파일 (MNG)
|
||||
|
||||
| 파일 | 변경 내용 |
|
||||
|------|----------|
|
||||
| `app/Http/Controllers/BendingBaseController.php` | `api()` 메서드에 `internal_url` + `Host` 헤더 분기 추가 |
|
||||
| `app/Http/Controllers/BendingProductController.php` | `api()` 메서드에 `internal_url` + `Host` 헤더 분기 추가 |
|
||||
| `app/Http/Controllers/FileViewController.php` | `show()` 메서드 HTTP 클라이언트에 `internal_url` 분기 추가 |
|
||||
| `app/Http/Controllers/DocumentTemplateController.php` | `getPresignedUrlFromApi()`, `getPresignedUrlByPath()` 두 메서드에 분기 추가 |
|
||||
|
||||
## 상세 변경 사항
|
||||
| `app/Http/Controllers/FileViewController.php` | `show()` 메서드에 `internal_url` 분기 추가 |
|
||||
| `app/Http/Controllers/DocumentTemplateController.php` | `getPresignedUrlFromApi()`, `getPresignedUrlByPath()` 분기 추가 |
|
||||
|
||||
### 수정 패턴
|
||||
|
||||
기존 `FormulaApiService::resolveApiConnection()` 패턴을 참고하여, `API_INTERNAL_URL`이 설정된 경우 해당 URL로 요청하되 `Host` 헤더에 원래 도메인을 전달하도록 수정했다.
|
||||
기존 `FormulaApiService::resolveApiConnection()` 패턴을 참고:
|
||||
|
||||
**수정 전:**
|
||||
```php
|
||||
$baseUrl = config('services.api.base_url', 'https://api.sam.kr');
|
||||
|
||||
return Http::baseUrl($baseUrl)
|
||||
->withoutVerifying()
|
||||
->withHeaders([...])
|
||||
```
|
||||
|
||||
**수정 후:**
|
||||
```php
|
||||
$baseUrl = config('services.api.base_url', 'https://api.sam.kr');
|
||||
$internalUrl = config('services.api.internal_url');
|
||||
|
||||
$headers = [...];
|
||||
|
||||
// Docker: internal_url(nginx 컨테이너) 경유, Host 헤더로 서버 블록 라우팅
|
||||
if ($internalUrl) {
|
||||
$headers['Host'] = parse_url($baseUrl, PHP_URL_HOST) ?: 'api.sam.kr';
|
||||
$baseUrl = $internalUrl;
|
||||
}
|
||||
|
||||
return Http::baseUrl($baseUrl)
|
||||
->withoutVerifying()
|
||||
->withHeaders($headers)
|
||||
```
|
||||
|
||||
### 동작 원리
|
||||
---
|
||||
|
||||
1. `API_INTERNAL_URL` 설정됨 (Docker): `https://nginx`로 요청 + `Host: api.sam.kr` 헤더 → Nginx가 Host 헤더로 서버 블록 라우팅
|
||||
2. `API_INTERNAL_URL` 미설정 (서버): `API_BASE_URL`로 직접 요청 (기존과 동일)
|
||||
## 수정 2: API 화이트리스트 미등록 (API)
|
||||
|
||||
### 원인
|
||||
|
||||
`ApiKeyMiddleware`의 `allowWithoutAuth` 화이트리스트에 절곡품 관련 라우트가 없어서, MNG에서 Bearer 토큰 없이 호출하면 401 Unauthorized가 반환되었다.
|
||||
|
||||
### 수정 파일 (API)
|
||||
|
||||
| 파일 | 변경 내용 |
|
||||
|------|----------|
|
||||
| `app/Http/Middleware/ApiKeyMiddleware.php` | `bending-items/*`, `guiderail-models/*`, `items/*/files`, `files/*/presigned-url` 화이트리스트 추가 |
|
||||
| `app/Http/Resources/Api/V1/BendingItemResource.php` | `presignedUrl()` 호출 시 S3 미설정 환경 에러 try-catch 처리 |
|
||||
|
||||
---
|
||||
|
||||
## 수정 3: API 오류 시 안내 메시지 (MNG)
|
||||
|
||||
### 원인
|
||||
|
||||
API 호출 실패 시 빈 데이터를 반환하여 "데이터가 없습니다"만 표시되었고, 사용자가 원인을 알 수 없었다.
|
||||
|
||||
### 수정 파일 (MNG)
|
||||
|
||||
| 파일 | 변경 내용 |
|
||||
|------|----------|
|
||||
| `app/Http/Controllers/BendingBaseController.php` | API 응답 상태별 에러 메시지 분기 |
|
||||
| `app/Http/Controllers/BendingProductController.php` | 동일 패턴 적용 |
|
||||
| `resources/views/bending/base/partials/table.blade.php` | 에러 안내 UI 추가 |
|
||||
| `resources/views/bending/products/partials/table.blade.php` | 에러 안내 UI 추가 |
|
||||
|
||||
### 안내 메시지 분기
|
||||
|
||||
| API 응답 | 표시 메시지 |
|
||||
|----------|-----------|
|
||||
| 연결 불가 (timeout/connection error) | "API 서버에 연결할 수 없습니다. API 서비스 상태를 확인해 주세요." |
|
||||
| 401 Unauthorized | "API 인증이 필요합니다. SAM 서비스에 로그인하여 API를 연결해 주세요." |
|
||||
| 403 Forbidden | "API 접근 권한이 없습니다. 관리자에게 문의해 주세요." |
|
||||
| 기타 HTTP 에러 | "API 오류가 발생했습니다. (HTTP {상태코드})" |
|
||||
| 정상 200 + 데이터 없음 | "데이터가 없습니다." |
|
||||
|
||||
---
|
||||
|
||||
## 신규 API 호출 코드 작성 시 필수 규칙
|
||||
|
||||
MNG에서 API를 호출하는 코드를 새로 작성할 때는 반드시 다음을 준수한다:
|
||||
MNG에서 API를 호출하는 코드를 새로 작성할 때:
|
||||
|
||||
```
|
||||
필수 config('services.api.internal_url') 확인 후 분기 처리
|
||||
필수 internal_url 사용 시 Host 헤더에 base_url의 도메인 전달
|
||||
필수 MNG에서 Bearer 없이 호출하려면 API 화이트리스트에 라우트 추가
|
||||
권장 FormulaApiService::resolveApiConnection() 참조
|
||||
권장 API 실패 시 사용자에게 구체적인 안내 메시지 표시
|
||||
```
|
||||
|
||||
## 테스트 체크리스트
|
||||
|
||||
- [x] 로컬 Docker에서 `/bending/base` 페이지 정상 로드
|
||||
- [x] 로컬 Docker에서 `/bending/base` 페이지 정상 로드 (266건)
|
||||
- [x] 로컬 Docker에서 `/bending/products` 페이지 정상 로드
|
||||
- [x] API 미인증 시 안내 메시지 표시 확인
|
||||
- [x] pint 코드 스타일 검사 통과
|
||||
|
||||
## 관련 문서
|
||||
|
||||
- `config/services.php` — `services.api.base_url`, `services.api.internal_url` 설정
|
||||
- `app/Services/FormulaApiService.php` — `resolveApiConnection()` 참조 구현
|
||||
- `api/app/Http/Middleware/ApiKeyMiddleware.php` — 화이트리스트 관리
|
||||
- `mng/config/services.php` — `services.api.base_url`, `services.api.internal_url` 설정
|
||||
- `mng/app/Services/FormulaApiService.php` — `resolveApiConnection()` 참조 구현
|
||||
|
||||
Reference in New Issue
Block a user