Skip to content

Commit

Permalink
feat: APIClient 구현 및 테스팅 기능 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
Parkhanyoung committed Jul 22, 2024
1 parent b9bc378 commit ba15ed6
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 0 deletions.
64 changes: 64 additions & 0 deletions frontend/src/apis/clients/APIClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import buildURL from './buildURL';
import HTTP_METHOD from './httpMethod';

interface APIClientType {
get<T>(path: string, queryParams?: Record<string, string>): Promise<T>;
post<T>(path: string, body?: Record<string, any>): Promise<T>;
patch<T>(path: string, body?: Record<string, any>): Promise<T>;
delete(path: string): Promise<void>;
}

export default class APIClient implements APIClientType {
private baseURL: URL;
private header?: Headers;

constructor(baseURL: string, header?: HeadersInit) {
this.baseURL = new URL(baseURL);
this.header = new Headers(header);
}

async get<T>(path: string, queryParams?: Record<string, string>): Promise<T> {
return this.request<T>({ path, method: HTTP_METHOD.get, queryParams });
}

async post<T>(path: string, body?: object): Promise<T> {
return this.request<T>({ path, method: HTTP_METHOD.post, body });
}

async patch<T>(path: string, body?: object): Promise<T> {
return this.request<T>({ path, method: HTTP_METHOD.patch, body });
}

async delete(path: string): Promise<void> {
return this.request<void>({ path, method: HTTP_METHOD.delete });
}

private async request<T>({
path,
method,
body,
queryParams,
}: {
path: string;
method: string;
body?: Record<string, any>;
queryParams?: Record<string, string>;
}): Promise<T> {
const url = buildURL({ baseURL: this.baseURL, path, queryParams });

const response = await fetch(url, {
method,
headers: this.header,
body: JSON.stringify(body),
});

if (!response.ok) {
throw new Error(response.statusText);
}

const data = await response.json();

return data;
}
}
16 changes: 16 additions & 0 deletions frontend/src/apis/clients/buildURL.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
interface BuildURLParams {
baseURL: URL;
path?: string;
queryParams?: Record<string, string>;
}

const buildURL = ({ baseURL, path, queryParams }: BuildURLParams): URL => {
const url = new URL(baseURL);

url.pathname = path ? path : '';
url.search = queryParams ? new URLSearchParams(queryParams).toString() : '';

return url;
};

export default buildURL;
10 changes: 10 additions & 0 deletions frontend/src/apis/clients/develupClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { BASE_URL } from '../baseUrl';
import APIClient from './APIClient';

const API_URL = process.env.NODE_ENV === 'development' ? BASE_URL.dev : BASE_URL.prod;

const header = {
'Content-type': 'application/json',
};

export const develupAPIClient = new APIClient(API_URL, header);
8 changes: 8 additions & 0 deletions frontend/src/apis/clients/httpMethod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const HTTP_METHOD = {
get: 'GET',
post: 'POST',
patch: 'PATCH',
delete: 'DELETE',
} as const;

export default HTTP_METHOD;
36 changes: 36 additions & 0 deletions frontend/src/apis/mission.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { Mission } from '@/types';
import { develupAPIClient } from './clients/develupClient';
import { PATH } from './paths';

// 이 파일 내의 타입 선언 및 api 요청 로직은 APIClient 사용법을 설명하기 위한 예시 코드입니다. @라이언
interface Submission {
id: number;
memberId: number;
missionId: number;
url: string;
comment: string;
}

interface GetMissionsResponse {
data: Mission[];
}

interface PostSubmissionResponse {
data: Submission;
}

export const getMissions = async (queryParams?: Record<string, string>): Promise<Mission[]> => {
const { data } = await develupAPIClient.get<GetMissionsResponse>(PATH.missionList, queryParams);

return data;
};

export const postSubmission = async (payload: {
missionId: number;
url: string;
comment: string;
}): Promise<Submission> => {
const { data } = await develupAPIClient.post<PostSubmissionResponse>(PATH.submissions, payload);

return data;
};
1 change: 1 addition & 0 deletions frontend/src/apis/paths.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const PATH = {
missionList: '/missions',
submissions: '/submissions',
};
9 changes: 9 additions & 0 deletions frontend/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import MissionSubmitPage from './pages/MissionSubmitPage';
import MyMissionPage from './pages/MyMissionPage';
import UserProfilePage from './pages/UserProfilePage';
import GuidePage from './pages/GuidePage';
import APITestPage from './pages/APITestPage';

const queryClient = new QueryClient();

Expand Down Expand Up @@ -66,6 +67,14 @@ const routes = [
</App>
),
},
{
path: '/api-test',
element: (
<App>
<APITestPage />
</App>
),
},
];

export const router = createBrowserRouter(routes, {
Expand Down
79 changes: 79 additions & 0 deletions frontend/src/pages/APITestPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { getMissions, postSubmission } from '@/apis/mission';

export default function APITestPage() {
const testGetMissions = async () => {
try {
// ✅ getMissions 로직을 확인해주세요
const missions = await getMissions();

alert(`요청 성공! \n응답: ${JSON.stringify(missions)}`);
} catch (err) {
alert('요청 실패. console을 확인하세요.');
console.error(err);
}
};

const testGetMissionsWithQueryParams = async () => {
try {
// ✅ getMissions에 쿼리 파라미터를 넘기는 방식을 확인해주세요.
const missions = await getMissions({ page: '1' });

alert(`요청 성공! \n응답: ${JSON.stringify(missions)}`);
} catch (err) {
alert('요청 실패. console을 확인하세요.');
console.error(err);
}
};

const testPostMissionSubmission = async () => {
try {
// ✅ postSubmission 로직을 확인해주세요
const createdSubmission = await postSubmission({
missionId: 999,
url: 'www.develup.com',
comment: '테스트용 데이터입니다 (언제든 지워도 됨)',
});
alert(`요청 성공! \n응답: ${JSON.stringify(createdSubmission)}`);
} catch (err) {
alert('요청 실패. console을 확인하세요.');
console.error(err);
}
};

return (
<div>
<button
style={{
background: 'lightblue',
fontSize: '5rem',
cursor: 'pointer',
}}
onClick={testGetMissions}
>
get 요청 test 🤖
</button>
<br />
<button
style={{
background: 'lightblue',
fontSize: '5rem',
cursor: 'pointer',
}}
onClick={testGetMissionsWithQueryParams}
>
쿼리파라미터가 담긴 get 요청 test 🤖
</button>
<br />
<button
style={{
background: 'pink',
fontSize: '5rem',
cursor: 'pointer',
}}
onClick={testPostMissionSubmission}
>
post 요청 test 🤖
</button>
</div>
);
}

0 comments on commit ba15ed6

Please sign in to comment.