Skip to content

Commit

Permalink
Merge pull request #546 from Hongjw030/part3-홍재원-week15
Browse files Browse the repository at this point in the history
[홍재원] week15
  • Loading branch information
kimjngyun authored Dec 20, 2023
2 parents 2249b4c + 4944acb commit cd4eaf4
Show file tree
Hide file tree
Showing 17 changed files with 252 additions and 109 deletions.
45 changes: 45 additions & 0 deletions src/api/axiosInstance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,49 @@ import axios from "axios";

export const axiosInstance = axios.create({
baseURL: "https://bootcamp-api.codeit.kr/api/",
headers: {
"Content-Type": "application/json",
withCredentials: true,
},
});

axiosInstance.interceptors.request.use((config) => {
if (!config.headers) return config;
if (typeof window !== "undefined") {
const accessToken = localStorage.getItem("accessToken");
if (accessToken && config.headers) {
config.headers["Authorization"] = accessToken;
}
}
return config;
});

axiosInstance.interceptors.response.use(
(res) => {
return res;
},
async (error) => {
if (error.config && error.response && error.response.status === 401) {
error.config._retry = true;
const refreshtoken = localStorage.getItem("refreshToken");
error.config.headers.RefreshToken = `${refreshtoken}`;
return axios
.get(`${process.env.NEXT_PUBLIC_API_URL}/auth/me`, {
headers: {
RefreshToken: `${refreshtoken}`,
"Content-Type": "application/json",
withCredentials: true,
},
})
.then(async (res) => {
if (res.status === 200 && res.data.accessToken) {
localStorage.setItem("accessToken", res.data.accessToken);
const accesstoken = localStorage.getItem("accessToken");
error.config.headers["Authorization"] = `${accesstoken}`;
return axiosInstance(error.config);
}
});
}
return Promise.reject(error);
},
);
16 changes: 7 additions & 9 deletions src/api/getAllCards.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { CardInterface } from "@/types";
import { USER_ID } from "./constants";
import { axiosInstance } from "./axiosInstance";

