[feat]: 보호된 대시보드 및 API 라우트 추가
- 인증된 사용자용 대시보드 페이지 구현 ((protected) 라우트 그룹) - API 엔드포인트 추가 (인증, 사용자 관리) - 커스텀 훅 추가 (useAuth) - 미들웨어 인증 로직 강화 - 환경변수 예제 업데이트 - 기존 dashboard 페이지 제거 후 보호된 라우트로 이동 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -27,7 +27,7 @@ export function LoginPage() {
|
||||
const [rememberMe, setRememberMe] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const handleLogin = () => {
|
||||
const handleLogin = async () => {
|
||||
setError("");
|
||||
|
||||
// Validation
|
||||
@@ -36,35 +36,45 @@ export function LoginPage() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Demo accounts
|
||||
const demoAccounts = [
|
||||
{ userId: "ceo", password: "demo1234", role: "CEO", name: "김대표", email: "ceo@demo.com" },
|
||||
{ userId: "manager", password: "demo1234", role: "ProductionManager", name: "이생산", email: "manager@demo.com" },
|
||||
{ userId: "worker", password: "demo1234", role: "Worker", name: "박작업", email: "worker@demo.com" },
|
||||
{ userId: "admin", password: "demo1234", role: "SystemAdmin", name: "최시스템", email: "admin@demo.com" },
|
||||
{ userId: "sales", password: "demo1234", role: "Sales", name: "박영업", email: "sales@demo.com" },
|
||||
];
|
||||
try {
|
||||
// ✅ HttpOnly Cookie 방식: Next.js API Route로 프록시
|
||||
// 토큰은 JavaScript에서 접근 불가능한 HttpOnly 쿠키로 저장됨
|
||||
const response = await fetch('/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
user_id: userId,
|
||||
user_pwd: password,
|
||||
}),
|
||||
});
|
||||
|
||||
const account = demoAccounts.find(acc => acc.userId === userId && acc.password === password);
|
||||
const data = await response.json();
|
||||
|
||||
if (account) {
|
||||
// Save user data to localStorage (client-side only)
|
||||
const userData = {
|
||||
userId: account.userId,
|
||||
email: account.email,
|
||||
role: account.role,
|
||||
name: account.name,
|
||||
companyName: "데모 기업",
|
||||
};
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
localStorage.setItem("user", JSON.stringify(userData));
|
||||
if (!response.ok) {
|
||||
throw {
|
||||
status: response.status,
|
||||
message: data.error || 'Login failed',
|
||||
};
|
||||
}
|
||||
|
||||
// Navigate to dashboard
|
||||
console.log('✅ 로그인 성공:', data.message);
|
||||
console.log('📦 사용자 정보:', data.user);
|
||||
console.log('🔐 토큰은 안전한 HttpOnly 쿠키에 저장됨 (JavaScript 접근 불가)');
|
||||
|
||||
// 대시보드로 이동
|
||||
router.push("/dashboard");
|
||||
} else {
|
||||
setError(t('invalidCredentials'));
|
||||
} catch (err: any) {
|
||||
console.error('❌ 로그인 실패:', err);
|
||||
|
||||
if (err.status === 422) {
|
||||
setError(t('invalidCredentials'));
|
||||
} else if (err.status === 429) {
|
||||
setError('너무 많은 로그인 시도가 있었습니다. 잠시 후 다시 시도해주세요.');
|
||||
} else {
|
||||
setError(err.message || t('invalidCredentials'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user