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
4 changes: 4 additions & 0 deletions src/app/detail/[postingId]/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import NotFoundErrorPage from "@/shared/error/ui/404";
export default function PostDetailNotFound() {
return <NotFoundErrorPage message="해당 게시글을 찾을 수 없습니다" />;
}
25 changes: 17 additions & 8 deletions src/app/detail/[postingId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { getPostDetail } from "@/entities/post/api/getPostDetail.server";
import { getUser } from "@/entities/user/api/getUser.server";
import PostDetailPageClient from "@/widgets/postDetail/ui/DetailPage.client";
import type { PostDetail } from "@/entities/post/model/types/post";
import { handleError } from "@/shared/error/handleError";

export default async function Page({
params,
Expand All @@ -17,18 +18,26 @@ export default async function Page({
const id = Number(postingId);
const queryClient = new QueryClient();

await queryClient.prefetchQuery({
queryKey: ["postDetail", id],
queryFn: () => getPostDetail(id),
});
try {
await queryClient.fetchQuery({
queryKey: ["postDetail", id],
queryFn: () => getPostDetail(id),
});
} catch (e) {
handleError(e);
}

const post = queryClient.getQueryData<PostDetail>(["postDetail", id]);

if (post && post.sellerId) {
await queryClient.prefetchQuery({
queryKey: ["seller", post.sellerId],
queryFn: () => getUser(post.sellerId),
});
try {
await queryClient.prefetchQuery({
queryKey: ["seller", post.sellerId],
queryFn: () => getUser(post.sellerId),
});
} catch (e) {
handleError(e);
}
}

return (
Expand Down
11 changes: 11 additions & 0 deletions src/app/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"use client";
import ServerErrorPage from "@/shared/error/ui/500";
export default function GlobalError({ error }: { error: Error }) {
return (
<html>
<body>
<ServerErrorPage message={error.message} />
</body>
</html>
);
}
54 changes: 6 additions & 48 deletions src/app/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,10 @@
"use client";

import React, { useEffect, useState } from "react";
import { LoginForm } from "@/features/auth/ui/LoginForm/LoginForm";
import { Modal } from "@/shared/ui/Modal/Modal";
import { useRouter } from "next/navigation";
import { useQueryClient } from "@tanstack/react-query";
import { useAuthStore } from "@/features/auth/model/auth.store";
import { useModalStore } from "@/shared/model/modal.store";

export default function LoginPage() {
const router = useRouter();
const queryClient = useQueryClient();
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const { setIsLogined } = useAuthStore();
const { openModal, closeModal } = useModalStore();

useEffect(() => {
queryClient.clear();
}, []);
import { Suspense } from "react";
import LoginPageClient from "@/widgets/main/ui/Client/LoginPage.client";

export default function Page() {
return (
<div className="flex min-h-[calc(100vh-70px)] flex-col items-center justify-center px-4 md:min-h-[calc(100vh-80px)] xl:min-h-[calc(100vh-100px)]">
<h1 className="mb-8 text-2xl font-bold text-white">로그인</h1>
<LoginForm
size="lg"
onSuccess={() => {
openModal("normal", {
message: "로그인에 성공하였습니다.",
onClick: () => {
closeModal();
setIsLogined(true);
router.push("/"); //메인 페이지 이동
},
});
}}
onError={(msg) => {
setErrorMessage(msg);
}}
/>

{!!errorMessage && (
<Modal
message={errorMessage}
buttonText="확인"
onClick={() => setErrorMessage(null)}
className=""
/>
)}
</div>
<Suspense fallback={"loading..."}>
<LoginPageClient />
</Suspense>
);
}
21 changes: 13 additions & 8 deletions src/app/my/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,26 @@ import {
import { getMyPosts } from "@/entities/user/api/getMyPosts.server";
import { getMyProfile } from "@/entities/user/api/getMyProfile.server";
import MyPageClient from "@/widgets/mypage/ui/Client/MyPage.client";
import { handleError } from "@/shared/error/handleError";

const DEFAULT_TAB = "selling";

export default async function Page() {
const queryClient = new QueryClient();

await queryClient.prefetchQuery({
queryKey: ["userProfile"],
queryFn: getMyProfile,
});
try {
await queryClient.fetchQuery({
queryKey: ["userProfile"],
queryFn: getMyProfile,
});

await queryClient.prefetchQuery({
queryKey: ["myPosts", DEFAULT_TAB],
queryFn: () => getMyPosts(DEFAULT_TAB),
});
await queryClient.fetchQuery({
queryKey: ["myPosts", DEFAULT_TAB],
queryFn: () => getMyPosts(DEFAULT_TAB),
});
} catch (e) {
handleError(e);
}

return (
<HydrationBoundary state={dehydrate(queryClient)}>
Expand Down
5 changes: 5 additions & 0 deletions src/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import NotFoundErrorPage from "@/shared/error/ui/404";

export default function GlobalNotFound() {
return <NotFoundErrorPage />;
}
27 changes: 16 additions & 11 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
} from "@tanstack/react-query";
import { getPosts } from "@/entities/post/api/getPosts.server";
import HomePageClient from "@/widgets/main/ui/Client/HomePage.client";
import { handleError } from "@/shared/error/handleError";

export default async function Page({
searchParams,
Expand All @@ -18,17 +19,21 @@ export default async function Page({

const queryClient = new QueryClient();

await queryClient.prefetchInfiniteQuery({
queryKey: ["posts", initialCategory, initialSort, initialKeyword],
queryFn: ({ pageParam = 1 }) =>
getPosts({
category: initialCategory,
sort: initialSort,
page: pageParam,
keyword: initialKeyword,
}),
initialPageParam: 1,
});
try {
await queryClient.fetchInfiniteQuery({
queryKey: ["posts", initialCategory, initialSort, initialKeyword],
queryFn: ({ pageParam = 1 }) =>
getPosts({
category: initialCategory,
sort: initialSort,
page: pageParam,
keyword: initialKeyword,
}),
initialPageParam: 1,
});
} catch (e) {
handleError(e);
}

return (
<HydrationBoundary state={dehydrate(queryClient)}>
Expand Down
15 changes: 3 additions & 12 deletions src/entities/chat/lib/useChatMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@ import {
useInfiniteQuery,
useQueryClient,
} from "@tanstack/react-query";
import { useEffect, useRef, useMemo } from "react";
import { useRef, useMemo } from "react";
import { apiFetch } from "@/shared/api/fetcher";
import { MessagesResponse, MessageProps, Chat } from "../model/types";
import { useModalStore } from "@/shared/model/modal.store";

export const useChatMessages = (chatId: number | null) => {
const queryClient = useQueryClient();
const scrollContainerRef = useRef<HTMLDivElement | null>(null);
const messagesEndRef = useRef<HTMLDivElement | null>(null);
const { openModal, closeModal } = useModalStore();

const {
data,
isError,
error,
fetchNextPage,
hasNextPage,
Expand All @@ -38,15 +37,6 @@ export const useChatMessages = (chatId: number | null) => {
lastPage.hasNext ? lastPage.nextCursor : undefined,
});

useEffect(() => {
if (error) {
openModal("normal", {
message: "메시지 로딩 중 에러가 발생했습니다.",
onClick: closeModal,
});
}
}, [error]);

const messages: MessageProps[] = useMemo(() => {
if (!data) return [];

Expand Down Expand Up @@ -121,6 +111,7 @@ export const useChatMessages = (chatId: number | null) => {
hasNextPage,
isMessagesFirstLoading,
isMessagesLoading,
isError,
error,
scrollContainerRef,
messagesEndRef,
Expand Down
18 changes: 17 additions & 1 deletion src/entities/chat/ui/ChattingRoom/ChattingRoom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { getPostDetail } from "@/entities/post/api/getPostDetail";
import { getUser } from "@/entities/user/api/getUser";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { createChattingRoom } from "@/features/chat/api/createChattingRoom";
import { handleError } from "@/shared/error/handleError";

export const ChattingRoom = ({
postingId,
Expand All @@ -36,31 +37,46 @@ export const ChattingRoom = ({
data: post,
isLoading: isPostLoading,
isError: isPostingError,
error: postingError,
} = useQuery({
queryKey: ["postDetail", postingId],
queryFn: () => getPostDetail(postingId),
});

if (isPostingError) {
handleError(postingError);
}

const {
data: otherUser,
isLoading: isOtherUserLoading,
isError: isOtherUserError,
error: otherUserError,
} = useQuery({
queryKey: ["otherUser", otherId],
queryFn: () => getUser(otherId),
});

if (isOtherUserError) {
handleError(otherUserError);
}

const {
messages,
pushMessageToCache,
fetchMoreMessages,
hasNextPage,
isMessagesFirstLoading,
isError: isChatMessagesError,
Copy link
Collaborator

Choose a reason for hiding this comment

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

useChatMessages.ts가 반환하는 값의 타입에 isError가 없어서 빌드가 실패되는 것 같아요..!

error: chatMessagesError,
isMessagesLoading,
scrollContainerRef,
messagesEndRef,
} = useChatMessages(chatId);

if (isChatMessagesError) {
handleError(chatMessagesError);
}

const {
postStatus: currentPostStatus,
dealStatus: currentDealStatus,
Expand Down
9 changes: 5 additions & 4 deletions src/features/chat/ui/ChatList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { fetchChatList } from "../model/chat.api";
import type { Chat } from "@/entities/chat/model/types";
import { useQuery } from "@tanstack/react-query";
import { DealStatus } from "@/entities/chat/model/types";
import { handleError } from "@/shared/error/handleError";

interface ChatListProps {
onSelect: (info: {
Expand All @@ -29,12 +30,12 @@ const ChatList = ({ onSelect, tab = "all" }: ChatListProps) => {
queryFn: () => fetchChatList(role),
});

if (isLoading) {
return <p className="p-4 text-center text-white/70">불러오는 중...</p>;
if (isError) {
handleError(error);
}

if (isError) {
console.error("채팅 목록 불러오기 실패:", error);
if (isLoading) {
return <p className="p-4 text-center text-white/70">불러오는 중...</p>;
}

if (!chats?.length) {
Expand Down
20 changes: 16 additions & 4 deletions src/shared/api/fetcher.server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { AuthorizationError, NotFoundError, ServerError } from "../error/error";

const BASE_URL = process.env.NEXT_PUBLIC_API_URL;

Expand Down Expand Up @@ -34,7 +34,9 @@ export async function serverFetch<T>(
const refreshToken = cookieStore.get("refreshToken")?.value;

if (!refreshToken) {
redirect("/login");
throw new AuthorizationError(
"세션이 만료되었습니다.\n다시 로그인 해주세요.",
);
}

const refreshRes = await fetch("/api/auth/refresh", {
Expand All @@ -59,13 +61,23 @@ export async function serverFetch<T>(
...restOptions,
});
} else {
redirect("/login");
throw new AuthorizationError(
"세션이 만료되었습니다.\n다시 로그인 해주세요.",
);
}
}

if (res.status === 404) {
throw new NotFoundError();
}

if (res.status >= 500) {
throw new ServerError();
}

if (!res.ok) {
const text = await res.text();
throw new Error(text || `API Error ${res.status}`);
throw new ServerError(text);
}

return res.json() as Promise<T>;
Expand Down
Loading