-
Notifications
You must be signed in to change notification settings - Fork 44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[김현서] Week15 #479
The head ref may contain hidden characters: "part3-\uAE40\uD604\uC11C-week15"
[김현서] Week15 #479
Changes from all commits
eaad2f2
3c06cc4
8e33bba
49deb4a
500dfed
1e8fa3e
c1b5dea
ee98c5a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,71 @@ | ||
import axios from "axios"; | ||
import { useRouter } from "next/router"; | ||
|
||
const instance = axios.create({ | ||
baseURL: "https://bootcamp-api.codeit.kr/api", | ||
headers: { "Content-Type": "application/json" }, | ||
}); | ||
|
||
// 요청 인터셉터 | ||
// 요청 인터셉터 | ||
instance.interceptors.request.use( | ||
function (config) { | ||
// 스토리지에서 토큰을 가져온다. | ||
const accessToken = localStorage.getItem("accessToken"); | ||
const refreshToken = localStorage.getItem("refreshToken"); | ||
|
||
// 토큰이 있으면 요청 헤더에 추가한다. | ||
if (accessToken) { | ||
config.headers["Authorization"] = `Bearer ${accessToken}`; | ||
} | ||
// Refresh 토큰을 보낼 경우 사용하고자 하는 커스텀 인증 헤더를 사용하면 된다. | ||
if (refreshToken) { | ||
config.headers["x-refresh-token"] = refreshToken; | ||
} | ||
|
||
return config; | ||
}, | ||
function (error) { | ||
// 요청 오류 처리 | ||
return Promise.reject(error); | ||
} | ||
); | ||
|
||
// 응답 인터셉터 | ||
instance.interceptors.response.use( | ||
async function (response) { | ||
return response; | ||
}, | ||
async function (error) { | ||
const { | ||
config, | ||
response: { status }, | ||
} = error; | ||
|
||
if (status === 401 && data.message === "InvalidTokenException") { | ||
// 토큰이 없거나 잘못되었을 경우 | ||
logout(); | ||
} | ||
if (status === 401 && data.message === "TokenExpired") { | ||
try { | ||
const tokenRefreshResult = await instance.post("/refresh-token"); | ||
if (tokenRefreshResult.status === 200) { | ||
const { accessToken, refreshToken } = tokenRefreshResult.data; | ||
// 새로 발급받은 토큰을 스토리지에 저장 | ||
localStorage.setItem("accessToken", accessToken); | ||
localStorage.setItem("refreshToken", refreshToken); | ||
// 토큰 갱신 성공. API 재요청 | ||
return instance(config); | ||
} else { | ||
logout(); | ||
} | ||
} catch (e) { | ||
logout(); | ||
} | ||
} | ||
|
||
return Promise.reject(error); | ||
} | ||
); | ||
|
||
export default instance; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,14 @@ | ||
import "@/styles/globals.css"; | ||
import { AppProps } from "next/app"; | ||
import React from "react"; | ||
import { FolderProvider } from "src/context/FolderContext"; | ||
|
||
function App({ Component, pageProps }: AppProps) { | ||
return <Component {...pageProps} />; | ||
return ( | ||
<FolderProvider> | ||
<Component {...pageProps} /> | ||
</FolderProvider> | ||
); | ||
} | ||
|
||
export default App; |
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { useEffect } from "react"; | ||
import { useRouter } from "next/router"; | ||
import FolderPageLayout from "@components/Layout/FolderPageLayout"; | ||
import { useFolder, useUser } from "src/context/FolderContext"; | ||
|
||
function FolderIdPage() { | ||
const router = useRouter(); | ||
const { folderId } = router.query; | ||
const { user, getUser } = useUser(); | ||
|
||
useEffect(() => { | ||
if (folderId) { | ||
getUser(); | ||
} | ||
}, [folderId]); | ||
|
||
return <FolderPageLayout user={user} />; | ||
} | ||
|
||
export default FolderIdPage; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import React, { useEffect } from "react"; | ||
import { useUser, useFolder } from "src/context/FolderContext"; | ||
import FolderPageLayout from "@components/Layout/FolderPageLayout"; | ||
|
||
function FolderPage() { | ||
const { user, getUser } = useUser(); | ||
|
||
useEffect(() => { | ||
getUser(); | ||
}, []); | ||
|
||
return <FolderPageLayout user={user} />; | ||
} | ||
|
||
export default FolderPage; |
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { useEffect, useState } from "react"; | ||
import { AxiosResponse } from "axios"; | ||
import instance from "lib/api"; | ||
|
||
export interface LinkData { | ||
data: { | ||
id: number; | ||
title: string; | ||
created_at: string; | ||
url: string; | ||
description?: string; | ||
image_source?: string; | ||
}[]; | ||
} | ||
|
||
export async function useFetchLinks(userId: number, folderId: number) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use 로 시작하는 훅의 경우 pages 말고 hooks 폴더에 따로 묶어주심 좋을 거 같아요! |
||
const [links, setLinks] = useState<LinkData>({ data: [] }); | ||
|
||
useEffect(() => { | ||
async function fetchLinks() { | ||
try { | ||
const response: AxiosResponse<LinkData> = await instance.get( | ||
`/api/users/${userId}/links`, | ||
{ | ||
params: { | ||
folderId: folderId, | ||
}, | ||
} | ||
); | ||
setLinks(response.data); | ||
} catch (error) { | ||
console.error("Error fetching links:", error); | ||
} | ||
} | ||
|
||
if (folderId) { | ||
fetchLinks(); | ||
} | ||
}, [userId, folderId]); | ||
|
||
return links; | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. useFetchLink 랑 되게 비슷한 구조라 공통화해봐도 좋을 거 같아요! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { useState, useEffect } from "react"; | ||
import instance from "lib/api"; | ||
|
||
interface FolderData { | ||
email: string; | ||
id: number; | ||
name: string; | ||
profileImageSource: string; | ||
} | ||
|
||
const useFolderData = ( | ||
folderId: string | ||
): { | ||
data: FolderData | null; | ||
isLoading: boolean; | ||
} => { | ||
const [data, setData] = useState<FolderData | null>(null); | ||
const [isLoading, setIsLoading] = useState(true); | ||
|
||
useEffect(() => { | ||
const fetchData = async () => { | ||
try { | ||
const response = await instance.get( | ||
`${process.env.NEXT_PUBLIC_BASE_URL}/folders/${folderId}` | ||
); | ||
const fetchedData: FolderData = await response.data.data; | ||
|
||
setData(fetchedData); | ||
} catch (error) { | ||
console.error("Error fetching folder data:", error); | ||
} finally { | ||
setIsLoading(false); | ||
} | ||
}; | ||
|
||
fetchData(); | ||
}, [folderId]); | ||
|
||
return { data, isLoading }; | ||
}; | ||
|
||
export default useFolderData; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
data: {} 로 오는 건 보통 서버에서 오는 응답인 경우가 많은데 이 경우엔 generic 함수를 이용해서 처리하는 경우가 많습니다!
https://velog.io/@ctdlog/Axios-Response-with-generic#%EC%99%9C-%EA%B8%B0%EB%B3%B8-response-%ED%83%80%EC%9E%85%EC%9D%84-%EB%B0%94%EA%BF%94%EC%84%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C%EC%9A%94