Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
80 changes: 80 additions & 0 deletions api/http.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import axios, {
AxiosError,
AxiosRequestConfig,
AxiosResponse,
InternalAxiosRequestConfig,
} from 'axios';
import qs from 'qs';

type APIResponse<T = unknown> = {
success: boolean;
msg: string;
data?: T;
};

export const client = axios.create({
baseURL: 'https://api.allyouraffle.co.kr',
timeout: 3000,
timeoutErrorMessage: '서버 요청 시간 초과되었습니다.',
paramsSerializer: (params) => qs.stringify(params, { arrayFormat: 'comma' }),
});

const handleHeadersWithAccessToken = (config: AxiosRequestConfig): InternalAxiosRequestConfig => {
const accessToken = localStorage.getItem('access_token');
config.headers = {
...config.headers,
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
};

return config as InternalAxiosRequestConfig;
};

client.interceptors.request.use(handleHeadersWithAccessToken);
client.interceptors.response.use(
(response: AxiosResponse) => response,
(error: AxiosError) => Promise.reject(error),
);

export const http = {
get: function get<Response = unknown>(url: string, config?: AxiosRequestConfig) {
return client.get<APIResponse<Response>>(url, config).then((res) => res.data);
},
post: function post<Response = unknown, Request = any>(
url: string,
data?: Request,
config?: AxiosRequestConfig,
) {
return client.post<APIResponse<Response>>(url, data, config).then((res) => res.data);
},
put: function put<Response = unknown, Request = any>(
url: string,
data?: Request,
config?: AxiosRequestConfig,
) {
return client.put<APIResponse<Response>>(url, data, config).then((res) => res.data);
},
patch: function patch<Response = unknown, Request = any>(
url: string,
data?: Request,
config?: AxiosRequestConfig,
) {
return client.patch<APIResponse<Response>>(url, data, config).then((res) => res.data);
},
delete: function del<Response = unknown>(url: string, config?: AxiosRequestConfig) {
return client.delete<APIResponse<Response>>(url, config).then((res) => res.data);
},
};

export const handleAxiosError = (error: Error) => {
if (axios.isAxiosError(error)) {
const axiosError: AxiosError = error;
console.error('Axios Error:', axiosError);
return axiosError.response?.data as APIResponse;
}
console.log('Unknown Error: ', error);
return {
success: false,
msg: '알 수 없는 오류가 발생했습니다.',
};
};
54 changes: 17 additions & 37 deletions api/user/ticketsApi.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,29 @@
import useAuthStore from '../../lib/store/useAuthStore';
import baseURL from '../baseURL';
import { AxiosResponse } from 'axios';
import { client } from '../http';

/**
* 유저의 응모권 갯수 가져오기
* @param userToken
* @description 유저의 응모권 갯수 가져오기
*/
async function getTickets(userToken: string): Promise<number> {
const { logout } = useAuthStore.getState();
const getTickets = async (): Promise<number> => {
try {
const response = await fetch(`${baseURL}/api/v1/user/tickets`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${userToken}`,
},
});
if (response.status === 401) {
console.error('인증 실패: 토큰이 만료되었습니다.');
logout();
}
if (!response.ok) {
throw new Error('응모권 불러오기 실패');
}
return response.json();
const response = await client.get('/api/v1/user/tickets');
return response.data;
} catch (error) {
throw new Error(`응모권 불러오기 실패: ${error}`);
console.log('응모권 불러오기 실패:', error);
throw error;
}
}
};

async function postTicketsPlusOne(userToken: string): Promise<number> {
/**
* @description 유저의 으옹권 갯수 +1 추가하기
*/
const postTicketsPlusOne = async (): Promise<AxiosResponse<number>> => {
try {
const response = await fetch(`${baseURL}/api/v1/user/tickets/plus_one`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${userToken}`,
},
});
if (!response.ok) {
throw new Error('티켓 추가 실패');
}
return response.json();
return await client.post('/api/v1/user/tickets/plus_one');
} catch (error) {
throw new Error(`티켓 추가 실패: ${error}`);
console.log('응모권 추가 실패:', error);
throw error;
}
}
};

