Skip to content

Commit fdb7f1d

Browse files
committed
fix: hook을 실제 사용 용례에 맞게 수정
1 parent 178a018 commit fdb7f1d

File tree

4 files changed

+227
-80
lines changed

4 files changed

+227
-80
lines changed
Lines changed: 160 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,206 @@
1-
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
2-
import * as R from 'remeda';
3-
4-
import { getCookie } from '@pyconkr-common/utils/cookie';
5-
import ShopAPISchema, { isObjectErrorResponseSchema } from "@pyconkr-shop/schemas";
6-
7-
const DEFAULT_TIMEOUT = 10000
8-
const DEFAULT_ERROR_MESSAGE = '알 수 없는 문제가 발생했습니다, 잠시 후 다시 시도해주세요.'
9-
const DEFAULT_ERROR_RESPONSE = { type: 'unknown', errors: [{ code: 'unknown', detail: DEFAULT_ERROR_MESSAGE, attr: null }] }
1+
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
2+
import * as R from "remeda";
3+
4+
import { getCookie } from "@pyconkr-common/utils/cookie";
5+
import ShopAPISchema, {
6+
isObjectErrorResponseSchema,
7+
} from "@pyconkr-shop/schemas";
8+
9+
const DEFAULT_TIMEOUT = 10000;
10+
const DEFAULT_ERROR_MESSAGE =
11+
"알 수 없는 문제가 발생했습니다, 잠시 후 다시 시도해주세요.";
12+
const DEFAULT_ERROR_RESPONSE = {
13+
type: "unknown",
14+
errors: [{ code: "unknown", detail: DEFAULT_ERROR_MESSAGE, attr: null }],
15+
};
1016

1117
export class ShopAPIClientError extends Error {
12-
readonly name = 'ShopAPIError'
13-
readonly status: number
14-
readonly detail: ShopAPISchema.ErrorResponseSchema
15-
readonly originalError: unknown
18+
readonly name = "ShopAPIError";
19+
readonly status: number;
20+
readonly detail: ShopAPISchema.ErrorResponseSchema;
21+
readonly originalError: unknown;
1622

1723
constructor(error?: unknown) {
18-
let message: string = DEFAULT_ERROR_MESSAGE
19-
let detail: ShopAPISchema.ErrorResponseSchema = DEFAULT_ERROR_RESPONSE
20-
let status = -1
24+
let message: string = DEFAULT_ERROR_MESSAGE;
25+
let detail: ShopAPISchema.ErrorResponseSchema = DEFAULT_ERROR_RESPONSE;
26+
let status = -1;
2127

2228
if (axios.isAxiosError(error)) {
23-
const response = error.response
29+
const response = error.response;
2430

2531
if (response) {
26-
status = response.status
27-
detail = isObjectErrorResponseSchema(response.data) ? response.data : {
28-
type: 'axios_error',
29-
errors: [{ code: 'unknown', detail: R.isString(response.data) ? response.data : DEFAULT_ERROR_MESSAGE, attr: null }]
30-
}
32+
status = response.status;
33+
detail = isObjectErrorResponseSchema(response.data)
34+
? response.data
35+
: {
36+
type: "axios_error",
37+
errors: [
38+
{
39+
code: "unknown",
40+
detail: R.isString(response.data)
41+
? response.data
42+
: DEFAULT_ERROR_MESSAGE,
43+
attr: null,
44+
},
45+
],
46+
};
3147
}
3248
} else if (error instanceof Error) {
33-
message = error.message
49+
message = error.message;
3450
detail = {
35-
type: error.name || typeof error || 'unknown',
36-
errors: [{ code: 'unknown', detail: error.message, attr: null }],
37-
}
51+
type: error.name || typeof error || "unknown",
52+
errors: [{ code: "unknown", detail: error.message, attr: null }],
53+
};
3854
}
3955

40-
super(message)
41-
this.originalError = error || null
42-
this.status = status
43-
this.detail = detail
56+
super(message);
57+
this.originalError = error || null;
58+
this.status = status;
59+
this.detail = detail;
4460
}
4561

4662
isRequiredAuth(): boolean {
47-
return this.status === 401 || this.status === 403
63+
return this.status === 401 || this.status === 403;
4864
}
4965
}
5066

51-
type AxiosRequestWithoutPayload = <T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>) => Promise<R>
52-
type AxiosRequestWithPayload = <T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) => Promise<R>
67+
type AxiosRequestWithoutPayload = <T = any, R = AxiosResponse<T>, D = any>(
68+
url: string,
69+
config?: AxiosRequestConfig<D>
70+
) => Promise<R>;
71+
type AxiosRequestWithPayload = <T = any, R = AxiosResponse<T>, D = any>(
72+
url: string,
73+
data?: D,
74+
config?: AxiosRequestConfig<D>
75+
) => Promise<R>;
5376

