Feat: 엑세스 토큰과 리프레쉬 토큰을 쓰는 apiConfig와 auth, project, workspace api 구현
This commit is contained in:
parent
cb33e44d9a
commit
450ff9277f
34
frontend/src/api/authApi.ts
Normal file
34
frontend/src/api/authApi.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import api from '@/api/axiosConfig';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
|
||||||
|
export const reissueTokenApi = async () => {
|
||||||
|
try {
|
||||||
|
const response = await api.post('/api/auth/reissue', null, {
|
||||||
|
withCredentials: true,
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
console.error('토큰 재발급 실패:', error.response?.data?.message || '알 수 없는 오류');
|
||||||
|
} else {
|
||||||
|
console.error('알 수 없는 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchProfileApi = async () => {
|
||||||
|
try {
|
||||||
|
const response = await api.get('/api/auth/profile', {
|
||||||
|
withCredentials: true,
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
console.error('사용자 정보 가져오기 실패:', error.response?.data?.message || '알 수 없는 오류');
|
||||||
|
} else {
|
||||||
|
console.error('알 수 없는 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
75
frontend/src/api/axiosConfig.ts
Normal file
75
frontend/src/api/axiosConfig.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import useAuthStore from '@/stores/useAuthStore';
|
||||||
|
|
||||||
|
const baseURL = 'https://j11s002.p.ssafy.io';
|
||||||
|
|
||||||
|
const api = axios.create({
|
||||||
|
baseURL,
|
||||||
|
withCredentials: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
api.interceptors.request.use((config) => {
|
||||||
|
const token = localStorage.getItem('accessToken');
|
||||||
|
if (token) {
|
||||||
|
config.headers.Authorization = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
|
||||||
|
api.interceptors.response.use(
|
||||||
|
(response) => response,
|
||||||
|
async (error) => {
|
||||||
|
const originalRequest = error.config;
|
||||||
|
|
||||||
|
if (error.response?.status === 401 && originalRequest._retry) {
|
||||||
|
console.error('토큰 재발급 중 401 오류가 발생했습니다. 로그아웃 처리합니다.');
|
||||||
|
alert('세션이 만료되었습니다. 다시 로그인해 주세요.');
|
||||||
|
useAuthStore.getState().clearAuth();
|
||||||
|
window.location.href = '/';
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.response?.status === 401 && !originalRequest._retry) {
|
||||||
|
originalRequest._retry = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await api.post('/api/auth/reissue', null, {
|
||||||
|
withCredentials: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.data?.data?.accessToken) {
|
||||||
|
alert('잘못된 토큰 재발급 응답입니다. 다시 로그인해 주세요.');
|
||||||
|
useAuthStore.getState().clearAuth();
|
||||||
|
window.location.href = '/';
|
||||||
|
return Promise.reject(new Error('Invalid token reissue response'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const newAccessToken = response.data.data.accessToken;
|
||||||
|
|
||||||
|
useAuthStore.getState().setLoggedIn(true, newAccessToken);
|
||||||
|
localStorage.setItem('accessToken', newAccessToken);
|
||||||
|
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
|
||||||
|
|
||||||
|
return api(originalRequest);
|
||||||
|
} catch (reissueError) {
|
||||||
|
console.error('토큰 재발급 실패:', reissueError);
|
||||||
|
alert('토큰 재발급에 실패했습니다. 다시 로그인해 주세요.');
|
||||||
|
useAuthStore.getState().clearAuth();
|
||||||
|
window.location.href = '/';
|
||||||
|
return Promise.reject(reissueError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.response?.status === 400) {
|
||||||
|
alert('잘못된 요청입니다. 다시 시도해 주세요.');
|
||||||
|
} else if (error.response?.status === 403) {
|
||||||
|
alert('권한이 없습니다. 다시 로그인해 주세요.');
|
||||||
|
useAuthStore.getState().clearAuth();
|
||||||
|
window.location.href = '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default api;
|
153
frontend/src/api/projectApi.ts
Normal file
153
frontend/src/api/projectApi.ts
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import api from '@/api/axiosConfig';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
|
||||||
|
export const getProjectApi = async (projectId: number, memberId: number) => {
|
||||||
|
try {
|
||||||
|
const response = await api.get(`/api/projects/${projectId}`, {
|
||||||
|
params: {
|
||||||
|
memberId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
console.error('프로젝트 조회 실패:', error.response?.data?.message || '알 수 없는 오류');
|
||||||
|
} else {
|
||||||
|
console.error('알 수 없는 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateProjectApi = async (
|
||||||
|
projectId: number,
|
||||||
|
memberId: number,
|
||||||
|
data: { title: string; projectType: string }
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const response = await api.put(`/api/projects/${projectId}`, data, {
|
||||||
|
params: {
|
||||||
|
memberId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
console.error('프로젝트 수정 실패:', error.response?.data?.message || '알 수 없는 오류');
|
||||||
|
} else {
|
||||||
|
console.error('알 수 없는 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteProjectApi = async (projectId: number, memberId: number) => {
|
||||||
|
try {
|
||||||
|
const response = await api.delete(`/api/projects/${projectId}`, {
|
||||||
|
params: {
|
||||||
|
memberId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
console.error('프로젝트 삭제 실패:', error.response?.data?.message || '알 수 없는 오류');
|
||||||
|
} else {
|
||||||
|
console.error('알 수 없는 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addProjectMemberApi = async (
|
||||||
|
projectId: number,
|
||||||
|
memberId: number,
|
||||||
|
newMemberId: number,
|
||||||
|
privilegeType: string
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const response = await api.post(
|
||||||
|
`/api/projects/${projectId}/members`,
|
||||||
|
{ memberId: newMemberId, privilegeType },
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
memberId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
console.error('프로젝트 멤버 추가 실패:', error.response?.data?.message || '알 수 없는 오류');
|
||||||
|
} else {
|
||||||
|
console.error('알 수 없는 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const removeProjectMemberApi = async (projectId: number, memberId: number, targetMemberId: number) => {
|
||||||
|
try {
|
||||||
|
const response = await api.delete(`/api/projects/${projectId}/members`, {
|
||||||
|
params: {
|
||||||
|
memberId,
|
||||||
|
},
|
||||||
|
data: { memberId: targetMemberId },
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
console.error('프로젝트 멤버 제거 실패:', error.response?.data?.message || '알 수 없는 오류');
|
||||||
|
} else {
|
||||||
|
console.error('알 수 없는 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAllProjectsApi = async (
|
||||||
|
workspaceId: number,
|
||||||
|
memberId: number,
|
||||||
|
lastProjectId?: number,
|
||||||
|
limit: number = 10
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const response = await api.get(`/api/workspaces/${workspaceId}/projects`, {
|
||||||
|
params: {
|
||||||
|
memberId,
|
||||||
|
lastProjectId,
|
||||||
|
limit,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
console.error('프로젝트 목록 조회 실패:', error.response?.data?.message || '알 수 없는 오류');
|
||||||
|
} else {
|
||||||
|
console.error('알 수 없는 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createProjectApi = async (
|
||||||
|
workspaceId: number,
|
||||||
|
memberId: number,
|
||||||
|
data: { title: string; projectType: string }
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const response = await api.post(`/api/workspaces/${workspaceId}/projects`, data, {
|
||||||
|
params: {
|
||||||
|
memberId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
console.error('프로젝트 생성 실패:', error.response?.data?.message || '알 수 없는 오류');
|
||||||
|
} else {
|
||||||
|
console.error('알 수 없는 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
145
frontend/src/api/workspaceApi.ts
Normal file
145
frontend/src/api/workspaceApi.ts
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
import api from '@/api/axiosConfig';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
|
||||||
|
interface Workspace {
|
||||||
|
id: number;
|
||||||
|
memberId: string;
|
||||||
|
title: string;
|
||||||
|
content: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
|
interface GetAllWorkspacesResponse {
|
||||||
|
status: number;
|
||||||
|
code: number;
|
||||||
|
message: string;
|
||||||
|
data: {
|
||||||
|
workspaceResponses: Workspace[];
|
||||||
|
};
|
||||||
|
errors: Array<{
|
||||||
|
field: string;
|
||||||
|
code: string;
|
||||||
|
message: string;
|
||||||
|
objectName: string;
|
||||||
|
}>;
|
||||||
|
isSuccess: boolean;
|
||||||
|
}
|
||||||
|
export const getWorkspaceApi = async (workspaceId: number, memberId: number) => {
|
||||||
|
try {
|
||||||
|
const response = await api.get(`/api/workspaces/${workspaceId}`, {
|
||||||
|
params: { memberId },
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
console.error('API 요청 실패:', error.response?.data?.message || '알 수 없는 오류');
|
||||||
|
} else {
|
||||||
|
console.error('알 수 없는 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateWorkspaceApi = async (
|
||||||
|
workspaceId: number,
|
||||||
|
memberId: number,
|
||||||
|
data: { title: string; content: string }
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const response = await api.put(`/api/workspaces/${workspaceId}`, data, {
|
||||||
|
params: { memberId },
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
console.error('API 요청 실패:', error.response?.data?.message || '알 수 없는 오류');
|
||||||
|
} else {
|
||||||
|
console.error('알 수 없는 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteWorkspaceApi = async (workspaceId: number, memberId: number) => {
|
||||||
|
try {
|
||||||
|
const response = await api.delete(`/api/workspaces/${workspaceId}`, {
|
||||||
|
params: { memberId },
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
console.error('API 요청 실패:', error.response?.data?.message || '알 수 없는 오류');
|
||||||
|
} else {
|
||||||
|
console.error('알 수 없는 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAllWorkspacesApi = async (
|
||||||
|
memberId: number,
|
||||||
|
lastWorkspaceId?: number,
|
||||||
|
limit?: number
|
||||||
|
): Promise<GetAllWorkspacesResponse> => {
|
||||||
|
try {
|
||||||
|
const response = await api.get('/api/workspaces', {
|
||||||
|
params: { memberId, lastWorkspaceId, limit },
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
console.error('API 요청 실패:', error.response?.data?.message || '알 수 없는 오류');
|
||||||
|
} else {
|
||||||
|
console.error('알 수 없는 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createWorkspaceApi = async (memberId: number, data: { title: string; content: string }) => {
|
||||||
|
try {
|
||||||
|
const response = await api.post('/api/workspaces', data, {
|
||||||
|
params: { memberId },
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
console.error('API 요청 실패:', error.response?.data?.message || '알 수 없는 오류');
|
||||||
|
} else {
|
||||||
|
console.error('알 수 없는 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addWorkspaceMemberApi = async (workspaceId: number, memberId: number, newMemberId: number) => {
|
||||||
|
try {
|
||||||
|
const response = await api.post(`/api/workspaces/${workspaceId}/members/${newMemberId}`, null, {
|
||||||
|
params: { memberId },
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
console.error('API 요청 실패:', error.response?.data?.message || '알 수 없는 오류');
|
||||||
|
} else {
|
||||||
|
console.error('알 수 없는 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const removeWorkspaceMemberApi = async (workspaceId: number, memberId: number, targetMemberId: number) => {
|
||||||
|
try {
|
||||||
|
const response = await api.delete(`/api/workspaces/${workspaceId}/members/${targetMemberId}`, {
|
||||||
|
params: { memberId },
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
console.error('API 요청 실패:', error.response?.data?.message || '알 수 없는 오류');
|
||||||
|
} else {
|
||||||
|
console.error('알 수 없는 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user