์ ํฌ์คํ ์์ keycloak๊ณผ React์
๊ธฐ๋ณธ์ ์ธ ์ฐ๊ฒฐ๊น์ง ํ๋ค.
์ด๋ฒ์ ํ ํฐ์ ๊ฐ์ ธ์ค๊ณ ,
์ฌ์ฉ์์ ๊ถํ์ ๋ฐ๋ผ ํ์ด์ง์ ์ถ์ ๊ตฌ๋ฅผ ๊ด๋ฆฌํ์.
[ Keycloak ์ค์ ํ๊ธฐ ]
[Keycloak, React, Docker] React์์ Keycloak์ ๋ณธ๊ฒฉ ์ ์ฉํ๊ณ ์ฝ๋๋ฅผ ํ๋ํ๋ ํํค์ณ๋ณด์.
keycloak.js๋ฅผ ๋ง๋ค์ด์keycloak ์๋ฒ์ ํต์ ํ ์ ์๋๋ก ํ๋ค.๋ค์์ ์ด ํต์ ์ ์ด์ฉํด์์ํ๋ ๋ฐฉํฅ์ผ๋ก ์ฝ๋๋ฅผ ์์ฑํด ๋ณด์. [ Keycloak ์ค์ ํ๊ธฐ ] [Keycloak, Docker, Mac] Keycloak์์ client, role, users ๋ฑ ์ค
post-this.tistory.com
1. ํ๋ฆ ์ค๋ช (๋ณต์ต์ ํฌํจํด์)
- keycloak.js์์ keycloak ์๋ฒ์ ํต์ ํ ์ ์๋ ๊ฐ์ฒด๋ฅผ ํ๋ ์์ฑํ๋ค. ๊ทธ๋ฆฌ๊ณ ๋ค์ด์จ ์ฌ์ฉ์๊ฐ ํ์ด์ง์ ๋ก๊ทธ์ธ์ ํ๋์ง, ์ํ๋์ง ํ์ธํ๋ main.jsx๋ฅผ ๋ง๋ค์๋ค. ๋ก๊ทธ์ธ์ ํ๋ค๋ฉด ๋ก๊ทธ์ธ ํ ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์๊ธฐ ์ํ AuthProvider.jsx๋ฅผ ๋ง๋ค์๋ค.
- authFetch.js
: ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ ๋ ํ ํฐ์ ์๋์ผ๋ก ์ฑ๊ฒจ์ ๋ณด๋ด๊ณ ์ ํจ๊ธฐ๊ฐ์ ์ฒดํฌํ๋ค. - ProtectedRoute.jsx
: ํน์ ํ์ด์ง์ ๋ค์ด๊ฐ๊ธฐ ์ , ๋ก๊ทธ์ธ ์ฌ๋ถ์ ๊ถํ์ ๊ฒ์ฌํด์ ์๊ฒฉ์ด ์์ผ๋ฉด ํ์ด์ง์ ๋ค์ด์ค์ง ๋ชปํ๋๋ก ๋ง๋๋ค. - App.jsx
: ์ด๋ค ํ์ด์ง๋ฅผ ๋ณด์ ๊ตฌ์ญ์ผ๋ก ์ค์ ํ์ง ๊ฒฐ์ ํ๋ค.
2. autoFetch.js
์๋ฒ(๋ฐฑ์๋)์์ ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด(loadMe), ํ ํฐ์ Keycloak.js์์ ๊ฐ์ ธ์ ์๋ฒ๋ก ๋ณด๋ธ๋ค.
- ์ ์ฒด ์ฝ๋
import keycloak from "../keycloak.js";
export async function authFetch(url, options = {}) {
if (!keycloak.authenticated) {
throw new Error("๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค.");
}
await keycloak.updateToken(30);
const headers = new Headers(options.headers || {});
if (keycloak.token) {
headers.set("Authorization", `Bearer ${keycloak.token}`);
}
return fetch(url, {
...options,
headers,
});
}
- ์ค๋ช ์ด ํฌํจ๋ ์ฝ๋
import keycloak from "../keycloak.js";
// ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ํ ํฐ์ ์๋ฒ์ ๋ณด๋ด์ ๊ทธ์ ๋ง๋ ๋ฐ์ดํฐ ๋ฐ๋๋ค.
// ์ด๋ keycloak.js๋ก ๊ฐ์ ํ ํฐ์ ๊ฐ์ ธ์จ๋ค.
// AuthProvider๋ ๋ก๊ทธ์ธํ ์ฌ๋์ ๋ํ ์์ธ ์ ๋ณด๊ฐ ํ์ํ๋ค.
// ๊ทธ๋์ authFetch๋ฅผ ํธ์ถํ๋ค.
export async function authFetch(url, options = {}) {
// ๋ก๊ทธ์ธ์ด ๋์ด์๋์ง ํ์ธํ๋ค.
if (!keycloak.authenticated) {
throw new Error("๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค.");
}
// ํ ํฐ ์ ํจ์๊ฐ์ด 30์ด ์ดํ๋ก ๋จ์์ผ๋ฉด ํ ํฐ ์
๋ฐ์ดํธ ๋๋๋ก ํ๋ค.
// ํ ํฐ์ ์ ํจ๊ธฐ๊ฐ์ ๋ณดํต 5๋ถ์์ 15๋ถ์ผ๋ก ์งง๋ค.
// ํ ํฐ์ด ๋ง๋ฃ๋๋ฉด ์๋ฌ๊ฐ ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์ ๋ฏธ๋ฆฌ ์ฒดํฌํด์ ์ํ ํฐ์ ๋ฐ์์จ๋ค.
await keycloak.updateToken(30);
// ํค๋์ ํ ํฐ ๋ฃ๋๋ค.
// ์๋ก, ๊ด๋ฆฌ์์๊ฒ Read ๊ถํ์ด ์๋ ๊ฒฝ์ฐ ๊ทธ๊ฒ ํ ํฐ์ ์๊ธฐ ๋๋ฌธ์ ์๋ฒ๋ ์ฝ์ ์ ์๋ค๊ณ ํ๋จํ๋ค.
// ์ด๋ ๊ฒ Bearer ํ ํฐ ์์คํ
์ด ์์ด์ผ ์ฃผ์์ฐฝ์ ํตํด ๊ณ ๊ฐ ์ ๋ณด๋ฅผ ๋ณด๋ ๊ฑธ ๋ง์ ์ ์๋ค.
// options.headers || {}๋ option ์์ headers๊ฐ ์์ผ๋ฉด ๊ทธ๊ฑฐ ์ฐ๊ณ , ์์ผ๋ฉด ๋น ๊ฐ์ฒด๋ฅผ ์ฐ๋ผ๋ ๋ป์ด๋ค.
const headers = new Headers(options.headers || {});
// keycloak.token์์ ์์์ ํค๋์ ๋ณต์กํ ํ ํฐ์ ๋ถ์ฌ์ฃผ๋๋ก ํ๋ค.
// "Authorization"์ HTTP ํ๋กํ ์ฝ์์ ์ ํด๋ key๊ณ , ์ด key์ ๋ด์ฉ์ ๋ฃ์ผ๋ฉด ๋น๋ฐ๋ฒํธ ํน์ ํ ํฐ์ ์๋ฏธํ๋ค.
// `Bearer ${keycloak.token}`๋ ๋ฐ์ดํฐ ๋ถ๋ถ์ด๋ค.
// Bearer ๋ค์ tokendl ๋ถ์ ๊ฒ์ด๋ค.
// Authorization: Bearer abc123xyz ์ด๋ฐ ๋๋์ผ๋ก...
if (keycloak.token) {
headers.set("Authorization", `Bearer ${keycloak.token}`);
}
// ์๋ฒ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ผ๋ฌ ๊ฐ๋ค.
// option์ ํฌํจ์์ผ์ ๋ชจ๋ ์์ฒญ์์ ํจ์๋ฅผ ๊ทธ๋๋ก ์ฐ๋๋ก ํ๋ค.
return fetch(url, {
...options,
headers,
});
}
authFetch๋ ํ ํฐ์ ์๋์ผ๋ก ๋ถ์ฐฉํด์ ๋ณด๋ด๋ ์ญํ ์ ํ๋ค. ์ด ํ์ผ์ ์์ฑํ์ง ์๋๋ค๊ณ ํ๋ฉด ๊ณ ๊ฐ ์ ๋ณด๊ฐ ํ์ํ ๊ณณ์ ๋งค๋ฒ ํ ํฐ์ ์์ฒญํ๋ ์ฝ๋๋ฅผ ์์ฑํด์ผํ๋ค.
Q. ํ ํฐ์ ์ด์ฉํด์ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์จ๋ค๊ณ ํ๋๋ฐ, ์ด๋ฏธ ํ ํฐ์ ์ฌ์ฉ์ ์ ๋ณด๊ฐ ์๋ ๊ฒ ์๋๊ฐ? ํ ํฐ์ ์๋ ์ ๋ณด๋ก ํ๋จํ๋ฉด ๋๋๊ฑฐ์ง, ๊ตณ์ด ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์์ฒญํด์ ๋ฐ์์ค๋ ์ด์ ๊ฐ ๋ญ๊น?
A. ํ ํฐ์ ์ ํจ๊ธฐ๊ฐ์ 15๋ถ์ด๋ผ๊ณ ๊ฐ์ ํ์. ๊ทธ๋ฌ๋ฉด ์ด ์ ํจ๊ธฐ๊ฐ ๋์์ ํ ํฐ ์ ๋ด์ฉ์ด ๋ณํ์ง ์๋๋ค. ๊ทธ๋ฐ๋ฐ ํ ํฐ์ ์ ํจ๊ธฐ๊ฐ์ด ๋ค ๊ฐ๊ธฐ ์ , ์ฌ์ฉ์์ ๊ถํ์ด ๋ฐ๋์๋ค. ํ ํฐ ์ ๋ด์ฉ์ 15๋ถ ๋์ ์ ํจํ๋ ํ ํฐ์ ๋ด์ฉ์ ๊บผ๋ด์ ์ฐ๋ฉด ๋ถ๋ช ์ทจ์๋ ๊ถํ์ด๋ผ๋ ์ฌ์ฉ์๋ ์ฌ์ฉํ ์ ์์ ๊ฒ์ด๋ค. ๊ทธ๋์ ์ด๋ฐ ์ ๋ค์ ๋ฐฉ์งํ๊ธฐ ์ํด ์๋ฒ์ ๋ฌผ์ด๋ณด๋๋ก ์ ๋ํ ๊ฒ์ด๋ค. ํ ํฐ์ ๋ด์ฉ์ ๋ณํ์ง ์๋๋ผ๋, ์ด ํ ํฐ์ ๊ฐ์ง๊ณ ์๋ฒ์๊ฒ ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ์ป์ด์ค๋ฉด ๊ทธ ์ ๋ณด์๋ ๊ถํ์ด ๋ณ๊ฒฝ๋์ด์์ ๊ฒ์ด๋ค.
ํ์ฌ ํ๋ก์ ํธ์๋ ์๋ฒ์ ์์ฒญํด์ผํ๋ ๋ ํฐ ์ด์ ๊ฐ ์๋ค. ์ผ๋จ keycloak์ ์ฌ์ฉํ ํ๋ก์ ํธ์ ์ฌ์ฉ์๋ง๋ค ์ธ์ธํ ๊ถํ์ ์ค์ ํ๋ค. ์ฝ๊ธฐ ๊ถํ / ์ถ๊ฐ ๊ถํ / ๊ฒ์ ๊ถํ ๋ฑ๋ฑ... Keycloak ์๋ฒ๋ ์ด๋ฐ ์ธ์ธํ ๊ถํ๋ค๊น์ง ๋ชจ๋ฅธ๋ค. ๊ทธ๋์ Keycloak์ "์ด ์ฌ์ฉ์๋ ์ต๊ณ ๊ด๋ฆฌ์ ์ญํ ์ด์ผ."๋ฅผ ์๋ ค์ค๋ค. ๊ทธ๋ฌ๋ฉด ์๋ฒ๋ ๊ทธ๊ฑธ ๋ฐ์์ "์ฐ๋ฆฌ ์๋น์ค์์ ์ต๊ณ ๊ด๋ฆฌ์๋ ๋ชจ๋ ๊ถํ์ ํ์ฉํ๊ณ ์์ด."๋ผ๊ณ ์์ธ ์ ๋ณด๋ฅผ ๋ฐ์์จ๋ค.
3. ProtectedRoute.jsx
ํ์ด์ง์ ๋ค์ฌ๋ณด๋ด๊ธฐ ์ , ๋๊ฐ ๋ค์ด๊ฐ ์ ์๋์ง ๋ณด์ ๊ท์น์ ์ ์ํ๋ค.
- ์ ์ฒด ์ฝ๋
import { Navigate, useLocation } from "react-router-dom";
import { useAuth } from "./AuthProvider.jsx";
export default function ProtectedRoute({ children, requiredRole, requireCustomerAccess = false }) {
const location = useLocation();
const auth = useAuth();
if (auth.loading) {
return (
<div style={{ padding: 24, textAlign: "center" }}>
๊ถํ ํ์ธ ์ค์
๋๋ค.
</div>
);
}
if (!auth.isAuthenticated) {
const redirect = encodeURIComponent(location.pathname);
return <Navigate to={`/login?redirect=${redirect}`} replace />;
}
if (requiredRole === "SUPER_ADMIN" && !auth.superAdmin) {
return <Navigate to="/" replace />;
}
if (requireCustomerAccess) {
const canAccessCustomerPage =
auth.superAdmin ||
auth.customerRead ||
auth.customerSearch ||
auth.customerAdd ||
auth.customerEdit ||
auth.customerDelete;
if (!canAccessCustomerPage) {
return <Navigate to="/" replace />;
}
}
return children;
}
- ์ค๋ช ์ด ํฌํจ๋ ์ฝ๋
import { Navigate, useLocation } from "react-router-dom";
import { useAuth } from "./AuthProvider.jsx";
// ํน์ ํ์ด์ง์ ๋ค์ฌ๋ณด๋ด๊ธฐ ์ํด์ ๋ก๊ทธ์ธ์ ํ๋์ง, ๊ถํ์ ์๋์ง ๊ฒ์ฌํ๋ค.
// requiredRole , requireCustomerAccess๋ ์กฐ๊ฑด์ด๋ค.
// ๋ค์ ๋์ฌ App.jsx์์ ํด๋น ์กฐ๊ฑด์ ํฌํจ์ํค๋ฉด ํ์๋ก ํ์ธํ์ง๋ง
// ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ false๊ฐ ๋ด๊ฒจ ๋์ด๊ฐ๊ฒ ๋๋ค.
export default function ProtectedRoute({ children, requiredRole, requireCustomerAccess = false }) {
const location = useLocation(); // ๊ฐ๊ณ ์ ํ๋ ํ์ด์ง
const auth = useAuth(); // ๋ด ๋ก๊ทธ์ธ ์ ๋ณด ์๋ฒ์์ ๊บผ๋ด์จ ๊ฒ
if (auth.loading) {
return (
// ์๋ฒ์์ ๊ถํ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๊ณ ์๋ ์ค์ธ ๊ฒฝ์ฐ ๋ก๋ฉ ํ๋ฉด์ ๋์ด๋ค.
<div style={{ padding: 24, textAlign: "center" }}>
๊ถํ ํ์ธ ์ค์
๋๋ค.
</div>
);
}
// ๋ก๊ทธ์ธ์ ์ํ ๊ฒฝ์ฐ
// ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ณด๋ธ ๋ค, ์๋ ๊ฐ๋ ค๊ณ ํ๋ ํ์ด์ง๋ฅผ ๊ธฐ์ตํด๋๋ค.
if (!auth.isAuthenticated) {
// ์ฌ๊ธฐ์ ๊ฐ๋ ค๋ ํ์ด์ง๋ฅผ ๊ธฐ์ตํด๋๊ณ
const redirect = encodeURIComponent(location.pathname);
// ๋ก๊ทธ์ธ ๋ค, ๊ฐ๋ ค๋ ํ์ด์ง๋ก ๋ณด๋ด์ค๋ค.
return <Navigate to={`/login?redirect=${redirect}`} replace />;
}
// ์ต๊ณ ๊ด๋ฆฌ์์ธ์ง ๊ฒ์ฌํ๊ณ , ๋ง์ฝ ์๋๋ฉด ๋ฉ์ธ ํ์ด์ง๋ก ๋ณด๋ธ๋ค.
if (requiredRole === "SUPER_ADMIN" && !auth.superAdmin) {
return <Navigate to="/" replace />;
}
// ๊ณ ๊ฐ ํ์ด์ง์ ๋ค์ด๊ฐ๋ ค๋ ๊ฒฝ์ฐ, ๋ง์ฝ ๊ถํ์ด ํ๋๋ ์์ผ๋ฉด ๋ค์ด๊ฐ์ง ๋ชปํ๊ฒ ๋ง๋๋ค.
if (requireCustomerAccess) {
// ๊ถํ์ด ์๋์ง ํ์ธํ๋ค.
const canAccessCustomerPage =
auth.superAdmin ||
auth.customerRead ||
auth.customerSearch ||
auth.customerAdd ||
auth.customerEdit ||
auth.customerDelete;
// ๊ถํ์ด ์๋ ๊ฒฝ์ฐ ๋ฉ์ธํ์ด์ง๋ก ๋ณด๋ธ๋ค.
if (!canAccessCustomerPage) {
return <Navigate to="/" replace />;
}
}
// ์๋ ๊ฐ๋ ค๋ ํ์ด์ง๋ก ๋ณด๋ธ๋ค.
// children์ด ๊ฐ๊ณ ์ ํ๋ ํ์ด์ง์ด๋ค.
return children;
}
<Route path="/permission" element={ <ProtectedRoute requiredRole="SUPER_ADMIN"> <PermissionPage /> </ProtectedRoute> } />
์์ ์ฝ๋๋ App.jsx์ ์ผ๋ถ๋ถ์ด๋ค. ProtectedRoute๋ฅผ ํธ์ถํ๋ฉด์ ์กฐ๊ฑด์ ๋ถ์๋ค. ๊ทธ๋ ๋ค๋ฉด PermissionPage๋ requiredRole="SUPER_ADMIN"์ด ํ์ํ ๊ฒ์ด๋ค. ์ฝ๊ฒ ์ค๋ช ํด์ SUPER_ADMIN์ด ์๋๋ฉด ์ด ํ์ด์ง๋ ๋ค์ด๊ฐ ์ ์๋ค๋ ์๋ฏธ์ด๋ค. ๋ง์ฝ ํด๋น ํ์ด์ง์ ์ด๋ค ์กฐ๊ฑด๋ ์๋ถ๋๋ค๋ฉด ๊ทธ๋ฅ false๊ฐ ๋ถ์ด์ ์กฐ๊ฑด ์๊ด์์ด ๋๋๋ค ์ ์๋ค๋ ์๋ฏธ๊ฐ ๋๋ค. ProtectedRoute์์ false๋ฅผ ์ค์ ํ์ง ์์๋ค๋ฉด, ์กฐ๊ฑด์ด ๋ฑํ ํ์ํ์ง ์๋ ํ์ด์ง์๋ requiredRole=false๋ฅผ ๋ถ์ฌ์ผํ๋ ๋ฒ๊ฑฐ๋ก์์ด ์๋ค.
๋งจ ๋ฐ์ return์ ๋ณด๋ฉด children์ ๋ฐํํ๊ณ ์๋ค. ์ children์ด๋ผ๊ณ ์ ์๊ฑธ๊น? App.jsx์ ์ฝ๋ ์ผ๋ถ๋ถ์ ๋ค์ ์ดํด๋ณด์. ์ฌ๊ธฐ์ ํ์ด์ง๊ฐ <ProtectedRoute> <PremissionPage /> </ProtectedRoute> ๋ผ๊ณ ๊ฐ์ธ์ ธ ์๋ค. ์ด๋ฐ ํํ๋ฅผ children์ด๋ผ๊ณ ๋ถ๋ฅธ๋ค. ๋ฆฌ์กํธ์์ children์ ๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ ์์ ์ปดํฌ๋ํธ๋ฅผ ํ์ ๋ ์ฌ์ฉ๋๋ ์ด๋ฆ์ด๋ค.
์ผ๋ จ์ ๊ณผ์ ์ ํ ๋ฒ ์ดํด๋ณด์. ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ์ ์ ํ ์ํ๋ก ๊ณ ๊ฐ๊ด๋ฆฌํ์ด์ง(/admin/customers)๋ฅผ ํด๋ฆญํ๋ค. ๊ทธ๋ฌ๋ฉด ProtectedRoute์์ ๋ก๊ทธ์ธ์ด ์๋์ด์์ผ๋ ๋ก๊ทธ์ธ์ ํ๋ผ๊ณ ๋ด๋ณด๋ธ๋ค. ์ด๋ ProtectedRoute๋ /admin/customers๋ฅผ ๊ธฐ์ตํด๋๋ค. ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ์ ๋ง์น๋ฉด ๊ธฐ์ตํด๋ /admin/customers๋ก ๋ค์ ๋ณด๋ด์ฃผ๊ณ ๊ถํ์ ํ์ธํ ๋ค, children(<Customerpage />๋ฅผ ํ๋ฉด์ ๋ณด์ฌ์ค๋ค.
4. App.jsx
authFetch๋ก ๊ฐ์ ธ์จ ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก, ProtectedRoute๋ฅผ ํ์ฉํด ๊ฐ ํ์ด์ง๋ฅผ ๊ฐ์ธ๊ณ ์ ์ฒด ๊ฒฝ๋ก๋ฅผ ์์ฑํ๋ค.
- ์ ์ฒด ์ฝ๋
import {BrowserRouter, Routes, Route, Navigate} from "react-router-dom";
import VipCode from "./invitecode/VipCode.jsx";
import CustomerPage from "./customer/CustomerPage.jsx";
import Login from "./admin/adminlogin/Login.jsx";
import VIP from "./invitecode/page/VipPage.jsx";
import VVIP from "./invitecode/page/VvipPage.jsx";
import DIAMOND from "./invitecode/page/DiamondPage.jsx";
import ProtectedRoute from "./auth/ProtectedRoute.jsx";
import MainPage from "./home/MainPage.jsx";
import PermissionPage from "./admin/adminsetting/PermissionPage.jsx";
export default function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<MainPage />} />
<Route path="/invite" element={<VipCode />} />
<Route path="/vip" element={<VIP />} />
<Route path="/vvip" element={<VVIP />} />
<Route path="/diamond" element={<DIAMOND />} />
<Route path="/login" element={<Login />} />
<Route
path="/admin/customers"
element={
<ProtectedRoute>
<CustomerPage />
</ProtectedRoute>
}
/>
<Route
path="/permission"
element={
<ProtectedRoute requiredRole="SUPER_ADMIN">
<PermissionPage />
</ProtectedRoute>
}
/>
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</BrowserRouter>
);
}
- ์ค๋ช ์ด ํฌํจ๋ ์ฝ๋
import {BrowserRouter, Routes, Route, Navigate} from "react-router-dom";
import VipCode from "./invitecode/VipCode.jsx";
import CustomerPage from "./customer/CustomerPage.jsx";
import Login from "./admin/adminlogin/Login.jsx";
import VIP from "./invitecode/page/VipPage.jsx";
import VVIP from "./invitecode/page/VvipPage.jsx";
import DIAMOND from "./invitecode/page/DiamondPage.jsx";
import ProtectedRoute from "./auth/ProtectedRoute.jsx";
import MainPage from "./home/MainPage.jsx";
import PermissionPage from "./admin/adminsetting/PermissionPage.jsx";
export default function App() {
return (
<BrowserRouter>
<Routes>
{/* ๊ณต๊ฐ ํ์ด์ง */}
{/* ProtectedRoute๋ก ๊ฐ์ธ์ ธ์์ง ์์ผ๋ฉด ๋๊ตฌ๋ ํ์ด์ง ๋ฐฉ๋ฌธ ๊ฐ๋ฅ */}
<Route path="/" element={<MainPage />} />
<Route path="/invite" element={<VipCode />} />
<Route path="/vip" element={<VIP />} />
<Route path="/vvip" element={<VVIP />} />
<Route path="/diamond" element={<DIAMOND />} />
{/* ๋ก๊ทธ์ธ ํ์ด์ง */}
<Route path="/login" element={<Login />} />
{/* ๊ด๋ฆฌ์ ํ์ด์ง: ๋ก๊ทธ์ธ ํ์ํจ */}
<Route
path="/admin/customers"
element={
<ProtectedRoute>
<CustomerPage />
</ProtectedRoute>
}
/>
{/* ๊ถํ ๊ด๋ฆฌ ํ์ด์ง: SUPER_ADMIN๋ง */}
{/* ์ฌ๊ธฐ๋ ์ญํ ์ด SUPER_ADMIN๋ง ๋ค์ด๊ฐ ์ ์๋ค. */}
<Route
path="/permission"
element={
<ProtectedRoute requiredRole="SUPER_ADMIN">
<PermissionPage />
</ProtectedRoute>
}
/>
{/* ์๋ชป๋ ์ฃผ์๋ ๋ฉ์ธ์ผ๋ก */}
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</BrowserRouter>
);
}
์๋ฒ๋ฅผ ์ด ๊ฒ๋ ์๋๊ณ , ๊ฐ๋จํ ํ์ด์ง์ ์ค๋ฅ ์ฒ๋ฆฌ ํ์ด์ง๋ ๋ฑํ ์์ด์ BrowserRouter๋ก ๊ฐ์๋ค.(์ฌ์ค ์ด๊ฒ๋ฐ์ ๋ชฐ๋์) ๊ทธ๋ฐ๋ฐ ์์๋ DataAPI ๋ฐฉ์์ธ createBrowserRouter๋ก ๋ง์ด ์ด๋ค๊ณ ํ๋ค. ๋ฌด์จ ์ฐจ์ด๊ฐ ์๋์ง๋ ๋ค์ ํฌ์คํ ์์ ๋ค๋ค๋ณด๊ฒ ๋ค.
[ browserRouter์ createBrowserRouter์ ์ฐจ์ด์ ]
[React] BrowserRouter์ createBrowserRouter์ ํน์ง๊ณผ ์ฝ๋์์ฑ์ฐจ์ด
ํ๋ก์ ํธ ์ดํ ์ฝ๋๋ฅผ ๊ณต๋ถํ๋ค๊ฐcreateBrowserRouter์ ๋ํด ์๊ฒ๋์๋ค.๋ ๋ฐฉ์์ ์ด๋ค ์ฐจ์ด์ ์ด ์๋์ง ์ดํด๋ณด์. 1. _ ์ปดํฌ๋ํธ ๊ธฐ๋ฐ ๋ผ์ฐํ ์ฑ์ด ์ฃผ์์ฐฝ์ ๋ณํ๋ฅผ ๊ฐ์งํ๊ณ ๊ทธ์ ๋ง๋ ํ๋ฉด์ ๋ณด์ฌ
post-this.tistory.com