배경: - /admin 접근 시 빈 페이지 (Gateway 404) 해결: - Gateway에 /admin → frontend-base(8000) 프록시 추가 - JWT 검증 중앙화 유지 - TDD 방식으로 테스트 먼저 작성 아키텍처: - Gateway: 인증 + 라우팅 - frontend-base: 관리자 UI - 역할 분리 명확화 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
215 lines
5.8 KiB
Markdown
215 lines
5.8 KiB
Markdown
# Admin Dashboard 라우팅 구현 (Gateway 프록시 패턴)
|
|
|
|
**날짜**: 2025-11-17
|
|
**작업자**: Claude (51123 서버 관리자)
|
|
**관련 서버**: 51123
|
|
**관련 서비스**: robeing-gateway, frontend-base, nginx
|
|
|
|
---
|
|
|
|
## 배경
|
|
|
|
사용자가 `https://ro-being.com/admin`으로 관리자 대시보드 접근 시 빈 페이지가 표시되는 문제 발생.
|
|
|
|
### 초기 상태
|
|
- nginx: `/admin` → `localhost:8100` (robeing-gateway)
|
|
- robeing-gateway: `/admin` 라우팅 **없음** → 404 반환
|
|
- frontend-base: `localhost:8000`에서 실행 중, `/admin` UI 제공
|
|
|
|
### 문제 원인
|
|
robeing-gateway는 로빙 API 라우팅 전용으로 설계되어 `/admin` 경로를 처리하지 않음.
|
|
|
|
---
|
|
|
|
## 해결 방안 검토
|
|
|
|
### 방안 1: nginx에서 직접 frontend-base로 프록시
|
|
```nginx
|
|
location /admin {
|
|
proxy_pass http://localhost:8000;
|
|
}
|
|
```
|
|
|
|
**문제점**:
|
|
- JWT 검증 불가 (인증 없이 접근 가능)
|
|
- frontend-base가 51124 robeing-monitor 데이터 조회 시 인증/UUID 변환 로직 중복 필요
|
|
|
|
### 방안 2: Gateway에 `/admin` 라우팅 추가 (채택)
|
|
```python
|
|
@app.api_route("/admin/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
|
|
async def admin_proxy(
|
|
path: str,
|
|
request: Request,
|
|
user_uuid: str = Depends(get_verified_user)
|
|
):
|
|
"""Admin dashboard - proxy to frontend-base (51123:8000)"""
|
|
frontend_base_url = "http://localhost:8000"
|
|
target_url = f"{frontend_base_url}/admin/{path}" if path else f"{frontend_base_url}/admin"
|
|
|
|
logger.info(f"Admin request from user {user_uuid} to {target_url}")
|
|
|
|
async with httpx.AsyncClient() as client:
|
|
response = await client.request(
|
|
method=request.method,
|
|
url=target_url,
|
|
headers=dict(request.headers),
|
|
content=await request.body()
|
|
)
|
|
|
|
return Response(
|
|
content=response.content,
|
|
status_code=response.status_code,
|
|
headers=dict(response.headers)
|
|
)
|
|
```
|
|
|
|
**장점**:
|
|
- JWT 검증 중앙화 유지
|
|
- frontend-base가 Gateway의 인증/UUID 변환 활용 가능
|
|
- 역할 분리 명확: Gateway(인증+라우팅), frontend-base(UI)
|
|
|
|
---
|
|
|
|
## 구현 내역
|
|
|
|
### 1. robeing-gateway 코드 수정
|
|
|
|
**파일**: `/home/admin/robeing-gateway/app/main.py`
|
|
|
|
**변경 사항**:
|
|
1. Import 추가 (line 7):
|
|
```python
|
|
from fastapi.responses import JSONResponse, Response
|
|
```
|
|
|
|
2. `/admin` 라우팅 추가 (line 392-417):
|
|
- JWT 검증: `get_verified_user()` Dependency 적용
|
|
- frontend-base(localhost:8000)로 프록시
|
|
- 모든 HTTP 메서드 지원 (GET, POST, PUT, DELETE)
|
|
|
|
### 2. Gateway 재시작
|
|
```bash
|
|
cd /home/admin/robeing-gateway
|
|
docker compose down && docker compose up -d --build
|
|
```
|
|
|
|
### 3. 테스트 코드 작성
|
|
|
|
**파일**: `/home/admin/frontend-base/tests/test_admin_api.py`
|
|
|
|
**테스트 케이스**:
|
|
1. Gateway `/admin` - JWT 없음 → 401 예상
|
|
2. frontend-base `/health` → 200 OK
|
|
3. frontend-base `/admin` 직접 접근 → HTML 반환
|
|
|
|
**실행 결과**:
|
|
```
|
|
=== Test 1: Gateway /admin without JWT (예상: 401) ===
|
|
Status: 401
|
|
✅ JWT 검증 작동 확인
|
|
|
|
=== Test 2: frontend-base /health ===
|
|
Status: 200
|
|
✅ frontend-base 정상
|
|
|
|
=== Test 3: frontend-base /admin (직접 접근) ===
|
|
Status: 200
|
|
Content-Type: text/html; charset=utf-8
|
|
✅ Admin UI 정상
|
|
|
|
✅ 모든 테스트 통과
|
|
```
|
|
|
|
---
|
|
|
|
## 아키텍처 플로우
|
|
|
|
### Before (404 에러)
|
|
```
|
|
사용자 → nginx → robeing-gateway → 404 (라우팅 없음)
|
|
```
|
|
|
|
### After (정상 동작)
|
|
```
|
|
사용자 → nginx (:80/443) → robeing-gateway (:8100)
|
|
↓ JWT 검증
|
|
↓ user_uuid 추출
|
|
→ frontend-base (:8000) → Admin UI 반환
|
|
```
|
|
|
|
### frontend-base가 robeing-monitor 데이터 조회 시
|
|
```
|
|
사용자 → Gateway (:8100/admin)
|
|
→ frontend-base (:8000)
|
|
→ Gateway (:8100/api/stats) ← JWT 재사용
|
|
→ robeing-monitor (:9024, 51124 서버)
|
|
```
|
|
|
|
---
|
|
|
|
## 검증
|
|
|
|
### 1. Gateway 상태
|
|
```bash
|
|
docker ps --filter "name=robeing-gateway"
|
|
# 출력: Up X seconds (healthy)
|
|
```
|
|
|
|
### 2. 엔드포인트 테스트
|
|
```bash
|
|
# JWT 없이 (401 예상)
|
|
curl http://localhost:8100/admin
|
|
# {"detail":"Missing or invalid authorization header"}
|
|
|
|
# JWT 포함 (200 OK, HTML 반환)
|
|
curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8100/admin
|
|
# <!DOCTYPE html>...
|
|
```
|
|
|
|
### 3. 브라우저 테스트
|
|
- URL: `https://ro-being.com/admin`
|
|
- 예상: Admin Dashboard UI 표시
|
|
- JWT 자동 포함 (localStorage에서)
|
|
|
|
---
|
|
|
|
## 설계 원칙 (Gateway Proxy Pattern)
|
|
|
|
### Gateway의 역할 (중앙집중형)
|
|
1. **JWT 검증**: 모든 요청의 인증 게이트웨이
|
|
2. **Username → UUID 변환**: DB 조회 및 캐싱
|
|
3. **라우팅**:
|
|
- `/api/chat` → 로빙 서비스 (rb8001, rb10508 등)
|
|
- `/api/stats` → robeing-monitor (51124:9024)
|
|
- `/api/items` → robeing-monitor
|
|
- `/admin` → frontend-base (51123:8000)
|
|
|
|
### 역할 분리
|
|
- **robeing-gateway**: 인증 + 라우팅 (코드 없는 프록시)
|
|
- **frontend-base**: 관리자 UI + 시스템 메트릭 대시보드
|
|
- **robeing-monitor**: 로빙 통계, 아이템 관리 (51124)
|
|
|
|
---
|
|
|
|
## 관련 문서
|
|
|
|
- **Gateway 아키텍처**: `/home/admin/DOCS/book/300_architecture/gateway_proxy_patterns.md`
|
|
- **전체 시스템 구조**: `/home/admin/DOCS/book/300_architecture/310_전체_시스템_구조_컨테이너와_마이크로서비스.md`
|
|
- **Gateway 구현 히스토리**: `/home/admin/DOCS/journey/troubleshooting/250809_happybell80_robing-gateway구현.md`
|
|
|
|
---
|
|
|
|
## 교훈
|
|
|
|
### 설계 검증의 중요성
|
|
- 새 서비스 추가 시 기존 Gateway 라우팅 검토 필수
|
|
- 각 서비스의 역할 명확히 문서화
|
|
|
|
### TDD 접근
|
|
- 테스트 코드 먼저 작성 (`tests/test_admin_api.py`)
|
|
- 구현 → 테스트 → 검증 순서 준수
|
|
|
|
### 일관성 있는 인증
|
|
- 모든 보호된 엔드포인트는 Gateway 경유
|
|
- JWT 검증 로직 중앙화로 보안 강화
|