5477
class ShopAPIClient {
55-
readonly baseURL: string
56-
protected readonly csrfCookieName: string
57-
private readonly shopAPI: AxiosInstance
78+
readonly baseURL: string;
79+
protected readonly csrfCookieName: string;
80+
private readonly shopAPI: AxiosInstance;
5881

5982
constructor(
6083
baseURL: string = import.meta.env.VITE_PYCONKR_SHOP_API_DOMAIN,
6184
csrfCookieName: string = import.meta.env.VITE_PYCONKR_SHOP_CSRF_COOKIE_NAME,
62-
timeout: number = DEFAULT_TIMEOUT,
85+
timeout: number = DEFAULT_TIMEOUT
6386
) {
64-
this.baseURL = baseURL
65-
this.csrfCookieName = csrfCookieName
66-
this.shopAPI = axios.create({ baseURL, timeout, headers: { 'Content-Type': 'application/json' } })
87+
this.baseURL = baseURL;
88+
this.csrfCookieName = csrfCookieName;
89+
this.shopAPI = axios.create({
90+
baseURL,
91+
timeout,
92+
withCredentials: true,
93+
headers: { "Content-Type": "application/json" },
94+
});
95+
this.shopAPI.interceptors.request.use(
96+
(config) => {
97+
config.headers["x-csrftoken"] = this.getCSRFToken();
98+
return config;
99+
},
100+
(error) => Promise.reject(error)
101+
);
67102
}
68103

69-
_safe_request_without_payload(requestFunc: AxiosRequestWithoutPayload): AxiosRequestWithoutPayload {
70-
return async <T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>) => {
104+
_safe_request_without_payload(
105+
requestFunc: AxiosRequestWithoutPayload
106+
): AxiosRequestWithoutPayload {
107+
return async <T = any, R = AxiosResponse<T>, D = any>(
108+
url: string,
109+
config?: AxiosRequestConfig<D>
110+
) => {
71111
try {
72-
return await requestFunc<T, R, D>(url, config)
112+
return await requestFunc<T, R, D>(url, config);
73113
} catch (error) {
74-
throw new ShopAPIClientError(error)
114+
throw new ShopAPIClientError(error);
75115
}
76-
}
116+
};
77117
}
78118

79-
_safe_request_with_payload(requestFunc: AxiosRequestWithPayload): AxiosRequestWithPayload {
80-
return async <T = any, R = AxiosResponse<T>, D = any>(url: string, data: D, config?: AxiosRequestConfig<D>) => {
119+
_safe_request_with_payload(
120+
requestFunc: AxiosRequestWithPayload
121+
): AxiosRequestWithPayload {
122+
return async <T = any, R = AxiosResponse<T>, D = any>(
123+
url: string,
124+
data: D,
125+
config?: AxiosRequestConfig<D>
126+
) => {
81127
try {
82-
return await requestFunc<T, R, D>(url, data, config)
128+
return await requestFunc<T, R, D>(url, data, config);
83129
} catch (error) {
84-
throw new ShopAPIClientError(error)
130+
throw new ShopAPIClientError(error);
85131
}
86-
}
132+
};
87133
}
88134

89-
getCSRFToken(): string | undefined { return getCookie(this.csrfCookieName) }
135+
getCSRFToken(): string | undefined {
136+
return getCookie(this.csrfCookieName);
137+
}
90138

91-
async get<T, D=any>(url: string, config?: AxiosRequestConfig<D>): Promise<T> {
92-
return (await this._safe_request_without_payload(this.shopAPI.get)<T, AxiosResponse<T>, D>(url, config)).data
139+
async get<T, D = any>(
140+
url: string,
141+
config?: AxiosRequestConfig<D>
142+
): Promise<T> {
143+
return (
144+
await this._safe_request_without_payload(this.shopAPI.get)<
145+
T,
146+
AxiosResponse<T>,
147+
D
148+
>(url, config)
149+
).data;
93150
}
94-
async post<T, D>(url: string, data: D, config?: AxiosRequestConfig<D>): Promise<T> {
95-
return (await this._safe_request_with_payload(this.shopAPI.post)<T, AxiosResponse<T>, D>(url, data, config)).data
151+
async post<T, D>(
152+
url: string,
153+
data: D,
154+
config?: AxiosRequestConfig<D>
155+
): Promise<T> {
156+
return (
157+
await this._safe_request_with_payload(this.shopAPI.post)<
158+
T,
159+
AxiosResponse<T>,
160+
D
161+
>(url, data, config)
162+
).data;
96163
}
97-
async put<T, D>(url: string, data: D, config?: AxiosRequestConfig<D>): Promise<T> {
98-
return (await this._safe_request_with_payload(this.shopAPI.put)<T, AxiosResponse<T>, D>(url, data, config)).data
164+
async put<T, D>(
165+
url: string,
166+
data: D,
167+
config?: AxiosRequestConfig<D>
168+
): Promise<T> {
169+
return (
170+
await this._safe_request_with_payload(this.shopAPI.put)<
171+
T,
172+
AxiosResponse<T>,
173+
D
174+
>(url, data, config)
175+
).data;
99176
}
100-
async patch<T, D>(url: string, data: D, config?: AxiosRequestConfig<D>): Promise<T> {
101-
return (await this._safe_request_with_payload(this.shopAPI.patch)<T, AxiosResponse<T>, D>(url, data, config)).data
177+
async patch<T, D>(
178+
url: string,
179+
data: D,
180+
config?: AxiosRequestConfig<D>
181+
): Promise<T> {
182+
return (
183+
await this._safe_request_with_payload(this.shopAPI.patch)<
184+
T,
185+
AxiosResponse<T>,
186+
D
187+
>(url, data, config)
188+
).data;
102189
}
103-
async delete<T, D=any>(url: string, config?: AxiosRequestConfig<D>): Promise<T> {
104-
return (await this._safe_request_without_payload(this.shopAPI.delete)<T, AxiosResponse<T>, D>(url, config)).data
190+
async delete<T, D = any>(
191+
url: string,
192+
config?: AxiosRequestConfig<D>
193+
): Promise<T> {
194+
return (
195+
await this._safe_request_without_payload(this.shopAPI.delete)<
196+
T,
197+
AxiosResponse<T>,
198+
D
199+
>(url, config)
200+
).data;
105201
}
106202
}
107203

108-
export const shopAPIClient = new ShopAPIClient(import.meta.env.VITE_PYCONKR_SHOP_API_DOMAIN)
204+
export const shopAPIClient = new ShopAPIClient(
205+
import.meta.env.VITE_PYCONKR_SHOP_API_DOMAIN
206+
);

package/pyconkr-shop/apis/index.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,22 @@ import ShopAPISchema from "@pyconkr-shop/schemas";
55
namespace ShopAPIRoute {
66
/**
77
* 로그인합니다.
8-
* @param username - 사용자 이름
9-
* @param password - 비밀번호
8+
* @param data - 로그인 정보
9+
* @param data.username - 사용자 이름
10+
* @param data.password - 비밀번호
1011
* @returns 로그인 정보
1112
* @throws 401 - 로그인 정보가 없습니다.
1213
*/
13-
export const signInWithEmail = (data: ShopAPISchema.EmailSignInRequest) =>
14-
shopAPIClient.post<
15-
ShopAPISchema.UserStatus,
14+
export const signInWithEmail = (data: ShopAPISchema.EmailSignInRequest) => {
15+
const requestPayload = {
16+
...data,
17+
csrfmiddlewaretoken: shopAPIClient.getCSRFToken() ?? "",
18+
};
19+
return shopAPIClient.post<
20+
ShopAPISchema.UserSignedInStatus,
1621
ShopAPISchema.EmailSignInRequest
17-
>("authn/social/browser/v1/auth/login", data);
22+
>("authn/social/browser/v1/auth/login", requestPayload);
23+
};
1824

1925
/**
2026
* SNS로 로그인합니다.
@@ -51,7 +57,7 @@ namespace ShopAPIRoute {
5157
* @throws 401 - 로그아웃이 성공할 시에도 항상 401 에러가 발생합니다.
5258
*/
5359
export const signOut = () =>
54-
shopAPIClient.delete<ShopAPISchema.UserStatus>(
60+
shopAPIClient.delete<ShopAPISchema.UserSignedInStatus>(
5561
"authn/social/browser/v1/auth/session"
5662
);
5763

@@ -61,7 +67,7 @@ namespace ShopAPIRoute {
6167
* @throws 401 - 로그인 정보가 없습니다.
6268
*/
6369
export const retrieveUserInfo = () =>
64-
shopAPIClient.get<ShopAPISchema.UserStatus>(
70+
shopAPIClient.get<ShopAPISchema.UserSignedInStatus>(
6571
"authn/social/browser/v1/auth/session"
6672
);
6773

@@ -80,7 +86,7 @@ namespace ShopAPIRoute {
8086
* @returns 현재 장바구니 상태
8187
*/
8288
export const retrieveCart = () =>
83-
shopAPIClient.get<ShopAPISchema.Order>("v1/orders/cart/");
89+
shopAPIClient.get<ShopAPISchema.OrderProductItem[]>("v1/orders/cart/");
8490

8591
/**
8692
* 장바구니에 상품을 추가합니다.
@@ -158,10 +164,10 @@ namespace ShopAPIRoute {
158164

159165
/**
160166
* 결제 완료된 주문 내역을 환불 시도합니다.
161-
* @param orderId - 환불할 주문 내역의 UUID
167+
* @param data.orderId - 환불할 주문 내역의 UUID
162168
*/
163-
export const refundAllItemsInOrder = (orderId: string) =>
164-
shopAPIClient.delete<void>(`v1/orders/${orderId}/`);
169+
export const refundAllItemsInOrder = (data: { order_id: string }) =>
170+
shopAPIClient.delete<void>(`v1/orders/${data.order_id}/`);
165171
}
166172

167173
export default ShopAPIRoute;

0 commit comments

Comments
 (0)