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

feat: api 연동 완료 및 2차 배포 #42

Merged
merged 5 commits into from
Oct 29, 2024
Merged
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "set HTTPS=false&&react-scripts start",
"start": "set HTTPS=true && react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
Expand Down
Binary file modified public/favicon.ico
Binary file not shown.
17 changes: 1 addition & 16 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,9 @@
pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}

.App-header {
background-color: #282c34;
min-height: 100vh;
min-height: 100dvh;
display: flex;
flex-direction: column;
align-items: center;
Expand All @@ -27,12 +21,3 @@
.App-link {
color: #61dafb;
}

@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
12 changes: 9 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import Navbar from "./components/navbar/navbar";
import Search from "./pages/search/search";
import Time from "./pages/main/time/time";
import SearchView from "./pages/search/searchView";
import MentorProfile from "./pages/mentorProfile/mentorProfile";
import ApplyCogoTime from "./pages/applyCogo/applyCogoTime";
import ApplyCogoMemo from "./pages/applyCogo/applyCogoMemo";
import ApplyCogoComplete from "./pages/applyCogo/applyCogoComplete";
Expand All @@ -25,6 +24,10 @@ import SendCogoDetail from "./pages/cogo/sendCogo/sendCogo_detail";
import Introduce from "./pages/mypage/Introduce/introduce";
import MyProfile from "./pages/mypage/myprofile/myprofile";
import TimeSelect from "./pages/mypage/timeselect/timeselect";
import Intro from "./pages/intro/intro";
import MentorDetails from "./pages/mentorDetails/mentorDetails";
import CompleteCogoDetail from "./pages/cogo/completeCogo/completeCogo_detail";
import CompleteCogo from "./pages/cogo/completeCogo/completeCogo";

function App() {
return (
Expand All @@ -40,7 +43,7 @@ function AppContent() {
const location = useLocation();

// 로그인, 콜백, 회원가입 페이지에서는 Navbar를 숨김
const hideNavbar = ["/login", "/callback", "/signup"].includes(
const hideNavbar = ["/login", "/callback", "/signup", "/intro"].includes(
location.pathname
);

Expand All @@ -49,6 +52,7 @@ function AppContent() {
{!hideNavbar && <Navbar />}
<Routes>
<Route path="/" element={<Main />} />
<Route path="/intro" element={<Intro />} />
<Route path="/login" element={<Login />} />
<Route path="/callback" element={<LoginCallback />} />
<Route path="/signup" element={<SignUp />} />
Expand All @@ -57,13 +61,15 @@ function AppContent() {
<Route path="/mypage/myprofile" element={<MyProfile />} />
<Route path="/mypage/timeselect" element={<TimeSelect />} />
<Route path="/search" element={<Search />} />
<Route path="/mentorprofile" element={<MentorProfile />} />
<Route path="/mentor-detail/:mentorid" element={<MentorDetails />} />
<Route path="/applycogotime" element={<ApplyCogoTime />} />
<Route path="/applycogomemo" element={<ApplyCogoMemo />} />
<Route path="/applycogocomplete" element={<ApplyCogoComplete />} />
<Route path="/cogo" element={<Cogo />} />
<Route path="/cogo/send" element={<SendCogo />} />
<Route path="/cogo/send/detail" element={<SendCogoDetail />} />
<Route path="/cogo/complete" element={<CompleteCogo />} />
<Route path="/cogo/complete/detail" element={<CompleteCogoDetail />} />
<Route path="/search/searchview" element={<SearchView />} />
<Route path="/time" element={<Time />} />
</Routes>
Expand Down
114 changes: 114 additions & 0 deletions src/apis/authAxiosInstance.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import axios, { AxiosError, AxiosRequestConfig } from "axios";

const getTokenFromLocalStorage = () => {
return localStorage.getItem("token");
};

const setTokenToLocalStorage = (token: string) => {
localStorage.setItem("token", token);
};

const saveRoleToLocalStorage = (role: string) => {
localStorage.setItem("role", role);
};

const authAxiosInstance = axios.create({
baseURL: "https://cogo.life",
headers: {
"Content-Type": "application/json",
},
});

let isRefreshing = false;
let failedQueue: any[] = [];

const processQueue = (error: AxiosError | null, token: string | null = null) => {
failedQueue.forEach((prom) => {
if (error) {
prom.reject(error);
} else {
prom.resolve(token);
}
});

failedQueue = [];
};

authAxiosInstance.interceptors.request.use(
(config) => {
const token = getTokenFromLocalStorage();
console.log("로컬스토리지에 토큰 저장: ", token);
if (token) {
config.headers = config.headers || {}; // headers가 undefined일 수 있으므로 초기화
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);

authAxiosInstance.interceptors.response.use(
(response) => {
return response;
},
async (error: AxiosError) => {
const originalRequest = error.config as AxiosRequestConfig & { _retry?: boolean };

if (error.response?.status === 401 && !originalRequest._retry) {
if (isRefreshing) {
try {
const token = await new Promise(function (resolve, reject) {
failedQueue.push({ resolve, reject });
});
originalRequest.headers = originalRequest.headers || {}; // headers가 undefined일 수 있으므로 초기화
originalRequest.headers.Authorization = "Bearer " + token;
return await authAxiosInstance(originalRequest);
} catch (err) {
return await Promise.reject(err);
}
}

originalRequest._retry = true;
isRefreshing = true;

return new Promise(function (resolve, reject) {
axios
.post(
"https://cogo.life/reissue",
{},
{
headers: {
"Content-Type": "application/json",
},
withCredentials: true,
}
)
.then(({ data }) => {
const newToken = data.accessToken;
setTokenToLocalStorage(newToken);
authAxiosInstance.defaults.headers.Authorization = "Bearer " + newToken;

originalRequest.headers = originalRequest.headers || {}; // headers가 undefined일 수 있으므로 초기화
originalRequest.headers.Authorization = "Bearer " + newToken;

processQueue(null, newToken);
resolve(authAxiosInstance(originalRequest));
})
.catch((err) => {
processQueue(err, null);
// 토큰 재발급 실패 시 로그아웃 처리 등 추가 작업
reject(err);
})
.finally(() => {
isRefreshing = false;
});
});
}

return Promise.reject(error);
}
);

export default authAxiosInstance;
11 changes: 11 additions & 0 deletions src/apis/authAxiosInstanceReissue.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import axios from "axios";

const authAxiosInstanceReissue = axios.create({
baseURL: "https://cogo.life",
headers: {
"Content-Type": "application/json",
},
withCredentials: true,
});

export default authAxiosInstanceReissue;
112 changes: 101 additions & 11 deletions src/apis/axiosInstance.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,114 @@
import axios from "axios";
import axios, { AxiosError, AxiosRequestConfig } from "axios";

const getTokenFromLocalStorage = () => {
return localStorage.getItem("token");
};

const setTokenToLocalStorage = (token: string) => {
localStorage.setItem("token", token);
};

const saveRoleToLocalStorage = (role: string) => {
localStorage.setItem("role", role);
};

const axiosInstance = axios.create({
baseURL: 'https://cogo.life/api/v2',
baseURL: "https://cogo.life/api/v2",
headers: {
"Content-Type": "application/json",
},
});

axiosInstance.interceptors.request.use((config) => {
const token = getTokenFromLocalStorage();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
let isRefreshing = false;
let failedQueue: any[] = [];

const processQueue = (error: AxiosError | null, token: string | null = null) => {
failedQueue.forEach((prom) => {
if (error) {
prom.reject(error);
} else {
prom.resolve(token);
}
});

failedQueue = [];
};

axiosInstance.interceptors.request.use(
(config) => {
const token = getTokenFromLocalStorage();
console.log("로컬스토리지에 토큰 저장: ", token);
if (token) {
config.headers = config.headers || {}; // headers가 undefined일 수 있으므로 초기화
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
return config;
}, (error) => {
return Promise.reject(error);
});
);

axiosInstance.interceptors.response.use(
(response) => {
return response;
},
async (error: AxiosError) => {
const originalRequest = error.config as AxiosRequestConfig & { _retry?: boolean };

if (error.response?.status === 401 && !originalRequest._retry) {
if (isRefreshing) {
try {
const token = await new Promise(function (resolve, reject) {
failedQueue.push({ resolve, reject });
});
originalRequest.headers = originalRequest.headers || {}; // headers가 undefined일 수 있으므로 초기화
originalRequest.headers.Authorization = "Bearer " + token;
return await axiosInstance(originalRequest);
} catch (err) {
return await Promise.reject(err);
}
}

originalRequest._retry = true;
isRefreshing = true;

return new Promise(function (resolve, reject) {
axios
.post(
"https://cogo.life/reissue",
{},
{
headers: {
"Content-Type": "application/json",
},
withCredentials: true,
}
)
.then(({ data }) => {
const newToken = data.accessToken;
setTokenToLocalStorage(newToken);
axiosInstance.defaults.headers.Authorization = "Bearer " + newToken;

originalRequest.headers = originalRequest.headers || {}; // headers가 undefined일 수 있으므로 초기화
originalRequest.headers.Authorization = "Bearer " + newToken;

processQueue(null, newToken);
resolve(axiosInstance(originalRequest));
})
.catch((err) => {
processQueue(err, null);
// 토큰 재발급 실패 시 로그아웃 처리 등 추가 작업
reject(err);
})
.finally(() => {
isRefreshing = false;
});
});
}

return Promise.reject(error);
}
);

export default axiosInstance;
export default axiosInstance;
Loading
Loading