export default async function getAllCards(id = "") {
let url = `${USER_ID}/links?`;
export default async function getAllCards(userId = "", folderId = "") {
let url = `${userId}/links?`;
const searchUrl = new URLSearchParams();
if (id !== "") {
searchUrl.append("folderId", id);
if (folderId !== "") {
searchUrl.append("folderId", folderId);
url += searchUrl.toString();
}

console.log(url);
const response = await axiosInstance.get(`${url}`);
const data: CardInterface[] = response?.data?.data;
return data;
const data = response?.data?.data;
return data?.folder;
}
5 changes: 2 additions & 3 deletions src/api/getFolderList.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { axiosInstance } from "./axiosInstance";
import { USER_ID } from "./constants";

export default async function getFolderList() {
const response = await axiosInstance.get(`${USER_ID}/folders`);
export default async function getFolderList(userId = "") {
const response = await axiosInstance.get(`${userId}/folders`);
return response?.data;
}
10 changes: 10 additions & 0 deletions src/api/getNewToken.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { axiosInstance } from "./axiosInstance";

export default async function getNewToken(refreshToken = "") {
if (!refreshToken) return;

const response = await axiosInstance.post("/refresh-token", {
refresh_token: refreshToken,
});
return response?.data;
}
2 changes: 1 addition & 1 deletion src/api/getSignUp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { axiosInstance } from "./axiosInstance";

export default async function getSignUp(email = "", password = "") {
try {
const response = await axiosInstance.post(`sign-up`, {
const response = await axiosInstance.post("/sign-up", {
email: email,
password: password,
});
Expand Down
3 changes: 2 additions & 1 deletion src/api/getSignin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { axiosInstance } from "./axiosInstance";

export default async function getSignIn(email = "", password = "") {
try {
const response = await axiosInstance.post(`sign-in`, {
const response = await axiosInstance.post("/sign-in", {
email: email,
password: password,
});
return response.data;
} catch (e) {
console.log(e);
return;
}
}
2 changes: 2 additions & 0 deletions src/api/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import getAllCards from "./getAllCards";
import getFolderList from "./getFolderList";
import getUser from "./getUser";
import getNewToken from "./getNewToken";

import getSharedFolder from "./getSharedFolder";
import getSharedUser from "./getSharedUser";
Expand All @@ -12,6 +13,7 @@ import getSignUp from "./getSignUp";
export {
getAllCards,
getFolderList,
getNewToken,
getUser,
getSharedFolder,
getSharedUser,
Expand Down
20 changes: 8 additions & 12 deletions src/components/common/Nav/Nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,15 @@

import Image from "next/image";
import Link from "next/link";
import { UserInterface } from "@/types";
import styles from "./Nav.module.scss";
import { useAuth } from "@/contexts/AuthProvider";

function Nav({
profile,
isSticky,
}: {
profile?: UserInterface;
isSticky?: boolean;
}) {
function Nav({ isSticky }: { isSticky?: boolean }) {
let navClassName = isSticky
? { className: `${styles["sticky"]} ${styles["nav"]}` }
: { className: `${styles["nav"]}` };

const { user, logout } = useAuth();
return (
<nav {...navClassName}>
<div className={styles["gnb"]}>
Expand All @@ -33,21 +28,22 @@ function Nav({
alt="로고 크기"
/>
</Link>
{!profile ? (
{user === null ? (
<button
className={`${styles["link-button"]} ${styles["signin-button"]}`}
>
<Link href="./">로그인</Link>
<Link href="/signin">로그인</Link>
</button>
) : (
<div className={styles["user-info"]}>
<Image
src={profile.image_source || "/public/images/no-profile.png"}
src={user.image_source || "/public/images/no-profile.png"}
alt="profile"
width={20}
height={20}
/>
<span>{profile.email}</span>
<span>{user.email}</span>
<button onClick={logout}>로그아웃</button>
</div>
)}
</div>
Expand Down
9 changes: 3 additions & 6 deletions src/components/sign/SignLayout/SignLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
*/

import Link from "next/link";
import { ReactNode } from "react";
import { PropsWithChildren } from "react";
import styles from "./SignLayout.module.scss";
import { SnsSign } from "..";

type defaultType = "signin" | "signup";
type PageType = "signin" | "signup";

const SIGNIN_TYPE = {
type: "signin",
Expand All @@ -29,10 +29,7 @@ const SIGNUP_TYPE = {
function SignLayout({
type = "signin",
children,
}: {
type: defaultType;
children: ReactNode;
}) {
}: PropsWithChildren<{ type: PageType }>) {
const pageType = type === "signin" ? SIGNIN_TYPE : SIGNUP_TYPE;

return (
Expand Down
120 changes: 120 additions & 0 deletions src/contexts/AuthProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
NOTE - 지금 user에 저장하는 값은 아래 형태와 같은 데이터다!!
{
"id": 25,
"created_at": "2023-08-31T02:18:03.139213+00:00",
"name": "온우림",
"image_source": "https://avatars.githubusercontent.com/u/75120340",
"email": "test@codeit.com",
"auth_id": "955801a8-85c5-4672-b729-612e646a640a"
}
*/
import {
createContext,
useState,
useContext,
useEffect,
ReactNode,
} from "react";
import { axiosInstance } from "@/api/axiosInstance";
import { getSignIn } from "@/api";
import { useRouter } from "next/router";
import { UserInterface } from "@/types";

interface INT {
user: any;
isPending: boolean;
login: any;
logout: any;
}

const initial: INT = {
user: null,
isPending: true,
login: () => {},
logout: () => {},
};
export const AuthContext = createContext(initial);

export function AuthProvider({ children }: { children: ReactNode }) {
const [values, setValues] = useState<{
user: UserInterface | null;
isPending: boolean;
}>({
user: null,
isPending: true,
});

// 유저 데이터를 가져오는 함수.
async function getMe() {
setValues((prev) => ({ ...prev, isPending: true }));
let nextUser: UserInterface;
try {
const res = await axiosInstance.get("/users");
nextUser = res.data;
setValues((prev) => ({
...prev,
user: nextUser,
isPending: false,
}));
} catch {
return;
}
}

// 로그인 함수. 로그인하면 해당 유저 정보를 post 리퀘를 보낸다.
// data 안에 accessToken과 refreshToken 이 있다.
// 전역 변수 context.user의 값을 getMe로 업데이트해야 한다.
async function login(email = "", password = "") {
const { data } = await getSignIn(email, password);
localStorage.setItem("accessToken", data.accessToken);
localStorage.setItem("refreshToken", data.refreshToken);
await getMe();
}

// TODO - 로그아웃 함수. 로그아웃하면 해당 유저 정보를 delete 리퀘를 보낸다. 어라 로그아웃 api 보내는 곳이 없네...
async function logout() {
setValues((prev) => ({
...prev,
user: null,
isPending: false,
}));
localStorage.removeItem("accessToken");
localStorage.removeItem("refreshToken");
}

// TODO - 나중에 setting 페이지도 만들 수 있음 만들어보자.
// async function updateMe(){}

return (
<AuthContext.Provider
value={{ user: values.user, isPending: values.isPending, login, logout }}
>
{children}
</AuthContext.Provider>
);
}

// 유저 인증이 필요한 페이지에선 required true 값을 주고,
// required가 true인데 context.user 값이 없고 로딩 중이 아니라면 로그인 페이지로 이동시킨다.
export function useAuth(required = false) {
const context = useContext(AuthContext);
const router = useRouter();
if (!context) {
throw new Error("반드시 AuthProvider 안에서 사용해야 합니다.");
}

useEffect(() => {
if (
router.route !== "/signup" &&
!context.user &&
required &&
!context.isPending
) {
router.push("/signin");
return;
}
}, [context.user, context.isPending, required]);

return context;
}
8 changes: 4 additions & 4 deletions src/hooks/useInputValid.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ChangeEvent, useState } from "react";
import { emailChecker, pswChecker } from "@/utils/checkReg";

const INITIAL_STATE = { hasError: false, errorMsg: "" };
const INITIAL_ERROR_STATUS = { hasError: false, errorMsg: "" };

const INITIAL_INPUT = {
email: "",
Expand All @@ -10,9 +10,9 @@ const INITIAL_INPUT = {
};

const INITIAL_ERROR = {
email: INITIAL_STATE,
password: INITIAL_STATE,
passwordRepeat: INITIAL_STATE,
email: INITIAL_ERROR_STATUS,
password: INITIAL_ERROR_STATUS,
passwordRepeat: INITIAL_ERROR_STATUS,
};

function useInputValid(inputs = "signin") {
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useRequest.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState, useCallback, useEffect } from "react";
import { AxiosResponse } from "axios";

export default function useAsync(asyncFunction: () => AxiosResponse) {
export default function useAsync(asyncFunction: () => Promise<AxiosResponse>) {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<unknown>(null);
const [data, setData] = useState({});
Expand Down
Loading

0 comments on commit cd4eaf4

Please sign in to comment.