Skip to content

Commit 42de569

Browse files
authored
Merge pull request #46 from PARADOX-BSSM/release-0.2.3
PW-418
2 parents f85d0a8 + 48c1271 commit 42de569

3 files changed

Lines changed: 233 additions & 169 deletions

File tree

DESIGN_PRD.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Design & Branding PRD - Windeath44 Admin Portal
2+
3+
## 1. 디자인 철학 (Design Philosophy)
4+
**"Premium, Immersive, Dynamic"**
5+
6+
Windeath44 Admin Portal은 단순한 관리 도구를 넘어, 사용자에게 시각적 즐거움과 몰입감을 제공하는 프리미엄 대시보드를 지향합니다.
7+
8+
- **Premium**: 고해상도 배경, 정교한 블러(Blur) 효과, 세련된 타이포그래피를 통해 고급스러운 느낌을 전달합니다.
9+
- **Immersive**: 도시(City) 테마를 기반으로 한 배경과 일관된 컬러 팔레트로 사용자가 해당 공간에 있는 듯한 몰입감을 줍니다.
10+
- **Dynamic**: 부드러운 트랜지션, 호버 효과, 미세한 애니메이션(Micro-interactions)을 통해 살아있는 인터페이스를 구현합니다.
11+
12+
## 2. 비주얼 아이덴티티 (Visual Identity)
13+
14+
### 2.1 타이포그래피 (Typography)
15+
- **Primary Font**: `IBM Plex Sans`
16+
- 기계적이고 현대적인 느낌을 주며, 가독성이 뛰어나 데이터 중심의 대시보드에 적합합니다.
17+
- Weights: 300(Light), 400(Regular), 500(Medium), 600(SemiBold), 700(Bold)
18+
- **Secondary Font**: `Inter`
19+
- 시스템 UI 및 본문 텍스트의 가독성을 보장하기 위한 보조 폰트입니다.
20+
21+
### 2.2 컬러 팔레트 (Color Palette)
22+
시스템은 테마에 따라 동적으로 변화하는 CSS 변수 기반의 컬러 시스템을 사용합니다.
23+
24+
#### 공통 (Common)
25+
- **Background**: `#050505` (Dark), `rgba(255, 255, 255, 0.6)` (Light)
26+
- **Foreground**: `#ffffff` (Dark), `#000000` (Light)
27+
- **Border**: `rgba(255, 255, 255, 0.03)` (Dark), `rgba(0, 0, 0, 0.1)` (Light)
28+
29+
#### 테마별 포인트 컬러 (Accent Colors)
30+
| 테마 (Theme) | Accent Color | Hex Code | 의미 |
31+
|---|---|---|---|
32+
| **New York (Default)** | Blue | `#3b82f6` | 신뢰, 기술, 도시의 차가움 |
33+
| **San Francisco** | Orange | `#f97316` | 골든 게이트 브리지, 따뜻함, 활력 |
34+
| **Windeath44** | Pink | `#ec4899` | 브랜드 시그니처, 개성, 팝(Pop) |
35+
| **Light Mode** | Black | `#000000` | 미니멀리즘, 깔끔함, 가독성 |
36+
37+
## 3. 테마 시스템 (Theming System)
38+
사용자의 기분이나 환경에 맞춰 전환할 수 있는 4가지 핵심 테마를 제공합니다.
39+
40+
### 3.1 New York (Default)
41+
- **컨셉**: 잠들지 않는 도시의 밤.
42+
- **배경**: 뉴욕의 야경, 마천루, 비 오는 거리.
43+
- **분위기**: 차분하면서도 웅장함.
44+
- **주요 요소**: Glassmorphism 패널이 어두운 배경 위에서 빛을 발함.
45+
46+
### 3.2 San Francisco
47+
- **컨셉**: 안개 낀 금문교와 샌프란시스코의 거리.
48+
- **배경**: 골든 게이트 브리지, 케이블카, 베이 브리지.
49+
- **분위기**: 몽환적이고 따뜻한 톤.
50+
- **주요 요소**: 오렌지색 포인트가 안개 낀 배경과 대비됨.
51+
52+
### 3.3 Windeath44 (Signature)
53+
- **컨셉**: 브랜드 아이덴티티를 강조한 전용 테마.
54+
- **배경**: 커스텀 브랜드 아트워크 (`windeath44.png`).
55+
- **분위기**: 유니크하고 강렬함.
56+
- **주요 요소**: 핑크색 글로우(Glow) 효과로 브랜드 존재감 부각.
57+
58+
### 3.4 Light Mode
59+
- **컨셉**: 밝고 깨끗한 업무 환경.
60+
- **배경**: 밝은 톤의 이미지 또는 단색 배경.
61+
- **분위기**: 전문적이고 명료함.
62+
- **주요 요소**: 높은 가독성, 그림자(Shadow)를 활용한 깊이감 표현 (Glassmorphism 대신).
63+
64+
## 4. UI/UX 패턴 (UI/UX Patterns)
65+
66+
### 4.1 Glassmorphism (유리 질감)
67+
- **적용 대상**: 사이드바, 헤더, 대시보드 카드.
68+
- **구현**: `backdrop-filter: blur(12px)`, 반투명 배경색, 미세한 테두리(`border-white/10`).
69+
- **목적**: 배경 이미지를 은은하게 비추어 공간감을 형성하고 콘텐츠에 집중하게 함.
70+
71+
### 4.2 Text Glow (텍스트 발광)
72+
- **적용 대상**: 주요 헤드라인, 활성화된 메뉴, 강조 데이터.
73+
- **구현**: `text-shadow: 0 0 20px rgba(255, 255, 255, 0.1)`.
74+
- **목적**: 사이버펑크/미래지향적인 느낌을 더하고 중요 정보를 강조.
75+
76+
### 4.3 Interactive Elements
77+
- **Hover Effects**:
78+
- 카드: 살짝 떠오르는 효과 (`transform: translateY(-2px)`), 테두리 밝기 증가.
79+
- 버튼: 배경색 불투명도 증가, 글로우 효과 강화.
80+
- **Transitions**: 모든 테마 변경 및 호버 효과는 `duration-300` 이상의 부드러운 트랜지션을 가짐.
81+
82+
## 5. 컴포넌트 가이드 (Component Guidelines)
83+
84+
### 5.1 Layout
85+
- **Sidebar**: 좌측 고정, 아이콘 위주의 네비게이션. 유리 질감 적용.
86+
- **Header**: 상단 고정, 페이지 타이틀 및 유틸리티 버튼(테마 변경 등). 유리 질감 적용.
87+
- **Content Area**: 중앙 그리드 레이아웃. 반응형으로 카드 배치 조정.
88+
89+
### 5.2 Cards (Widgets)
90+
- **구조**: 타이틀, 메인 콘텐츠(데이터/차트), 푸터(액션/상태).
91+
- **스타일**: `glass-card` 유틸리티 클래스 사용.
92+
- **인터랙션**: 클릭 가능한 카드는 커서 변경 및 호버 효과 적용.
93+
94+
### 5.3 Buttons
95+
- **Primary**: Accent Color 배경, 흰색 텍스트, 약간의 글로우.
96+
- **Secondary**: 투명 배경, Accent Color 테두리/텍스트.
97+
- **Ghost**: 투명 배경, 호버 시 배경색 나타남.
98+
99+
## 6. 향후 디자인 과제 (Future Design Tasks)
100+
- **모바일 최적화**: 작은 화면에서도 배경과 Glassmorphism이 조화롭게 보이도록 조정.
101+
- **데이터 시각화**: 차트 라이브러리(Recharts 등) 도입 시 테마 컬러와 연동.
102+
- **애니메이션 강화**: 페이지 전환 시 페이드/슬라이드 효과 추가.

