Skip to content
Closed
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
14 changes: 0 additions & 14 deletions src/app/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@

import React, { useState } from "react";
import { LoginForm } from "@/features/auth/ui/LoginForm/LoginForm";
import { Modal } from "@/shared/ui/Modal/Modal";
import { useRouter } from "next/navigation";
import { useAuthStore } from "@/features/auth/model/auth.store";

export default function SignUpPage() {
const router = useRouter();
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const { setIsLogined } = useAuthStore();
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)]">
Expand All @@ -19,19 +17,7 @@ export default function SignUpPage() {
setIsLogined(true);
router.push("/"); //메인 페이지 이동
}}
onError={(msg) => {
setErrorMessage(msg);
}}
/>

{!!errorMessage && (
<Modal
message={errorMessage}
buttonText="확인"
onClick={() => setErrorMessage(null)}
className=""
/>
)}
</div>
);
}
6 changes: 3 additions & 3 deletions src/app/my/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Tab from "@/widgets/mypage/ui/Tab.tsx/Tab";
import { apiFetch } from "@/shared/api/fetcher";
import { useModalStore } from "@/shared/model/modal.store";
import { usePostCreateModal } from "@/features/createPost/lib/usePostCreateModal";

import { handleError } from "@/shared/error/errorHandler";
const options = [
{ label: "판매중 상품", value: "selling" },
{ label: "판매완료 상품", value: "sold" },
Expand Down Expand Up @@ -48,7 +48,7 @@ const Mypage = () => {
});
setUserProfile(data);
} catch (error) {
console.error("유저 정보 로딩 실패: ", error);
handleError(error, "유저 프로필을 불러오는 중 오류가 발생했습니다.");
}
}

