Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
db5dba0
[chore] package-lock.json 파일 삭제
jihyun-j Dec 18, 2024
4994e5c
[chore] yarn lock 재설치
jihyun-j Dec 18, 2024
aab0473
[refector] axios 요청 URL 상수화
jihyun-j Dec 18, 2024
8d1f308
[refector] Atoms : button, message, text input 컴포넌트 생성
jihyun-j Dec 18, 2024
d73ea91
[refector] Molecule : formfield 공통 컴포넌트 생성 (text input + message)
jihyun-j Dec 18, 2024
6a57f8a
[refector] Organism : fromfield와 button컴포넌트를 결합한 로그인 폼 생성
jihyun-j Dec 18, 2024
ad4d60c
[refector] Template : 로그인 폼과 버튼을 결합해 로그인 템플릿 생성
jihyun-j Dec 18, 2024
1dbe07f
[refector] 스웨거 API 엔드포인트(server, user, auth) 별로 타입 정의 파일 분리
jihyun-j Dec 18, 2024
dd5332a
[refector] API 엔드포인트(server, user, auth)별로 API 요청 파일 분리
jihyun-j Dec 18, 2024
74cc53b
[feat] 로그인/회원가입 폼 유효성 검사용 커스텀 훅 생성
jihyun-j Dec 18, 2024
e83a61c
[refector] 로그인/회원가입 폼 유효성 검사 수정
jihyun-j Dec 18, 2024
ebb9ad5
[chore] Pages: pages폴더 components폴더로 이동 (Atomic Design 적용)
jihyun-j Dec 18, 2024
8b37c7d
[chore] 회원가입/로그입 폴더 경로 변경
jihyun-j Dec 18, 2024
aec4b3f
[chore] token파일명 auth 변경 후 파일 경로 수정
jihyun-j Dec 18, 2024
2d95b1d
[feat] CRA + Typescript 절대경로 설정 (craco 설치 밀 설정)
jihyun-j Dec 18, 2024
0221595
[chore] tailwindcss theme config로 관리하기 위해 불필요한 스타일 파일 삭제
jihyun-j Dec 20, 2024
bf8079f
[refector] 페이지 라우터 수정
jihyun-j Dec 20, 2024
f30d005
[refector] Orhanisms: 서버 생성 폼
jihyun-j Dec 22, 2024
a3b1a36
[feat] Atom: 라디오 버튼 생성
jihyun-j Dec 22, 2024
f339f2c
[faet] Molecule: 서버 생성 폼에 사용할 카테고리 셀렉터와 테마 셀렉터 생성
jihyun-j Dec 22, 2024
1215b53
[chore] 리팩토링 후 불필요한 파일 및 컴포넌트 삭제
jihyun-j Dec 22, 2024
bbdbe5e
[feat] Atom: 공통 탭 버튼 생성
jihyun-j Dec 22, 2024
0873b95
[feat] Moledule: 탭 헤더 생성
jihyun-j Dec 22, 2024
ca07e22
[feat] Organism: 공통 탭 컴포넌트 생성
jihyun-j Dec 22, 2024
89bcc5b
[feat] Molecule: 친구 목록에 사용할 리스트 아이템 컴포넌트 생성
jihyun-j Dec 22, 2024
76c6883
[feat] Organism: 오른쪽 친구 목록 사이드바 컴포넌트 생성
jihyun-j Dec 22, 2024
7930fb4
[refector] 서버 관련 api 파일 정리 (카테고리조회, 서버생성, 조회, 삭제 api 추가)
jihyun-j Dec 22, 2024
56b23b8
[feat] Molecule: 왼쪽 사이드바 유저 아바타 및 서버 생성 버튼 추가
jihyun-j Dec 22, 2024
ba0f268
[feat] Molecule: 왼쪽 사이드바 유저가 속한 서버들 나열한 리스트
jihyun-j Dec 22, 2024
627cf27
[feat] Organism: 왼쪽 사이드바 완성 (유저프로필 조회 / 서버생성 모달 추가)
jihyun-j Dec 22, 2024
972a359
[feat] 서버조회 커스텀 훅 생성
jihyun-j Dec 22, 2024
84299c5
[feat] 모달 상태 관리 커스텀 훅 생성
jihyun-j Dec 22, 2024
74b5e4c
[feat] 유저의 친구 조회 커스텀 훅 생성
jihyun-j Dec 22, 2024
0391761
[feat] 선택한 유저의 프로필 조회 커스텀 훅 생성
jihyun-j Dec 22, 2024
9059b11
[refector] Organism: 모달 컴포넌트 생성
jihyun-j Dec 22, 2024
ee0379d
[feat] Moledule: 친구신청 폼 생성, 네브바에 친구추가 아이콘 및 기능 추가
jihyun-j Dec 24, 2024
fe58160
[chore] 리팩토링 후 불필요한 파일들 삭제
jihyun-j Dec 24, 2024
497b26e
[chore] MUI 라이브러리 삭제 후 Video 툴바 코드 수정
jihyun-j Dec 24, 2024
73ca3cb
[chore] Video 관련 컴포넌트에서 MUI 제거
jihyun-j Dec 24, 2024
ad86634
[refector] TextInput컴포넌트에 name props 추가
jihyun-j Dec 24, 2024
8cae18a
[feat] Atom: 북마크 아이콘, 텍스트, 구분선 컴포넌트 생성
jihyun-j Dec 24, 2024
079d837
[style] Atom: Button 스타일 변경
jihyun-j Dec 24, 2024
a1dbb43
[feat] Molecule: 홈 화면에 추가할 서버 정보가 담긴 카드 컴포넌트
jihyun-j Dec 24, 2024
d6b19da
[feat] Template: 홈 화면에 추가할 서버 카드들을 나열, 카테고리 별 필터 추가
jihyun-j Dec 24, 2024
37e1013
[feat] Orgnism: 서버 카테고리 별 필터를 위한 컴포넌트 (컴포넌트 이름 변경 후 홈화면에 다시 추가)
jihyun-j Dec 24, 2024
5ff2486
[refector] 모달 타입 추가
jihyun-j Dec 24, 2024
b49c4db
[feat] Molecule: 오른쪽 사이드바 종류에 따라 사이드 바 제목이 변경되는 컴포넌트 추가
jihyun-j Dec 24, 2024
11c1de7
[feat] Organism: 새로운 친구요청을 볼 수 있는 사이드바 컴포넌트 추가
jihyun-j Dec 24, 2024
1cef555
[feat] Molecule: 현재 유저 또는 친구의 아바타를 클릭했을 때 모달 children으로 들어가는 컴포넌트 추가
jihyun-j Dec 24, 2024
b84fc24
[feat] Template: 메세지 사이드바에 탭 추가 및 선택한 탭에 따라 다른 리스트 정보 보여주기 기능 추가
jihyun-j Dec 24, 2024
5e31420
[feat] Organism: 오른쪽 사이드 바 컴포넌트 추가
jihyun-j Dec 24, 2024
140d355
[chore] Organism: 오른쪽 사이드 바에 들어가 유저간의 대화 요청 및 확인을 위한 컴포넌트 (로직구현 필요)
jihyun-j Dec 24, 2024
d9b9e51
[refector] FormField name props 추가
jihyun-j Dec 24, 2024
9a32e7d
[feat] Molecule: 왼쪽 사이드바에서 특정 서버를 클릭했을 때 보여지는 정보 컴포넌트
jihyun-j Dec 24, 2024
b47f4cf
[refector] Molecule: 서버 북마크 아이콘 컴포넌트 생성 및 store 수정
jihyun-j Dec 24, 2024
9c3415f
[chore] Molecule: 채팅 인풋 컴포넌트 생성 (로직구현 필요)
jihyun-j Dec 24, 2024
4825757
[refector] 서버관련 api 타입 설정
jihyun-j Dec 24, 2024
1f6d6a6
[feat] 친구 요청 유저 목록 커스텀 훅
jihyun-j Dec 24, 2024
4f3b005
[refector] api method 추가
jihyun-j Dec 24, 2024
14ea9c0
[chore] ThemeType 제거
jihyun-j Dec 24, 2024
1247628
[refector] Molecule: 홈 화면 히어로 섹션 케러셀
jihyun-j Dec 24, 2024
7276a8f
[refector] Organism/Page: 히어로 섹션 컴포넌트 생성 및 홈 화면에 추가
jihyun-j Dec 24, 2024
c7b3eaf
[feat] Organism: 회원가입 및 로그인 폼 컴포넌트 생성
jihyun-j Dec 24, 2024
e5d5f08
[feat] Templat: 회원가입 및 로그인 템플릿 추가
jihyun-j Dec 24, 2024
da7c364
[feat] Page: 회원가입 및 로그인 페이지 생성
jihyun-j Dec 24, 2024
07c8e36
[refector] 로그인 관련 인터셉터 및 스토어 리팩토링 중...(미완성)
jihyun-j Dec 24, 2024
166228c
[refector] Template: 홈 화면 레이아웃 수정
jihyun-j Dec 24, 2024
36e69da
[refector] 캠핑 테마 사진 교체 및 라우터 수정
jihyun-j Dec 24, 2024
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
16 changes: 16 additions & 0 deletions front/craco.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const path = require("path");