export { getTickets, postTicketsPlusOne };
24 changes: 4 additions & 20 deletions components/Advertisement/Advertisement.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,17 @@
'use client';

import { useEffect, useState } from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import useTicketPlusOne from '@/lib/hooks/useTicketPlusOne';
import { KakaoAdFit } from '../KakaoAdFit';
import { postTicketsPlusOne } from '../../api/user/ticketsApi';
import Button from '../../lib/common/Button';
import useAuthStore from '../../lib/store/useAuthStore';
import { AdvertisementProps } from '../../lib/types/advertisement';

export default function Advertisement({ onClose }: AdvertisementProps) {
const [isToggle, setIsToggle] = useState<boolean>(false);
const [countdown, setCountdown] = useState<number>(3);
const [isButtonEnabled, setIsButtonEnabled] = useState<boolean>(false);

const userToken = useAuthStore((state) => state.userToken);
const queryClient = useQueryClient();

const mutate = useMutation({
mutationKey: ['postTicketsPlusOne'],
mutationFn: () => postTicketsPlusOne(userToken),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['getTickets'] });
alert('티켓 추가 성공');
onClose();
},
onError: (error: Error) => {
alert('티켓 추가 실패');
throw error;
},
});
const { mutate } = useTicketPlusOne();

useEffect(() => {
if (countdown > 0) {
Expand All @@ -43,8 +26,9 @@ export default function Advertisement({ onClose }: AdvertisementProps) {

const handleCloseButton = () => {
if (isButtonEnabled) {
mutate.mutate();
mutate();
setIsToggle(!isToggle);
onClose();
}
};

Expand Down
13 changes: 11 additions & 2 deletions components/Header/HeaderNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,23 @@ export default function HeaderNav() {
* 사용자 응모권 갯수를 나타내는 useQuery
* !!enabled: userToken이 존재할 때만 실행
*/
const { data: ticketsCount, isLoading } = useQuery({
const {
data: ticketsCount,
isLoading,
isError,
error,
} = useQuery({
queryKey: ['getTickets'],
queryFn: () => getTickets(userToken),
queryFn: getTickets,
staleTime: 1000 * 60 * 1,
enabled: !!userToken,
});

if (isLoading) return <div>Loading...</div>;

if (isError)
return <div>Error: {error instanceof Error ? error.message : JSON.stringify(error)}</div>;

const handleProfileClick = () => {
setIsPopverOpen(!isPopverOpen);
};
Expand Down
2 changes: 1 addition & 1 deletion components/Items/ItemDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default function ItemDetail({ params: { id } }: { params: { id: number }

const { data: ticketsCount } = useQuery({
queryKey: ['getTickets'],
queryFn: () => getTickets(userToken),
queryFn: () => getTickets(),
enabled: !!userToken,
});

Expand Down
2 changes: 1 addition & 1 deletion components/Items/ItemStyle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default function ItemStyle({
*/
const { data: ticketsCount } = useQuery({
queryKey: ['getTickets'],
queryFn: () => getTickets(userToken),
queryFn: () => getTickets(),
enabled: !!userToken,
staleTime: 1000 * 60,
});
Expand Down
4 changes: 1 addition & 3 deletions lib/hooks/useTicketPlusOne.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import useAuthStore from '../store/useAuthStore';
import { postTicketsPlusOne } from '../../api/user/ticketsApi';

export default function useTicketPlusOne() {
const userToken = useAuthStore((state) => state.userToken);
const queryClient = useQueryClient();

return useMutation({
mutationKey: ['postTicketPlusOne'],
mutationFn: () => postTicketsPlusOne(userToken),
mutationFn: postTicketsPlusOne,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['getTickets'] });
alert('응모권이 추가되었습니다.');
Expand Down
74 changes: 72 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading