-
Notifications
You must be signed in to change notification settings - Fork 35
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
[이형준]sprint10 #295
The head ref may contain hidden characters: "Next-\uC774\uD615\uC900-sprint10"
[이형준]sprint10 #295
Changes from all commits
a6d9f28
1e122bd
f6ef68e
e761f42
db2c5cf
ce26be3
0ea5e73
bc8ae5b
4e47af7
092f3b6
ad525e2
60e9572
79fa8e1
ef844a5
77a126c
c23bdbb
058ec01
3db9927
fbff985
2c99d20
69a4d98
eb8ebdc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import axiosInstance from "./instance"; | ||
|
||
interface Writer { | ||
id: number; | ||
nickname: string; | ||
} | ||
|
||
export interface Article { | ||
id: number; | ||
title: string; | ||
content: string; | ||
image: string | null; | ||
likeCount: number; | ||
writer: Writer; | ||
createdAt: string; | ||
updatedAt: string; | ||
} | ||
|
||
export type OrderOption = "like" | "recent"; | ||
|
||
interface GetArticlesProps { | ||
page?: number; | ||
pageSize: number; | ||
orderBy?: OrderOption; | ||
keyword?: string; | ||
} | ||
|
||
interface ArticlesResponse { | ||
list: Article[]; | ||
totalCount: number; | ||
} | ||
|
||
export async function getArticles({ | ||
page = 1, | ||
pageSize, | ||
orderBy = "recent", | ||
keyword = "", | ||
}: GetArticlesProps) { | ||
let query = `page=${page}&pageSize=${pageSize}&orderBy=${orderBy}`; | ||
if (keyword) { | ||
query += `&keyword=${keyword}`; | ||
} | ||
|
||
try { | ||
const res = await axiosInstance.get(`/articles?${query}`); | ||
const { list, totalCount }: ArticlesResponse = res.data; | ||
return { list, totalCount }; | ||
} catch (error) { | ||
return { list: [], totalCount: 0 }; | ||
} | ||
} | ||
|
||
interface GetArticleByIDProps { | ||
articleId: number; | ||
} | ||
|
||
export async function getArticleByID({ articleId }: GetArticleByIDProps) { | ||
const res = await axiosInstance.get(`/articles/${articleId}`); | ||
try { | ||
const article: Article = res.data; | ||
return article; | ||
} catch { | ||
throw new Error("게시글 응답이 올바르지 않습니다."); | ||
} | ||
} | ||
|
||
export interface PostArticleProps { | ||
image: string | null; | ||
content: string; | ||
title: string; | ||
} | ||
|
||
export async function postArticle({ image, content, title }: PostArticleProps) { | ||
try { | ||
let res; | ||
if (image) { | ||
res = await axiosInstance.post("/articles", { image, content, title }); | ||
} else { | ||
res = await axiosInstance.post("/articles", { content, title }); | ||
} | ||
const postedArticle: Article = res.data; | ||
return postedArticle; | ||
} catch (error) { | ||
console.log(error); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import axiosInstance from "./instance"; | ||
|
||
export interface LogInUserProps { | ||
email: string; | ||
password: string; | ||
} | ||
|
||
export interface SignUpUserProps extends LogInUserProps { | ||
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. extends 활용하신거 좋네요~ |
||
nickname: string; | ||
passwordConfirmation: string; | ||
} | ||
|
||
export async function signUpUser({ | ||
email, | ||
nickname, | ||
password, | ||
passwordConfirmation, | ||
}: SignUpUserProps) { | ||
try { | ||
const res = await axiosInstance.post("/auth/signUp", { | ||
email, | ||
nickname, | ||
password, | ||
passwordConfirmation, | ||
}); | ||
const { accessToken, refreshToken } = res.data; | ||
|
||
localStorage.setItem("access_token", accessToken); | ||
localStorage.setItem("refresh_token", refreshToken); | ||
window.location.href = "/"; | ||
Comment on lines
+28
to
+30
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. api 요청을 보내는 부분에서는 api 요청만 보내고 이런 로직들은 훅으로 만들어서 api 호출할때 추가해주시는 것을 추천드립니다. 각 함수의 역할을 분리해주는 것이죠! |
||
} catch (error) { | ||
console.log(error); | ||
} | ||
} | ||
|
||
export async function logInUser({ email, password }: LogInUserProps) { | ||
try { | ||
const res = await axiosInstance.post("/auth/signIn", { email, password }); | ||
const { accessToken, refreshToken } = res.data; | ||
|
||
localStorage.setItem("access_token", accessToken); | ||
localStorage.setItem("refresh_token", refreshToken); | ||
window.location.href = "/"; | ||
} catch (error) { | ||
console.log(error); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import axiosInstance from "./instance"; | ||
|
||
interface Writer { | ||
id: number; | ||
image: string; | ||
nickname: string; | ||
} | ||
|
||
export interface Comment { | ||
id: number; | ||
content: string; | ||
updatedAt: string; | ||
createdAt: string; | ||
writer: Writer; | ||
} | ||
|
||
interface GetArticleCommentsProps { | ||
articleId: number; | ||
limit: number; | ||
cursor?: number; | ||
} | ||
|
||
export interface ArticleCommentsResponse { | ||
nextCursor: number | null; | ||
list: Comment[]; | ||
} | ||
|
||
export async function getArticleComments({ articleId, limit, cursor }: GetArticleCommentsProps) { | ||
let query = `limit=${limit}`; | ||
if (cursor) { | ||
query += `&cursor=${cursor}`; | ||
} | ||
const res = await axiosInstance.get(`/articles/${articleId}/comments?${query}`); | ||
const { nextCursor, list }: ArticleCommentsResponse = res.data; | ||
return { nextCursor, list }; | ||
} | ||
|
||
interface PostArticleCommentProps { | ||
articleId: number; | ||
content: string; | ||
} | ||
|
||
export async function postArticleComment({ articleId, content }: PostArticleCommentProps) { | ||
const res = await axiosInstance.post(`/articles/${articleId}/comments`, { content }); | ||
return res.data as Comment; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import axiosInstance from "./instance"; | ||
|
||
interface UploadImageProps { | ||
imageFile: File; | ||
} | ||
|
||
export default async function uploadImage({ imageFile }: UploadImageProps) { | ||
// image file을 form data로 변경 | ||
const formDataForSubmit = new FormData(); | ||
formDataForSubmit.append("image", imageFile); | ||
|
||
try { | ||
const res = await axiosInstance.post("/images/upload", formDataForSubmit, { | ||
headers: { "Content-Type": "multipart/form-data" }, | ||
}); | ||
const { url } = res.data; | ||
return url; | ||
} catch (error) { | ||
console.log(error); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import axios, { AxiosResponse, AxiosError, InternalAxiosRequestConfig } from "axios"; | ||
|
||
const axiosInstance = axios.create({ | ||
baseURL: process.env.NEXT_PUBLIC_API_URL, | ||
}); | ||
|
||
/** | ||
* http request가 넘어가기 전에 call 되는 함수 | ||
*/ | ||
const onRequest = (config: InternalAxiosRequestConfig) => { | ||
if (typeof window !== "undefined") { | ||
const accessToken = localStorage.getItem("access_token"); | ||
|
||
if (accessToken && config.headers) { | ||
config.headers["Authorization"] = `Bearer ${accessToken}`; | ||
} | ||
} | ||
return config; | ||
}; | ||
|
||
/** | ||
* http response가 catch로 넘어가기 전에 call 되는 함수 | ||
*/ | ||
const onErrorResponse = async (error: AxiosError | Error) => { | ||
if (axios.isAxiosError(error)) { | ||
const { status, data, config } = error.response as AxiosResponse; | ||
|
||
switch (status) { | ||
case 400: { | ||
alert("입력한 정보가 올바르지 않습니다."); | ||
break; | ||
} | ||
|
||
case 401: { | ||
const refreshToken = localStorage.getItem("refresh_token"); | ||
|
||
if (refreshToken) { | ||
if (data.message === "jwt expired") { | ||
const res = await axiosInstance.post("/auth/refresh-token", { refreshToken }); | ||
const { accessToken } = res.data; | ||
|
||
localStorage.setItem("access_token", accessToken); | ||
|
||
config.headers["Authorization"] = `Bearer ${accessToken}`; | ||
return axiosInstance(config); | ||
} else if (data.message === "jwt malformed") { | ||
localStorage.removeItem("access_token"); | ||
localStorage.removeItem("refresh_token"); | ||
alert("세션이 올바르지 않습니다. 다시 로그인 해주세요."); | ||
window.location.href = "/login"; | ||
} else { | ||
console.log(data.message); | ||
} | ||
} else { | ||
alert("로그인이 필요합니다."); | ||
window.location.href = "/login"; | ||
} | ||
break; | ||
} | ||
|
||
default: { | ||
console.log(error.message); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
return Promise.reject(error); | ||
}; | ||
|
||
axiosInstance.interceptors.request.use(onRequest); | ||
axiosInstance.interceptors.response.use((response: AxiosResponse) => response, onErrorResponse); | ||
Comment on lines
+71
to
+72
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. interceptor 활용 잘해주셨네요! |
||
|
||
export default axiosInstance; |
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.
이 타입이 api 요청을 보낼때 매개변수 타입으로 쓰인다면, GetArticlesParams 같은 이름도 추천드립니다.