Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 9 additions & 147 deletions src/App.tsx
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 이거 protectedRouter 따로 배열로 만들어가지고 넣어주기만 하면 되는데

Original file line number Diff line number Diff line change
@@ -1,156 +1,18 @@
import "./App.css";
import PublicLayout from "./layout/PublicLayout";
import {
createBrowserRouter,
Navigate,
RouterProvider,
type RouteObject,
} from "react-router-dom";
import MainPage from "./pages/MainPage";
import ErrorPage from "./pages/ErrorPage";
import LoginPage from "./pages/LoginPage.tsx";
import SearchPage from "./pages/SearchPage";
import { AuthProvider } from "./context/AuthContext.tsx";
import PostWrite from "./pages/Post/PostWritePage.tsx";
import PostSuccess from "./pages/Post/PostSuccess.tsx";
import RandomFeedPage from "./pages/RandomFeedPage.tsx";
import LuckyDrawPage from "./pages/LuckyDrawPage.tsx";
import MyPageLayout from "./pages/MyPage/MyPageLayout.tsx";
import PostDetailPage from "./pages/Post/PostDetailPage.tsx";
import FavoriteFeedPage from "./pages/FavoriteFeedPage.tsx";
import CategoryFeedPage from "./pages/CategoryFeedPage.tsx";
import BestFeedPage from "./pages/BestFeedPage.tsx";
import ServiceTerm from "./components/Terms/ServiceTerm.tsx";
import PrivacyTerms from "./components/Terms/PrivacyTerms.tsx";
import MarketingTerm from "./components/Terms/Marketing.tsx";
import ProfileEditPage from "./pages/MyPage/ProfileEditPage.tsx";
import MyPostPage from "./pages/MyPage/MyPostPage.tsx";
import MyLessonPage from "./pages/MyPage/MyLessonPage.tsx";
import SignUpPage from "./pages/SignUp/SignUpPage.tsx";
import FindPasswordPage from "./pages/FindPasswordPage.tsx";
import SetNewPassWordPage from "./pages/SetNewPasswordPage.tsx";
import PostEditPage from "./pages/Post/PostEditPage.tsx";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { AuthProvider } from "./context/AuthContext";

//로그인 구현 필요 없이 들어가는 페이지 라우터
const publicRoutes: RouteObject[] = [
{
path: "/",
element: <PublicLayout />,
children: [
{
// path : "" 와 동일함 -> 하지만 react 에서 index: true를 적극 권장
index: true,
element: <MainPage />,
},
{
path: "login",
element: <LoginPage />,
},
//TODO: login 사항 없도록 라우터 분리
{
path: "terms1",
element: <ServiceTerm />,
},
{
path: "terms2",
element: <PrivacyTerms />,
},
{
path: "terms3",
element: <MarketingTerm />,
},
/////////////////////////
{
path: "signup",
element: <SignUpPage />,
},
{
path: "find-password",
element: <FindPasswordPage />
},
{
path: "set-password",
element: <SetNewPassWordPage />
},
{
path: "search",
element: <SearchPage />,
},
{
path: "random-feed",
element: <RandomFeedPage />,
},
{
path: "lucky-draw",
element: <LuckyDrawPage />,
},
{
path: "favorite-feed",
element: <FavoriteFeedPage />,
},
{
path: "category-feed/:categoryId",
element: <CategoryFeedPage />,
},
{
path: "best-failer",
element: <BestFeedPage />,
},
{
path: "my-profile",
element: <MyPageLayout />,
children: [
{ index: true, element: <Navigate to="profile" replace /> },
{ path: "profile", element: <ProfileEditPage /> },
{ path: "posts", element: <MyPostPage /> },
{ path: "lessons", element: <MyLessonPage /> },
],
},
// 설정한 path 이외에 모든 Path 에 대해 ErrorPage 랜더링
{
path: "*",
element: <ErrorPage />,
},
{
path: "post",
element: <PostWrite />,
},
{
path: "posts/edit/:postId",
element: <PostEditPage />
},
import { publicRoutes } from "./routes/publicRoutes";
import { protectedRoutes } from "./routes/protectedRoutes";

{
path: "post/success",
element: <PostSuccess />,
},
{
path: "posts/:postId",
element: <PostDetailPage />,
},
],
},
];
const router = createBrowserRouter([...publicRoutes, ...protectedRoutes]);