Expand All @@ -68,7 +68,7 @@ const Mypage = () => {
);
setPosts(res.data);
} catch (error) {
console.error("현재 유저 관련 게시글 불러오기 실패 : ", error);
handleError(error, "현재 유저 게시물을 불러오는 중 오류가 발생했습니다.");
setPosts([]);
} finally {
setLoading(false);
Expand Down
10 changes: 3 additions & 7 deletions src/entities/chat/lib/useChatOtherUser.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { useEffect, useState } from "react";
import { User } from "@/entities/user/model/types/user";
import { apiFetch } from "@/shared/api/fetcher";
import { useModalStore } from "@/shared/model/modal.store";
import { handleError } from "@/shared/error/errorHandler";

export const useChatOtherUser = (otherId: number) => {
const [otherUser, setOtherUser] = useState<User | null>(null);
const [isLoading, setIsLoading] = useState(false);
const { openModal, closeModal } = useModalStore();

useEffect(() => {
async function fetchUser() {
Expand All @@ -16,11 +15,8 @@ export const useChatOtherUser = (otherId: number) => {
method: "GET",
});
setOtherUser(res);
} catch {
openModal("normal", {
message: "상대 유저 정보 조회에 실패했습니다.",
onClick: () => closeModal(),
});
} catch (error) {
handleError(error, "상대 유저 정보를 가져오는 중 오류가 발생했습니다.");
} finally {
setIsLoading(false);
}
Expand Down
8 changes: 3 additions & 5 deletions src/entities/chat/lib/useChatPost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
import { PostDetail } from "@/entities/post/model/types/post";
import { apiFetch } from "@/shared/api/fetcher";
import { useModalStore } from "@/shared/model/modal.store";
import { handleError } from "@/shared/error/errorHandler";

export const useChatPost = (postingId: number) => {
const [post, setPost] = useState<PostDetail | null>(null);
Expand All @@ -16,11 +17,8 @@ export const useChatPost = (postingId: number) => {
method: "GET",
});
setPost(res);
} catch {
openModal("normal", {
message: "게시물 정보 조회에 실패했습니다.",
onClick: () => closeModal(),
});
} catch (error) {
handleError(error, "게시물 정보 조회 중 오류가 발생했습니다.");
} finally {
setIsLoading(false);
}
Expand Down
15 changes: 12 additions & 3 deletions src/entities/chat/model/socket.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { error } from "console";
import { MessageProps } from "./types";
import { useAuthStore } from "@/features/auth/model/auth.store";
import { refreshAccessToken } from "@/shared/api/refresh";
import { handleError } from "@/shared/error/errorHandler";
import { ServerError } from "@/shared/error/error";

export interface ChatSocketEvents {
onOpen?: () => void;
Expand Down Expand Up @@ -62,7 +63,11 @@ export class ChatSocket {
this.events.onMessage?.(msg);
}
} catch (err) {
console.error("[Socket] Message parse error:", err);
handleError(
new ServerError(undefined, () => {
location.replace("/");
}),
);
}
};

Expand All @@ -79,7 +84,11 @@ export class ChatSocket {
};

this.socket.onerror = (err) => {
console.error("[Socket] Error:", err);
handleError(
new ServerError(undefined, () => {
location.replace("/");
}),
);
this.events.onError?.(err);
reject(err);
};
Expand Down
8 changes: 3 additions & 5 deletions src/entities/chat/ui/ChattingRoom/ChattingRoom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { useInfiniteScroll } from "@/shared/lib/useInfiniteScroll";
import { getPostDetail } from "@/entities/post/api/getPostDetail";
import { getUser } from "@/entities/user/api/getUser";
import { useQuery } from "@tanstack/react-query";
import { handleError } from "@/shared/error/errorHandler";

export const ChattingRoom = ({
postingId,
Expand Down Expand Up @@ -157,11 +158,8 @@ export const ChattingRoom = ({
},
);
setChatId(res.chatId); //useChatSocket hook을 통한 소켓 자동 재연결.
} catch {
openModal("normal", {
message: "채팅방 생성에 실패했습니다.",
onClick: closeModal,
});
} catch (error) {
handleError(error, "채팅방 생성 중 오류가 발생했습니다.");
}
}

Expand Down
12 changes: 8 additions & 4 deletions src/features/auth/ui/LoginForm/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ import { Input } from "@/entities/user/ui/Input/Input";
import Button from "@/shared/ui/Button/Button";
import { useAuthStore } from "../../model/auth.store";
import { apiFetch } from "@/shared/api/fetcher";
import { handleError } from "@/shared/error/errorHandler";
import { AuthorizationError } from "@/shared/error/error";

type FormSize = "sm" | "md" | "lg";

interface LoginFormProps {
size?: FormSize;
onSuccess?: () => void;
onError?: (msg: string) => void;
}

export const LoginForm = ({
size = "lg",
onSuccess,
onError,
...props
}: LoginFormProps) => {
const [email, setEmail] = useState("");
Expand All @@ -44,8 +44,12 @@ export const LoginForm = ({

setAccessToken(res.accessToken);
onSuccess?.();
} catch {
onError?.("로그인에 실패하였습니다.\n이메일과 비밀번호를 확인해주세요.");
} catch (error) {
handleError(
new AuthorizationError(
"로그인에 실패하였습니다.\n이메일과 비밀번호를 확인해주세요.",
),
);
}
};

Expand Down
1 change: 0 additions & 1 deletion src/features/auth/ui/SignUpForm/SignUpForm.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const meta: Meta<typeof SignUpForm> = {
},
argTypes: {
onSuccess: { action: "success" },
onError: { action: "error" },
},
};

Expand Down
11 changes: 8 additions & 3 deletions src/features/auth/ui/SignUpForm/SignUpForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import { DropDown } from "@/shared/ui/DropDown/DropDown";
import Button from "@/shared/ui/Button/Button";
import { apiFetch } from "@/shared/api/fetcher";
import cn from "@/shared/lib/cn";
import { handleError } from "@/shared/error/errorHandler";
import { ServerError } from "@/shared/error/error";

interface SignUpFormProps {
onSuccess?: () => void;
onError?: (msg: string) => void;
}

export const SignUpForm = ({ onSuccess, onError }: SignUpFormProps) => {
export const SignUpForm = ({ onSuccess }: SignUpFormProps) => {
//input text 상태
const [email, setEmail] = useState("");
const [nickname, setNickname] = useState("");
Expand Down Expand Up @@ -149,7 +150,11 @@ export const SignUpForm = ({ onSuccess, onError }: SignUpFormProps) => {
} else if (error.message.includes("NICKNAME_DUPLICATE")) {
setNicknameError("이미 사용 중인 닉네임입니다.");
} else {
onError?.("회원가입에 실패했습니다. 잠시 후 다시 시도해주세요.");
handleError(
new ServerError(
"회원가입 중 에러가 발생했습니다.\n잠시후 다시 시도해주세요.",
),
);
}
}
}
Expand Down
11 changes: 0 additions & 11 deletions src/features/createPost/lib/usePostCreateModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,6 @@ export const usePostCreateModal = (handlers?: {
});
}, 100);
},
onError: (message: string) => {
closeModal();

handlers?.onFailure?.();

openModal("normal", {
message: "게시물 등록 중 오류가 발생했습니다. " + message,
buttonText: "확인",
onClick: () => closeModal(),
});
},
});
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,18 @@ import "swiper/css/navigation";
import "swiper/css/pagination";
import { apiFetch } from "@/shared/api/fetcher";
import { uploadImage } from "@/shared/api/uploadImage";
import { handleError } from "@/shared/error/errorHandler";