middleware.ts

Lines changed: 65 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -4,90 +4,83 @@ import type { NextRequest } from 'next/server';
44
export async function middleware(request: NextRequest) {
55
const { pathname } = request.nextUrl;
66

7-
// /admin/dashboard 경로에 대한 인증 확인
8-
if (pathname.startsWith('/admin/dashboard') && !pathname.startsWith('/admin/dashboard/auth')) {
9-
console.log('Middleware: Checking auth for:', pathname);
10-
11-
// 모든 쿠키 로그
12-
const allCookies = request.cookies.getAll();
13-
console.log('Middleware: All cookies:', allCookies);
14-
15-
// 여러 방법으로 토큰 읽기 시도
16-
const token = request.cookies.get('auth_token')?.value;
17-
const cookieHeader = request.headers.get('cookie');
18-
console.log('Middleware: Raw cookie header:', cookieHeader);
19-
console.log('Middleware: auth_token value:', token ? `Found token (${token.length} chars)` : 'No token');
20-
21-
if (!token) {
22-
console.log('Middleware: No token found, redirecting to login');
23-
// 토큰이 없으면 로그인 페이지로 리다이렉트
7+
// 정적 자원 및 로그인 페이지는 미들웨어 체크 제외 (matcher에서도 처리하지만 이중 안전장치)
8+
if (
9+
pathname.startsWith('/_next') ||
10+
pathname.startsWith('/static') ||
11+
pathname.startsWith('/auth/login') ||
12+
pathname === '/favicon.ico' ||
13+
pathname === '/windeath44/windeath44.png'
14+
) {
15+
return NextResponse.next();
16+
}
17+
18+
console.log('Middleware: Checking auth for:', pathname);
19+
20+
// auth_token 쿠키 확인
21+
const token = request.cookies.get('auth_token')?.value;
22+
23+
if (!token) {
24+
console.log('Middleware: No token found, redirecting to login');
25+
const loginUrl = new URL('/admin/dashboard/auth/login', request.url);
26+
// 로그인 후 원래 페이지로 돌아오기 위해 callbackUrl 추가 가능 (선택 사항)
27+
// loginUrl.searchParams.set('callbackUrl', pathname);
28+
return NextResponse.redirect(loginUrl);
29+
}
30+
31+
// 서버 사이드에서 토큰 검증 및 ADMIN 역할 확인
32+
try {
33+
const response = await fetch('https://prod.windeath44.wiki/api/users/profile', {
34+
method: 'GET',
35+
headers: {
36+
'Authorization': `Bearer ${token}`,
37+
'User-Agent': 'windeath44-admin-middleware',
38+
},
39+
cache: 'no-store',
40+
});
41+
42+
if (!response.ok) {
43+
console.log('Token validation failed in middleware:', response.status);
2444
const loginUrl = new URL('/admin/dashboard/auth/login', request.url);
25-
return NextResponse.redirect(loginUrl);
45+
const redirectResponse = NextResponse.redirect(loginUrl);
46+
redirectResponse.cookies.set('auth_token', '', { expires: new Date(0) });
47+
return redirectResponse;
2648
}
27-
28-
// 서버 사이드에서 토큰 검증 및 ADMIN 역할 확인
29-
try {
30-
const response = await fetch('https://prod.windeath44.wiki/api/users/profile', {
31-
method: 'GET',
32-
headers: {
33-
'Authorization': `Bearer ${token}`,
34-
'User-Agent': 'windeath44-admin-middleware',
35-
},
36-
// Edge Runtime에서는 cache 옵션 사용
37-
cache: 'no-store',
38-
});
39-
40-
if (!response.ok) {
41-
console.log('Token validation failed in middleware:', response.status);
42-
// 토큰이 유효하지 않으면 로그인 페이지로 리다이렉트하고 토큰 삭제
43-
const loginUrl = new URL('/admin/dashboard/auth/login', request.url);
44-
const redirectResponse = NextResponse.redirect(loginUrl);
45-
redirectResponse.cookies.set('auth_token', '', { expires: new Date(0) });
46-
return redirectResponse;
47-
}
48-
49-
const userProfile = await response.json();
50-
console.log('Middleware: API response:', JSON.stringify(userProfile, null, 2));
51-
52-
// role은 data 객체 안에 중첩되어 있음
53-
const userRole = userProfile.data?.role;
54-
console.log('Middleware: User role found:', userRole);
55-
56-
if (userRole !== 'ADMIN') {
57-
console.log('User does not have ADMIN role in middleware:', userRole);
58-
// ADMIN 역할이 아니면 로그인 페이지로 리다이렉트하고 토큰 삭제
59-
const loginUrl = new URL('/admin/dashboard/auth/login', request.url);
60-
loginUrl.searchParams.set('access_denied', 'true');
61-
const redirectResponse = NextResponse.redirect(loginUrl);
62-
redirectResponse.cookies.set('auth_token', '', { expires: new Date(0) });
63-
return redirectResponse;
64-
}
65-
66-
// 유효한 ADMIN 토큰이면 진행
67-
console.log('Valid ADMIN token verified in middleware');
68-
return NextResponse.next();
69-
70-
} catch (error) {
71-
console.error('Token validation error in middleware:', error);
72-
// 에러 발생 시 로그인 페이지로 리다이렉트하고 토큰 삭제
49+
50+
const userProfile = await response.json();
51+
const userRole = userProfile.data?.role;
52+
53+
if (userRole !== 'ADMIN') {
54+
console.log('User does not have ADMIN role in middleware:', userRole);
7355
const loginUrl = new URL('/admin/dashboard/auth/login', request.url);
56+
loginUrl.searchParams.set('access_denied', 'true');
7457
const redirectResponse = NextResponse.redirect(loginUrl);
7558
redirectResponse.cookies.set('auth_token', '', { expires: new Date(0) });
7659
return redirectResponse;
7760
}
78-
}
7961

80-
// 기본 /admin 경로는 /admin/dashboard로 리다이렉트
81-
if (pathname === '/admin') {
82-
const dashboardUrl = new URL('/admin/dashboard', request.url);
83-
return NextResponse.redirect(dashboardUrl);
84-
}
62+
// 유효한 ADMIN 토큰이면 진행
63+
return NextResponse.next();
8564

86-
return NextResponse.next();
65+
} catch (error) {
66+
console.error('Token validation error in middleware:', error);
67+
const loginUrl = new URL('/admin/dashboard/auth/login', request.url);
68+
const redirectResponse = NextResponse.redirect(loginUrl);
69+
redirectResponse.cookies.set('auth_token', '', { expires: new Date(0) });
70+
return redirectResponse;
71+
}
8772
}
8873

8974
export const config = {
9075
matcher: [
91-
'/admin/:path*',
76+
/*
77+
* Match all request paths except for the ones starting with:
78+
* - api (API routes)
79+
* - _next/static (static files)
80+
* - _next/image (image optimization files)
81+
* - favicon.ico (favicon file)
82+
* - auth/login (login page)
83+
*/
84+
'/((?!_next/static|_next/image|favicon.ico|auth/login).*)',
9285
],
9386
};

0 commit comments

Comments
 (0)