Node.js JWT 인증 시스템 완성하기 Part 4: 대시보드 구현 및 완전한 인증 플로우
Node.js JWT 인증 시스템 완성하기 Part 4: 대시보드 구현 및 완전한 인증 플로우
🔗 시리즈 연결
이 포스트는 JWT 인증 시스템 시리즈의 Part 4입니다.
- Part 1: 기본 JWT 인증 시스템 구축하기
- Part 2: 고급 JWT 인증 기능
- Part 3: React 프론트엔드 구현
- Part 4: 대시보드 및 로그인 상태 관리 ← 현재 글
🤔 Part 3에서 남은 문제
Part 3에서 로그인/회원가입 폼을 완성했지만… 한 가지 문제가 있었어요! 😅
“로그인은 성공하는데, 로그인 후에 뭘 보여줘야 하지?” 🤷♂️
지금까지는 로그인 성공해도 계속 로그인 폼만 보여주고 있었거든요. 실제 웹사이트처럼 로그인 후 사용자 대시보드가 필요해요!
🎯 Part 4에서 구현할 기능들
- ✅ 로그인 상태 기반 화면 전환 🔄
- ✅ 사용자 대시보드 컴포넌트 📊
- ✅ 사용자 정보 표시 (이름, 이메일, 가입일) 👤
- ✅ 로그아웃 기능 구현 🚪
- ✅ 모던하고 깔끔한 대시보드 디자인 ✨
🏗️ Step 1: App.js 라우팅 로직 개선
먼저 로그인 상태에 따라 화면을 전환하는 로직을 만들어보겠습니다!
🔍 현재 App.js의 문제점
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 기존 App.js - 문제가 있는 코드
function App() {
const [currentView, setCurrentView] = useState("login");
// 🚨 문제: 로그인 상태를 확인하지 않음!
return (
<div className="App">
{currentView === "login" ? (
<LoginForm onSwitchToRegister={() => setCurrentView("register")} />
) : (
<RegisterForm onSwitchToLogin={() => setCurrentView("login")} />
)}
</div>
);
}
✨ 개선된 App.js
src/App.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import React, { useState, useContext } from "react";
import { AuthContext } from "./components/context/AuthContext";
import LoginForm from "./components/Auth/LoginForm";
import RegisterForm from "./components/Auth/RegisterForm";
import Dashboard from "./components/Dashboard/Dashboard";
import "./App.css";
function App() {
const [currentView, setCurrentView] = useState("login");
const { user } = useContext(AuthContext); // 🔑 핵심! 로그인 상태 확인
// 로그인되어 있으면 대시보드 보여주기
if (user) {
return <Dashboard />;
}
// 로그인 안되어 있으면 로그인/회원가입 폼 보여주기
return (
<div className="App">
{currentView === "login" ? (
<LoginForm
onSwitchToRegister={() => setCurrentView("register")}
/>
) : (
<RegisterForm onSwitchToLogin={() => setCurrentView("login")} />
)}
</div>
);
}
export default App;
💡 핵심 변경점
- AuthContext import:
useContext(AuthContext)
로 로그인 상태 확인 - 조건부 렌더링:
if (user)
조건으로 대시보드/로그인폼 전환 - 깔끔한 로직: 로그인되면 자동으로 대시보드 표시
📊 Step 2: Dashboard 컴포넌트 구현
이제 로그인 후 보여줄 멋진 대시보드를 만들어보겠습니다!
📁 폴더 구조
1
2
3
src/components/Dashboard/
├── Dashboard.jsx
└── Dashboard.css
🎯 Dashboard 컴포넌트
src/components/Dashboard/Dashboard.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import React, { useContext } from "react";
import { AuthContext } from "../context/AuthContext";
import "./Dashboard.css";
const Dashboard = () => {
const { user, logout } = useContext(AuthContext);
const handleLogout = async () => {
try {
await logout();
} catch (error) {
console.error("로그아웃 에러:", error);
}
};
return (
<div className="dashboard-container">
<div className="dashboard-card">
<div className="dashboard-header">
<h1 className="dashboard-title">환영합니다!</h1>
<p className="dashboard-subtitle">
로그인이 성공적으로 완료되었습니다
</p>
</div>
<div className="user-info">
<div className="user-avatar">
<span className="avatar-icon">👤</span>
</div>
<div className="user-details">
<h2 className="user-name">{user?.username}</h2>
<p className="user-email">{user?.email}</p>
<p className="user-joined">
가입일:{" "}
{new Date(user?.createdAt).toLocaleDateString("ko-KR")}
</p>
</div>
</div>
<div className="dashboard-actions">
<button onClick={handleLogout} className="logout-button">
로그아웃
</button>
</div>
</div>
</div>
);
};
export default Dashboard;
🔧 주요 기능 설명
- 사용자 정보 표시:
user?.username
,user?.email
등으로 안전하게 접근 - 가입일 포맷팅:
toLocaleDateString('ko-KR')
로 한국어 날짜 형식 - 로그아웃 처리:
handleLogout
함수로 안전한 로그아웃 - 에러 처리: try-catch로 로그아웃 에러 핸들링
🎨 Step 3: 모던한 대시보드 스타일링
사용자가 깔끔하고 모던하다고 느낄만한 디자인을 만들어보겠습니다!
src/components/Dashboard/Dashboard.css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
.dashboard-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 1rem;
}
.dashboard-card {
background: white;
padding: 3rem;
border-radius: 16px;
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 500px;
text-align: center;
}
.dashboard-header {
margin-bottom: 2.5rem;
}
.dashboard-title {
color: #333;
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 0.5rem;
}
.dashboard-subtitle {
color: #666;
font-size: 1.1rem;
}
.user-info {
background: #f8f9fa;
padding: 2rem;
border-radius: 12px;
margin-bottom: 2rem;
display: flex;
align-items: center;
gap: 1.5rem;
}
.user-avatar {
flex-shrink: 0;
}
.avatar-icon {
display: inline-block;
width: 80px;
height: 80px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 50%;
font-size: 2.5rem;
line-height: 80px;
color: white;
}
.user-details {
flex: 1;
text-align: left;
}
.user-name {
color: #333;
font-size: 1.8rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.user-email {
color: #666;
font-size: 1.1rem;
margin-bottom: 0.5rem;
}
.user-joined {
color: #888;
font-size: 0.95rem;
}
.dashboard-actions {
margin-top: 1.5rem;
}
.logout-button {
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
color: white;
border: none;
padding: 0.9rem 2rem;
border-radius: 8px;
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
}
.logout-button:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(231, 76, 60, 0.3);
}
/* 반응형 디자인 */
@media (max-width: 768px) {
.user-info {
flex-direction: column;
text-align: center;
}
.user-details {
text-align: center;
}
.dashboard-card {
padding: 2rem;
}
}
🎨 디자인 특징
- 그라데이션 배경: 로그인 폼과 일관된 디자인
- 카드 레이아웃: 깔끔한 카드 스타일
- 사용자 아바타: 원형 아이콘으로 개성 표현
- 호버 효과: 버튼에 부드러운 애니메이션
- 반응형: 모바일에서도 완벽하게 동작
🔧 Step 4: 로그아웃 기능 개선
로그아웃에서 발생할 수 있는 에러를 안전하게 처리해보겠습니다!
🚨 발생할 수 있는 문제
- 백엔드 서버가 다운된 경우
- 토큰이 이미 만료된 경우
- 네트워크 연결 문제
✨ 개선된 로그아웃 함수
src/components/context/AuthContext.jsx
수정:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 로그아웃 함수 - 에러 처리 개선
const logout = async () => {
try {
// 토큰이 있을 때만 서버에 로그아웃 알리기
const token = localStorage.getItem("accessToken");
if (token) {
await api.post("/auth/logout");
}
} catch (error) {
// 서버 에러가 있어도 로컬에서는 로그아웃 진행
console.warn("서버 로그아웃 실패, 로컬 로그아웃 진행:", error);
} finally {
// 어떤 경우든 로컬 상태는 정리
localStorage.removeItem("accessToken");
setUser(null);
}
};
💡 개선된 점
- 토큰 확인: 토큰이 있을 때만 서버 호출
- 에러 무시: 서버 에러가 있어도 로컬 로그아웃 진행
- 안전한 정리:
finally
블록으로 확실한 상태 정리 - 사용자 경험: 어떤 상황에서도 로그아웃 성공
🚀 Step 5: 실행 및 테스트
이제 완성된 인증 플로우를 테스트해보겠습니다!
🔧 서버 실행
1
2
3
4
5
6
7
# 백엔드 서버 실행
cd backend
npm run dev
# 프론트엔드 서버 실행
cd frontend
npm start
🧪 완전한 테스트 시나리오
- 초기 화면 📱
http://localhost:3000
접속- 로그인 폼이 표시되는지 확인
- 회원가입 플로우 📝
- “회원가입” 클릭 → 회원가입 폼으로 전환
- 새 계정 생성
- 성공 메시지 후 로그인 폼으로 자동 이동
- 로그인 플로우 🔑
- 가입한 계정으로 로그인
- 자동으로 대시보드 화면으로 전환!
- 대시보드 확인 📊
- 사용자 이름, 이메일 정보 표시
- 가입일이 한국어 형식으로 표시
- 예쁜 아바타 아이콘 확인
- 로그아웃 테스트 🚪
- “로그아웃” 버튼 클릭
- 다시 로그인 폼으로 돌아가는지 확인
- 페이지 새로고침 테스트 🔄
- 로그인 상태에서
F5
새로고침 - 여전히 대시보드가 표시되는지 확인 (토큰 자동 복원!)
- 로그인 상태에서
💡 핵심 포인트 정리
1. 상태 기반 라우팅 🔄
- AuthContext 활용:
user
상태로 로그인 여부 판단 - 조건부 렌더링: 간단한
if
문으로 화면 전환 - 자동 전환: 로그인 성공 시 즉시 대시보드 표시
2. 사용자 경험 개선 ✨
- 즉시 피드백: 로그인 성공 시 바로 대시보드 표시
- 정보 표시: 사용자 이름, 이메일, 가입일 등 개인화된 정보
- 직관적 UI: 아바타, 카드 레이아웃으로 친근한 느낌
3. 에러 처리 강화 🛡️
- Graceful Logout: 서버 에러가 있어도 로컬 로그아웃 진행
- 안전한 접근:
user?.username
등으로 null 체크 - 사용자 친화적: 에러가 있어도 기능은 정상 동작
4. 모던한 디자인 🎨
- 일관된 테마: 로그인 폼과 동일한 그라데이션 배경
- 반응형 레이아웃: 모바일에서도 완벽하게 동작
- 부드러운 애니메이션: 호버 효과로 인터랙티브한 느낌
🎉 Part 4 완성!
축하합니다! 🎊 이제 우리의 JWT 인증 시스템이 완전한 사용자 플로우를 갖게 되었어요!
✅ 지금까지 완성된 기능들
- 🔐 안전한 회원가입/로그인 (Part 1, 2)
- 🎨 아름다운 React UI (Part 3)
- 📊 사용자 대시보드 (Part 4)
- 🔄 완전한 인증 플로우 (Part 4)
- 🛡️ 강력한 에러 처리 (Part 4)
🔮 다음 Part 5에서는
- 🛣️ React Router - 여러 페이지 간 이동
- 🔄 자동 토큰 갱신 - 끊김 없는 사용자 경험
- ⚡ Protected Routes - 인증이 필요한 페이지 보호
- 🎯 사용자 프로필 관리 - 정보 수정 기능
🔗 GitHub 저장소
현재까지의 완성된 코드는 GitHub 리포지토리에서 확인할 수 있습니다! 🚀
1
2
3
4
# Part 4 브랜치 클론
git clone https://github.com/hoondongseo/SimpleAuthSystem.git
cd SimpleAuthSystem
git checkout feature/frontend
🎯 실무에서 이런 식으로 사용해요!
오늘 구현한 패턴은 실제 회사에서도 많이 사용하는 방식이에요:
- 네이버, 카카오: 로그인 후 메인 페이지로 리다이렉트
- GitHub, Notion: 사용자 대시보드에서 개인화된 정보 표시
- Instagram, Facebook: 프로필 아바타와 사용자 정보 UI
여러분도 이제 실무급 인증 시스템을 만들 수 있게 되었어요! 💪
💡 질문이나 피드백이 있으시면 댓글로 남겨주세요! 😊
Part 5에서는 더욱 고급 기능들을 다뤄보겠습니다! 🚀
This post is licensed under CC BY 4.0 by the author.