//router 생성 : 이후 portected를 위해 배열로 넣음
const router = createBrowserRouter([...publicRoutes]);

/*
useQueryClient 사용을 위해 코드 생성 후 주석 처리
export const queryClient = new QueryClient();
*/

// 이후 쿼리, authprovider 사용을 위해 주석 처리
function App() {
return (
<>
<AuthProvider>
{/* <QueryClientProvider client={queryClient} */}
<RouterProvider router={router} />
{/* </QueryClientProvider> */}
</AuthProvider>
</>
<AuthProvider>
<RouterProvider router={router} />
</AuthProvider>
);
}

export default App;
export default App;
1 change: 1 addition & 0 deletions src/apis/SignUp/getTerms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ export const getTerms = async ():Promise<CommonResponse<TermItem>> => {
const {data} = await axiosInstance.get<CommonResponse<TermItem>>(
`/terms`
);
console.log("약관 조회", data);
return data;
}
1 change: 1 addition & 0 deletions src/apis/SignUp/postSinUp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ export const postSignUp = async ({
verificationToken,
}
);
console.log("회원가입", data);
return data;
}
1 change: 0 additions & 1 deletion src/apis/mypage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ export const editMyProfile = async ({
"Content-Type": "multipart/form-data",
},
});

return data;
};

Expand Down
1 change: 0 additions & 1 deletion src/components/SignUpPage/TermsAgreement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ export const TermsAgreement = () => {
}, [location.key, terms.length, requiredTerms, optionalTerms]);

