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
31 changes: 31 additions & 0 deletions src/app/(home)/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"use client";

import { useEffect, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { createPortal } from "react-dom";
import LoginModal from "../_components/LoginModal/LoginModal";

export default function Error({ error }: { error: Error }) {
const router = useRouter();
const [isLoginModalOpened, setIsLoginModalOpened] = useState(false);
const closeLoginModal = () => {
setIsLoginModalOpened(false);
router.replace("/");
};
useEffect(() => {
if (error.message === "Auth Error") {
setIsLoginModalOpened(true);
}
}, []);

return (
<>
{isLoginModalOpened &&
createPortal(
<LoginModal closeLoginModal={closeLoginModal} />,
document.body
)}
</>
);
}
2 changes: 1 addition & 1 deletion src/app/_components/NewGroupModal/NewGroupModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const NewGroup = ({
const [groupName, setGroupName] = useState("");

const { mutate: createNewGroup } = useMutation({
mutationFn: () => createGroup(groupName, tokenData.accessToken),
mutationFn: () => createGroup(groupName),
onSuccess: () =>
queryClient.invalidateQueries({ queryKey: ["groupListData"] }),
});
Expand Down
25 changes: 8 additions & 17 deletions src/app/_components/SaveContentModal/SaveContentModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,20 @@ const SaveContent = ({
openNewGroupModal: () => void;
contentId: number;
}) => {
const localTokenData = localStorage.getItem("tokenData");
if (localTokenData === null) throw new Error("Token is not found");
const tokenData = JSON.parse(localTokenData);
// const localTokenData = localStorage.getItem("tokenData");
// if (localTokenData === null) throw new Error("Token is not found");
// const tokenData = JSON.parse(localTokenData);

const { data: groupListData } = useQuery<{ content: IGroup[] }>({
queryKey: ["groupListData"],
queryFn: () => getGroupList(tokenData.accessToken),
enabled: !!tokenData,
queryFn: () => getGroupList(),
// enabled: !!tokenData,
});

const { data: containedGroupList, refetch: refetchContainedGroupList } =
useQuery({
queryKey: ["containedGroupList", contentId],
queryFn: () =>
getContainedGroupList(contentId.toString(), tokenData.accessToken),
queryFn: () => getContainedGroupList(contentId.toString()),
enabled: !!groupListData,
});

Expand All @@ -50,11 +49,7 @@ const SaveContent = ({
}) => {
if (groupListData === undefined)
throw new Error("GroupListData is not found");
return saveContentToGroup(
groupListData.content[index].name,
contentId,
tokenData.accessToken
);
return saveContentToGroup(groupListData.content[index].name, contentId);
},
onSuccess: () => refetchContainedGroupList(),
});
Expand All @@ -69,11 +64,7 @@ const SaveContent = ({
}) => {
if (groupListData === undefined)
throw new Error("GroupListData is not found");
return deleteContentInGroup(
groupListData.content[index].name,
contentId,
tokenData.accessToken
);
return deleteContentInGroup(groupListData.content[index].name, contentId);
},
onSuccess: () => refetchContainedGroupList(),
});
Expand Down
73 changes: 32 additions & 41 deletions src/app/_utils/api.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { IContentData } from "..";
import getAuth from "./getAuth";

export const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;
const fetchUrl = new URL(BASE_URL || "");

const { getAccessToken, isLoggedIn } = getAuth();

