Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

apiClient 변경 및 기존 customFetch 함수 제거 #929

Merged
merged 18 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
e5af0d5
refactor(ApiClient): apiClient 인스턴스를 config 파일에서 선언
Jaymyong66 Nov 21, 2024
61b5bd3
refactor(categories): 카테고리 get, post, put - apiClient로 변경
Jaymyong66 Nov 21, 2024
8192cc6
refactor(api): 카테고리 delete - apiClient로 변경
Jaymyong66 Nov 21, 2024
bd20e68
refactor(mocks): handlers - 카테고리 url을 config의 API_URL과 END_POINTS를 활용
Jaymyong66 Nov 21, 2024
182341a
feat(mocks): handlers - member name GET에 대한 핸들러 추가
Jaymyong66 Nov 21, 2024
433ae91
refactor(api): members - apiClient로 변경
Jaymyong66 Nov 21, 2024
a89d255
refactor(mocks): handlers - MEMBER_API_URL 의존성 제거
Jaymyong66 Nov 21, 2024
2cb566d
refactor(api): tags - apiClient로 변경, MSW handler - tags의 API_URL 의존성 변경
Jaymyong66 Nov 21, 2024
8b75c4d
refactor(api): 좋아요 POST, DELETE - apiClient로 변경, handler의 API_URL 의존성 변경
Jaymyong66 Nov 21, 2024
ceb82d1
refactor(api): templates - get, post, edit, delete 요청을 apiClient로 변경
Jaymyong66 Nov 21, 2024
c718c2a
refactor(mocks): TEMPLATE_API_URL 요청 경로 의존성 제거
Jaymyong66 Nov 21, 2024
2e18300
refactor(api): authentication - 회원가입, 로그인 요청을 apiClient로 변경
Jaymyong66 Nov 21, 2024
1518d28
refactor(api): 로그아웃 요청을 apiClient로 변경
Jaymyong66 Nov 21, 2024
5efa301
feat(Error): errorCode, instance를 통해 getErrorMessage 함수 적용
Jaymyong66 Nov 21, 2024
e20477b
refactor(api): authentication - checkName 함수를 apiClient로 변경
Jaymyong66 Nov 21, 2024
7caab2a
refactor(mocks): handlers - authentication 관련 엔드 포인트의 API_URL 의존성 변경
Jaymyong66 Nov 21, 2024
cf71479
refactor(api): customFetch 제거, 사용하지 않는 URL 변수 제거
Jaymyong66 Nov 21, 2024
f748d07
refactor(api): API_URL을 index에서 export
Jaymyong66 Nov 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 4 additions & 14 deletions frontend/src/api/ApiClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ApiError } from './Error/ApiError';
import { getErrorMessage } from './Error/getErrorMessage';
import { HTTP_STATUS } from './Error/statusCode';
Comment on lines 1 to 3
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

절대 경로 다 고쳤는데,,,, 또르륵 ㅜ


type HttpMethod =
Expand Down Expand Up @@ -27,7 +28,7 @@ interface RequestParams {
[key: string]: string | number | boolean;
}

