-
Notifications
You must be signed in to change notification settings - Fork 79
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
[김민재] Sprint11 #693
The head ref may contain hidden characters: "Next.js-\uAE40\uBBFC\uC7AC-sprint11"
[김민재] Sprint11 #693
Changes from all commits
3b48c15
91ed5aa
19d5df6
b65432b
74f3e7d
904058a
38e9a4b
8d728c0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
BASE_URL = https://panda-market-api.vercel.app | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,8 @@ | |
/.next/ | ||
/out/ | ||
|
||
/.env | ||
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. 어라?
|
||
|
||
# production | ||
/build | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,9 @@ const nextConfig = { | |
"example.com", | ||
], | ||
}, | ||
env: { | ||
BASE_URL: process.env.BASE_URL, | ||
}, | ||
}; | ||
|
||
export default nextConfig; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -1,25 +1,19 @@ | ||||||||||
import { Token } from "@/types/api"; | ||||||||||
import axiosInstance from "../axiosInstance"; | ||||||||||
import { PostData } from "@/components/addBoardComponents/Form"; | ||||||||||
import { PostData } from "@/components/addBoardComponents/ArticleForm"; | ||||||||||
|
||||||||||
const postArticles = async ( | ||||||||||
option: PostData = { | ||||||||||
content: "", | ||||||||||
title: "", | ||||||||||
image: null, | ||||||||||
}, | ||||||||||
token?: string | null | ||||||||||
} | ||||||||||
) => { | ||||||||||
const { image, ...restOption } = option; | ||||||||||
const payload = image ? option : restOption; | ||||||||||
|
||||||||||
try { | ||||||||||
const { data } = await axiosInstance.post<any>("/articles", payload, { | ||||||||||
headers: { | ||||||||||
Authorization: `Bearer ${token}`, | ||||||||||
}, | ||||||||||
}); | ||||||||||
return data; | ||||||||||
await axiosInstance.post<any>("/articles", payload); | ||||||||||
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. 우리
|
await axiosInstance.post<any>("/articles", payload); | |
await axiosInstance.post<PostArticleResponse>("/articles", payload); |
혹은
await axiosInstance.post<any>("/articles", payload); | |
await axiosInstance.post<{success: boolean, id: string 등등..}>("/articles", payload); |
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.
/**
* @todo 나중에 고쳐야됨
*/
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,11 +1,9 @@ | ||||||
import { Token } from "@/types/api"; | ||||||
import axiosInstance from "../axiosInstance"; | ||||||
import { Token } from "@/types/api"; | ||||||
|
||||||
const PostRefreshToken = async ( | ||||||
refreshToken: string | null | ||||||
): Promise<string> => { | ||||||
const PostRefreshToken = async (refreshToken?: string): Promise<Token> => { | ||||||
try { | ||||||
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. 컴포넌트가 아닌
|
const PostRefreshToken = async (refreshToken?: string): Promise<Token> => { | |
const postRefreshToken = async (refreshToken?: string): Promise<Token> => { |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,17 @@ | ||
import axios, { AxiosRequestConfig } from "axios"; | ||
// git push origin Next.js-김민재-sprint10 실수하지않을려고 push config설정 안했는데 브랜치이름 너무 치기어려워서 넣었습니다. | ||
import axios, { AxiosRequestConfig, AxiosError } from "axios"; | ||
import { | ||
getToken, | ||
loadTokenFromLocalStorage, | ||
saveTokenToLocalStorage, | ||
} from "@/utils/localStorageToken"; | ||
import PostRefreshToken from "@/apis/auth/PostRefreshToken"; | ||
|
||
interface InternalAxiosRequestConfig extends AxiosRequestConfig { | ||
_retry?: boolean; | ||
} | ||
|
||
Comment on lines
+9
to
+11
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. 오호 ~! 굳굳 ! 기존 타입을 커스텀해서 사용했군요?인터셉터를 위해서 |
||
const axiosConfig: AxiosRequestConfig = { | ||
baseURL: "https://panda-market-api.vercel.app/", | ||
baseURL: `${process.env.BASE_URL}`, | ||
headers: { | ||
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. 굳굳굳 ~! 환경 변수를 적용하셨군요 😊 |
||
"Content-Type": "application/json", | ||
Accept: "application/json", | ||
|
@@ -10,4 +20,44 @@ const axiosConfig: AxiosRequestConfig = { | |
|
||
const axiosInstance = axios.create(axiosConfig); | ||
|
||
axiosInstance.interceptors.request.use( | ||
async (config) => { | ||
const accessToken = await getToken(); | ||
if (accessToken) { | ||
config.headers.Authorization = `Bearer ${accessToken}`; | ||
} | ||
return config; | ||
}, | ||
(error) => { | ||
return Promise.reject(error); | ||
} | ||
); | ||
|
||
axiosInstance.interceptors.response.use( | ||
(response) => response, | ||
async (error: AxiosError) => { | ||
const originalRequest = error.config as InternalAxiosRequestConfig; | ||
if (!originalRequest.headers) { | ||
return Promise.reject(error); | ||
} | ||
if (error.response?.status === 401 && !originalRequest._retry) { | ||
originalRequest._retry = true; | ||
const token = loadTokenFromLocalStorage(); | ||
if (token?.refreshToken) { | ||
try { | ||
const newToken = await PostRefreshToken(token.refreshToken); | ||
saveTokenToLocalStorage(newToken); | ||
originalRequest.headers.Authorization = `Bearer ${newToken.accessToken}`; | ||
return axiosInstance(originalRequest); | ||
} catch (refreshError) { | ||
console.error(`Failed to refresh token: ${refreshError}`); | ||
return Promise.reject(refreshError); | ||
} | ||
} | ||
} | ||
|
||
return Promise.reject(error); | ||
} | ||
); | ||
|
||
export default axiosInstance; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,7 +21,12 @@ interface FormProps { | |
onChangeImage: (e: React.ChangeEvent<HTMLInputElement>) => void; | ||
} | ||
|
||
function Form({ postData, file, onChangeInput, onChangeImage }: FormProps) { | ||
function ArticleForm({ | ||
postData, | ||
file, | ||
onChangeInput, | ||
onChangeImage, | ||
}: FormProps) { | ||
const [imageUrl, setImageUrl] = useState<string | StaticImageData>(imageAdd); | ||
|
||
useEffect(() => { | ||
|
@@ -35,36 +40,42 @@ function Form({ postData, file, onChangeInput, onChangeImage }: FormProps) { | |
return ( | ||
<div className={cx(formStyle, css({ marginTop: "24px" }))}> | ||
<div className={labelInput}> | ||
<label className={labelBasicStyle}>*제목</label> | ||
<input | ||
name="title" | ||
type="text" | ||
value={postData.title} | ||
onChange={onChangeInput} | ||
className={inputRecipe()} | ||
placeholder="제목을 입력해주세요" | ||
/> | ||
<label className={labelBasicStyle}> | ||
*제목 | ||
<input | ||
name="title" | ||
type="text" | ||
value={postData.title} | ||
onChange={onChangeInput} | ||
className={inputRecipe()} | ||
placeholder="제목을 입력해주세요" | ||
/> | ||
</label> | ||
Comment on lines
+43
to
+53
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. 굳굳 ~!
|
||
</div> | ||
<div className={labelInput}> | ||
<label className={labelBasicStyle}>*내용</label> | ||
<textarea | ||
name="content" | ||
value={postData.content} | ||
onChange={onChangeInput} | ||
className={inputRecipe({ visual: "xLarge" })} | ||
placeholder="내용을 입력해주세요" | ||
/> | ||
<label className={labelBasicStyle}> | ||
*내용 | ||
<textarea | ||
name="content" | ||
value={postData.content} | ||
onChange={onChangeInput} | ||
className={inputRecipe({ visual: "xLarge" })} | ||
placeholder="내용을 입력해주세요" | ||
/> | ||
</label> | ||
</div> | ||
<div className={labelInput}> | ||
<label className={labelBasicStyle}>이미지</label> | ||
<input | ||
type="file" | ||
name="image" | ||
accept="image/*" | ||
onChange={onChangeImage} | ||
className={css({ display: "none" })} | ||
id="image-upload" | ||
/> | ||
<label className={labelBasicStyle}> | ||
이미지 | ||
<input | ||
type="file" | ||
name="image" | ||
accept="image/*" | ||
onChange={onChangeImage} | ||
className={css({ display: "none" })} | ||
id="image-upload" | ||
/> | ||
</label> | ||
<label htmlFor="image-upload" className={css({ cursor: "pointer" })}> | ||
<Image | ||
src={imageUrl} | ||
|
@@ -83,4 +94,4 @@ function Form({ postData, file, onChangeInput, onChangeImage }: FormProps) { | |
); | ||
} | ||
|
||
export default Form; | ||
export default ArticleForm; |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -12,9 +12,15 @@ import { useRouter } from "next/router"; | |||||
import Image from "next/image"; | ||||||
import { buttonRecipe } from "@/css/recipe/buttonRecipe.styled"; | ||||||
import userPassiveIcon from "@/assets/icons/user_passive_ic.svg"; | ||||||
import { useEffect, useState } from "react"; | ||||||
|
||||||
function Header() { | ||||||
const router = useRouter(); | ||||||
const [isToken, setIsToken] = useState<string | undefined>(""); | ||||||
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. 다음과 같이 처리하면
|
const [isToken, setIsToken] = useState<string | undefined>(""); | |
const [isToken, setIsToken] = useState<string>(); |
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.
그리고 토큰이 빈 값인 경우는 "없음"이란 의미로 보기 어려울 것 같아요 ! 차라리 undefined
로 들어가있으면 어떨까요?
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.
해당 파일은 프로젝트와 무관하므로
.gitignore
에 다음과 같이 작성하시면 git의 추적을 무시할 수 있습니다.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.
.gitignore
에 env 파일을 추가해 주셨군요!하지만 아직 github에서 확인되는 걸 보니 캐시를 지워줘야 할 것으로 보입니다 :)
gitignore에 .env 파일을 추가해도 commit history에 env파일이 남아있게 됩니다!
git에 env파일을 완전히 지우려면 추가 작업이 필요해요 😂
아래 블로그를 참고해 보시면 좋을 것 같아요!