function shuffleArray(array: object[]) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
Expand All @@ -28,19 +31,14 @@ const getContents = async (page: number, searchParams: string) => {
fetchUrl.pathname = "/api/content/v1/contents";
fetchUrl.search = `page=${page}&size=10&${searchParams}`;

let tokenData;

if (typeof window !== "undefined") {
const localData = localStorage.getItem("tokenData");
if (localData !== null) {
tokenData = JSON.parse(localData);
}
}
const access_token = await getAccessToken();

const data = await fetch(fetchUrl.href, {
headers: tokenData && {
Authorization: `Bearer ${tokenData?.accessToken}`,
},
headers: access_token
? {
Authorization: `Bearer ${access_token}`,
}
: undefined,
});

if (!data.ok) {
Expand All @@ -55,19 +53,14 @@ const getContentById = async (
): Promise<IContentData> => {
fetchUrl.pathname = `/api/content/v1/contents/${id}`;

let tokenData;

if (typeof window !== "undefined") {
const localData = localStorage.getItem("tokenData");
if (localData !== null) {
tokenData = JSON.parse(localData);
}
}
const access_token = await getAccessToken();

const data = await fetch(fetchUrl.href, {
headers: tokenData && {
Authorization: `Bearer ${tokenData?.accessToken}`,
},
headers: access_token
? {
Authorization: `Bearer ${access_token}`,
}
: undefined,
});

if (!data.ok) {
Expand Down Expand Up @@ -209,25 +202,30 @@ const logout = async (refreshToken: string) => {
return data;
};

const getGroupList = async (access_token: string) => {
const getGroupList = async () => {
fetchUrl.pathname = "/api/bookmark/v1/groups";

const access_token = await getAccessToken();
const data = await fetch(fetchUrl.href, {
headers: {
Authorization: `Bearer ${access_token}`,
},
});

if (!data.ok) {
if (data.status === 401) {
throw new Error("Auth Error");
}
throw new Error("API Error");
}

return await data.json();
};

const createGroup = async (groupName: string, access_token: string) => {
const createGroup = async (groupName: string) => {
fetchUrl.pathname = "/api/bookmark/v1/groups";

const access_token = await getAccessToken();
const data = await fetch(fetchUrl.href, {
method: "POST",
headers: {
Expand Down Expand Up @@ -276,11 +274,11 @@ const saveContentToGroup_Deprecated = async (

const saveContentToGroup = async (
groupName: string | null,
contentId: number | null,
access_token: string
contentId: number | null
) => {
fetchUrl.pathname = `/api/bookmark/v1/groups/${groupName}/contents/${contentId}`;

const access_token = await getAccessToken();
const data = await fetch(fetchUrl.href, {
method: "PUT",
headers: {
Expand All @@ -297,13 +295,10 @@ const saveContentToGroup = async (
return data;
};

const getContentListInGroup = async (
groupName: string,
access_token: string
) => {
const getContentListInGroup = async (groupName: string) => {
fetchUrl.pathname = "/api/bookmark/v1/bookmarks";
fetchUrl.search = `groupName=${groupName}`;

const access_token = await getAccessToken();
const data = await fetch(fetchUrl.href, {
headers: {
Authorization: `Bearer ${access_token}`,
Expand All @@ -317,9 +312,9 @@ const getContentListInGroup = async (
return await data.json();
};

const deleteGroup = async (groupName: string, access_token: string) => {
const deleteGroup = async (groupName: string) => {
fetchUrl.pathname = `/api/bookmark/v1/groups/${groupName}`;

const access_token = await getAccessToken();
const data = await fetch(fetchUrl.href, {
method: "DELETE",
headers: {
Expand All @@ -338,11 +333,10 @@ const deleteGroup = async (groupName: string, access_token: string) => {

const deleteContentInGroup = async (
groupName: string,
contentId: number | null,
access_token: string
contentId: number | null
) => {
fetchUrl.pathname = `/api/bookmark/v1/groups/${groupName}/contents/${contentId}`;

const access_token = await getAccessToken();
const data = await fetch(fetchUrl.href, {
method: "DELETE",
headers: {
Expand All @@ -359,13 +353,10 @@ const deleteContentInGroup = async (
return data;
};

const getContainedGroupList = async (
contentId: string,
access_token: string
) => {
const getContainedGroupList = async (contentId: string) => {
fetchUrl.pathname = "/api/bookmark/v1/groups-with-contains";
fetchUrl.search = `contentId=${contentId}`;

const access_token = await getAccessToken();
const data = await fetch(fetchUrl.href, {
headers: {
Authorization: `Bearer ${access_token}`,
Expand Down
77 changes: 77 additions & 0 deletions src/app/_utils/getAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { ITokenData } from "..";
import { refresh } from "./api";

const getAccessToken = async () => {
if (typeof window === "undefined") return false;

const localTokenData = localStorage.getItem("tokenData");
const now = new Date();
const localTime = new Date(now.getTime());

// 기존에 존재하는 토큰이 있을 때
if (localTokenData !== null) {
const parsedTokenData: ITokenData = JSON.parse(localTokenData);
// 엑세스 토큰 유효기간이 남아있을 때
const accessTokenExpiresAt = new Date(parsedTokenData.accessTokenExpiresAt);
const refreshTokenExpiresAt = new Date(
parsedTokenData.refreshTokenExpiresAt
);

if (accessTokenExpiresAt > localTime) {
return parsedTokenData.accessToken;
}
// 엑세스 토큰 유효기간 지났고 리프레시 토큰 유효기간 남았을 떄 재발급
else if (
accessTokenExpiresAt < localTime &&
refreshTokenExpiresAt > localTime
) {
const tokenData = await refresh(parsedTokenData.refreshToken);
if (tokenData) {
localStorage.setItem("tokenData", JSON.stringify(tokenData));
return parsedTokenData.accessToken;
}
}
// 모두 유효기간 지났을 때
}
return false;
};

const isLoggedIn = async () => {
if (typeof window === "undefined") return false;

const localTokenData = localStorage.getItem("tokenData");
const now = new Date();
const localTime = new Date(now.getTime());

// 기존에 존재하는 토큰이 있을 때
if (localTokenData !== null) {
const parsedTokenData = JSON.parse(localTokenData);
// 엑세스 토큰 유효기간이 남아있을 때
const accessTokenExpiresAt = new Date(parsedTokenData.accessTokenExpiresAt);
const refreshTokenExpiresAt = new Date(
parsedTokenData.refreshTokenExpiresAt
);

if (accessTokenExpiresAt > localTime) {
return true;
}
// 엑세스 토큰 유효기간 지났고 리프레시 토큰 유효기간 남았을 떄 재발급
else if (
accessTokenExpiresAt < localTime &&
refreshTokenExpiresAt > localTime
) {
const tokenData = await refresh(parsedTokenData.refreshToken);
if (tokenData) {
localStorage.setItem("tokenData", JSON.stringify(tokenData));
return true;
}
}
// 모두 유효기간 지났을 때
}
return false;
};
const getAuth = () => {
return { getAccessToken, isLoggedIn };
};

export default getAuth;
6 changes: 4 additions & 2 deletions src/app/error.tsx → src/app/global-error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@
import { useEffect } from "react";
import styles from "./not-found.module.css";
import Link from "next/link";
import { useRouter } from "next/navigation";

export default function Error({ error }: { error: Error }) {
const router = useRouter();
useEffect(() => {
console.log(error);
console.log(error.cause, error.message, error.name);
}, []);
return (
<div className={styles.container}>
<div className={styles.contentBox}>
<h1 className={styles.text}>서버 에러가 발생했습니다. ㅠㅠ</h1>
<h1 className={styles.text}>알 수 없는 에러가 발생했습니다. ㅠㅠ</h1>\
<Link href="/" style={{ marginTop: 30 }}>
<h1 className={styles.linkText} onClick={() => location.reload()}>
돌아가기
Expand Down
18 changes: 9 additions & 9 deletions src/app/groupContents/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ const GroupContents = ({ params }: { params: { id: string } }) => {
);
useEffect(() => {
const getContentList = async () => {
const localTokenData = localStorage.getItem("tokenData");
if (localTokenData === null) throw new Error("Token is not found");
const tokenData = JSON.parse(localTokenData);
// const localTokenData = localStorage.getItem("tokenData");
// if (localTokenData === null) throw new Error("Token is not found");
// const tokenData = JSON.parse(localTokenData);
const contentsData: { content: IContentData[] } =
await getContentListInGroup(params.id, tokenData.accessToken);
await getContentListInGroup(params.id);
setContentsData(contentsData);
};
getContentList();
Expand All @@ -34,13 +34,13 @@ const GroupContents = ({ params }: { params: { id: string } }) => {
);
setContentsData({ content: tempContentsData });

const localTokenData = localStorage.getItem("tokenData");
if (localTokenData === null) throw new Error("Token is not found");
const tokenData = JSON.parse(localTokenData);
await deleteContentInGroup(params.id, contentId, tokenData.accessToken);
// const localTokenData = localStorage.getItem("tokenData");
// if (localTokenData === null) throw new Error("Token is not found");
// const tokenData = JSON.parse(localTokenData);
await deleteContentInGroup(params.id, contentId);

const newContentsData: { content: IContentData[] } =
await getContentListInGroup(params.id, tokenData.accessToken);
await getContentListInGroup(params.id);
setContentsData(newContentsData);
};

Expand Down
Loading
Loading