module.exports = {
webpack: {
alias: {
"@": path.resolve(__dirname, "src/"),
},
},
jest: {
configure: {
moduleNameMapper: {
"^@(.*)$": "<rootDir>/src$1",
},
},
},
};
18,142 changes: 0 additions & 18,142 deletions front/package-lock.json

This file was deleted.

16 changes: 9 additions & 7 deletions front/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@mui/icons-material": "^5.15.17",
"@mui/material": "^5.15.17",
"@craco/craco": "^7.1.0",
"@tanstack/react-query": "^5.45.1",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/js-cookie": "^3.0.6",
"@types/node": "^16.18.97",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
Expand All @@ -20,6 +18,7 @@
"embla-carousel-autoplay": "^8.2.1",
"embla-carousel-react": "^8.2.1",
"express": "^4.19.2",
"js-cookie": "^3.0.5",
"jwt-decode": "^4.0.0",
"openvidu-browser": "^2.30.1",
"phaser": "^3.80.1",
Expand All @@ -38,9 +37,9 @@
"zustand": "^4.5.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "react-scripts eject"
},
"eslintConfig": {
Expand All @@ -60,5 +59,8 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"craco-alias": "^3.0.1"
}
}
Binary file modified front/public/assets/map/camping/camping.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 0 additions & 3 deletions front/src/App.css

