diff --git a/journey/troubleshooting/251117_admin_dashboard_standard_deployment_refactoring.md b/journey/troubleshooting/251117_admin_dashboard_standard_deployment_refactoring.md index b6e766c..5e62dba 100644 --- a/journey/troubleshooting/251117_admin_dashboard_standard_deployment_refactoring.md +++ b/journey/troubleshooting/251117_admin_dashboard_standard_deployment_refactoring.md @@ -244,3 +244,118 @@ curl -X POST http://localhost:8100/admin/api/login \ - 프론트엔드 JavaScript는 브라우저 환경에서 실행되므로 상대 경로(`/admin/api/login`) 사용 가능 - Gateway는 nginx를 통해 프록시되므로 nginx 설정(`/admin/api/` → Gateway) 확인 필요 +### JavaScript 문법 오류 및 auth 리다이렉트 제거 + +**문제**: +1. JavaScript "Illegal return statement" 오류 발생 +2. JWT 토큰이 없을 때 auth.ro-being.com으로 리다이렉트됨 (admin dashboard는 자체 로그인 사용) + +**원인**: +1. 스크립트 최상위 레벨에서 `return` 문 사용 (함수 밖에서 사용 불가) +2. admin dashboard는 자체 비밀번호 로그인을 사용해야 하는데 auth 서버로 리다이렉트하는 로직이 있음 + +**해결**: + +1. **IIFE로 스크립트 감싸기** (`admin-dashboard/frontend/index.html`): + ```javascript + + ``` + - 전체 스크립트를 즉시 실행 함수로 감싸서 `return` 문 사용 가능하도록 수정 + +2. **auth 리다이렉트 로직 제거**: + ```javascript + // Before: JWT가 없으면 auth.ro-being.com으로 리다이렉트 + if (!jwtToken && !window.location.hostname.includes('auth.ro-being.com')) { + window.location.href = 'https://auth.ro-being.com/login?redirect=...'; + return; + } + + // After: JWT가 있으면 대시보드, 없으면 로그인 화면 + if (jwtToken) { + showDashboard(); + } else { + showLogin(); + } + ``` + +3. **401 에러 처리 수정**: + ```javascript + // Before: 401 에러 시 auth 서버로 리다이렉트 + if (response.status === 401) { + window.location.href = 'https://auth.ro-being.com/login?redirect=...'; + } + + // After: 401 에러 시 로그인 화면으로 전환 + if (response.status === 401) { + localStorage.removeItem('auth_token'); + showLogin(); + } + ``` + +**교훈**: +- JavaScript에서 최상위 레벨의 `return` 문은 함수 내부에서만 사용 가능 +- IIFE를 사용하면 스크립트 전체를 함수로 감싸서 `return` 문 사용 가능 +- Admin dashboard는 자체 인증 시스템을 사용하므로 auth 서버 리다이렉트 불필요 + +### API 경로 수정 및 JWT Secret Key 일치 + +**문제**: +1. API 호출이 `/admin/*` 경로로 되어 있어 Gateway 라우팅과 불일치 +2. JWT 토큰 검증 실패 (401 Unauthorized) + +**원인**: +1. 프론트엔드에서 `/admin/system/overview` 같은 경로로 호출하지만 Gateway는 `/admin/api/*`만 처리 +2. Gateway의 JWT_SECRET_KEY와 admin-dashboard backend의 SECRET_KEY가 불일치 + +**해결**: + +1. **API 경로 자동 변환** (`admin-dashboard/frontend/index.html`): + ```javascript + async function apiCall(endpoint) { + // endpoint가 /admin/로 시작하면 /admin/api/로 변경 + const apiEndpoint = endpoint.startsWith('/admin/') && !endpoint.startsWith('/admin/api/') + ? endpoint.replace('/admin/', '/admin/api/') + : endpoint; + + const response = await fetch(apiEndpoint, { + headers: { + 'Authorization': `Bearer ${token}` + } + }); + // ... + } + ``` + +2. **JWT Secret Key 일치** (`robeing-gateway/docker-compose.yml`): + ```yaml + services: + robeing-gateway: + environment: + - JWT_SECRET_KEY=admin_secret_key_robeing_2025 + ``` + - admin-dashboard backend의 `SECRET_KEY = "admin_secret_key_robeing_2025"`와 동일하게 설정 + - Gateway 재빌드 필요: `docker compose down && docker compose up -d --build` + +**검증**: +```bash +# JWT 토큰 발급 +TOKEN=$(curl -s -X POST http://localhost:8000/admin/login \ + -H "Content-Type: application/json" \ + -d '{"password":"19800508"}' | jq -r '.access_token') + +# API 호출 테스트 +curl -X GET http://localhost:8100/admin/api/system/overview \ + -H "Authorization: Bearer $TOKEN" +# {"timestamp":"...","cpu":{"percent":...},...} +``` + +**교훈**: +- Gateway 라우팅 경로(`/admin/api/*`)와 프론트엔드 API 호출 경로 일치 필요 +- JWT 검증을 위해서는 발급 서버와 검증 서버의 secret key가 반드시 일치해야 함 +- Docker Compose의 `environment` 섹션으로 환경변수 오버라이드 가능 +