diff --git a/frontend/src/__test__/vote/useGetVote.test.tsx b/frontend/src/__test__/vote/useGetVote.test.tsx index fc94089a5..0024e63b4 100644 --- a/frontend/src/__test__/vote/useGetVote.test.tsx +++ b/frontend/src/__test__/vote/useGetVote.test.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { QueryClient, QueryClientProvider } from 'react-query'; import { BrowserRouter } from 'react-router-dom'; -import { registerVoteItems } from '@/api/vote'; +import { registerVoteItems } from '@/api/vote/vote'; import useVote from '@/hooks/vote/useGetVote'; import { VoteHandler } from '@/mock'; import { theme } from '@/styles/Theme'; diff --git a/frontend/src/api/article.ts b/frontend/src/api/article.ts deleted file mode 100644 index 9e52b4d30..000000000 --- a/frontend/src/api/article.ts +++ /dev/null @@ -1,209 +0,0 @@ -import axios from 'axios'; - -import { ACCESSTOKEN_KEY } from '@/constants'; -import { HOME_URL } from '@/constants/apiUrl'; -import { AllArticleResponse, ArticleType } from '@/types/articleResponse'; -import { convertSort } from '@/utils/converter'; - -export interface WritingArticles { - title: string; - content: string; - category: string; -} - -export const postWritingArticle = (article: WritingArticles) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - return axios.post(`${HOME_URL}/api/articles`, article, { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }); -}; - -export interface PopularArticles { - articles: ArticleType[]; - hastNext: boolean; -} - -export const getPopularArticles = async () => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - - const result = await axios.get( - `${HOME_URL}/api/articles?category=all&sort=views&size=10`, - { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - return result.data; -}; - -export const getDetailArticle = async (id: string) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - const { data } = await axios.get(`${HOME_URL}/api/articles/${id}`, { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }); - return data; -}; - -export const getAllArticle = async ({ - category, - sort, - cursorId, - cursorViews, - cursorLikes, -}: { - category: string; - sort: '추천순' | '조회순' | '최신순'; - cursorId: string; - cursorViews: string; - cursorLikes: string; -}) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - - if (sort === '추천순') { - const data = await getAllArticlesByLikes({ category, cursorId, cursorLikes, accessToken }); - return data; - } - - const data = await getAllArticleByViewsOrLatest({ - category, - sort, - cursorId, - cursorViews, - accessToken, - }); - return data; -}; - -export const getAllArticleByViewsOrLatest = async ({ - category, - sort, - cursorId, - cursorViews, - accessToken, -}: { - category: string; - sort: '추천순' | '조회순' | '최신순'; - cursorId: string; - cursorViews: string; - accessToken: string | null; -}) => { - const currentSort = convertSort(sort); - - const { data } = await axios.get( - `${HOME_URL}/api/articles?category=${category}&sort=${currentSort}&cursorId=${cursorId}&cursorViews=${cursorViews}&size=6`, - { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - return { - articles: data.articles, - hasNext: data.hasNext, - cursorId: - data.articles && data.articles[data.articles.length - 1] - ? String(data.articles[data.articles.length - 1].id) - : '', - cursorViews: - data.articles && data.articles[data.articles.length - 1] - ? String(data.articles[data.articles.length - 1].views) - : '', - cursorLikes: - data.articles && data.articles[data.articles.length - 1] - ? String(data.articles[data.articles.length - 1].likeCount) - : '', - }; -}; - -export const getAllArticlesByLikes = async ({ - category, - cursorId, - cursorLikes = '', - accessToken, -}: { - category: string; - cursorId: string; - cursorLikes: string; - accessToken: string | null; -}) => { - const { data } = await axios.get( - `${HOME_URL}/api/articles/likes?category=${category}&cursorId=${cursorId}&cursorLikes=${cursorLikes}&size=6`, - { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - return { - articles: data.articles, - hasNext: data.hasNext, - cursorId: - data && data.articles[data.articles.length - 1] - ? String(data.articles[data.articles.length - 1].id) - : '', - cursorLikes: - data && data.articles[data.articles.length - 1] - ? String(data.articles[data.articles.length - 1].likeCount) - : '', - cursorViews: - data.articles && data.articles[data.articles.length - 1] - ? String(data.articles[data.articles.length - 1].views) - : '', - }; -}; - -export const postArticle = (article: { id: string; title: string; content: string }) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - - return axios.post<{ id: number; category: string }>( - `${HOME_URL}/api/articles/${article.id}`, - { title: article.title, content: article.content }, - { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }, - ); -}; - -export const putArticle = (article: { - id: string; - title: string; - content: string; - tag: string[]; -}) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - return axios.put<{ id: number; category: string }>( - `${HOME_URL}/api/articles/${article.id}`, - { title: article.title, content: article.content, tag: article.tag }, - { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }, - ); -}; - -export const deleteArticle = (id: string) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - return axios.delete(`${HOME_URL}/api/articles/${id}`, { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }); -}; diff --git a/frontend/src/api/article/article.ts b/frontend/src/api/article/article.ts new file mode 100644 index 000000000..e3c4cb74d --- /dev/null +++ b/frontend/src/api/article/article.ts @@ -0,0 +1,171 @@ +import { + DetailArticleResponseType, + TotalArticleInquiredResponseType, +} from '@/api/article/articleType'; +import { convertSort } from '@/utils/converter'; +import { generateAxiosInstanceWithAccessToken } from '@/utils/generateAxiosInstance'; + +export interface WritingArticlesProp { + title: string; + content: string; + category: string; +} + +export const postWritingArticle = (article: WritingArticlesProp) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + return axiosInstance.post('/api/articles', article); +}; + +export const getPopularArticles = async () => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + const { data } = await axiosInstance.get( + '/api/articles?category=all&sort=views&size=10', + ); + return data; +}; + +export const getDetailArticle = async (id: string) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + const { data } = await axiosInstance.get(`/api/articles/${id}`); + + return data; +}; + +interface getArticleProps { + category: string; + sort: '추천순' | '조회순' | '최신순'; + cursorId: string; + cursorViews: string; + cursorLikes: string; +} + +export const getAllArticle = async ({ + category, + sort, + cursorId, + cursorViews, + cursorLikes, +}: getArticleProps) => { + if (sort === '추천순') { + const data = await getAllArticlesByLikes({ category, cursorId, cursorLikes }); + return data; + } + + const data = await getAllArticleByViewsOrLatest({ + category, + sort, + cursorId, + cursorViews, + }); + return data; +}; + +interface getAllArticleByViewsOrLatestProps { + category: string; + sort: '추천순' | '조회순' | '최신순'; + cursorId: string; + cursorViews: string; +} + +export const getAllArticleByViewsOrLatest = async ({ + category, + sort, + cursorId, + cursorViews, +}: getAllArticleByViewsOrLatestProps) => { + const currentSort = convertSort(sort); + const axiosInstance = generateAxiosInstanceWithAccessToken(); + const { data } = await axiosInstance.get( + `/api/articles?category=${category}&sort=${currentSort}&cursorId=${cursorId}&cursorViews=${cursorViews}&size=6`, + ); + return { + articles: data.articles, + hasNext: data.hasNext, + cursorId: + data.articles && data.articles[data.articles.length - 1] + ? String(data.articles[data.articles.length - 1].id) + : '', + cursorViews: + data.articles && data.articles[data.articles.length - 1] + ? String(data.articles[data.articles.length - 1].views) + : '', + cursorLikes: + data.articles && data.articles[data.articles.length - 1] + ? String(data.articles[data.articles.length - 1].likeCount) + : '', + }; +}; + +interface getAllArticlesByLikesProps { + category: string; + cursorId: string; + cursorLikes: string; +} + +export const getAllArticlesByLikes = async ({ + category, + cursorId, + cursorLikes = '', +}: getAllArticlesByLikesProps) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + + const { data } = await axiosInstance.get( + `/api/articles/likes?category=${category}&cursorId=${cursorId}&cursorLikes=${cursorLikes}&size=6`, + ); + + return { + articles: data.articles, + hasNext: data.hasNext, + cursorId: + data && data.articles[data.articles.length - 1] + ? String(data.articles[data.articles.length - 1].id) + : '', + cursorLikes: + data && data.articles[data.articles.length - 1] + ? String(data.articles[data.articles.length - 1].likeCount) + : '', + cursorViews: + data.articles && data.articles[data.articles.length - 1] + ? String(data.articles[data.articles.length - 1].views) + : '', + }; +}; + +interface postArticleProps { + article: { + id: string; + title: string; + content: string; + }; +} + +export const postArticle = (article: postArticleProps['article']) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + return axiosInstance.post<{ id: number; category: string }>(`/api/articles/${article.id}`, { + title: article.title, + content: article.content, + }); +}; + +interface putArticleProps { + article: { + id: string; + title: string; + content: string; + tag: string[]; + }; +} + +export const putArticle = (article: putArticleProps['article']) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + return axiosInstance.put<{ id: number; category: string }>(`/api/articles/${article.id}`, { + title: article.title, + content: article.content, + tag: article.tag, + }); +}; + +export const deleteArticle = (id: string) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + return axiosInstance.delete(`/api/articles/${id}`); +}; diff --git a/frontend/src/api/article/articleType.ts b/frontend/src/api/article/articleType.ts new file mode 100644 index 000000000..b5eb0eb1e --- /dev/null +++ b/frontend/src/api/article/articleType.ts @@ -0,0 +1,72 @@ +import { SingleTempArticleItemResponseType } from '@/api/tempArticle/tempArticleType'; + +export type AuthorType = { name: string; avatarUrl: string }; + +export type CategoryType = 'question' | 'discussion'; + +export interface ArticleTotalType { + id: number; + title: string; + tag: string[]; + content: string; + category: CategoryType; + createdAt: string; + updatedAt: string; + author: AuthorType; + + views: number; + likeCount: number; + commentCount: number; + + isAuthor: boolean; + hasVote: boolean; + isLike: boolean; +} + +export type DetailArticleResponseType = Omit; + +export type SingleArticleItemResponseType = Omit< + ArticleTotalType, + 'updatedAt' | 'hasVote' | 'isAuthor' +>; + +export interface TotalArticleInquiredResponseType { + articles: SingleArticleItemResponseType[]; + hasNext: boolean; +} + +export interface InfiniteArticleResponseType extends TotalArticleInquiredResponseType { + cursorId: string; + cursorViews?: string; + cursorLikes?: string; +} + +export interface SingleMyPageUserArticleResponseType extends SingleTempArticleItemResponseType { + commentCount: number; + updatedAt: string; + views: number; +} + +export interface MyPageUserArticleResponseType { + articles: SingleMyPageUserArticleResponseType[]; +} + +export type CreateArticleResponseType = Pick; +export type UpdateArticleResponseType = Pick; + +export interface ArticleTotalRequestType { + title: string; + content: string; + category: CategoryType; + tag: string[]; + isAnonymous: boolean; + tempArticleId: number | ''; + id: string; +} + +export type UpdateArticleRequestType = Pick< + ArticleTotalRequestType, + 'title' | 'content' | 'tag' | 'id' +>; + +export type CreateArticleRequestType = Omit; diff --git a/frontend/src/api/image.ts b/frontend/src/api/article/image.ts similarity index 100% rename from frontend/src/api/image.ts rename to frontend/src/api/article/image.ts diff --git a/frontend/src/api/article/like.ts b/frontend/src/api/article/like.ts new file mode 100644 index 000000000..1a1e025e6 --- /dev/null +++ b/frontend/src/api/article/like.ts @@ -0,0 +1,12 @@ +import { generateAxiosInstanceWithAccessToken } from '@/utils/generateAxiosInstance'; + +export const postAddLikeArticle = (articleId: string) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + return axiosInstance.post(`/api/articles/${articleId}/like`, null); +}; + +export const deleteLikeArticle = (articleId: string) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + + return axiosInstance.delete(`/api/articles/${articleId}/like`); +}; diff --git a/frontend/src/api/comment/commentType.ts b/frontend/src/api/comment/commentType.ts new file mode 100644 index 000000000..0e6bffdea --- /dev/null +++ b/frontend/src/api/comment/commentType.ts @@ -0,0 +1,36 @@ +import { AuthorType } from '@/api/article/articleType'; + +export interface SingleCommentItemType { + id: number; + content: string; + author: AuthorType; + isAuthor: boolean; + createdAt: string; + updatedAt: string; +} + +export interface TotalCommentResponseType { + comments: SingleCommentItemType[]; +} + +export interface MyPageCommentItemResponse { + id: number; + content: string; + createdAt: string; + updatedAt: string; + articleId: number; + articleTitle: string; + category: string; +} + +export interface MyPageCommentResponse { + comments: MyPageCommentItemResponse[]; +} + +export interface CreateCommentRequestType { + content: string; + isAnonymous: boolean; +} +export interface UpdateCommentRequestType { + content: string; +} diff --git a/frontend/src/api/comment/comments.ts b/frontend/src/api/comment/comments.ts new file mode 100644 index 000000000..fa18a200a --- /dev/null +++ b/frontend/src/api/comment/comments.ts @@ -0,0 +1,38 @@ +import { TotalCommentResponseType } from '@/api/comment/commentType'; +import { generateAxiosInstanceWithAccessToken } from '@/utils/generateAxiosInstance'; + +interface postCommentsProps { + id: string; + content: string; + isAnonymous: boolean; +} + +export const postComments = ({ id, content, isAnonymous }: postCommentsProps) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + + return axiosInstance.post(`/api/articles/${id}/comments`, { content, isAnonymous }); +}; + +export const getComments = async (id: string) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + + const { data } = await axiosInstance.get( + `/api/articles/${id}/comments`, + ); + return data; +}; + +interface putCommentsProps { + content: string; + commentId: string; +} + +export const putComments = ({ content, commentId }: putCommentsProps) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + return axiosInstance.put(`/api/articles/comments/${commentId}`, { content }); +}; + +export const deleteComments = ({ commentId }: { commentId: string }) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + return axiosInstance.delete(`/api/articles/comments/${commentId}`); +}; diff --git a/frontend/src/api/comments.ts b/frontend/src/api/comments.ts deleted file mode 100644 index 1c280b401..000000000 --- a/frontend/src/api/comments.ts +++ /dev/null @@ -1,68 +0,0 @@ -import axios from 'axios'; - -import { ACCESSTOKEN_KEY } from '@/constants'; -import { HOME_URL } from '@/constants/apiUrl'; -import { CommentType } from '@/types/commentResponse'; - -export const postComments = ({ - content, - id, - isAnonymous, -}: { - content: string; - id: string; - isAnonymous: boolean; -}) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - return axios.post( - `${HOME_URL}/api/articles/${id}/comments`, - { content, isAnonymous }, - { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }, - ); -}; - -export const getComments = async (id: string) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - - const { data } = await axios.get<{ comments: CommentType[] }>( - `${HOME_URL}/api/articles/${id}/comments`, - { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - return data; -}; - -export const putComments = ({ content, commentId }: { content: string; commentId: string }) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - - return axios.put( - `${HOME_URL}/api/articles/comments/${commentId}`, - { content }, - { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }, - ); -}; - -export const deleteComments = ({ commentId }: { commentId: string }) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - - return axios.delete(`${HOME_URL}/api/articles/comments/${commentId}`, { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }); -}; diff --git a/frontend/src/api/hashTag/hashTag.ts b/frontend/src/api/hashTag/hashTag.ts new file mode 100644 index 000000000..e75c4bbad --- /dev/null +++ b/frontend/src/api/hashTag/hashTag.ts @@ -0,0 +1,8 @@ +import { HashTagResponseType } from '@/api/hashTag/hashTagType'; +import { generateAxiosInstanceWithAccessToken } from '@/utils/generateAxiosInstance'; + +export const getAllHashTag = async () => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + const { data } = await axiosInstance.get('/api/tags'); + return data; +}; diff --git a/frontend/src/api/hashTag/hashTagType.ts b/frontend/src/api/hashTag/hashTagType.ts new file mode 100644 index 000000000..3087acd86 --- /dev/null +++ b/frontend/src/api/hashTag/hashTagType.ts @@ -0,0 +1,20 @@ +import { ArticleTotalType } from '@/api/article/articleType'; + +export interface HashTagResponseType { + tag: string[]; +} + +export type SingleHashTagSearchItemType = Omit< + ArticleTotalType, + 'updatedAt' | 'isAuthor' | 'hasVote' +>; + +export interface HashTagSearchResponseType { + articles: SingleHashTagSearchItemType[]; + hasNext: boolean; +} + +export interface InfiniteHashTagSearchResponseType extends HashTagSearchResponseType { + cursorId: string; + hashTags: string; +} diff --git a/frontend/src/api/like.ts b/frontend/src/api/like.ts deleted file mode 100644 index 5ff9731ae..000000000 --- a/frontend/src/api/like.ts +++ /dev/null @@ -1,24 +0,0 @@ -import axios from 'axios'; - -import { ACCESSTOKEN_KEY } from '@/constants'; -import { HOME_URL } from '@/constants/apiUrl'; - -export const postAddLikeArticle = (articleId: string) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - return axios.post(`${HOME_URL}/api/articles/${articleId}/like`, null, { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }); -}; - -export const deleteLikeArticle = (articleId: string) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - return axios.delete(`${HOME_URL}/api/articles/${articleId}/like`, { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }); -}; diff --git a/frontend/src/api/myPage.ts b/frontend/src/api/myPage.ts deleted file mode 100644 index 8fd2a2288..000000000 --- a/frontend/src/api/myPage.ts +++ /dev/null @@ -1,55 +0,0 @@ -import axios, { AxiosResponse } from 'axios'; - -import { ACCESSTOKEN_KEY } from '@/constants'; -import { HOME_URL } from '@/constants/apiUrl'; -import { UserArticlesResponse } from '@/types/articleResponse'; -import { Author } from '@/types/author'; -import { UserCommentResponse } from '@/types/commentResponse'; - -export const getUserInfo = async () => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - const result = await axios.get(`${HOME_URL}/api/members/me`, { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }); - return result.data; -}; - -export const getUserArticles = async () => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - const result = await axios.get(`${HOME_URL}/api/members/me/articles`, { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }); - return result.data; -}; - -export const getUserComments = async () => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - const result = await axios.get(`${HOME_URL}/api/members/me/comments`, { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }); - return result.data; -}; - -export const editUserInfo = ({ name }: { name: string }) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - - return axios.patch<{ name: string }, AxiosResponse<{ name: string }>>( - `${HOME_URL}/api/members/me`, - { name }, - { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }, - ); -}; diff --git a/frontend/src/api/search.ts b/frontend/src/api/search/search.ts similarity index 51% rename from frontend/src/api/search.ts rename to frontend/src/api/search/search.ts index ed22c534f..d4cc21f57 100644 --- a/frontend/src/api/search.ts +++ b/frontend/src/api/search/search.ts @@ -1,29 +1,22 @@ -import axios from 'axios'; +import { HashTagSearchResponseType } from '@/api/hashTag/hashTagType'; +import { ArticleSearchResponseType } from '@/api/search/searchType'; +import { generateAxiosInstanceWithAccessToken } from '@/utils/generateAxiosInstance'; -import { ACCESSTOKEN_KEY } from '@/constants'; -import { HOME_URL } from '@/constants/apiUrl'; -import { SearchResultType } from '@/types/searchResponse'; +interface getUserSearchResultProps { + target: string; + cursorId: string; + searchIndex: string; +} export const getUserSearchResult = async ({ - accessToken, target, cursorId, searchIndex, -}: { - accessToken: string | null; - target: string; - cursorId: string; - searchIndex: string; -}) => { +}: getUserSearchResultProps) => { const encodedTarget = encodeURIComponent(target); - const { data } = await axios.get( - `${HOME_URL}/api/articles/search/author?author=${encodedTarget}&cursorId=${cursorId}&size=6`, - { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }, + const axiosInstance = generateAxiosInstanceWithAccessToken(); + const { data } = await axiosInstance.get( + `/api/articles/search/author?author=${encodedTarget}&cursorId=${cursorId}&size=6`, ); return { articles: data.articles, @@ -37,26 +30,21 @@ export const getUserSearchResult = async ({ }; }; +interface getArticleSearchResultProps { + target: string; + cursorId: string; + searchIndex: string; +} + export const getArticleSearchResult = async ({ - accessToken, target, cursorId, searchIndex, -}: { - accessToken: string | null; - target: string; - cursorId: string; - searchIndex: string; -}) => { +}: getArticleSearchResultProps) => { const encodedTarget = encodeURIComponent(target); - const { data } = await axios.get( - `${HOME_URL}/api/articles/search/text?text=${encodedTarget}&cursorId=${cursorId}&size=6`, - { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }, + const axiosInstance = generateAxiosInstanceWithAccessToken(); + const { data } = await axiosInstance.get( + `/api/articles/search/text?text=${encodedTarget}&cursorId=${cursorId}&size=6`, ); return { @@ -71,23 +59,19 @@ export const getArticleSearchResult = async ({ }; }; +interface getArticleByHashTagProps { + hashTags: string; + cursorId: string; +} + export const getArticleByHashTag = async ({ hashTags, cursorId = '', -}: { - hashTags: string; - cursorId: string; -}) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); +}: getArticleByHashTagProps) => { const encodedTarget = encodeURIComponent(hashTags); - const { data } = await axios.get( - `${HOME_URL}/api/articles/search/tags?tagsText=${encodedTarget}&cursorId=${cursorId}&size=6`, - { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }, + const axiosInstance = generateAxiosInstanceWithAccessToken(); + const { data } = await axiosInstance.get( + `/api/articles/search/tags?tagsText=${encodedTarget}&cursorId=${cursorId}&size=6`, ); return { articles: data.articles, @@ -100,22 +84,22 @@ export const getArticleByHashTag = async ({ }; }; -export const getSearchResult = async ({ - target, - searchIndex, - cursorId = '', -}: { +interface getSearchResultProps { target: string; searchIndex: string; cursorId: string; -}) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); +} +export const getSearchResult = async ({ + target, + searchIndex, + cursorId = '', +}: getSearchResultProps) => { if (searchIndex === '유저') { - const data = await getUserSearchResult({ accessToken, target, searchIndex, cursorId }); + const data = await getUserSearchResult({ target, searchIndex, cursorId }); return data; } - const data = await getArticleSearchResult({ accessToken, target, searchIndex, cursorId }); + const data = await getArticleSearchResult({ target, searchIndex, cursorId }); return data; }; diff --git a/frontend/src/api/search/searchType.ts b/frontend/src/api/search/searchType.ts new file mode 100644 index 000000000..b1bcbd689 --- /dev/null +++ b/frontend/src/api/search/searchType.ts @@ -0,0 +1,12 @@ +import { ArticleTotalType } from '@/api/article/articleType'; + +export interface ArticleSearchResponseType { + articles: Omit[]; + hasNext: boolean; +} + +export interface InfiniteArticleSearchResponseType extends ArticleSearchResponseType { + cursorId: string; + target: string; + searchIndex: string; +} diff --git a/frontend/src/api/tempArticle.ts b/frontend/src/api/tempArticle.ts deleted file mode 100644 index 5e4c5eb36..000000000 --- a/frontend/src/api/tempArticle.ts +++ /dev/null @@ -1,52 +0,0 @@ -import axios from 'axios'; - -import { ACCESSTOKEN_KEY } from '@/constants'; -import { HOME_URL } from '@/constants/apiUrl'; -import { postTempArticleProps } from '@/hooks/tempArticle/usePostTempArticle'; -import { TempArticleDetailResponse, TempArticleResponse } from '@/types/articleResponse'; - -export const getTempArticles = async () => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - const result = await axios.get(`${HOME_URL}/api/temp-articles`, { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }); - return result.data; -}; - -export const getTempDetailArticle = ({ id }: { id: number | '' }) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - return axios.get(`${HOME_URL}/api/temp-articles/${id}`, { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }); -}; - -export const postTempArticle = ({ ...props }: postTempArticleProps) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - - return axios.post<{ id: number }>( - `${HOME_URL}/api/temp-articles`, - { ...props }, - { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }, - ); -}; - -export const deleteArticleItem = ({ tempArticleId }: { tempArticleId: number }) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - return axios.delete(`${HOME_URL}/api/temp-articles/${tempArticleId}`, { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }); -}; diff --git a/frontend/src/api/tempArticle/tempArticle.ts b/frontend/src/api/tempArticle/tempArticle.ts new file mode 100644 index 000000000..f13753374 --- /dev/null +++ b/frontend/src/api/tempArticle/tempArticle.ts @@ -0,0 +1,27 @@ +import { + DetailTempArticleResponseType, + TotalTempArticleResponseType, +} from '@/api/tempArticle/tempArticleType'; +import { postTempArticleProps } from '@/hooks/tempArticle/usePostTempArticle'; +import { generateAxiosInstanceWithAccessToken } from '@/utils/generateAxiosInstance'; + +export const getTempArticles = async () => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + const { data } = await axiosInstance.get(`/api/temp-articles`); + return data; +}; + +export const getTempDetailArticle = ({ id }: { id: number | '' }) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + return axiosInstance.get(`/api/temp-articles/${id}`); +}; + +export const postTempArticle = ({ ...props }: postTempArticleProps) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + return axiosInstance.post<{ id: number }>('/api/temp-articles', { ...props }); +}; + +export const deleteArticleItem = ({ tempArticleId }: { tempArticleId: number }) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + return axiosInstance.delete(`/api/temp-articles/${tempArticleId}`); +}; diff --git a/frontend/src/api/tempArticle/tempArticleType.ts b/frontend/src/api/tempArticle/tempArticleType.ts new file mode 100644 index 000000000..483720d2a --- /dev/null +++ b/frontend/src/api/tempArticle/tempArticleType.ts @@ -0,0 +1,16 @@ +import { ArticleTotalType } from '@/api/article/articleType'; + +export type SingleTempArticleItemResponseType = Pick< + ArticleTotalType, + 'id' | 'title' | 'createdAt' | 'category' +>; + +export interface TotalTempArticleResponseType { + values: SingleTempArticleItemResponseType[]; +} + +export interface DetailTempArticleResponseType extends SingleTempArticleItemResponseType { + tag: string[]; + content: string; + isAnonymous: boolean; +} diff --git a/frontend/src/api/login.ts b/frontend/src/api/user/login.ts similarity index 100% rename from frontend/src/api/login.ts rename to frontend/src/api/user/login.ts diff --git a/frontend/src/api/user/myPage.ts b/frontend/src/api/user/myPage.ts new file mode 100644 index 000000000..93e9fe6a6 --- /dev/null +++ b/frontend/src/api/user/myPage.ts @@ -0,0 +1,32 @@ +import { AxiosResponse } from 'axios'; + +import { AuthorType, MyPageUserArticleResponseType } from '@/api/article/articleType'; +import { MyPageCommentResponse } from '@/api/comment/commentType'; +import { generateAxiosInstanceWithAccessToken } from '@/utils/generateAxiosInstance'; + +export const getUserInfo = async () => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + const { data } = await axiosInstance.get('/api/members/me'); + return data; +}; + +export const getUserArticles = async () => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + const { data } = await axiosInstance.get( + '/api/members/me/articles', + ); + return data; +}; + +export const getUserComments = async () => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + const { data } = await axiosInstance.get('/api/members/me/comments'); + return data; +}; + +export const editUserInfo = ({ name }: { name: string }) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + return axiosInstance.patch<{ name: string }, AxiosResponse<{ name: string }>>('/api/members/me', { + name, + }); +}; diff --git a/frontend/src/api/vote.ts b/frontend/src/api/vote.ts deleted file mode 100644 index 5a383123b..000000000 --- a/frontend/src/api/vote.ts +++ /dev/null @@ -1,74 +0,0 @@ -import axios from 'axios'; - -import { ACCESSTOKEN_KEY } from '@/constants'; -import { HOME_URL } from '@/constants/apiUrl'; - -export interface VoteItems { - id: number; - content: string; - amount: number; -} - -export interface TVote { - articleId: string; - voteItems: VoteItems[]; - votedItemId: number | null; - expired: boolean; -} - -export const getVoteItems = async (articleId: string) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - - const { data } = await axios.get(`${HOME_URL}/api/articles/${articleId}/votes`, { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }); - - return data; -}; - -export const registerVoteItems = ({ - articleId, - items, - expiryDate, -}: { - articleId: string; - items: string[]; - expiryDate: string; -}) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - - return axios.post<{ articleId: string }>( - `${HOME_URL}/api/articles/${articleId}/votes`, - { items, expiryDate }, - { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }, - ); -}; - -export const checkVoteItems = ({ - articleId, - voteItemId, -}: { - articleId: string; - voteItemId: string; -}) => { - const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - - return axios.post( - `${HOME_URL}/api/articles/${articleId}/votes/do`, - { voteItemId }, - { - headers: { - 'Access-Control-Allow-Origin': '*', - Authorization: `Bearer ${accessToken}`, - }, - }, - ); -}; diff --git a/frontend/src/api/vote/vote.ts b/frontend/src/api/vote/vote.ts new file mode 100644 index 000000000..74d594dee --- /dev/null +++ b/frontend/src/api/vote/vote.ts @@ -0,0 +1,25 @@ +import { CheckVoteRequestType, CreateVoteRequestType, VoteResponseType } from '@/api/vote/voteType'; +import { generateAxiosInstanceWithAccessToken } from '@/utils/generateAxiosInstance'; + +export const getVoteItems = async (articleId: string) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + + const { data } = await axiosInstance.get(`/api/articles/${articleId}/votes`); + + return data; +}; + +export const registerVoteItems = ({ articleId, items, expiryDate }: CreateVoteRequestType) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + + return axiosInstance.post<{ articleId: string }>(`/api/articles/${articleId}/votes`, { + items, + expiryDate, + }); +}; + +export const checkVoteItems = ({ articleId, voteItemId }: CheckVoteRequestType) => { + const axiosInstance = generateAxiosInstanceWithAccessToken(); + + return axiosInstance.post(`/api/articles/${articleId}/votes/do`, { voteItemId }); +}; diff --git a/frontend/src/api/vote/voteType.ts b/frontend/src/api/vote/voteType.ts new file mode 100644 index 000000000..dba2d64a8 --- /dev/null +++ b/frontend/src/api/vote/voteType.ts @@ -0,0 +1,23 @@ +export interface SingleVoteItemType { + id: number; + content: string; + amount: number; +} + +export interface VoteResponseType { + articleId: string; + voteItems: SingleVoteItemType[]; + votedItemId: number | null; + expired: boolean; +} + +export interface CreateVoteRequestType { + items: string[]; + expiryDate: string; + articleId: string; +} + +export interface CheckVoteRequestType { + voteItemId: string; + articleId: string; +} diff --git a/frontend/src/components/@helper/accessHandler/LoginHandler.tsx b/frontend/src/components/@helper/accessHandler/LoginHandler.tsx index 6236b9d7c..34acfad75 100644 --- a/frontend/src/components/@helper/accessHandler/LoginHandler.tsx +++ b/frontend/src/components/@helper/accessHandler/LoginHandler.tsx @@ -3,7 +3,7 @@ import { useEffect } from 'react'; import { useMutation } from 'react-query'; import { useNavigate, useSearchParams } from 'react-router-dom'; -import { postLogin } from '@/api/login'; +import { postLogin } from '@/api/user/login'; import LoginLoading from '@/components/login/LoginLoading/LoginLoading'; import { ACCESSTOKEN_KEY } from '@/constants'; import { URL } from '@/constants/url'; diff --git a/frontend/src/components/@helper/accessHandler/RefreshTokenHandler.tsx b/frontend/src/components/@helper/accessHandler/RefreshTokenHandler.tsx index 8efa6c18e..a9946e7c1 100644 --- a/frontend/src/components/@helper/accessHandler/RefreshTokenHandler.tsx +++ b/frontend/src/components/@helper/accessHandler/RefreshTokenHandler.tsx @@ -2,7 +2,7 @@ import { AxiosError } from 'axios'; import { useEffect } from 'react'; import { useQuery } from 'react-query'; -import { getAccessTokenByRefreshToken } from '@/api/login'; +import { getAccessTokenByRefreshToken } from '@/api/user/login'; import LoginLoading from '@/components/login/LoginLoading/LoginLoading'; import { ErrorMessage } from '@/constants/ErrorMessage'; import { ACCESSTOKEN_KEY } from '@/constants/index'; diff --git a/frontend/src/components/@helper/observer/InfiniteScrollObserver.tsx b/frontend/src/components/@helper/observer/InfiniteScrollObserver.tsx index 91da5ab5d..d3b5c5833 100644 --- a/frontend/src/components/@helper/observer/InfiniteScrollObserver.tsx +++ b/frontend/src/components/@helper/observer/InfiniteScrollObserver.tsx @@ -2,10 +2,10 @@ import { PropsWithStrictChildren } from 'gongseek-types'; import { useEffect, useRef } from 'react'; import { InfiniteQueryObserverResult } from 'react-query'; -import { infiniteArticleResponse } from '@/types/articleResponse'; -import { InfiniteSearchResultType } from '@/types/searchResponse'; +import { InfiniteArticleResponseType } from '@/api/article/articleType'; +import { InfiniteArticleSearchResponseType } from '@/api/search/searchType'; -export type ObserverResponseType = infiniteArticleResponse | InfiniteSearchResultType; +export type ObserverResponseType = InfiniteArticleResponseType | InfiniteArticleSearchResponseType; interface infiniteScrollObserverProps { hasNext: boolean; fetchNextPage: () => Promise>; diff --git a/frontend/src/components/article/ArticleContent/ArticleContent.tsx b/frontend/src/components/article/ArticleContent/ArticleContent.tsx index e934ffe92..d918d0a08 100644 --- a/frontend/src/components/article/ArticleContent/ArticleContent.tsx +++ b/frontend/src/components/article/ArticleContent/ArticleContent.tsx @@ -1,26 +1,26 @@ +import { Suspense } from 'react'; import { useNavigate } from 'react-router-dom'; +import { ArticleTotalType } from '@/api/article/articleType'; import Card from '@/components/@common/Card/Card'; +import Loading from '@/components/@common/Loading/Loading'; import ToastUiViewer from '@/components/@common/ToastUiViewer/ToastUiViewer'; import * as S from '@/components/article/ArticleContent/ArticleContent.styles'; import useDeleteArticleContent from '@/hooks/article/useDeleteArticleContent'; import useHeartClick from '@/hooks/article/useHeartClick'; import { ArticleContentCardStyle } from '@/styles/cardStyle'; -import { ArticleType } from '@/types/articleResponse'; -import { Author } from '@/types/author'; import { dateTimeConverter } from '@/utils/converter'; export interface ArticleContentProps { + article: Omit; category: string; - article: ArticleType; - author: Author; articleId: string; } -const ArticleContent = ({ category, article, author, articleId }: ArticleContentProps) => { +const ArticleContent = ({ article, category, articleId }: ArticleContentProps) => { const { handleClickFillHeart, handleClickEmptyHeart, isLike, likeCount } = useHeartClick({ - prevIsLike: article.isLike, - prevLikeCount: article.likeCount, + prevIsLike: article?.isLike || false, + prevLikeCount: article?.likeCount || 0, articleId, }); const { handleDeleteArticle } = useDeleteArticleContent(); @@ -32,93 +32,101 @@ const ArticleContent = ({ category, article, author, articleId }: ArticleContent }; return ( - - - - {category} - - - - {author.name} - - - - - - {article.title} - - - }> + {article && ( + + + - {dateTimeConverter(article.createdAt)} - - 조회수 {article.views} - - - - - - - - {article.isAuthor && ( - - + + + {article.author.name} + + + + + + {article.title} + + + - - - { - handleDeleteArticle(articleId); - }} - aria-label="글 삭제하기 버튼" - role="button" - tabIndex={0} - > - - - - )} - - - {isLike ? ( - - - - ) : ( - - - - )} -
{likeCount}
-
-
- - - {article.tag && - article.tag.length >= 1 && - article.tag.map((item) => ( - - # - {item} - - ))} - -
-
+ {dateTimeConverter(article.createdAt)} + + 조회수 {article.views} + + + + + + + + {article.isAuthor && ( + + + + + { + handleDeleteArticle(articleId); + }} + aria-label="글 삭제하기 버튼" + role="button" + tabIndex={0} + > + + + + )} + + + {isLike ? ( + + + + ) : ( + + + + )} +
{likeCount}
+
+
+ + + {article.tag && + article.tag.length >= 1 && + article.tag.map((item) => ( + + # + {item} + + ))} + + + + )} + ); }; diff --git a/frontend/src/components/article/ArticleItem/ArticleItem.tsx b/frontend/src/components/article/ArticleItem/ArticleItem.tsx index fd6ded84b..79f5f215e 100644 --- a/frontend/src/components/article/ArticleItem/ArticleItem.tsx +++ b/frontend/src/components/article/ArticleItem/ArticleItem.tsx @@ -1,26 +1,13 @@ +import { ArticleTotalType } from '@/api/article/articleType'; import Card from '@/components/@common/Card/Card'; import * as S from '@/components/article/ArticleItem/ArticleItem.styles'; import useHeartClick from '@/hooks/article/useHeartClick'; import { ArticleItemCardStyle } from '@/styles/cardStyle'; -import { Category } from '@/types/articleResponse'; -import { Author } from '@/types/author'; import { convertGithubAvatarUrlForResize } from '@/utils/converter'; import { dateTimeConverter } from '@/utils/converter'; export interface ArticleItemProps { - article: { - id: number; - title: string; - author: Author; - content: string; - category: Category; - commentCount: number; - createdAt: string; - tag: string[]; - isLike: boolean; - likeCount: number; - views: number; - }; + article: Omit; onClick: () => void; } diff --git a/frontend/src/components/article/PopularArticle/PopularArticle.tsx b/frontend/src/components/article/PopularArticle/PopularArticle.tsx index da0d17ba4..ecbb07bf3 100644 --- a/frontend/src/components/article/PopularArticle/PopularArticle.tsx +++ b/frontend/src/components/article/PopularArticle/PopularArticle.tsx @@ -1,59 +1,58 @@ +import { Suspense } from 'react'; import { useNavigate } from 'react-router-dom'; +import { CategoryType } from '@/api/article/articleType'; import EmptyMessage from '@/components/@common/EmptyMessage/EmptyMessage'; import Loading from '@/components/@common/Loading/Loading'; import * as S from '@/components/article/PopularArticle/PopularArticle.styles'; import PopularArticleItem from '@/components/article/PopularArticleItem/PopularArticleItem'; import useGetPopularArticles from '@/hooks/article/useGetPopularArticles'; import useCarousel from '@/hooks/common/useCarousel'; -import { Category } from '@/types/articleResponse'; const PopularArticle = () => { const { handleCarouselElementRef, handleLeftSlideEvent, handleRightSlideEvent, currentIndex } = useCarousel(); - const { data, isLoading } = useGetPopularArticles(); + const { data } = useGetPopularArticles(); const navigate = useNavigate(); - if (isLoading) { - return ; - } - if (!data?.articles.length) { return 게시글이 존재하지 않습니다; } - const handleClickArticleItem = ({ id, category }: { id: string; category: Category }) => { + const handleClickArticleItem = ({ id, category }: { id: string; category: CategoryType }) => { navigate(`/articles/${category}/${id}`); }; return data ? ( - - - - - - - {data.articles.map((article, idx) => ( - - handleClickArticleItem({ id: String(article.id), category: article.category }) - } - rightSlide={handleRightSlideEvent} - /> - ))} - - - - - - + }> + + + + + + + {data.articles.map((article, idx) => ( + + handleClickArticleItem({ id: String(article.id), category: article.category }) + } + rightSlide={handleRightSlideEvent} + /> + ))} + + + + + + + ) : null; }; diff --git a/frontend/src/components/article/PopularArticleItem/PopularArticleItem.styles.tsx b/frontend/src/components/article/PopularArticleItem/PopularArticleItem.styles.tsx index 0d5716a34..805ac670e 100644 --- a/frontend/src/components/article/PopularArticleItem/PopularArticleItem.styles.tsx +++ b/frontend/src/components/article/PopularArticleItem/PopularArticleItem.styles.tsx @@ -1,12 +1,12 @@ import { AiOutlineEye, AiOutlineMessage, AiFillHeart } from 'react-icons/ai'; +import { CategoryType } from '@/api/article/articleType'; import { UserProfile, ArticleItemTitle, HashTagListBox, } from '@/components/article/ArticleItem/ArticleItem.styles'; import { TextOverflow, TwoLineTextOverFlow } from '@/styles/mixin'; -import { Category } from '@/types/articleResponse'; import { css } from '@emotion/react'; import styled from '@emotion/styled'; @@ -76,7 +76,7 @@ export const PopularArticleUserProfile = styled(UserProfile)` background-clip: content-box, border-box; `; -export const PopularArticleHeader = styled.div<{ category: Category }>` +export const PopularArticleHeader = styled.div<{ category: CategoryType }>` ${({ theme, category }) => css` background: ${category === 'question' ? theme.colors.RED_500 : theme.colors.BLUE_500}; opacity: 0.8; diff --git a/frontend/src/components/article/PopularArticleItem/PopularArticleItem.tsx b/frontend/src/components/article/PopularArticleItem/PopularArticleItem.tsx index 829536ef5..d55b24552 100644 --- a/frontend/src/components/article/PopularArticleItem/PopularArticleItem.tsx +++ b/frontend/src/components/article/PopularArticleItem/PopularArticleItem.tsx @@ -1,11 +1,11 @@ import { useEffect, useRef } from 'react'; +import { ArticleTotalType } from '@/api/article/articleType'; import Card from '@/components/@common/Card/Card'; import * as S from '@/components/article/ArticleItem/ArticleItem.styles'; import * as PopularS from '@/components/article/PopularArticleItem/PopularArticleItem.styles'; import { CAROUSEL_AUTO_PLAY_TIME } from '@/constants/index'; import { PopularArticleItemCardStyle } from '@/styles/cardStyle'; -import { CommonArticleType } from '@/types/articleResponse'; import { convertGithubAvatarUrlForResize, dateTimeConverter } from '@/utils/converter'; const PopularArticleItem = ({ @@ -14,7 +14,7 @@ const PopularArticleItem = ({ onClick, rightSlide, }: { - article: CommonArticleType; + article: Omit; isActive: boolean; onClick?: () => void; rightSlide?: () => void; diff --git a/frontend/src/components/comment/Comment/Comment.tsx b/frontend/src/components/comment/Comment/Comment.tsx index f70d1b3c6..af5561bff 100644 --- a/frontend/src/components/comment/Comment/Comment.tsx +++ b/frontend/src/components/comment/Comment/Comment.tsx @@ -1,13 +1,15 @@ +import { Suspense } from 'react'; + +import { SingleCommentItemType } from '@/api/comment/commentType'; import Loading from '@/components/@common/Loading/Loading'; import ToastUiViewer from '@/components/@common/ToastUiViewer/ToastUiViewer'; import * as S from '@/components/comment/Comment/Comment.styles'; import useDeleteComment from '@/hooks/comment/useDeleteComment'; import useDetailCommentState from '@/hooks/comment/useDetailCommentState'; -import { CommentType } from '@/types/commentResponse'; import { convertGithubAvatarUrlForResize } from '@/utils/converter'; import { dateTimeConverter } from '@/utils/converter'; -export interface CommentProps extends CommentType { +export interface CommentProps extends Omit { articleId: string; tabIndex: number; } @@ -26,51 +28,51 @@ const Comment = ({ commentId: String(id), content, }); - const { isLoading, handleClickCommentDeleteButton } = useDeleteComment(); - - if (isLoading) return ; + const { handleClickCommentDeleteButton } = useDeleteComment(); return ( - - - - - - - {author.name} - - - {dateTimeConverter(createdAt)} - - - - {isAuthor && ( - - - 수정 - - { - handleClickCommentDeleteButton(id); - }} + }> + + + + - 삭제 - - - )} - - - - - + /> + + + {author.name} + + + {dateTimeConverter(createdAt)} + + + + {isAuthor && ( + + + 수정 + + { + handleClickCommentDeleteButton(id); + }} + tabIndex={tabIndex} + > + 삭제 + + + )} + + + + + + ); }; diff --git a/frontend/src/components/comment/CommentContent/CommentContent.tsx b/frontend/src/components/comment/CommentContent/CommentContent.tsx index c33f5939d..96d953ac7 100644 --- a/frontend/src/components/comment/CommentContent/CommentContent.tsx +++ b/frontend/src/components/comment/CommentContent/CommentContent.tsx @@ -1,18 +1,22 @@ +import { Suspense } from 'react'; + import EmptyMessage from '@/components/@common/EmptyMessage/EmptyMessage'; +import Loading from '@/components/@common/Loading/Loading'; import Comment from '@/components/comment/Comment/Comment'; import * as S from '@/components/comment/CommentContent/CommentContent.styles'; import useDetailCommentState from '@/hooks/comment/useDetailCommentState'; -import { CommentType } from '@/types/commentResponse'; +import useGetDetailComment from '@/hooks/comment/useGetDetailComment'; export interface CommentContentProps { articleId: string; - commentList: CommentType[]; } -const CommentContent = ({ articleId, commentList }: CommentContentProps) => { +const CommentContent = ({ articleId }: CommentContentProps) => { const { handleClickCommentPlusButton, isLogin } = useDetailCommentState({ articleId }); + const { data } = useGetDetailComment(articleId); + return ( - <> + }> { 댓글 -
- {commentList.length || 0}개 +
+ {data?.comments.length || 0}개
- {commentList && commentList.length > 0 ? ( - commentList.map((item) => ( + {data?.comments && data.comments.length > 0 ? ( + data.comments.map((item) => ( { 첫 번째 댓글을 달아주세요! )} - + ); }; diff --git a/frontend/src/components/hashTag/HashTagSearchBox/HashTagSearchBox.styles.tsx b/frontend/src/components/hashTag/HashTagSearchBox/HashTagSearchBox.styles.tsx index 15f07557e..4945781fc 100644 --- a/frontend/src/components/hashTag/HashTagSearchBox/HashTagSearchBox.styles.tsx +++ b/frontend/src/components/hashTag/HashTagSearchBox/HashTagSearchBox.styles.tsx @@ -119,7 +119,7 @@ export const OpenButton = styled(AiOutlineDown)` `} `; -export const CloseButton = styled(AiOutlineUp)` +export const CloseButton = styled(AiOutlineDown)` border: none; background-color: transparent; diff --git a/frontend/src/components/hashTag/HashTagSearchResult/HashTagSearchResult.tsx b/frontend/src/components/hashTag/HashTagSearchResult/HashTagSearchResult.tsx index 59d6b3d2d..3e671227e 100644 --- a/frontend/src/components/hashTag/HashTagSearchResult/HashTagSearchResult.tsx +++ b/frontend/src/components/hashTag/HashTagSearchResult/HashTagSearchResult.tsx @@ -1,12 +1,13 @@ +import { Suspense } from 'react'; import { useNavigate } from 'react-router-dom'; +import { ArticleTotalType } from '@/api/article/articleType'; import Loading from '@/components/@common/Loading/Loading'; import ResponsiveInfiniteCardList from '@/components/@common/ResponsiveInfiniteCardList/ResponsiveInfiniteCardList'; import ArticleItem from '@/components/article/ArticleItem/ArticleItem'; import * as S from '@/components/hashTag/HashTagSearchResult/HashTagSearchResult.styles'; import useGetArticleByHashTag from '@/hooks/hashTag/useGetArticleByHashTag'; import { EmptyMessage } from '@/pages/Search/index.styles'; -import { CommonArticleType } from '@/types/articleResponse'; export interface HashTagSearchResultProps { hashTags: string[]; @@ -15,40 +16,40 @@ export interface HashTagSearchResultProps { const HashTagSearchResult = ({ hashTags }: HashTagSearchResultProps) => { const navigate = useNavigate(); - const { data, isLoading, fetchNextPage } = useGetArticleByHashTag(hashTags); + const { data, fetchNextPage } = useGetArticleByHashTag(hashTags); - const handleClickArticleItem = (article: CommonArticleType) => { + const handleClickArticleItem = ( + article: Omit, + ) => { navigate(`/articles/${article.category}/${article.id}`); }; - if (isLoading) { - return ; - } - return ( - - 검색 결과 - {data && data.pages[0].articles.length >= 1 ? ( - - <> - {data.pages.map(({ articles }) => - articles.map((article) => ( - handleClickArticleItem(article)} - /> - )), - )} - - - ) : ( - 검색결과가 존재하지 않습니다 - )} - + }> + + 검색 결과 + {data && data.pages[0].articles.length >= 1 ? ( + + <> + {data.pages.map(({ articles }) => + articles.map((article) => ( + handleClickArticleItem(article)} + /> + )), + )} + + + ) : ( + 검색결과가 존재하지 않습니다 + )} + + ); }; diff --git a/frontend/src/components/search/SearchResult/SearchResult.tsx b/frontend/src/components/search/SearchResult/SearchResult.tsx index 480ddc074..bd410caf5 100644 --- a/frontend/src/components/search/SearchResult/SearchResult.tsx +++ b/frontend/src/components/search/SearchResult/SearchResult.tsx @@ -1,59 +1,59 @@ import React, { Suspense, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; +import { ArticleTotalType } from '@/api/article/articleType'; import EmptyMessage from '@/components/@common/EmptyMessage/EmptyMessage'; import Loading from '@/components/@common/Loading/Loading'; import ArticleItem from '@/components/article/ArticleItem/ArticleItem'; import * as S from '@/components/search/SearchResult/SearchResult.styles'; import useGetSearch from '@/hooks/search/useGetSearch'; -import { CommonArticleType } from '@/types/articleResponse'; const ResponsiveInfiniteCardList = React.lazy( () => import('@/components/@common/ResponsiveInfiniteCardList/ResponsiveInfiniteCardList'), ); const SearchResult = ({ target, searchIndex }: { target: string; searchIndex: string }) => { - const { data, isLoading, isIdle, refetch, fetchNextPage } = useGetSearch({ target, searchIndex }); + const { data, refetch, fetchNextPage } = useGetSearch({ target, searchIndex }); const navigate = useNavigate(); useEffect(() => { refetch(); }, [target, searchIndex]); - const handleClickArticleItem = (article: CommonArticleType) => { + const handleClickArticleItem = ( + article: Omit, + ) => { navigate(`/articles/${article.category}/${article.id}`); }; - if (isLoading || isIdle) { - return ; - } - return ( - - 검색 결과 - }> - {data && data.pages[0].articles.length >= 1 ? ( - - <> - {data.pages.map(({ articles }) => - articles.map((article) => ( - handleClickArticleItem(article)} - /> - )), - )} - - - ) : ( - 검색 결과가 존재하지 않습니다 - )} - - + }> + + 검색 결과 + }> + {data && data.pages[0].articles.length >= 1 ? ( + + <> + {data.pages.map(({ articles }) => + articles.map((article) => ( + handleClickArticleItem(article)} + /> + )), + )} + + + ) : ( + 검색 결과가 존재하지 않습니다 + )} + + + ); }; diff --git a/frontend/src/components/tempArticle/TemporaryArticleItem/TemporaryArticleItem.tsx b/frontend/src/components/tempArticle/TemporaryArticleItem/TemporaryArticleItem.tsx index 96c33d456..a87060c84 100644 --- a/frontend/src/components/tempArticle/TemporaryArticleItem/TemporaryArticleItem.tsx +++ b/frontend/src/components/tempArticle/TemporaryArticleItem/TemporaryArticleItem.tsx @@ -1,8 +1,9 @@ +import { SingleTempArticleItemResponseType } from '@/api/tempArticle/tempArticleType'; import * as S from '@/components/tempArticle/TemporaryArticleItem/TemporaryArticleItem.styles'; import { categoryNameConverter, dateTimeConverter } from '@/utils/converter'; export interface TemporaryArticleItemProps { - article: { title: string; createAt: string; category: string }; + article: Omit; onClick: () => void; } @@ -13,7 +14,7 @@ const TemporaryArticleItem = ({ article, onClick }: TemporaryArticleItemProps) = {categoryNameConverter(article.category)} - {article.createAt && dateTimeConverter(article.createAt)} + {article.createdAt && dateTimeConverter(article.createdAt)} ); diff --git a/frontend/src/components/tempArticle/TemporaryArticleList/TemporaryArticleList.tsx b/frontend/src/components/tempArticle/TemporaryArticleList/TemporaryArticleList.tsx index 446c953f5..cfd119ba0 100644 --- a/frontend/src/components/tempArticle/TemporaryArticleList/TemporaryArticleList.tsx +++ b/frontend/src/components/tempArticle/TemporaryArticleList/TemporaryArticleList.tsx @@ -1,20 +1,16 @@ import { useNavigate } from 'react-router-dom'; +import { SingleTempArticleItemResponseType } from '@/api/tempArticle/tempArticleType'; import EmptyMessage from '@/components/@common/EmptyMessage/EmptyMessage'; -import Loading from '@/components/@common/Loading/Loading'; import TemporaryArticleItem from '@/components/tempArticle/TemporaryArticleItem/TemporaryArticleItem'; import * as S from '@/components/tempArticle/TemporaryArticleList/TemporaryArticleList.styles'; import useDeleteTempArticle from '@/hooks/tempArticle/useDeleteTempArticle'; import useGetTempArticles from '@/hooks/tempArticle/useGetTempArticles'; -import { TempArticleItem } from '@/types/articleResponse'; const TemporaryArticleList = () => { - const { data, isLoading } = useGetTempArticles(); + const { data } = useGetTempArticles(); const { deleteTempArticleId } = useDeleteTempArticle(); const navigate = useNavigate(); - if (isLoading) { - return ; - } const handleClickTemporaryArticlekDeleteButton = (id: number) => { if (window.confirm('해당 임시 저장 글을 삭제하시겠습니까?')) { @@ -22,7 +18,7 @@ const TemporaryArticleList = () => { } }; - const handleClickTemporaryArticleItem = (item: TempArticleItem) => { + const handleClickTemporaryArticleItem = (item: SingleTempArticleItemResponseType) => { navigate(`/temp-article/${item.category}/${item.id}`); }; diff --git a/frontend/src/components/user/UserArticleItem/UserArticleItem.tsx b/frontend/src/components/user/UserArticleItem/UserArticleItem.tsx index a4a515d90..35e45ffed 100644 --- a/frontend/src/components/user/UserArticleItem/UserArticleItem.tsx +++ b/frontend/src/components/user/UserArticleItem/UserArticleItem.tsx @@ -1,10 +1,10 @@ import { useNavigate } from 'react-router-dom'; +import { SingleMyPageUserArticleResponseType } from '@/api/article/articleType'; import * as S from '@/components/user/UserArticleItem/UserArticleItem.styles'; -import { UserArticleItemType } from '@/types/articleResponse'; import { categoryNameConverter, dateTimeConverter } from '@/utils/converter'; -const UserArticleItem = ({ article }: { article: UserArticleItemType }) => { +const UserArticleItem = ({ article }: { article: SingleMyPageUserArticleResponseType }) => { const navigate = useNavigate(); const { id, title, category, commentCount, createdAt, updatedAt, views } = article; diff --git a/frontend/src/components/user/UserCommentBox/UserCommentBox.tsx b/frontend/src/components/user/UserCommentBox/UserCommentBox.tsx index 857c7e8b8..ef2c56142 100644 --- a/frontend/src/components/user/UserCommentBox/UserCommentBox.tsx +++ b/frontend/src/components/user/UserCommentBox/UserCommentBox.tsx @@ -1,3 +1,5 @@ +import { Suspense } from 'react'; + import EmptyMessage from '@/components/@common/EmptyMessage/EmptyMessage'; import Loading from '@/components/@common/Loading/Loading'; import * as S from '@/components/user/UserCommentBox/UserCommentBox.styles'; @@ -5,18 +7,10 @@ import UserCommentItem from '@/components/user/UserCommentItem/UserCommentItem'; import useGetUserComments from '@/hooks/user/useGetUserComments'; const UserCommentBox = () => { - const { - data: comments, - isSuccess: isCommentsSuccess, - isLoading: isCommentsLoading, - } = useGetUserComments(); - - if (isCommentsLoading) { - return ; - } + const { data: comments, isSuccess: isCommentsSuccess } = useGetUserComments(); return ( - <> + }> {isCommentsSuccess ? ( {comments ? ( @@ -30,7 +24,7 @@ const UserCommentBox = () => { ) : (
정보를 가져오는데 실패하였습니다
)} - +
); }; diff --git a/frontend/src/components/user/UserCommentItem/UserCommentItem.tsx b/frontend/src/components/user/UserCommentItem/UserCommentItem.tsx index 238031daf..9e9ddde11 100644 --- a/frontend/src/components/user/UserCommentItem/UserCommentItem.tsx +++ b/frontend/src/components/user/UserCommentItem/UserCommentItem.tsx @@ -1,10 +1,10 @@ import { useNavigate } from 'react-router-dom'; +import { MyPageCommentItemResponse } from '@/api/comment/commentType'; import * as S from '@/components/user/UserCommentItem/UserCommentItem.styles'; -import { UserComment } from '@/types/commentResponse'; import { dateTimeConverter } from '@/utils/converter'; -const UserCommentItem = ({ comment }: { comment: UserComment }) => { +const UserCommentItem = ({ comment }: { comment: MyPageCommentItemResponse }) => { const { id, content, createdAt, updatedAt, articleId, category, articleTitle } = comment; const navigate = useNavigate(); const 한글_카테고리 = category === 'question' ? '질문' : '토론'; diff --git a/frontend/src/components/user/UserProfileIcon/UserProfileIcon.tsx b/frontend/src/components/user/UserProfileIcon/UserProfileIcon.tsx index e6b617792..5865fc12e 100644 --- a/frontend/src/components/user/UserProfileIcon/UserProfileIcon.tsx +++ b/frontend/src/components/user/UserProfileIcon/UserProfileIcon.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { Suspense, useEffect, useState } from 'react'; import { useRecoilState } from 'recoil'; import gongseek from '@/assets/gongseek.png'; @@ -8,7 +8,7 @@ import useGetUserInfo from '@/hooks/user/useGetUserInfo'; import { dropdownState } from '@/store/dropdownState'; const UserProfileIcon = () => { - const { data, isLoading, isSuccess } = useGetUserInfo(); + const { data } = useGetUserInfo(); const [dropdown, setDropdown] = useRecoilState(dropdownState); const [isLog, setIsLog] = useState(false); @@ -46,8 +46,11 @@ const UserProfileIcon = () => { } onKeyDown={handleUserProfileIconKeydown} > - {isLoading && } - {isSuccess && } + { + }> + + + } {dropdown.isOpen && } {isLog && 닫힘} diff --git a/frontend/src/components/vote/Vote/Vote.tsx b/frontend/src/components/vote/Vote/Vote.tsx index 6d4c3f820..ae3c0b89a 100644 --- a/frontend/src/components/vote/Vote/Vote.tsx +++ b/frontend/src/components/vote/Vote/Vote.tsx @@ -1,3 +1,4 @@ +import { Suspense } from 'react'; import { MdOutlineHowToVote } from 'react-icons/md'; import Loading from '@/components/@common/Loading/Loading'; @@ -6,36 +7,36 @@ import VoteItem from '@/components/vote/VoteItem/VoteItem'; import useGetVote from '@/hooks/vote/useGetVote'; const Vote = ({ articleId }: { articleId: string }) => { - const { data, isLoading, totalCount } = useGetVote(articleId); - - if (isLoading) return ; + const { data, totalCount } = useGetVote(articleId); return ( - - - {`투표(${data?.expired ? '만료됨' : '진행중'})`} - - - 총 {totalCount}표 - - - - {data && - data.voteItems.map((datum, idx) => ( - - ))} - - + }> + + + {`투표(${data?.expired ? '만료됨' : '진행중'})`} + + + 총 {totalCount}표 + + + + {data && + data.voteItems.map((datum, idx) => ( + + ))} + + + ); }; diff --git a/frontend/src/hooks/article/useDeleteArticleContent.tsx b/frontend/src/hooks/article/useDeleteArticleContent.tsx index 7c9ad0615..66cc10946 100644 --- a/frontend/src/hooks/article/useDeleteArticleContent.tsx +++ b/frontend/src/hooks/article/useDeleteArticleContent.tsx @@ -3,7 +3,7 @@ import { useEffect } from 'react'; import { useMutation } from 'react-query'; import { useNavigate } from 'react-router-dom'; -import { deleteArticle } from '@/api/article'; +import { deleteArticle } from '@/api/article/article'; import { ErrorMessage } from '@/constants/ErrorMessage'; import { URL } from '@/constants/url'; import useSnackBar from '@/hooks/common/useSnackBar'; diff --git a/frontend/src/hooks/article/useGetAllArticles.tsx b/frontend/src/hooks/article/useGetAllArticles.tsx index 99acf8138..a458dc3b6 100644 --- a/frontend/src/hooks/article/useGetAllArticles.tsx +++ b/frontend/src/hooks/article/useGetAllArticles.tsx @@ -3,18 +3,18 @@ import { useEffect, useState } from 'react'; import { useInfiniteQuery } from 'react-query'; import { useRecoilState } from 'recoil'; -import { getAllArticle } from '@/api/article'; +import { getAllArticle } from '@/api/article/article'; +import { InfiniteArticleResponseType } from '@/api/article/articleType'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; import { categoryState } from '@/store/categoryState'; -import { infiniteArticleResponse } from '@/types/articleResponse'; const useGetAllArticles = () => { const [currentCategory, setCurrentCategory] = useRecoilState(categoryState); const [sortIndex, setSortIndex] = useState('최신순'); const { data, isLoading, isError, isSuccess, error, refetch, fetchNextPage } = useInfiniteQuery< - infiniteArticleResponse, + InfiniteArticleResponseType, AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }> >( ['all-articles', currentCategory], diff --git a/frontend/src/hooks/article/useGetCategoryArticles.tsx b/frontend/src/hooks/article/useGetCategoryArticles.tsx index 718a5387f..1d61b7d40 100644 --- a/frontend/src/hooks/article/useGetCategoryArticles.tsx +++ b/frontend/src/hooks/article/useGetCategoryArticles.tsx @@ -2,16 +2,16 @@ import { AxiosError } from 'axios'; import { useEffect, useState } from 'react'; import { useInfiniteQuery } from 'react-query'; -import { getAllArticle } from '@/api/article'; +import { getAllArticle } from '@/api/article/article'; +import { InfiniteArticleResponseType } from '@/api/article/articleType'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; -import { infiniteArticleResponse } from '@/types/articleResponse'; const useGetCategoryArticles = (category: string) => { const [sortIndex, setSortIndex] = useState('최신순'); const { data, isLoading, isError, isSuccess, error, refetch, fetchNextPage } = useInfiniteQuery< - infiniteArticleResponse, + InfiniteArticleResponseType, AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }> >( ['articles', category], diff --git a/frontend/src/hooks/article/useGetDetailArticle.tsx b/frontend/src/hooks/article/useGetDetailArticle.tsx index 86f557345..dfa9b8af8 100644 --- a/frontend/src/hooks/article/useGetDetailArticle.tsx +++ b/frontend/src/hooks/article/useGetDetailArticle.tsx @@ -3,15 +3,18 @@ import { useEffect } from 'react'; import { useQuery } from 'react-query'; import { useSetRecoilState } from 'recoil'; -import { getDetailArticle } from '@/api/article'; +import { getDetailArticle } from '@/api/article/article'; +import { DetailArticleResponseType } from '@/api/article/articleType'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; import { articleState } from '@/store/articleState'; -import { ArticleType } from '@/types/articleResponse'; -const useGetDetailArticle = (id: string) => { - const { data, isSuccess, isError, isLoading, error, isIdle } = useQuery< - ArticleType, +const useGetDetailArticle = (id: string | undefined) => { + if (typeof id === 'undefined') { + throw new Error('id를 찾을 수 없습니다.'); + } + const { data, isSuccess, isError, error } = useQuery< + DetailArticleResponseType, AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }> >(['detail-article', `article${id}`], () => getDetailArticle(id), { retry: false, @@ -26,7 +29,7 @@ const useGetDetailArticle = (id: string) => { } }, [isSuccess]); - return { isSuccess, isLoading, data, isIdle }; + return { isSuccess, data }; }; export default useGetDetailArticle; diff --git a/frontend/src/hooks/article/useGetPopularArticles.tsx b/frontend/src/hooks/article/useGetPopularArticles.tsx index 727f4dbbb..a8d8c4ddc 100644 --- a/frontend/src/hooks/article/useGetPopularArticles.tsx +++ b/frontend/src/hooks/article/useGetPopularArticles.tsx @@ -1,13 +1,14 @@ import { AxiosError } from 'axios'; import { useQuery } from 'react-query'; -import { getPopularArticles, PopularArticles } from '@/api/article'; +import { getPopularArticles } from '@/api/article/article'; +import { TotalArticleInquiredResponseType } from '@/api/article/articleType'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; const useGetPopularArticles = () => { const { data, error, isError, isLoading } = useQuery< - PopularArticles, + TotalArticleInquiredResponseType, AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }> >('popular-articles', getPopularArticles, { retry: 1, diff --git a/frontend/src/hooks/article/useHeartClick.tsx b/frontend/src/hooks/article/useHeartClick.tsx index 2519a9f2d..81d75e861 100644 --- a/frontend/src/hooks/article/useHeartClick.tsx +++ b/frontend/src/hooks/article/useHeartClick.tsx @@ -2,19 +2,17 @@ import { AxiosError, AxiosResponse } from 'axios'; import React, { useEffect, useState } from 'react'; import { useMutation } from 'react-query'; -import { deleteLikeArticle, postAddLikeArticle } from '@/api/like'; +import { deleteLikeArticle, postAddLikeArticle } from '@/api/article/like'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; -const useHeartClick = ({ - prevIsLike, - prevLikeCount, - articleId, -}: { +interface useHeartClickProps { prevIsLike: boolean; prevLikeCount: number; articleId: string; -}) => { +} + +const useHeartClick = ({ prevIsLike, prevLikeCount, articleId }: useHeartClickProps) => { const [isLike, setIsLike] = useState(prevIsLike); const [likeCount, setLikeCount] = useState(prevLikeCount); const { diff --git a/frontend/src/hooks/article/usePostUpdateWritingArticle.tsx b/frontend/src/hooks/article/usePostUpdateWritingArticle.tsx index 7cc692857..e6f09b53f 100644 --- a/frontend/src/hooks/article/usePostUpdateWritingArticle.tsx +++ b/frontend/src/hooks/article/usePostUpdateWritingArticle.tsx @@ -4,7 +4,8 @@ import { useMutation } from 'react-query'; import { useNavigate } from 'react-router-dom'; import { useRecoilValue } from 'recoil'; -import { putArticle } from '@/api/article'; +import { putArticle } from '@/api/article/article'; +import { UpdateArticleRequestType } from '@/api/article/articleType'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; import { queryClient } from '@/index'; @@ -27,7 +28,7 @@ const usePostUpdateWritingArticle = () => { const { data, isSuccess, isError, isLoading, error, mutate } = useMutation< AxiosResponse<{ id: number; category: string }>, AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }>, - { title: string; content: string; id: string; tag: string[] } + UpdateArticleRequestType >(putArticle, { retry: 1 }); useEffect(() => { diff --git a/frontend/src/hooks/article/usePostWritingArticles.tsx b/frontend/src/hooks/article/usePostWritingArticles.tsx index c6917117f..cc1ae5dcf 100644 --- a/frontend/src/hooks/article/usePostWritingArticles.tsx +++ b/frontend/src/hooks/article/usePostWritingArticles.tsx @@ -3,7 +3,12 @@ import { useEffect, useRef, useState } from 'react'; import { useMutation } from 'react-query'; import { useNavigate } from 'react-router-dom'; -import { postWritingArticle } from '@/api/article'; +import { postWritingArticle } from '@/api/article/article'; +import { + CategoryType, + CreateArticleRequestType, + CreateArticleResponseType, +} from '@/api/article/articleType'; import { ErrorMessage } from '@/constants/ErrorMessage'; import { CATEGORY } from '@/constants/categoryType'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; @@ -14,25 +19,20 @@ const usePostWritingArticles = ({ category, isAnonymous, }: { - category?: string; + category?: CategoryType | string; isAnonymous: boolean; }) => { const { data, mutate, isError, isLoading, isSuccess, error } = useMutation< - AxiosResponse<{ id: string }>, + AxiosResponse, AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }>, - { - title: string; - category: string; - content: string; - tag: string[]; - isAnonymous: boolean; - tempArticleId: number | ''; - } + CreateArticleRequestType >(postWritingArticle, { retry: 1 }); const content = useRef(null); const [title, setTitle] = useState(''); - const [categoryOption, setCategoryOption] = useState(category ? category : ''); + const [categoryOption, setCategoryOption] = useState( + category ? category : 'question', + ); const [isValidTitleInput, setIsValidTitleInput] = useState(true); const [hashTags, setHashTags] = useState([]); const titleInputRef = useRef(null); @@ -57,10 +57,16 @@ const usePostWritingArticles = ({ } }, [isSuccess]); - const handleSubmitButtonClick = (categoryOption: string, tempArticleId: number | '') => { + const handleSubmitButtonClick = ( + categoryOption: CategoryType | string, + tempArticleId: number | '', + ) => { if (content.current === null) { return; } + if (categoryOption !== 'question' && categoryOption !== 'discussion') { + return; + } if (!validatedTitleInput(title)) { setIsValidTitleInput(false); diff --git a/frontend/src/hooks/comment/useDeleteComment.tsx b/frontend/src/hooks/comment/useDeleteComment.tsx index f085ace9a..d3446521b 100644 --- a/frontend/src/hooks/comment/useDeleteComment.tsx +++ b/frontend/src/hooks/comment/useDeleteComment.tsx @@ -2,7 +2,7 @@ import { AxiosError } from 'axios'; import { useEffect } from 'react'; import { useMutation } from 'react-query'; -import { deleteComments } from '@/api/comments'; +import { deleteComments } from '@/api/comment/comments'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; import { queryClient } from '@/index'; diff --git a/frontend/src/hooks/comment/useGetDetailComment.tsx b/frontend/src/hooks/comment/useGetDetailComment.tsx index 33a491f6a..4b16966e7 100644 --- a/frontend/src/hooks/comment/useGetDetailComment.tsx +++ b/frontend/src/hooks/comment/useGetDetailComment.tsx @@ -1,20 +1,20 @@ import { AxiosError } from 'axios'; import { useQuery } from 'react-query'; -import { getComments } from '@/api/comments'; +import { TotalCommentResponseType } from '@/api/comment/commentType'; +import { getComments } from '@/api/comment/comments'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; -import { CommentType } from '@/types/commentResponse'; const useGetDetailComment = (id: string) => { - const { data, isSuccess, isError, isLoading, isIdle, error } = useQuery< - { comments: CommentType[] }, + const { data, isSuccess, isError, isIdle, error } = useQuery< + TotalCommentResponseType, AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }> >('comments', () => getComments(id), { retry: false, refetchOnWindowFocus: false }); useThrowCustomError(isError, error); - return { isLoading, isSuccess, data, isIdle }; + return { isSuccess, data, isIdle }; }; export default useGetDetailComment; diff --git a/frontend/src/hooks/comment/usePostCommentInputModal.tsx b/frontend/src/hooks/comment/usePostCommentInputModal.tsx index 26b3d4aae..345dd26f5 100644 --- a/frontend/src/hooks/comment/usePostCommentInputModal.tsx +++ b/frontend/src/hooks/comment/usePostCommentInputModal.tsx @@ -2,7 +2,7 @@ import { AxiosError } from 'axios'; import { useEffect } from 'react'; import { useMutation } from 'react-query'; -import { postComments } from '@/api/comments'; +import { postComments } from '@/api/comment/comments'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useSnackBar from '@/hooks/common/useSnackBar'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; diff --git a/frontend/src/hooks/comment/usePutCommentInputModal.tsx b/frontend/src/hooks/comment/usePutCommentInputModal.tsx index 49e3f4209..75e40a3a2 100644 --- a/frontend/src/hooks/comment/usePutCommentInputModal.tsx +++ b/frontend/src/hooks/comment/usePutCommentInputModal.tsx @@ -2,7 +2,7 @@ import { AxiosError } from 'axios'; import { useEffect } from 'react'; import { useMutation } from 'react-query'; -import { putComments } from '@/api/comments'; +import { putComments } from '@/api/comment/comments'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useSnackBar from '@/hooks/common/useSnackBar'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; diff --git a/frontend/src/hooks/common/useToastImageConverter.tsx b/frontend/src/hooks/common/useToastImageConverter.tsx index 28f8fd0ff..5b3c9fcbf 100644 --- a/frontend/src/hooks/common/useToastImageConverter.tsx +++ b/frontend/src/hooks/common/useToastImageConverter.tsx @@ -2,7 +2,7 @@ import { AxiosError, AxiosResponse } from 'axios'; import { MutableRefObject, useEffect, useState } from 'react'; import { useMutation } from 'react-query'; -import { postImageUrlConverter } from '@/api/image'; +import { postImageUrlConverter } from '@/api/article/image'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useSnackBar from '@/hooks/common/useSnackBar'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; diff --git a/frontend/src/hooks/hashTag/useGetAllHashTags.tsx b/frontend/src/hooks/hashTag/useGetAllHashTags.tsx index a765e2247..4a656b48e 100644 --- a/frontend/src/hooks/hashTag/useGetAllHashTags.tsx +++ b/frontend/src/hooks/hashTag/useGetAllHashTags.tsx @@ -1,13 +1,14 @@ import { AxiosError } from 'axios'; import { useQuery } from 'react-query'; -import { getAllHashTag } from '@/api/hashTag'; +import { getAllHashTag } from '@/api/hashTag/hashTag'; +import { HashTagResponseType } from '@/api/hashTag/hashTagType'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; const useGetAllHashTags = () => { const { data, isLoading, isError, isSuccess, error } = useQuery< - { tag: string[] }, + HashTagResponseType, AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }> >('all-hash-tag', getAllHashTag); diff --git a/frontend/src/hooks/hashTag/useGetArticleByHashTag.tsx b/frontend/src/hooks/hashTag/useGetArticleByHashTag.tsx index 7631ed8d9..21abb3b96 100644 --- a/frontend/src/hooks/hashTag/useGetArticleByHashTag.tsx +++ b/frontend/src/hooks/hashTag/useGetArticleByHashTag.tsx @@ -2,16 +2,16 @@ import { AxiosError } from 'axios'; import { useEffect } from 'react'; import { useInfiniteQuery } from 'react-query'; -import { getArticleByHashTag } from '@/api/search'; +import { InfiniteHashTagSearchResponseType } from '@/api/hashTag/hashTagType'; +import { getArticleByHashTag } from '@/api/search/search'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; -import { InfiniteHashTagSearchResultType } from '@/types/searchResponse'; const useGetArticleByHashTag = (hashTag: string[]) => { const tags = hashTag.join(','); const cursorId = ''; const { data, isLoading, isSuccess, isError, error, refetch, fetchNextPage } = useInfiniteQuery< - InfiniteHashTagSearchResultType, + InfiniteHashTagSearchResponseType, AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }> >( ['hashtag-search-result', tags], diff --git a/frontend/src/hooks/login/useDeleteRefreshToken.tsx b/frontend/src/hooks/login/useDeleteRefreshToken.tsx index a4d522745..335d175b9 100644 --- a/frontend/src/hooks/login/useDeleteRefreshToken.tsx +++ b/frontend/src/hooks/login/useDeleteRefreshToken.tsx @@ -1,7 +1,7 @@ import { AxiosError, AxiosResponse } from 'axios'; import { useMutation } from 'react-query'; -import { deleteRefreshToken } from '@/api/login'; +import { deleteRefreshToken } from '@/api/user/login'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; diff --git a/frontend/src/hooks/login/useGetLoginURL.tsx b/frontend/src/hooks/login/useGetLoginURL.tsx index 91344dbda..ab8e0e586 100644 --- a/frontend/src/hooks/login/useGetLoginURL.tsx +++ b/frontend/src/hooks/login/useGetLoginURL.tsx @@ -2,7 +2,7 @@ import { AxiosError } from 'axios'; import { useEffect, useState } from 'react'; import { useQuery } from 'react-query'; -import { getGithubURL } from '@/api/login'; +import { getGithubURL } from '@/api/user/login'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; diff --git a/frontend/src/hooks/search/useGetSearch.tsx b/frontend/src/hooks/search/useGetSearch.tsx index 003e302d4..c048e2f85 100644 --- a/frontend/src/hooks/search/useGetSearch.tsx +++ b/frontend/src/hooks/search/useGetSearch.tsx @@ -1,53 +1,50 @@ import { AxiosError } from 'axios'; import { useInfiniteQuery } from 'react-query'; -import { getSearchResult } from '@/api/search'; +import { getSearchResult } from '@/api/search/search'; +import { InfiniteArticleSearchResponseType } from '@/api/search/searchType'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; -import { InfiniteSearchResultType } from '@/types/searchResponse'; const useGetSearch = ({ target, searchIndex }: { target: string; searchIndex: string }) => { const cursorId = ''; - const { data, isSuccess, isLoading, isError, isIdle, error, refetch, fetchNextPage } = - useInfiniteQuery< - InfiniteSearchResultType, - AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }> - >( - ['search-result', `${searchIndex}-${target}`], - ({ - pageParam = { - target, - searchIndex, - cursorId, - }, - }) => getSearchResult(pageParam), - { - getNextPageParam: (lastPage) => { - const { hasNext, articles, cursorId, target, searchIndex } = lastPage; + const { data, isSuccess, isError, error, refetch, fetchNextPage } = useInfiniteQuery< + InfiniteArticleSearchResponseType, + AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }> + >( + ['search-result', `${searchIndex}-${target}`], + ({ + pageParam = { + target, + searchIndex, + cursorId, + }, + }) => getSearchResult(pageParam), + { + getNextPageParam: (lastPage) => { + const { hasNext, articles, cursorId, target, searchIndex } = lastPage; - if (hasNext) { - return { - articles, - hasNext, - cursorId, - target, - searchIndex, - }; - } - return; - }, - retry: 1, - refetchOnWindowFocus: false, + if (hasNext) { + return { + articles, + hasNext, + cursorId, + target, + searchIndex, + }; + } + return; }, - ); + retry: 1, + refetchOnWindowFocus: false, + }, + ); useThrowCustomError(isError, error); return { data, isSuccess, - isLoading, - isIdle, refetch, fetchNextPage, }; diff --git a/frontend/src/hooks/tempArticle/useDeleteTempArticle.tsx b/frontend/src/hooks/tempArticle/useDeleteTempArticle.tsx index 9b1f741e2..2dd1b1f12 100644 --- a/frontend/src/hooks/tempArticle/useDeleteTempArticle.tsx +++ b/frontend/src/hooks/tempArticle/useDeleteTempArticle.tsx @@ -1,7 +1,7 @@ import { AxiosError } from 'axios'; import { useMutation } from 'react-query'; -import { deleteArticleItem } from '@/api/tempArticle'; +import { deleteArticleItem } from '@/api/tempArticle/tempArticle'; import CustomError from '@/components/@helper/errorBoundary/CustomError'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useSnackBar from '@/hooks/common/useSnackBar'; diff --git a/frontend/src/hooks/tempArticle/useGetTempArticles.tsx b/frontend/src/hooks/tempArticle/useGetTempArticles.tsx index 5dda77c2a..099ec06ca 100644 --- a/frontend/src/hooks/tempArticle/useGetTempArticles.tsx +++ b/frontend/src/hooks/tempArticle/useGetTempArticles.tsx @@ -1,14 +1,14 @@ import { AxiosError } from 'axios'; import { useQuery } from 'react-query'; -import { getTempArticles } from '@/api/tempArticle'; +import { getTempArticles } from '@/api/tempArticle/tempArticle'; +import { TotalTempArticleResponseType } from '@/api/tempArticle/tempArticleType'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; -import { TempArticleResponse } from '@/types/articleResponse'; const useGetTempArticles = () => { const { data, isError, isLoading, error } = useQuery< - TempArticleResponse, + TotalTempArticleResponseType, AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }> >('temp-articles', getTempArticles, { retry: false, diff --git a/frontend/src/hooks/tempArticle/useGetTempDetailArticles.tsx b/frontend/src/hooks/tempArticle/useGetTempDetailArticles.tsx index 965a17ac0..2b8165c0d 100644 --- a/frontend/src/hooks/tempArticle/useGetTempDetailArticles.tsx +++ b/frontend/src/hooks/tempArticle/useGetTempDetailArticles.tsx @@ -1,14 +1,14 @@ import { AxiosError, AxiosResponse } from 'axios'; import { useMutation } from 'react-query'; -import { getTempDetailArticle } from '@/api/tempArticle'; +import { getTempDetailArticle } from '@/api/tempArticle/tempArticle'; +import { DetailTempArticleResponseType } from '@/api/tempArticle/tempArticleType'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; -import { TempArticleDetailResponse } from '@/types/articleResponse'; const useGetTempDetailArticles = ({ tempArticleId }: { tempArticleId: number | '' }) => { const { data, isLoading, isSuccess, isError, error, mutate } = useMutation< - AxiosResponse, + AxiosResponse, AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }>, { tempArticleId: number; diff --git a/frontend/src/hooks/tempArticle/usePostTempArticle.tsx b/frontend/src/hooks/tempArticle/usePostTempArticle.tsx index f232d07e8..45f6a5568 100644 --- a/frontend/src/hooks/tempArticle/usePostTempArticle.tsx +++ b/frontend/src/hooks/tempArticle/usePostTempArticle.tsx @@ -2,7 +2,7 @@ import { AxiosError, AxiosResponse } from 'axios'; import { Dispatch, SetStateAction } from 'react'; import { useMutation } from 'react-query'; -import { postTempArticle } from '@/api/tempArticle'; +import { postTempArticle } from '@/api/tempArticle/tempArticle'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useSnackBar from '@/hooks/common/useSnackBar'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; diff --git a/frontend/src/hooks/user/useGetUserArticles.tsx b/frontend/src/hooks/user/useGetUserArticles.tsx index 5614d37bb..5fcfd6e1c 100644 --- a/frontend/src/hooks/user/useGetUserArticles.tsx +++ b/frontend/src/hooks/user/useGetUserArticles.tsx @@ -1,14 +1,14 @@ import { AxiosError } from 'axios'; import { useQuery } from 'react-query'; -import { getUserArticles } from '@/api/myPage'; +import { MyPageUserArticleResponseType } from '@/api/article/articleType'; +import { getUserArticles } from '@/api/user/myPage'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; -import { UserArticlesResponse } from '@/types/articleResponse'; const useGetUserArticles = () => { const { data, isSuccess, isError, isLoading, error } = useQuery< - UserArticlesResponse, + MyPageUserArticleResponseType, AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }> >('user-articles', getUserArticles, { retry: 1, refetchOnWindowFocus: false }); diff --git a/frontend/src/hooks/user/useGetUserComments.tsx b/frontend/src/hooks/user/useGetUserComments.tsx index d2fe19df2..60d779fe4 100644 --- a/frontend/src/hooks/user/useGetUserComments.tsx +++ b/frontend/src/hooks/user/useGetUserComments.tsx @@ -1,14 +1,14 @@ import { AxiosError } from 'axios'; import { useQuery } from 'react-query'; -import { getUserComments } from '@/api/myPage'; +import { MyPageCommentResponse } from '@/api/comment/commentType'; +import { getUserComments } from '@/api/user/myPage'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; -import { UserCommentResponse } from '@/types/commentResponse'; const useGetUserComments = () => { const { data, isSuccess, isError, isLoading, error } = useQuery< - UserCommentResponse, + MyPageCommentResponse, AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }> >('user-comments', getUserComments, { retry: 1, refetchOnWindowFocus: false }); diff --git a/frontend/src/hooks/user/useGetUserInfo.tsx b/frontend/src/hooks/user/useGetUserInfo.tsx index 36e9e0bc3..b5096f560 100644 --- a/frontend/src/hooks/user/useGetUserInfo.tsx +++ b/frontend/src/hooks/user/useGetUserInfo.tsx @@ -1,14 +1,14 @@ import { AxiosError } from 'axios'; import { useQuery } from 'react-query'; -import { getUserInfo } from '@/api/myPage'; +import { AuthorType } from '@/api/article/articleType'; +import { getUserInfo } from '@/api/user/myPage'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; -import { Author } from '@/types/author'; const useGetUserInfo = () => { const { data, isSuccess, isError, isLoading, error } = useQuery< - Author, + AuthorType, AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }> >('user-info', getUserInfo, { retry: 1, refetchOnWindowFocus: false }); diff --git a/frontend/src/hooks/user/usePutUserProfile.tsx b/frontend/src/hooks/user/usePutUserProfile.tsx index 5a57e3c62..9f2cef25f 100644 --- a/frontend/src/hooks/user/usePutUserProfile.tsx +++ b/frontend/src/hooks/user/usePutUserProfile.tsx @@ -1,7 +1,7 @@ import { AxiosError, AxiosResponse } from 'axios'; import { useMutation } from 'react-query'; -import { editUserInfo } from '@/api/myPage'; +import { editUserInfo } from '@/api/user/myPage'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; diff --git a/frontend/src/hooks/vote/useGetVote.tsx b/frontend/src/hooks/vote/useGetVote.tsx index 981afef9c..a763b4ade 100644 --- a/frontend/src/hooks/vote/useGetVote.tsx +++ b/frontend/src/hooks/vote/useGetVote.tsx @@ -2,13 +2,14 @@ import { AxiosError } from 'axios'; import { useEffect, useState } from 'react'; import { useQuery } from 'react-query'; -import { getVoteItems, TVote } from '@/api/vote'; +import { getVoteItems } from '@/api/vote/vote'; +import { VoteResponseType } from '@/api/vote/voteType'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; const useVote = (articleId: string) => { - const { data, isLoading, isError, isSuccess, error } = useQuery< - TVote, + const { data, isError, isSuccess, error } = useQuery< + VoteResponseType, AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }> >(['vote', `vote${articleId}`], () => getVoteItems(articleId), { retry: false, @@ -24,7 +25,7 @@ const useVote = (articleId: string) => { useThrowCustomError(isError, error); - return { data, isLoading, totalCount, isSuccess }; + return { data, totalCount, isSuccess }; }; export default useVote; diff --git a/frontend/src/hooks/vote/usePostVoteItem.tsx b/frontend/src/hooks/vote/usePostVoteItem.tsx index 1b5542d85..e5898deb7 100644 --- a/frontend/src/hooks/vote/usePostVoteItem.tsx +++ b/frontend/src/hooks/vote/usePostVoteItem.tsx @@ -2,7 +2,8 @@ import { AxiosError } from 'axios'; import { useEffect } from 'react'; import { useMutation } from 'react-query'; -import { checkVoteItems } from '@/api/vote'; +import { checkVoteItems } from '@/api/vote/vote'; +import { CheckVoteRequestType } from '@/api/vote/voteType'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; import { queryClient } from '@/index'; @@ -11,7 +12,7 @@ const usePostVoteItem = (articleId: string) => { const { error, mutate, isError, isSuccess, isLoading } = useMutation< unknown, AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }>, - { articleId: string; voteItemId: string } + CheckVoteRequestType >(checkVoteItems); useEffect(() => { diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 50bdc7435..25fbc6da2 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -7,20 +7,26 @@ import App from '@/App'; import GlobalModal from '@/components/@helper/Modal/GlobalModal'; import FallbackErrorBoundary from '@/components/@helper/errorBoundary/FallbackErrorBoundary'; import LogicErrorBoundary from '@/components/@helper/errorBoundary/LogicErrorBoundary'; -import { worker } from '@/mock/browser'; import NotFound from '@/pages/NotFound'; import ServerErrorPage from '@/pages/ServerErrorPage'; import { theme } from '@/styles/Theme'; import { reset } from '@/styles/reset'; import { ThemeProvider, Global } from '@emotion/react'; -// if (process.env.NODE_ENV === 'development') { -// worker.start(); -// } - const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); -export const queryClient = new QueryClient(); +export const queryClient = new QueryClient({ + defaultOptions: { + queries: { + suspense: true, + useErrorBoundary: false, + }, + mutations: { + useErrorBoundary: false, + }, + }, +}); + let isUnhandledError = false; window.addEventListener('unhandledrejection', () => { diff --git a/frontend/src/mock/comment.ts b/frontend/src/mock/comment.ts index 71e98336e..43c342094 100644 --- a/frontend/src/mock/comment.ts +++ b/frontend/src/mock/comment.ts @@ -1,12 +1,12 @@ import { rest } from 'msw'; +import { SingleCommentItemType } from '@/api/comment/commentType'; import { HOME_URL } from '@/constants/apiUrl'; import mockData from '@/mock/data/comment.json'; -import { CommentType } from '@/types/commentResponse'; const data = localStorage.getItem('mock-comments'); -const mockComments = data ? (JSON.parse(data) as CommentType[][]) : []; +const mockComments = data ? (JSON.parse(data) as SingleCommentItemType[][]) : []; export const CommentHandler = [ rest.post<{ content: string }>( @@ -31,6 +31,7 @@ export const CommentHandler = [ }, createdAt: '2022-07-28', isAuthor: true, + updatedAt: '2022-08-08', }); localStorage.setItem('mock-comments', JSON.stringify(mockComments)); diff --git a/frontend/src/mock/myPage.ts b/frontend/src/mock/myPage.ts index 47a37a0ac..d3941cc25 100644 --- a/frontend/src/mock/myPage.ts +++ b/frontend/src/mock/myPage.ts @@ -1,12 +1,11 @@ import { rest } from 'msw'; +import { AuthorType, MyPageUserArticleResponseType } from '@/api/article/articleType'; +import { MyPageCommentResponse } from '@/api/comment/commentType'; import { HOME_URL } from '@/constants/apiUrl'; -import { UserArticlesResponse } from '@/types/articleResponse'; -import { Author } from '@/types/author'; -import { UserCommentResponse } from '@/types/commentResponse'; export const MyPageHandler = [ - rest.get(`${HOME_URL}/api/members/me`, (req, res, ctx) => + rest.get(`${HOME_URL}/api/members/me`, (req, res, ctx) => res( ctx.status(200), ctx.json({ @@ -16,7 +15,7 @@ export const MyPageHandler = [ ), ), - rest.get(`${HOME_URL}/api/members/me/articles`, (req, res, ctx) => + rest.get(`${HOME_URL}/api/members/me/articles`, (req, res, ctx) => res( ctx.status(200), ctx.json({ @@ -56,7 +55,7 @@ export const MyPageHandler = [ ), ), - rest.get(`${HOME_URL}/api/members/me/comments`, (req, res, ctx) => + rest.get(`${HOME_URL}/api/members/me/comments`, (req, res, ctx) => res( ctx.status(200), ctx.json({ diff --git a/frontend/src/mock/search.ts b/frontend/src/mock/search.ts index c58fdc406..9615c54ec 100644 --- a/frontend/src/mock/search.ts +++ b/frontend/src/mock/search.ts @@ -1,11 +1,11 @@ import { rest } from 'msw'; +import { ArticleTotalType } from '@/api/article/articleType'; +import { ArticleSearchResponseType } from '@/api/search/searchType'; import { HOME_URL } from '@/constants/apiUrl'; -import { CommonArticleType } from '@/types/articleResponse'; -import { SearchResultType } from '@/types/searchResponse'; export const SearchHandler = [ - rest.get(`${HOME_URL}/api/articles/search/author`, (req, res, ctx) => { + rest.get(`${HOME_URL}/api/articles/search/author`, (req, res, ctx) => { const cursorId = req.url.searchParams.get('cursorId'); const pageSize = req.url.searchParams.get('size'); const author = req.url.searchParams.get('author'); @@ -62,7 +62,7 @@ export const SearchHandler = [ }), ); }), - rest.get(`${HOME_URL}/api/articles/search/text`, (req, res, ctx) => { + rest.get(`${HOME_URL}/api/articles/search/text`, (req, res, ctx) => { const cursorId = req.url.searchParams.get('cursorId'); const pageSize = req.url.searchParams.get('size'); const searchText = req.url.searchParams.get('text'); @@ -119,7 +119,7 @@ export const SearchHandler = [ ); }), - rest.get<{ articles: CommonArticleType[] }>( + rest.get<{ articles: Omit[] }>( `${HOME_URL}/api/articles/search/tags`, (req, res, ctx) => res( diff --git a/frontend/src/mock/vote.ts b/frontend/src/mock/vote.ts index bca4fa3bd..9c5ab3086 100644 --- a/frontend/src/mock/vote.ts +++ b/frontend/src/mock/vote.ts @@ -1,12 +1,12 @@ import { rest } from 'msw'; -import { TVote } from '@/api/vote'; +import { VoteResponseType } from '@/api/vote/voteType'; import { HOME_URL } from '@/constants/apiUrl'; export const VoteHandler = [ rest.post<{ items: string[] }>(`${HOME_URL}/api/articles/:articleId/votes`, (req, res, ctx) => { const data = localStorage.getItem('mock-votes'); - const mockVotes = data ? (JSON.parse(data) as TVote[]) : []; + const mockVotes = data ? (JSON.parse(data) as VoteResponseType[]) : []; const { articleId } = req.params; const { items } = req.body; @@ -42,7 +42,7 @@ export const VoteHandler = [ rest.get(`${HOME_URL}/api/articles/:articleId/votes`, (req, res, ctx) => { const { articleId } = req.params; const data = localStorage.getItem('mock-votes'); - const mockVotes = data ? (JSON.parse(data) as TVote[]) : []; + const mockVotes = data ? (JSON.parse(data) as VoteResponseType[]) : []; const vote = mockVotes.find((mockVote) => mockVote.articleId === articleId); if (typeof vote === 'undefined') { @@ -56,7 +56,7 @@ export const VoteHandler = [ `${HOME_URL}/api/articles/:articleId/votes/do`, (req, res, ctx) => { const data = localStorage.getItem('mock-votes'); - const mockVotes = data ? (JSON.parse(data) as TVote[]) : []; + const mockVotes = data ? (JSON.parse(data) as VoteResponseType[]) : []; const { articleId } = req.params; const { voteItemId } = req.body; diff --git a/frontend/src/pages/CategoryArticles/CategoryArticles.tsx b/frontend/src/pages/CategoryArticles/CategoryArticles.tsx index 7be3d10f4..64909e3c5 100644 --- a/frontend/src/pages/CategoryArticles/CategoryArticles.tsx +++ b/frontend/src/pages/CategoryArticles/CategoryArticles.tsx @@ -1,5 +1,6 @@ import { useNavigate, useParams } from 'react-router-dom'; +import { ArticleTotalType } from '@/api/article/articleType'; import EmptyMessage from '@/components/@common/EmptyMessage/EmptyMessage'; import Loading from '@/components/@common/Loading/Loading'; import ResponsiveInfiniteCardList from '@/components/@common/ResponsiveInfiniteCardList/ResponsiveInfiniteCardList'; @@ -8,7 +9,6 @@ import ArticleItem from '@/components/article/ArticleItem/ArticleItem'; import { URL } from '@/constants/url'; import useGetCategoryArticles from '@/hooks/article/useGetCategoryArticles'; import * as S from '@/pages/CategoryArticles/CategoryArticles.styles'; -import { CommonArticleType } from '@/types/articleResponse'; import { categoryNameConverter } from '@/utils/converter'; const CategoryArticles = () => { @@ -27,7 +27,9 @@ const CategoryArticles = () => { const { data, fetchNextPage, sortIndex, setSortIndex, isLoading } = useGetCategoryArticles(category); - const handleClickArticleItem = (item: CommonArticleType) => { + const handleClickArticleItem = ( + item: Omit, + ) => { navigate(`/articles/${category}/${item.id}`); }; diff --git a/frontend/src/pages/Detail/index.tsx b/frontend/src/pages/Detail/index.tsx index 540076551..056d3eee4 100644 --- a/frontend/src/pages/Detail/index.tsx +++ b/frontend/src/pages/Detail/index.tsx @@ -1,23 +1,20 @@ import { PropsWithOptionalChildren } from 'gongseek-types'; +import { ArticleTotalType } from '@/api/article/articleType'; import ArticleContent from '@/components/article/ArticleContent/ArticleContent'; import CommentContent from '@/components/comment/CommentContent/CommentContent'; import useScrollToTop from '@/hooks/common/useScrollToTop'; import * as S from '@/pages/Detail/index.styles'; -import { ArticleType } from '@/types/articleResponse'; -import { CommentType } from '@/types/commentResponse'; export interface DetailProps { - article: ArticleType; - commentList: CommentType[]; + article: Omit; articleId: string; category: string; } const Detail = ({ - children, article, - commentList, + children, articleId, category, }: PropsWithOptionalChildren) => { @@ -25,14 +22,9 @@ const Detail = ({ return ( - + {children} - + ); }; diff --git a/frontend/src/pages/DiscussionDetail/index.tsx b/frontend/src/pages/DiscussionDetail/index.tsx index fd6859fd6..22db7ddba 100644 --- a/frontend/src/pages/DiscussionDetail/index.tsx +++ b/frontend/src/pages/DiscussionDetail/index.tsx @@ -1,50 +1,39 @@ +import { Suspense } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import Loading from '@/components/@common/Loading/Loading'; import Vote from '@/components/vote/Vote/Vote'; import VoteGenerateButton from '@/components/vote/VoteGenerateButton/VoteGenerateButton'; import useGetDetailArticle from '@/hooks/article/useGetDetailArticle'; -import useGetDetailComment from '@/hooks/comment/useGetDetailComment'; import Detail from '@/pages/Detail'; const DiscussionDetail = () => { const { id } = useParams(); const navigate = useNavigate(); + const { data } = useGetDetailArticle(id); if (typeof id === 'undefined') { throw new Error('id 값을 받아오지 못했습니다'); } - const { data: articleData, isLoading: isArticleLoading } = useGetDetailArticle(id); - const { data: commentData, isLoading: isCommentLoading } = useGetDetailComment(id); - const handleClickVoteGenerateButton = () => { navigate(`/votes/${id}`); }; - if (isCommentLoading || isArticleLoading) { - return ; - } - return ( - <> - {typeof articleData !== 'undefined' && typeof commentData !== 'undefined' && ( - - {articleData.hasVote ? ( + }> + {data && ( + + {data.hasVote ? ( - ) : articleData.isAuthor ? ( + ) : data.isAuthor ? ( 투표 만들기 ) : null} )} - + ); }; diff --git a/frontend/src/pages/Home/index.tsx b/frontend/src/pages/Home/index.tsx index bb9e1329d..9aa40f770 100644 --- a/frontend/src/pages/Home/index.tsx +++ b/frontend/src/pages/Home/index.tsx @@ -1,13 +1,13 @@ import React, { Suspense } from 'react'; import { useNavigate } from 'react-router-dom'; +import { ArticleTotalType } from '@/api/article/articleType'; import EmptyMessage from '@/components/@common/EmptyMessage/EmptyMessage'; import Loading from '@/components/@common/Loading/Loading'; import SortDropdown from '@/components/@common/SortDropdown/SortDropDown'; import useGetAllArticles from '@/hooks/article/useGetAllArticles'; import useEnterToClick from '@/hooks/common/useEnterToClick'; import * as S from '@/pages/Home/index.styles'; -import { CommonArticleType } from '@/types/articleResponse'; const ResponsiveInfiniteCardList = React.lazy( () => import('@/components/@common/ResponsiveInfiniteCardList/ResponsiveInfiniteCardList'), @@ -29,7 +29,9 @@ const Home = () => { setCurrentCategory(category); }; - const handleLinkToArticleDetail = (item: CommonArticleType) => { + const handleLinkToArticleDetail = ( + item: Omit, + ) => { navigate(`/articles/${currentCategory}/${item.id}`); }; diff --git a/frontend/src/pages/Login/index.tsx b/frontend/src/pages/Login/index.tsx index 0e9f6584f..012929c50 100644 --- a/frontend/src/pages/Login/index.tsx +++ b/frontend/src/pages/Login/index.tsx @@ -1,3 +1,5 @@ +import { Suspense } from 'react'; + import Card from '@/components/@common/Card/Card'; import Loading from '@/components/@common/Loading/Loading'; import LoginButton from '@/components/login/LoginButton/LoginButton'; @@ -7,19 +9,19 @@ import * as S from '@/pages/Login/index.styles'; import { LoginCardStyle } from '@/styles/cardStyle'; const Login = () => { - const { isLoading, handleLoginButtonClick } = useGetLoginURL(); - - if (isLoading) return ; + const { handleLoginButtonClick } = useGetLoginURL(); return ( - - -

로그인

- - github로 로그인하기 - -
-
+ }> + + +

로그인

+ + github로 로그인하기 + +
+
+
); }; diff --git a/frontend/src/pages/MyPage/index.tsx b/frontend/src/pages/MyPage/index.tsx index 8ab7876f9..55acfa05d 100644 --- a/frontend/src/pages/MyPage/index.tsx +++ b/frontend/src/pages/MyPage/index.tsx @@ -14,33 +14,30 @@ const TemporaryArticleList = React.lazy( ); const MyPage = () => { - const { data: info, isSuccess: isInfoSuccess, isLoading: isInfoLoading } = useGetUserInfo(); + const { data: info, isSuccess: isInfoSuccess } = useGetUserInfo(); const [category, setCategory] = useState('article'); - - if (isInfoLoading) { - return ; - } - return ( - - 마이 페이지 - {isInfoSuccess && info ? ( - <> - - - }> - - {category === 'article' && } - {category === 'comment' && } - {category === 'tempArticle' && } - - - - ) : ( -
정보를 가져오는데 실패하였습니다
- )} -
+ }> + + 마이 페이지 + {isInfoSuccess && info ? ( + <> + + + }> + + {category === 'article' && } + {category === 'comment' && } + {category === 'tempArticle' && } + + + + ) : ( +
정보를 가져오는데 실패하였습니다
+ )} +
+
); }; diff --git a/frontend/src/pages/QuestionDetail/index.tsx b/frontend/src/pages/QuestionDetail/index.tsx index 6d10ee50c..ce70ae1ea 100644 --- a/frontend/src/pages/QuestionDetail/index.tsx +++ b/frontend/src/pages/QuestionDetail/index.tsx @@ -1,7 +1,8 @@ +import { Suspense } from 'react'; import { useParams } from 'react-router-dom'; +import Loading from '@/components/@common/Loading/Loading'; import useGetDetailArticle from '@/hooks/article/useGetDetailArticle'; -import useGetDetailComment from '@/hooks/comment/useGetDetailComment'; import Detail from '@/pages/Detail/index'; const QuestionDetail = () => { @@ -11,24 +12,12 @@ const QuestionDetail = () => { throw new Error('id 값을 받아오지 못했습니다'); } - const { data: articleData, isLoading: isArticleLoading } = useGetDetailArticle(id); - const { data: commentData, isLoading: isCommentLoading } = useGetDetailComment(id); - - if (isCommentLoading || isArticleLoading) { - return
로딩중...
; - } + const { data } = useGetDetailArticle(id); return ( -
- {typeof articleData !== 'undefined' && typeof commentData !== 'undefined' && ( - - )} -
+ }> + {data && } + ); }; diff --git a/frontend/src/pages/UpdateWriting/index.tsx b/frontend/src/pages/UpdateWriting/index.tsx index 7cf58e29a..f00aa554d 100644 --- a/frontend/src/pages/UpdateWriting/index.tsx +++ b/frontend/src/pages/UpdateWriting/index.tsx @@ -1,8 +1,6 @@ -import { useEffect } from 'react'; import { useParams } from 'react-router-dom'; import Card from '@/components/@common/Card/Card'; -import Loading from '@/components/@common/Loading/Loading'; import ToastUiEditor from '@/components/@common/ToastUiEditor/ToastUiEditor'; import HashTag from '@/components/hashTag/HashTag/HashTag'; import usePostWritingArticle from '@/hooks/article/usePostUpdateWritingArticle'; @@ -15,7 +13,6 @@ const UpdateWriting = () => { const { category } = useParams(); const { - isLoading, title, setTitle, tempArticle, @@ -33,10 +30,6 @@ const UpdateWriting = () => { throw new Error('id와 category 값을 가지고 오지 못하였습니다'); } - if (isLoading) { - return ; - } - return ( diff --git a/frontend/src/pages/VoteDeadlineGenerator/index.tsx b/frontend/src/pages/VoteDeadlineGenerator/index.tsx index f705a2782..6800f8a13 100644 --- a/frontend/src/pages/VoteDeadlineGenerator/index.tsx +++ b/frontend/src/pages/VoteDeadlineGenerator/index.tsx @@ -3,8 +3,7 @@ import React, { useEffect, useState } from 'react'; import { useMutation } from 'react-query'; import { useNavigate } from 'react-router-dom'; -import { registerVoteItems } from '@/api/vote'; -import Loading from '@/components/@common/Loading/Loading'; +import { registerVoteItems } from '@/api/vote/vote'; import { ErrorMessage } from '@/constants/ErrorMessage'; import useLocationState from '@/hooks/common/useLocationState'; import useThrowCustomError from '@/hooks/common/useThrowCustomError'; @@ -14,7 +13,7 @@ import { afterWeekGenerator, currentTimeGenerator, todayGenerator } from '@/util const VoteDeadlineGenerator = () => { const { articleId, items } = useLocationState<{ articleId: string; items: string[] }>(); const navigate = useNavigate(); - const { isLoading, mutate, isError, error, isSuccess } = useMutation< + const { mutate, isError, error, isSuccess } = useMutation< AxiosResponse<{ articleId: string }>, AxiosError<{ errorCode: keyof typeof ErrorMessage; message: string }>, { articleId: string; items: string[]; expiryDate: string } @@ -67,8 +66,6 @@ const VoteDeadlineGenerator = () => { setIsExpireDate(false); }, [deadlineDate]); - if (isLoading) return ; - return ( 마감 기한을 설정해주세요. diff --git a/frontend/src/storybook/Article/ArticleContent.stories.tsx b/frontend/src/storybook/Article/ArticleContent.stories.tsx index 9a2299ed0..80322ded1 100644 --- a/frontend/src/storybook/Article/ArticleContent.stories.tsx +++ b/frontend/src/storybook/Article/ArticleContent.stories.tsx @@ -42,11 +42,5 @@ DefaultComment.args = { name: '자스민', avatarUrl: 'http://openimage.interpark.com/goods_image_big/0/3/2/7/8317700327e_l.jpg', }, - category: 'discussion', - commentCount: 2, - }, - author: { - name: '자스민', - avatarUrl: 'http://openimage.interpark.com/goods_image_big/0/3/2/7/8317700327e_l.jpg', }, }; diff --git a/frontend/src/storybook/Article/TemporaryArticleItem.stories.tsx b/frontend/src/storybook/Article/TemporaryArticleItem.stories.tsx index a1fe38aa6..dd7f19966 100644 --- a/frontend/src/storybook/Article/TemporaryArticleItem.stories.tsx +++ b/frontend/src/storybook/Article/TemporaryArticleItem.stories.tsx @@ -24,7 +24,7 @@ Default.args = { article: { title: '글 상세페이지에서의 글 제목이 들어가는 곳, 글 제목이 2줄 이상이 넘어갔을때 어떻게 처리할 것인지 처리하기 위한 예시 문장입니다', - createAt: '2022-08-11T13:34:11', + createdAt: '2022-08-11T13:34:11', category: 'question', }, onClick: () => { diff --git a/frontend/src/storybook/User/UserArticleItem.stories.tsx b/frontend/src/storybook/User/UserArticleItem.stories.tsx index 14bce128a..3e677fbd2 100644 --- a/frontend/src/storybook/User/UserArticleItem.stories.tsx +++ b/frontend/src/storybook/User/UserArticleItem.stories.tsx @@ -1,7 +1,7 @@ import { BrowserRouter } from 'react-router-dom'; +import { SingleMyPageUserArticleResponseType } from '@/api/article/articleType'; import UserArticleItem from '@/components/user/UserArticleItem/UserArticleItem'; -import { UserArticleItemType } from '@/types/articleResponse'; import { Meta, Story } from '@storybook/react'; export default { @@ -18,7 +18,9 @@ export default { ], } as Meta; -const Template: Story<{ article: UserArticleItemType }> = (args) => ; +const Template: Story<{ article: SingleMyPageUserArticleResponseType }> = (args) => ( + +); export const DiscussionUserItemBox = Template.bind({}); DiscussionUserItemBox.args = { diff --git a/frontend/src/storybook/User/UserCommentItem.stories.tsx b/frontend/src/storybook/User/UserCommentItem.stories.tsx index 81a6919b1..777a0fc6a 100644 --- a/frontend/src/storybook/User/UserCommentItem.stories.tsx +++ b/frontend/src/storybook/User/UserCommentItem.stories.tsx @@ -1,7 +1,7 @@ import { BrowserRouter } from 'react-router-dom'; +import { MyPageCommentItemResponse } from '@/api/comment/commentType'; import UserCommentItem from '@/components/user/UserCommentItem/UserCommentItem'; -import { UserComment } from '@/types/commentResponse'; import { Meta, Story } from '@storybook/react'; export default { @@ -18,7 +18,9 @@ export default { ], } as Meta; -const Template: Story<{ comment: UserComment }> = (args) => ; +const Template: Story<{ comment: MyPageCommentItemResponse }> = (args) => ( + +); export const UserCommentItemTemplate = Template.bind({}); diff --git a/frontend/src/storybook/Vote/Vote.stories.tsx b/frontend/src/storybook/Vote/Vote.stories.tsx index 1bd72a0e0..4786f4570 100644 --- a/frontend/src/storybook/Vote/Vote.stories.tsx +++ b/frontend/src/storybook/Vote/Vote.stories.tsx @@ -1,4 +1,4 @@ -import { registerVoteItems } from '@/api/vote'; +import { registerVoteItems } from '@/api/vote/vote'; import Vote from '@/components/vote/Vote/Vote'; import { VoteHandler } from '@/mock/vote'; import { Meta, Story } from '@storybook/react'; diff --git a/frontend/src/types/articleResponse.ts b/frontend/src/types/articleResponse.ts deleted file mode 100644 index a1a126d3e..000000000 --- a/frontend/src/types/articleResponse.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Author } from '@/types/author'; - -export type Category = 'question' | 'discussion'; - -export interface CommonArticleType { - id: number; - title: string; - author: Author; - content: string; - category: Category; - commentCount: number; - createdAt: string; - views: number; - tag: string[]; - isLike: boolean; - likeCount: number; - hasVote: boolean; -} - -export interface ArticleType extends CommonArticleType { - isAuthor: boolean; -} - -export interface AllArticleResponse { - articles: CommonArticleType[]; - hasNext: boolean; -} - -export interface infiniteArticleResponse extends AllArticleResponse { - cursorId: string; - cursorViews?: string; - cursorLikes?: string; -} - -export interface UserArticleItemType { - id: number; - title: string; - category: string; - commentCount: number; - createdAt: string; - updatedAt: string; - views: number; -} - -export interface UserArticlesResponse { - articles: UserArticleItemType[]; -} - -export interface TempArticleItem { - id: number; - title: string; - createAt: string; - category: 'discussion' | 'question'; -} - -export interface TempArticleResponse { - values: TempArticleItem[]; -} - -export interface TempArticleDetailResponse { - id: number; - title: string; - content: string; - tag: string[]; - category: 'question' | 'discussion'; - isAnonymous: boolean; - createdAt: string; -} diff --git a/frontend/src/types/author.ts b/frontend/src/types/author.ts deleted file mode 100644 index 57a6197cb..000000000 --- a/frontend/src/types/author.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface Author { - name: string; - avatarUrl: string; -} diff --git a/frontend/src/types/commentResponse.ts b/frontend/src/types/commentResponse.ts deleted file mode 100644 index 8ccb48b87..000000000 --- a/frontend/src/types/commentResponse.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Author } from '@/types/author'; - -export interface CommentType { - id: number; - author: Author; - content: string; - createdAt: string; - isAuthor: boolean; -} - -export interface UserComment { - id: number; - content: string; - createdAt: string; - updatedAt: string; - articleId: number; - articleTitle: string; - category: string; -} - -export interface UserCommentResponse { - comments: UserComment[]; -} diff --git a/frontend/src/types/searchResponse.ts b/frontend/src/types/searchResponse.ts deleted file mode 100644 index bbfdf6716..000000000 --- a/frontend/src/types/searchResponse.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { CommonArticleType } from '@/types/articleResponse'; - -export interface SearchResultType { - articles: CommonArticleType[]; - hasNext: boolean; -} - -export interface InfiniteSearchResultType extends SearchResultType { - cursorId: string; - target: string; - searchIndex: string; -} - -export interface InfiniteHashTagSearchResultType extends SearchResultType { - cursorId: string; - hashTags: string; -} diff --git a/frontend/src/utils/converter.ts b/frontend/src/utils/converter.ts index cc9ae7510..59fa8e867 100644 --- a/frontend/src/utils/converter.ts +++ b/frontend/src/utils/converter.ts @@ -77,7 +77,7 @@ export const convertGithubAvatarUrlForResize = (avatarUrl: string) => { return avatarUrl; } - if (typeof global.process === 'undefined') { + if (process.env.STORYBOOK) { return avatarUrl; } diff --git a/frontend/src/api/hashTag.ts b/frontend/src/utils/generateAxiosInstance.ts similarity index 67% rename from frontend/src/api/hashTag.ts rename to frontend/src/utils/generateAxiosInstance.ts index 633d28f0f..95dc5a4ed 100644 --- a/frontend/src/api/hashTag.ts +++ b/frontend/src/utils/generateAxiosInstance.ts @@ -3,13 +3,13 @@ import axios from 'axios'; import { ACCESSTOKEN_KEY } from '@/constants'; import { HOME_URL } from '@/constants/apiUrl'; -export const getAllHashTag = async () => { +export const generateAxiosInstanceWithAccessToken = () => { const accessToken = localStorage.getItem(ACCESSTOKEN_KEY); - const data = await axios.get<{ tag: string[] }>(`${HOME_URL}/api/tags`, { + return axios.create({ + baseURL: HOME_URL, headers: { 'Access-Control-Allow-Origin': '*', Authorization: `Bearer ${accessToken}`, }, }); - return data.data; }; diff --git a/frontend/src/utils/takeToastImgEditor.ts b/frontend/src/utils/takeToastImgEditor.ts new file mode 100644 index 000000000..b934ccec0 --- /dev/null +++ b/frontend/src/utils/takeToastImgEditor.ts @@ -0,0 +1,18 @@ +import { MutableRefObject } from 'react'; + +import { postImageUrlConverter } from '@/api/article/image'; +import { Editor } from '@toast-ui/react-editor'; + +export const takeToastImgEditor = (content: MutableRefObject) => { + if (content.current) { + content.current.getInstance().removeHook('addImageBlobHook'); + content.current.getInstance().addHook('addImageBlobHook', (blob, callback) => { + (async () => { + const formData = new FormData(); + formData.append('imageFile', blob); + const url = await postImageUrlConverter(formData); + callback(url, 'alt-text'); + })(); + }); + } +};