This file was deleted.

53 changes: 3 additions & 50 deletions front/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,5 @@
import { Navigate, Route, Routes } from "react-router-dom";
import "./App.css";
import AuthLayout from "./components/Auth/AuthLayout";
import PrivateRoute from "./components/Auth/PrivateRoute";
import Layout from "./components/Layout/Layout";
import Home from "./pages/Home";
import Join from "./pages/Join";
import Login from "./pages/Login";
import ServerMapWithTheme from "./components/Server/ServerMapWithTheme";

function App() {
return (
<div className="App">
<Routes>
<Route element={<AuthLayout />}>
<Route path="/login" element={<Login />} />
<Route path="/join" element={<Join />} />
</Route>
<Route element={<Layout />}>
<Route
path="/"
element={
<PrivateRoute>
<Home />
</PrivateRoute>
}
/>
<Route
path="/server/:serverId"
element={
<PrivateRoute>
<ServerMapWithTheme />
</PrivateRoute>
}
/>

<Route
path="/:theme"
element={
<PrivateRoute>
<ServerMapWithTheme />
</PrivateRoute>
}
/>
<Route path="*" element={<Navigate replace to="/" />} />
</Route>
</Routes>
</div>
);
}
const App = () => {
return <div className="relative"></div>;
};

export default App;
69 changes: 35 additions & 34 deletions front/src/api/token.ts → front/src/api/auth.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import axiosInstance from "./axiosInstance";
import Cookies from "js-cookie";
import axiosInstance from "./axios/axiosInstance";
import { AUTH_API } from "./axios/requests";