class ApiClient {
export class ApiClient {
baseUrl: string;
headers: HeadersInit;
credentials: RequestCredentials;
Expand Down Expand Up @@ -84,24 +85,13 @@ class ApiClient {
}

private async handleError(response: Response) {
const errorBody = await response.json();
const { errorCode, instance, detail } = await response.json();

if (response.status === HTTP_STATUS.UNAUTHORIZED) {
localStorage.removeItem('name');
localStorage.removeItem('memberId');
}

// TODO: 에러코드에 따른 메시지 반환 함수 만들기
throw new ApiError('에러메시지입니다.', response.status, errorBody.errorCode, errorBody.detail);
throw new ApiError(getErrorMessage(errorCode, instance), response.status, errorCode, detail);
}
}

const API_URL = process.env.REACT_APP_API_URL || 'https://default-url.com';

const httpHeader = {
'Content-Type': 'application/json',
};

const httpCredentials = 'include';

export const apiClient = new ApiClient(API_URL, httpHeader, httpCredentials);
10 changes: 10 additions & 0 deletions frontend/src/api/Error/getErrorMessage.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

에러 반환 함수 만들어주셨군요👍
다음에 에러 코드 상수로 분리하면서, 에러 메세지에 대해서도 같이 논의 + 상수 분리 하면 좋을 것 같네요!!👍

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { END_POINTS } from '@/routes';

// TODO: errorCode magic number
export const getErrorMessage = (errorCode: number, instance: string) => {
if (errorCode === 1202 && instance === END_POINTS.CHECK_NAME) {
return '중복된 아이디입니다';
}

return 'APIError 입니다';
};
64 changes: 7 additions & 57 deletions frontend/src/api/authentication.ts
Original file line number Diff line number Diff line change
@@ -1,72 +1,22 @@
import { END_POINTS } from '@/routes';
import type { LoginRequest, SignupRequest } from '@/types';
import { MemberInfo } from '@/types';

import { apiClient } from './ApiClient';
import { customFetch } from './customFetch';
import { apiClient } from './config';

const API_URL = process.env.REACT_APP_API_URL || 'https://default-url.com';
export const postSignup = async (signupInfo: SignupRequest) => await apiClient.post(END_POINTS.SIGNUP, signupInfo);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코드가 확연히 줄었네요 매우 좋습니다~! 👍👍👍


export const SIGNUP_API_URL = `${API_URL}${END_POINTS.SIGNUP}`;
export const LOGIN_API_URL = `${API_URL}${END_POINTS.LOGIN}`;
export const LOGOUT_API_URL = `${API_URL}${END_POINTS.LOGOUT}`;
export const LOGIN_STATE_API_URL = `${API_URL}${END_POINTS.LOGIN_CHECK}`;
export const CHECK_NAME_API_URL = `${API_URL}${END_POINTS.CHECK_NAME}`;
export const postLogin = async (loginInfo: LoginRequest) => {
const response = await apiClient.post(END_POINTS.LOGIN, loginInfo);

export const postSignup = async (signupInfo: SignupRequest) =>
await customFetch({
method: 'POST',
url: `${SIGNUP_API_URL}`,
body: JSON.stringify(signupInfo),
});

export const postLogin = async (loginInfo: LoginRequest): Promise<MemberInfo> => {
const response = await customFetch<MemberInfo>({
method: 'POST',
url: `${LOGIN_API_URL}`,
body: JSON.stringify(loginInfo),
});

if ('memberId' in response) {
return response;
}

if (response.status >= 400) {
return { memberId: undefined, name: undefined };
}

throw new Error(response.detail);
return await response.json();
};

export const postLogout = async () => {
const response = await customFetch<unknown>({
method: 'POST',
url: `${LOGOUT_API_URL}`,
});

return response;
};
export const postLogout = async () => await apiClient.post(END_POINTS.LOGOUT, {});

export const getLoginState = async () => apiClient.get(END_POINTS.LOGIN_CHECK);

Comment on lines 16 to 17
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 파일에 get 요청에는 바로 apliClient 를 리턴하고 다른 파일에는 json 으로 리턴하네요? 차이가 있는 걸까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 요청에 대한 응답이 없고, 사용하지 않기 때문입니다. 혹시 일관성을 위해 구조를 통일해야할까요?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

흠,,, 이건 개발자 취향일거 같긴하지만,,, 전 일관성과 통일감을 위해 통일하는 편이긴합니다...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 평소 통일성을 위해 일관된 반환값을 선호하는 편이지만, 지금 이 경우에는 애초에 서버에서 오는 반환값이 없기 때문에 json()이 아예 불가능한 상황이라고 생각하였습니다. 그래서 지금 이대로도 괜찮을 것 같아요..!

export const checkName = async (name: string) => {
const params = new URLSearchParams({ name });
const url = `${CHECK_NAME_API_URL}?${params}`;

const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
});

if (response.status === 409) {
throw new Error('중복된 아이디입니다.');
}

if (!response.ok) {
throw new Error('서버 에러가 발생했습니다.');
}

return {};
await apiClient.get(`${END_POINTS.CHECK_NAME}?${params.toString()}`);
};
61 changes: 14 additions & 47 deletions frontend/src/api/categories.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,25 @@
import { HttpResponse } from 'msw';

import { END_POINTS } from '@/routes';
import type {
CategoryListResponse,
CategoryUploadRequest,
CategoryEditRequest,
CategoryDeleteRequest,
CustomError,
Category,
} from '@/types';

import { customFetch } from './customFetch';
import type { CategoryUploadRequest, CategoryEditRequest, CategoryDeleteRequest } from '@/types';

const API_URL = process.env.REACT_APP_API_URL || 'https://default-url.com';

export const CATEGORY_API_URL = `${API_URL}${END_POINTS.CATEGORIES}`;
import { apiClient } from './config';