const setOptionalTrue = () => {
// 요구사항 4: 전체약관 동의 누르면 OPTIONAL은 자동 체크
for (const t of optionalTerms) {
localStorage.setItem(lsKey(t.id), "true");
}
Expand Down
3 changes: 2 additions & 1 deletion src/components/Terms/TermsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ const TermsContainer = ({ id }: Props) => {
localStorage.setItem(lsKey(id), "true");
navigate(-1);
};

return (
<div className="w-full flex flex-col gap-[3.12rem]">
<div className="flex justify-center items-center text-[1.5rem] font-bold">
{term?.title}
</div>
<div>{term?.content}</div>
<div className="whitespace-pre-wrap">{term?.content}</div>
{/* 누르면 체크 ok되고 넘어가는거로 */}
<button
type="button"
Expand Down
18 changes: 16 additions & 2 deletions src/hooks/post/usePostDetail.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { useEffect, useState } from "react";
import { getPostDetail } from "../../apis/Post/getPostDetail";
import { PostDetailResponse } from "../../types/post";
import { useNavigate } from "react-router-dom";

export const usePostDetail = (postId: number) => {
const [data, setData] = useState<PostDetailResponse | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<unknown>(null);

const navigate = useNavigate();

useEffect(() => {
if (!postId) return;

Expand All @@ -18,8 +21,19 @@ export const usePostDetail = (postId: number) => {
setError(null);
const result = await getPostDetail(postId);
if (mounted) setData(result);
} catch (e) {
if (mounted) setError(e);
} catch (e: any) {
if (!mounted) return;

// 에러 분기 처리
const status = e?.response?.status || e?.status;

if (status === 403) {
alert("로그인이 필요합니다");
navigate("/login");
return; // 에러 상태를 굳이 set 하지 않고 종료 가능
}

setError(e);
} finally {
if (mounted) setLoading(false);
}
Expand Down
2 changes: 1 addition & 1 deletion src/pages/MyPage/MyPageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import MyPageSidebar from "../../components/MyPage/EditProfilePage/MyPageSideb

export default function MyPageLayout() {
return (
<div className="">
<div className="w-full">
<h1 className="text-xl font-semibold mb-6">마이 페이지</h1>

<div className="flex gap-[1.88rem]">
Expand Down
8 changes: 4 additions & 4 deletions src/pages/MyPage/ProfileEditPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { GetMyProfileResponse } from "../../types/MyPage";

import ProfileAvatarSection from "../../components/MyPage/EditProfilePage/ProfileAvatarSection";
import NicknameField from "../../components/MyPage/EditProfilePage/NicknameField";
import EmailField from "../../components/MyPage/EditProfilePage/EmailField";
// import EmailField from "../../components/MyPage/EditProfilePage/EmailField";
import ProfileSaveSection from "../../components/MyPage/EditProfilePage/ProfileSaveSection";

const ProfileEditPage = () => {
Expand All @@ -20,15 +20,15 @@ const ProfileEditPage = () => {

// 폼 상태
const [userName, setUserName] = useState("");
const [emailDraft, setEmailDraft] = useState(""); // 아직 저장 로직 없음
// const [emailDraft, setEmailDraft] = useState(""); // 아직 저장 로직 없음
const [file, setFile] = useState<File | null>(null);
const [previewUrl, setPreviewUrl] = useState<string | null>(null);

// 조회 성공 시 초기값 세팅
useEffect(() => {
if (!profile) return;
setUserName(profile.userName ?? "");
setEmailDraft(profile.email ?? "");
// setEmailDraft(profile.email ?? "");
// 이미지 preview는 "선택한 파일"이 우선이라, 여기서 previewUrl은 건드리지 않음
}, [profile?.userName, profile?.email]); // profile 전체 의존성 걸면 불필요 렌더 가능

Expand Down Expand Up @@ -107,7 +107,7 @@ const ProfileEditPage = () => {

<NicknameField value={userName} onChange={setUserName} />

<EmailField value={emailDraft} onChange={setEmailDraft} />
{/* <EmailField value={emailDraft} onChange={setEmailDraft} /> */}

<ProfileSaveSection
onSave={onSave}
Expand Down
4 changes: 1 addition & 3 deletions src/pages/Post/PostDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,7 @@ export default function PostDetailPage() {
<div className="py-10 text-center text-[#b2b2b2]">불러오는 중...</div>
);
if (error || !data || !stageMap)
return (
<div className="py-10 text-center text-[#b2b2b2]">불러오기 실패</div>
);
return;

return (
<div className="w-full flex flex-col px-[3.63rem] gap-[2.5rem]">
Expand Down
15 changes: 15 additions & 0 deletions src/routes/ProtectedRoute.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Navigate } from "react-router-dom";
import { useAuth } from "../context/AuthContext";

export const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
const { isAuthenticated, isLoading } = useAuth();

if (isLoading) return <div>로딩 중...</div>;

if (!isAuthenticated) {
alert("로그인이 필요한 서비스입니다.");
return <Navigate to="/login" replace />;
}

return <>{children}</>;
};
66 changes: 66 additions & 0 deletions src/routes/protectedRoutes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import type { RouteObject } from "react-router-dom";
import { Navigate } from "react-router-dom";
import PublicLayout from "../layout/PublicLayout";

import { ProtectedRoute } from "./ProtectedRoute";

import PostWrite from "../pages/Post/PostWritePage";
import PostSuccess from "../pages/Post/PostSuccess";
import PostEditPage from "../pages/Post/PostEditPage";

import MyPageLayout from "../pages/MyPage/MyPageLayout";
import ProfileEditPage from "../pages/MyPage/ProfileEditPage";
import MyPostPage from "../pages/MyPage/MyPostPage";
import MyLessonPage from "../pages/MyPage/MyLessonPage";

export const protectedRoutes: RouteObject[] = [
{
path: "/",
element: <PublicLayout />,
children: [
// 글쓰기
{
path: "post",
element: (
<ProtectedRoute>
<PostWrite />
</ProtectedRoute>
),
},
// 작성 성공
{
path: "post/success",
element: (
<ProtectedRoute>
<PostSuccess />
</ProtectedRoute>
),
},
// 수정
{
path: "posts/edit/:postId",
element: (
<ProtectedRoute>
<PostEditPage />
</ProtectedRoute>
),
},

// 마이페이지 전체 보호
{
path: "my-profile",
element: (
<ProtectedRoute>
<MyPageLayout />
</ProtectedRoute>
),
children: [
{ index: true, element: <Navigate to="profile" replace /> },
{ path: "profile", element: <ProfileEditPage /> },
{ path: "posts", element: <MyPostPage /> },
{ path: "lessons", element: <MyLessonPage /> },
],
},
],
},
];
Loading