interface TokenPayload {
exp: number; // 만료 시간 (유닉스 타임스탬프)
}
Expand Down Expand Up @@ -38,31 +41,34 @@ export function getTokenExpiration(token: string): number {
}

// 토큰 검증 API
export async function validateAccessToken(token: string): Promise<boolean> {
console.log("액세스토큰:", token);
export async function validateAccessToken(
token: string
): Promise<{ isValid: boolean; userId: string | null }> {
try {
const response = await axiosInstance.post(
"/auth/api/v2/validation",
{},
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
// console.log("액세스 토큰 응답 상태(status):", response.status);
// console.log("액세스토큰 응답 데이터:", response.data);
const response = await axiosInstance({
...AUTH_API.POST_REQUEST.validation,
headers: {
Authorization: `Bearer ${token}`,
},
});

console.log("토큰 검증 응답 상태(status):", response.status);
if (response.status === 200) {
// console.log("토큰 검증 성공:", response.data);
return true;
const userId = response.data.userId;
console.log("토큰 검증 성공:", response.data);
Cookies.set("userId", userId, {
path: "/",
expires: 1 / 24,
secure: true,
});
return { isValid: true, userId };
} else {
// console.error("토큰 검증 실패:", response.data);
return false;
console.error("토큰 검증 실패:", response.data);
return { isValid: false, userId: null };
}
} catch (error) {
// console.error("토큰 검증 중 오류 발생:", error);
return false;
console.error("토큰 검증 중 오류 발생:", error);
return { isValid: false, userId: null };
}
}

Expand All @@ -72,27 +78,22 @@ export async function refreshAccessToken(
): Promise<{ accessToken: string; refreshToken: string } | null> {
// console.log("리프레시 토큰으로 새로운 액세스 토큰 요청:", refreshToken);
try {
const response = await axiosInstance.post(
"/auth/api/v1/access_token",
{ refreshToken },
{
headers: {
"Content-Type": "application/json",
},
}
);
// console.log("새로운 액세스 토큰 응답 상태(status):", response.status);
// console.log("새로운 액세스 토큰 응답 데이터 :", response.data);
const response = await axiosInstance({
...AUTH_API.POST_REQUEST.accessToken,
data: { refreshToken },
});
console.log("새로운 액세스 토큰 응답 상태(status):", response.status);
console.log("새로운 액세스 토큰 응답 데이터 :", response.data);

if (response.status === 200) {
// console.log("accessToken 갱신 성공:", response.data);
console.log("accessToken 갱신 성공:", response.data);
return response.data.data;
} else {
// console.error("accessToken 갱신 실패:", response.data);
console.error("accessToken 갱신 실패:", response.data);
return null;
}
} catch (error) {
// console.error("API 호출 중 오류 발생:", error);
console.error("API 호출 중 오류 발생:", error);
return null;
}
}
10 changes: 10 additions & 0 deletions front/src/api/axios/axiosInstance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import axios from "axios";

const axiosInstance = axios.create({
baseURL: process.env.REACT_APP_BASE_URL,
headers: {
"Content-Type": "application/json",
},
});

export default axiosInstance;
118 changes: 118 additions & 0 deletions front/src/api/axios/axiosInterceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import axios, {
AxiosError,
AxiosResponse,
InternalAxiosRequestConfig,
} from "axios";
import { refreshAccessToken, validateAccessToken } from "../auth";
import Cookies from "js-cookie";
import { LoginState } from "@/store/useLoginStore";

interface RetryConfig extends InternalAxiosRequestConfig {
_retry?: boolean;
}

const clearAuthData = () => {
localStorage.clear();
Cookies.remove("dicoTown_RefreshToken");
// Add any other cookies or local storage items that need to be cleared
};

// Axios 인터셉터 설정 함수

const interceptorSetup = (store: LoginState) => {
// 로그인 시 리프레쉬 토큰은 쿠키에 저장. 쿠키에 저장된 리프레쉬 토큰 가져오기.
const refreshToken = Cookies.get("dicoTown_RefreshToken");

// 요청 인터셉터 설정
axios.interceptors.request.use(
async (config: RetryConfig) => {
const token = store.accessToken;

console.log("요청 인터셉터 실행", token);
if (token) {
// 액세스 토큰 유효성 검사
const isTokenValid = await validateAccessToken(token);

if (!isTokenValid) {
if (refreshToken) {
const newTokens = await refreshAccessToken(refreshToken);
if (newTokens) {
// 새로운 토큰을 상태와 로컬 스토리지에 저장
store.setTokens(newTokens.accessToken, newTokens.refreshToken);
// 요청 헤더에 새로운 액세스 토큰 추가
config.headers.Authorization = `Bearer ${newTokens.accessToken}`;
console.log("새로운 토큰:", newTokens);
} else {
// 새로운 토큰 발급 실패 시 로그인 페이지로 이동
console.error(
"리프레시 토큰을 이용한 발급이 실패, 로그인 페이지로 이동"
);
store.clearTokens();
clearAuthData();
window.location.href = "/login"; // Redirect to login page

return Promise.reject(new Error("토큰이 만료되었습니다."));
}
}
} else {
// 액세스 토큰이 유효하면 요청 헤더에 추가
config.headers.Authorization = `Bearer ${token}`;
}
}
return config;
},
(error: AxiosError) => {
// 요청 에러 처리
console.error("Request Error:", error);
return Promise.reject(error);
}
);

// 응답 인터셉터 설정
axios.interceptors.response.use(
(response: AxiosResponse) => {
return response;
},
async (error: AxiosError) => {
const originalRequest = error.config as RetryConfig;
if (
error.response &&
// 인증 오류 (401) 처리
error.response.status === 401 &&
originalRequest &&
// 요청이 재시도되지 않았는지 확인
!originalRequest._retry
) {
originalRequest._retry = true;
console.log(
"응답 인터셉터 401 오류, 리프레시 토큰을 토큰을 사용해 새로운 액세스 토큰 발급 "
);
// 리프레시 토큰을 사용해 새로운 액세스 토큰 발급
if (refreshToken) {
const newTokens = await refreshAccessToken(refreshToken);
if (newTokens) {
// 새로운 토큰을 상태와 로컬 스토리지에 저장
store.setTokens(newTokens.accessToken, newTokens.refreshToken);
// 원래 요청 헤더에 새로운 액세스 토큰 추가
originalRequest.headers.Authorization = `Bearer ${newTokens.accessToken}`;
console.log("401 오류 이후의 새로운 토큰 :", newTokens);
// 원래 요청을 재시도
return axios(originalRequest);
} else {
// 새로운 토큰 발급 실패 시 로그인 페이지로 이동
console.error(
"401 오류 이후 토큰을 새로운 토큰 발급 실패, 로그인페이지로 이동"
);

return Promise.reject(new Error("토큰이 만료되었습니다."));
}
}
}
// 그 외의 응답 에러 처리
console.error("응답 오류:", error);
return Promise.reject(error);
}
);
};

export default interceptorSetup;
Loading
Loading