export const getCategoryList = async (memberId: number) => {
const url = `${CATEGORY_API_URL}?memberId=${memberId}`;

const response = await customFetch<CategoryListResponse>({
url,
const queryParams = new URLSearchParams({
memberId: memberId.toString(),
});
const response = await apiClient.get(`${END_POINTS.CATEGORIES}?${queryParams.toString()}`);

if ('categories' in response) {
return response;
}

throw new Error(response.detail);
return await response.json();
};

export const postCategory = async (newCategory: CategoryUploadRequest): Promise<Category | CustomError> =>
await customFetch({
method: 'POST',
url: `${CATEGORY_API_URL}`,
body: JSON.stringify(newCategory),
});
export const postCategory = async (newCategory: CategoryUploadRequest) => {
const response = await apiClient.post(`${END_POINTS.CATEGORIES}`, newCategory);

export const editCategory = async ({ id, name }: CategoryEditRequest) =>
await customFetch({
method: 'PUT',
url: `${CATEGORY_API_URL}/${id}`,
body: JSON.stringify({ name }),
});

export const deleteCategory = async ({ id }: CategoryDeleteRequest) => {
const response = await customFetch<HttpResponse>({
method: 'DELETE',
url: `${CATEGORY_API_URL}/${id}`,
});
return await response.json();
};

if (typeof response === 'object' && response !== null && 'status' in response) {
throw response as CustomError;
}
export const editCategory = async ({ id, name }: CategoryEditRequest) =>
await apiClient.put(`${END_POINTS.CATEGORIES}/${id}`, { name });

return response;
};
export const deleteCategory = async ({ id }: CategoryDeleteRequest) =>
await apiClient.delete(`${END_POINTS.CATEGORIES}/${id}`);
10 changes: 10 additions & 0 deletions frontend/src/api/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ApiClient } from './ApiClient';

export const API_URL = process.env.REACT_APP_API_URL ?? 'https://default-url.com';

const httpHeader = {
'Content-Type': 'application/json',
};
const httpCredentials = 'include';

export const apiClient = new ApiClient(API_URL, httpHeader, httpCredentials);
63 changes: 0 additions & 63 deletions frontend/src/api/customFetch.ts

This file was deleted.

23 changes: 6 additions & 17 deletions frontend/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
export { CATEGORY_API_URL, getCategoryList, postCategory, deleteCategory } from './categories';
export { TAG_API_URL, getTagList } from './tags';
export { customFetch } from './customFetch';
export { API_URL } from './config';
export { QUERY_KEY } from './queryKeys';

export {
TEMPLATE_API_URL,
PAGE_SIZE,
SORTING_OPTIONS,
DEFAULT_SORTING_OPTION,
Expand All @@ -14,17 +12,8 @@ export {
editTemplate,
deleteTemplate,
} from './templates';
export {
CHECK_NAME_API_URL,
LOGIN_API_URL,
LOGIN_STATE_API_URL,
SIGNUP_API_URL,
LOGOUT_API_URL,
postSignup,
postLogin,
postLogout,
getLoginState,
checkName,
} from './authentication';
export { LIKE_API_URL, postLike, deleteLike } from './like';
export { postSignup, postLogin, postLogout, getLoginState, checkName } from './authentication';
export { getCategoryList, postCategory, deleteCategory } from './categories';
export { getTagList } from './tags';
export { postLike, deleteLike } from './like';
export { getMemberName } from './members';
27 changes: 3 additions & 24 deletions frontend/src/api/like.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,7 @@
import { HttpResponse } from 'msw';

import { END_POINTS } from '@/routes';
import { LikeDeleteRequest, LikePostRequest } from '@/types';

import { customFetch } from './customFetch';

const API_URL = process.env.REACT_APP_API_URL || 'https://default-url.com';

export const LIKE_API_URL = `${API_URL}${END_POINTS.LIKES}`;

export const postLike = async ({ templateId }: LikePostRequest) => {
const response = await customFetch<HttpResponse>({
method: 'POST',
url: `${LIKE_API_URL}/${templateId}`,
});

return response;
};
import { apiClient } from './config';

export const deleteLike = async ({ templateId }: LikeDeleteRequest) => {
const response = await customFetch<HttpResponse>({
method: 'DELETE',
url: `${LIKE_API_URL}/${templateId}`,
});
export const postLike = async (templateId: number) => await apiClient.post(`${END_POINTS.LIKES}/${templateId}`, {});
Comment on lines -12 to +5
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 부분에서 templateId가 객체로 전달되고 있었는데, 굳이 객체일 필요가 없어서 변경했습니다.


return response;
};
export const deleteLike = async (templateId: number) => await apiClient.delete(`${END_POINTS.LIKES}/${templateId}`);
19 changes: 3 additions & 16 deletions frontend/src/api/members.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,9 @@
import { END_POINTS } from '@/routes';
import { GetMemberNameResponse } from '@/types';

import { customFetch } from './customFetch';

const API_URL = process.env.REACT_APP_API_URL || 'https://default-url.com';

export const MEMBER_API_URL = `${API_URL}${END_POINTS.MEMBERS}`;
import { apiClient } from './config';

export const getMemberName = async (memberId: number) => {
const url = `${MEMBER_API_URL}/${memberId}/name`;

const response = await customFetch<GetMemberNameResponse>({
url,
});

if ('name' in response) {
return response;
}
const response = await apiClient.get(`${END_POINTS.MEMBERS}/${memberId}/name`);

throw new Error(response.detail);
return await response.json();
};
Loading