export interface PostCreateModalProps {
className?: string;
onClose?: () => void;
onCreate?: () => void;
onError?: (message: string) => void;
}

export const PostCreateModal = ({
className,
onClose,
onCreate,
onError,
}: PostCreateModalProps) => {
const [images, setImages] = useState<File[]>([]);
const [title, setTitle] = useState("");
Expand Down Expand Up @@ -87,11 +86,7 @@ export const PostCreateModal = ({
console.log("게시글 생성 성공! : ", res);
onCreate?.();
} catch (error) {
console.error("게시글 생성 실패 : ", error);
if (error instanceof Error) {
onError?.(error.message);
}
onError?.(String(error));
handleError(error, "게시글 생성 중 오류가 발생했습니다.");
}
};

Expand Down
5 changes: 2 additions & 3 deletions src/features/editPost/ui/PostEditModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import "swiper/css/navigation";
import "swiper/css/pagination";
import { apiFetch } from "@/shared/api/fetcher";
import { uploadImage } from "@/shared/api/uploadImage";
import { handleError } from "@/shared/error/errorHandler";

const ImageSwiperSlide = (
idx: number,
Expand Down Expand Up @@ -144,9 +145,7 @@ export const PostEditModal = ({
console.log("게시글 수정 성공! : ", res);
onEdit?.();
} catch (error) {
console.error("게시글 수정 실패 : ", error);
const message = error instanceof Error ? error.message : String(error);
onError?.(message);
handleError(error, "게시글 수정 중 오류가 발생했습니다.");
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { DropDown } from "@/shared/ui/DropDown/DropDown";
import Button from "@/shared/ui/Button/Button";
import { apiFetch } from "@/shared/api/fetcher";
import { uploadImage } from "@/shared/api/uploadImage";
import { handleError } from "@/shared/error/errorHandler";

export interface ProfileEditModalProps {
imageUrl?: string;
Expand Down Expand Up @@ -79,10 +80,7 @@ export const ProfileEditModal = ({

onSave?.();
} catch (error) {
if (error instanceof Error) {
console.error("프로필 수정 실패:", error);
onError?.(error);
} else onError?.(new Error(String(error)));
handleError(error, "프로필 수정 중 오류가 발생했습니다.");
} finally {
setLoading(false);
}
Expand Down
34 changes: 23 additions & 11 deletions src/shared/api/fetcher.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useAuthStore } from "@/features/auth/model/auth.store";
import { refreshAccessToken } from "./refresh";
import { useModalStore } from "@/shared/model/modal.store";
import { AuthorizationError, NotFoundError, ServerError } from "../error/error";

const BASE_URL = process.env.NEXT_PUBLIC_API_URL;

Expand All @@ -9,8 +10,7 @@ export async function apiFetch<T>(
options: RequestInit & { noAuth?: boolean },
): Promise<T> {
const { headers, noAuth, ...restOptions } = options;
const { accessToken, setAccessToken, logout } = useAuthStore.getState();
const { openModal, closeModal } = useModalStore.getState();
const { accessToken } = useAuthStore.getState();

const defaultHeaders: HeadersInit = {
"Content-Type": "application/json",
Expand All @@ -31,7 +31,11 @@ export async function apiFetch<T>(
if (res.status === 401 && !noAuth) {
const newToken = await refreshAccessToken();

if (!newToken) throw new Error("세션 만료");
if (!newToken)
throw new AuthorizationError(
"세션이 만료되었습니다.\n다시 로그인해주세요",
() => location.replace("/login"),
);

defaultHeaders["Authorization"] = `Bearer ${newToken}`;

Expand All @@ -45,15 +49,23 @@ export async function apiFetch<T>(
}

if (!res.ok) {
let message = `API Error ${res.status}`;
try {
const data = await res.json();
message = data.message ?? data.detail ?? message;
} catch {
const text = await res.text();
if (text) message = text;
// 분기처리
if (res.status === 404) {
throw new NotFoundError("요청한 리소스를 찾을 수 없습니다.", () => {
location.replace("/");
});
}
throw new Error(message);

if (res.status >= 500) {
throw new ServerError("서버 오류가 발생했습니다.", () => {
location.replace("/");
});
}

// 기타 오류 → 500 취급
throw new ServerError("알 수 없는 서버 오류", () => {
location.replace("/");
});
}

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