Skip to content

Commit 5b69958

Browse files
committed
feat: 어드민 화면 권한 제어
1 parent 326d971 commit 5b69958

File tree

6 files changed

+60
-15
lines changed

6 files changed

+60
-15
lines changed

src/App.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import FilteredLetterManage from './pages/Admin/FilteredLetter';
99
import FilteringManage from './pages/Admin/Filtering';
1010
import ReportManage from './pages/Admin/Report';
1111
import AdminRollingPaper from './pages/Admin/RollingPaper';
12+
import AdminRoute from './layouts/AdminRoute';
1213
import AuthCallbackPage from './pages/Auth';
1314
import Home from './pages/Home';
1415
import Landing from './pages/Landing';
@@ -28,12 +29,10 @@ import RollingPaperPage from './pages/RollingPaper';
2829
import WritePage from './pages/Write';
2930
import ShareApprovalPage from './pages/Share';
3031
import useThemeStore from './stores/themeStore';
31-
import { useServerSentEvents } from './hooks/useServerSentEvents';
3232

3333
const App = () => {
3434
const theme = useThemeStore((state) => state.theme);
3535
useViewport();
36-
useServerSentEvents();
3736

3837
const initializeTheme = () => {
3938
if (theme === 'dark') {
@@ -81,7 +80,7 @@ const App = () => {
8180
</Route>
8281
</Route>
8382

84-
<Route element={<PrivateRoute />}>
83+
<Route element={<AdminRoute />}>
8584
<Route path="admin" element={<AdminPage />}>
8685
<Route path="report" element={<ReportManage />} />
8786
<Route path="badwords" element={<FilteringManage />} />

src/layouts/AdminRoute.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useEffect } from 'react';
2+
import { useNavigate, Outlet } from 'react-router';
3+
4+
import useAuthStore from '@/stores/authStore';
5+
6+
export default function AdminRoute() {
7+
const isLoggedIn = useAuthStore((state) => state.isLoggedIn);
8+
const isAdmin = useAuthStore((state) => state.isAdmin);
9+
const navigate = useNavigate();
10+
11+
useEffect(() => {
12+
if (!isLoggedIn) {
13+
navigate('/login', { replace: true });
14+
}
15+
if (!isAdmin) {
16+
navigate('/', { replace: true });
17+
}
18+
}, [isLoggedIn, navigate]);
19+
20+
if (!isLoggedIn || !isAdmin) {
21+
return null;
22+
}
23+
24+
return (
25+
<>
26+
<Outlet />
27+
</>
28+
);
29+
}

src/layouts/PrivateRoute.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
1-
import { useEffect, useState } from 'react';
1+
import { useEffect } from 'react';
22
import { useNavigate, Outlet } from 'react-router';
33

44
import useAuthStore from '@/stores/authStore';
55
import { useServerSentEvents } from '@/hooks/useServerSentEvents';
66
import Toast from '@/components/Toast';
77

88
export default function PrivateRoute() {
9-
useServerSentEvents();
109
const isLoggedIn = useAuthStore((state) => state.isLoggedIn);
1110
const navigate = useNavigate();
12-
const [shouldRender, setShouldRender] = useState(false);
11+
12+
useServerSentEvents();
1313

1414
useEffect(() => {
1515
if (!isLoggedIn) {
1616
navigate('/login', { replace: true });
17-
} else {
18-
setShouldRender(true);
1917
}
2018
}, [isLoggedIn, navigate]);
2119

22-
if (!shouldRender) {
20+
if (!isLoggedIn) {
2321
return null;
2422
}
2523

src/pages/Admin/components/Sidebar.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import { twMerge } from 'tailwind-merge';
44
import { AlarmIcon, ArrowDownIcon } from '@/assets/icons';
55

66
import { ADMIN_MENU_LIST } from '../constants';
7+
import useAuthStore from '@/stores/authStore';
78

89
export default function Sidebar() {
910
const location = useLocation();
11+
const logout = useAuthStore((state) => state.logout);
1012

1113
return (
1214
<section className="border-gray-10 flex w-65 shrink-0 flex-col border-r">
@@ -16,7 +18,7 @@ export default function Sidebar() {
1618
</h1>
1719
<section className="mt-2 flex flex-col px-5 py-4">
1820
<h2 className="body-l-b py-2">현재 로그인 계정</h2>
19-
<p className="body-l-r py-2">{'admin123@test.com'}</p>
21+
<p className="body-l-r py-2">{'wl990@naver.com'}</p>
2022
</section>
2123
<hr className="border-gray-20 mx-2.5" />
2224
<section className="flex flex-col py-5">
@@ -54,7 +56,9 @@ export default function Sidebar() {
5456
</section>
5557
<button className="mt-auto flex w-full items-center gap-3 px-5 py-3 hover:bg-amber-100">
5658
<AlarmIcon className="text-gray-80 h-5 w-5" />
57-
<span className="text-gray-80 body-l-m">로그아웃</span>
59+
<span className="text-gray-80 body-l-m" onClick={logout}>
60+
로그아웃
61+
</span>
5862
</button>
5963
</section>
6064
);

src/pages/Auth/index.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ const AuthCallbackPage = () => {
1313
const logout = useAuthStore((state) => state.logout);
1414
const setAccessToken = useAuthStore((state) => state.setAccessToken);
1515
const setZipCode = useAuthStore((state) => state.setZipCode);
16+
const setIsAdmin = useAuthStore((state) => state.setIsAdmin);
1617
const navigate = useNavigate();
18+
let accessToken = '';
19+
let role = '';
1720

1821
const handleError = (error: unknown) => {
1922
console.error('AuthCallback Error:', error);
@@ -32,8 +35,7 @@ const AuthCallbackPage = () => {
3235

3336
login();
3437
if (userInfo.accessToken) setAccessToken(userInfo.accessToken);
35-
36-
console.log(redirectURL);
38+
accessToken = userInfo.accessToken;
3739

3840
switch (redirectURL) {
3941
case 'home':
@@ -54,14 +56,23 @@ const AuthCallbackPage = () => {
5456
if (!newAccessToken) throw new Error('Missing new access token');
5557

5658
setAccessToken(newAccessToken);
59+
accessToken = newAccessToken;
5760
}
5861
break;
5962

6063
default:
6164
navigate('/notFound');
6265
return;
6366
}
64-
navigate(redirectURL === 'onboarding' ? '/onboarding' : '/');
67+
68+
role = JSON.parse(atob(accessToken.split('.')[1])).role;
69+
70+
if (role === 'ADMIN') {
71+
setIsAdmin();
72+
navigate('/admin');
73+
} else {
74+
navigate(redirectURL === 'onboarding' ? '/onboarding' : '/');
75+
}
6576
} catch (error) {
6677
handleError(error);
6778
}

src/stores/authStore.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ interface AuthStore {
66
isLoggedIn: boolean;
77
zipCode: string;
88
accessToken: string;
9+
isAdmin: boolean;
910
login: () => void;
1011
logout: () => Promise<void>;
1112
setZipCode: (zipCode: string) => void;
1213
setAccessToken: (accessToken: string) => void;
14+
setIsAdmin: () => void;
1315
}
1416

1517
const useAuthStore = create(
@@ -18,6 +20,7 @@ const useAuthStore = create(
1820
isLoggedIn: false,
1921
accessToken: '',
2022
zipCode: '',
23+
isAdmin: false,
2124
login: () => set({ isLoggedIn: true }),
2225
logout: async () => {
2326
const theme = useThemeStore.getState().theme;
@@ -26,7 +29,7 @@ const useAuthStore = create(
2629
if (theme === 'dark') {
2730
toggleTheme();
2831
}
29-
set({ isLoggedIn: false, zipCode: '', accessToken: '' });
32+
set({ isLoggedIn: false, zipCode: '', accessToken: '', isAdmin: false });
3033
// location.reload();
3134
// try {
3235
// await postLogout();
@@ -36,6 +39,7 @@ const useAuthStore = create(
3639
},
3740
setZipCode: (zipCode) => set({ zipCode: zipCode }),
3841
setAccessToken: (accessToken) => set({ accessToken: accessToken }),
42+
setIsAdmin: () => set({ isAdmin: true }),
3943
}),
4044
{
4145
name: 'userInfo',

0 commit comments

Comments
 (0)