메뉴관리, 다국어관리, 토큰문제 해결

This commit is contained in:
kjs
2025-08-21 14:47:07 +09:00
parent 71d34ffd88
commit 86017c257d
14 changed files with 881 additions and 227 deletions

View File

@@ -837,6 +837,9 @@ export const logger = winston.createLogger({
12. **JWT 토큰 관리**: 프론트엔드 API 클라이언트에서 JWT 토큰을 자동으로 포함하여 인증 문제 해결
13. **환경변수 관리**: Prisma 스키마에서 직접 데이터베이스 URL 설정으로 환경변수 로딩 문제 해결
14. **어드민 메뉴 인증**: 새 탭에서 열리는 어드민 페이지의 토큰 인증 문제 해결 - localStorage 공유 활용
15. **관리자 메뉴 내 페이지 이동 토큰 문제**: 레이아웃 레벨 토큰 확인 및 동기화 구현
16. **API 클라이언트 통일**: 모든 API에서 apiClient 사용으로 토큰 자동 전달 보장
17. **토큰 동기화 유틸리티**: localStorage와 sessionStorage 간 토큰 동기화 및 복원 기능
## 🔐 인증 및 보안 가이드
@@ -972,6 +975,175 @@ const TokenManager = {
};
```
#### 토큰 동기화 유틸리티
```typescript
// lib/sessionManager.ts
export const tokenSync = {
// 토큰 상태 확인
checkToken: () => {
const token = localStorage.getItem("authToken");
console.log("🔍 토큰 상태 확인:", token ? "존재" : "없음");
return !!token;
},
// 토큰 강제 동기화 (다른 탭에서 설정된 토큰을 현재 탭에 복사)
forceSync: () => {
const token = localStorage.getItem("authToken");
if (token) {
// sessionStorage에도 복사
sessionStorage.setItem("authToken", token);
console.log("🔄 토큰 강제 동기화 완료");
return true;
}
return false;
},
// 토큰 복원 시도 (sessionStorage에서 복원)
restoreFromSession: () => {
const sessionToken = sessionStorage.getItem("authToken");
if (sessionToken) {
localStorage.setItem("authToken", sessionToken);
console.log("🔄 sessionStorage에서 토큰 복원 완료");
return true;
}
return false;
},
// 토큰 유효성 검증
validateToken: (token: string) => {
if (!token) return false;
try {
// JWT 토큰 구조 확인 (header.payload.signature)
const parts = token.split(".");
if (parts.length !== 3) return false;
// payload 디코딩 시도
const payload = JSON.parse(atob(parts[1]));
const now = Math.floor(Date.now() / 1000);
// 만료 시간 확인
if (payload.exp && payload.exp < now) {
console.log("❌ 토큰 만료됨");
return false;
}
console.log("✅ 토큰 유효성 검증 통과");
return true;
} catch (error) {
console.log("❌ 토큰 유효성 검증 실패:", error);
return false;
}
},
};
```
#### 관리자 레이아웃 토큰 확인
```typescript
// app/(main)/admin/layout.tsx
export default function AdminLayout({
children,
}: {
children: React.ReactNode;
}) {
const [isAuthorized, setIsAuthorized] = useState<boolean | null>(null);
// 토큰 확인 및 인증 상태 체크
useEffect(() => {
const checkToken = () => {
const token = localStorage.getItem("authToken");
// 토큰이 없으면 sessionStorage에서 복원 시도
if (!token && sessionToken) {
const restored = tokenSync.restoreFromSession();
if (restored) {
setIsAuthorized(true);
return;
}
}
// 토큰 유효성 검증
if (token && !tokenSync.validateToken(token)) {
localStorage.removeItem("authToken");
sessionStorage.removeItem("authToken");
setIsAuthorized(false);
return;
}
if (!token) {
setIsAuthorized(false);
return;
}
// 토큰이 있으면 인증된 것으로 간주
setIsAuthorized(true);
// 토큰 강제 동기화 (다른 탭과 동기화)
tokenSync.forceSync();
};
// 초기 토큰 확인
checkToken();
// localStorage 변경 이벤트 리스너 추가
const handleStorageChange = (e: StorageEvent) => {
if (e.key === "authToken") {
checkToken();
}
};
// 페이지 포커스 시 토큰 재확인
const handleFocus = () => {
checkToken();
};
window.addEventListener("storage", handleStorageChange);
window.addEventListener("focus", handleFocus);
return () => {
window.removeEventListener("storage", handleStorageChange);
window.removeEventListener("focus", handleFocus);
};
}, [pathname]);
}
```
#### API 클라이언트 통일
```typescript
// lib/api/user.ts - 수정 전 (fetch 사용)
async function apiCall<T = any>(
endpoint: string,
options: RequestInit = {}
): Promise<ApiResponse<T>> {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
headers: {
"Content-Type": "application/json",
...options.headers,
},
credentials: "include",
...options,
});
// 토큰 수동 추가 필요
}
// lib/api/user.ts - 수정 후 (apiClient 사용)
export async function getUserList(params?: Record<string, any>) {
try {
const response = await apiClient.get("/admin/users", {
params: params,
});
// 토큰 자동 추가됨
return response.data;
} catch (error) {
console.error("❌ 사용자 목록 API 오류:", error);
throw error;
}
}
```
#### 백엔드 토큰 검증
```typescript
@@ -1011,6 +1183,38 @@ export const authenticateToken = (
};
```
### 토큰 인증 문제 해결 완료 사항
#### ✅ 해결된 문제들
1. **어드민 메뉴 토큰 인증 문제**
- 새 탭에서 열리는 어드민 페이지의 토큰 공유 ✅
- localStorage 기반 토큰 동기화 ✅
2. **관리자 메뉴 내 페이지 이동 시 토큰 문제**
- 레이아웃 레벨에서 토큰 확인 로직 추가 ✅
- 실시간 토큰 동기화 및 검증 ✅
3. **사용자 관리 메뉴 특정 인증 문제**
- API 클라이언트 통일 (fetch → apiClient) ✅
- 토큰 자동 전달 활성화 ✅
#### 🔧 구현된 기능들
- **토큰 동기화 유틸리티**: `tokenSync` 모듈
- **강화된 인증 체크**: 레이아웃 레벨 토큰 검증
- **API 클라이언트 통일**: 모든 API에서 토큰 자동 전달
- **디버깅 도구**: 상세한 토큰 상태 확인 및 API 테스트
#### 📝 테스트 방법
1. **Admin 버튼 클릭** → 어드민 페이지 열기
2. **사이드바 메뉴 클릭** → 다른 관리자 페이지로 이동
3. **디버깅 페이지 확인**`/admin/debug-layout`에서 토큰 상태 확인
4. **API 테스트** → 각 메뉴에서 API 호출 정상 작동 확인
## 🎯 성공 지표
1. **성능 개선**: API 응답 시간 30% 단축
@@ -1021,6 +1225,6 @@ export const authenticateToken = (
---
**마지막 업데이트**: 2024년 12월 20일
**버전**: 1.8.0
**버전**: 1.9.0
**작성자**: AI Assistant
**현재 상태**: Phase 1 완료, Phase 2-1A 완료, Phase 2-1B 완료, Phase 2-2A 완료 ✅ (메뉴 API 구현 완료, 어드민 메뉴 인증 문제 해결)
**현재 상태**: Phase 1 완료, Phase 2-1A 완료, Phase 2-1B 완료, Phase 2-2A 완료 ✅ (메뉴 API 구현 완료, 어드민 메뉴 인증 문제 해결, 토큰 인증 문제 완전 해결)