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

Feature/add bookmark #81

Open
wants to merge 4 commits into
base: front
Choose a base branch
from
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
59 changes: 59 additions & 0 deletions client/package-lock.json

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

4 changes: 4 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.7.1",
"@fortawesome/free-solid-svg-icons": "^6.7.1",
"@fortawesome/react-fontawesome": "^0.2.2",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.7.7",
"framer-motion": "^11.11.10",
"jwt-decode": "^4.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-lazyload": "^3.2.1",
Expand Down
Binary file added client/public/images/search-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions client/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import SignInPage from "./pages/SignInPage";
import Mypage from "./pages/mypage";
import SignUpPage from "./pages/SignUpPage";
import DetailAlcoholPage from "./pages/DetailAlcoholPage";
import SearchPage from "./components/search/search";
import ResultsPage from "./pages/searchresult";

import { BrowserRouter, Router, Route, Routes, Link } from "react-router-dom";

function App() {
Expand All @@ -17,6 +20,8 @@ function App() {
<Route path="/signIn" element={<SignInPage />} />
<Route path="/signUp" element={<SignUpPage />} />
<Route path="/mypage" element={<Mypage />} />
<Route path="/" element={<SearchPage />} />
<Route path="/results" element={<ResultsPage />} />
<Route path="/alcohol/:id" element={<DetailAlcoholPage />} />
</Routes>
</BrowserRouter>
Expand Down
2 changes: 0 additions & 2 deletions client/src/api/alcoholListApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,9 @@ const alcoholListApi = async (category, page, filters = {}) => {
// console.log("서버 응답 메시지:", response.data.message);
return response.data.data; // 서버에서 필요한 데이터 반환
} catch (error) {
// 서버 응답이 있을 경우 처리
if (error.response && error.response.data && error.response.data.message) {
// console.log(`서버 에러 메시지: ${error.response.data.message}`);
} else {
// 서버 응답이 없거나 다른 종류의 에러일 경우 기본 처리
console.log(`API 요청 실패: ${error.message}`);
alert(`API 요청 실패: ${error.message}`);
}
Expand Down
29 changes: 7 additions & 22 deletions client/src/api/apiClientToken.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,18 @@ apiClientToken.interceptors.request.use((config) => {
return config;
});

// 응답 인터셉터: 토큰 갱신 로직 추가
// 응답 인터셉터: Access Token 만료 시 로그인 리디렉션 처리
apiClientToken.interceptors.response.use(
(response) => response, // 정상 응답은 그대로 전달
async (error) => {
const originalRequest = error.config;
if (error.response.status === 401) {
console.warn("Access Token 만료 또는 인증 실패");

// Access Token 만료 시 Refresh Token으로 갱신
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
alert("세션이 만료되었습니다. 다시 로그인해주세요.");

const refreshToken = localStorage.getItem("refreshToken");
if (refreshToken) {
try {
const { data } = await axios.post("/auth/refresh", {
refreshToken,
});

const newAccessToken = data.accessToken;
localStorage.setItem("accessToken", newAccessToken);
originalRequest.headers["Authorization"] = `Bearer ${newAccessToken}`;

return apiClientToken(originalRequest); // 원래 요청 재전송
} catch (refreshError) {
console.log("Token refresh failed:", refreshError);
// 필요 시 로그아웃 처리
}
}
localStorage.removeItem("accessToken");
localStorage.removeItem("refreshToken");
window.location.href = "/signIn";
}

return Promise.reject(error);
Expand Down
2 changes: 1 addition & 1 deletion client/src/api/logoutApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const logoutApi = async (refreshToken) => {
}
);

console.log("서버 응답 메시지:", response.data.message);
// console.log("서버 응답 메시지:", response.data);
return response.data; // 성공 시 응답 데이터 반환
} catch (error) {
console.log("로그아웃 API 에러:", error.name, "/", error.message);
Expand Down
119 changes: 119 additions & 0 deletions client/src/api/mypageapi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import axios from "axios";

// Axios 인스턴스 생성
const apiClient = axios.create({
baseURL: process.env.REACT_APP_API_ROUTE, // .env에서 설정한 baseURL 사용
});

// 인증 토큰 추가 (요청 인터셉터 활용)
apiClient.interceptors.request.use((config) => {
const token = localStorage.getItem("accessToken"); // 토큰을 로컬 스토리지에서 가져옴
console.log(!!token)
if (token) {
config.headers.Authorization = `Bearer ${token}`; // 인증 헤더 추가
}
return config;
}, (error) => {
return Promise.reject(error);
});

// 1. 내 정보 확인
const getUserInfo = async () => {
try {
const response = await apiClient.get("api/mypage/info");
if (response.data.code === "200") {
console.log(response.data.data)
return response.data.data; // 내 정보 반환
} else {
throw new Error(response.data.message || "내 정보 조회 실패");
}
} catch (error) {
console.error("내 정보 조회 중 에러 발생:", error.message);
throw error;
}
};

// 2. 북마크 확인
const getUserBookmarks = async () => {
try {
const response = await apiClient.get("api/mypage/bookmarks");
if (response.data.code === "200") {
return response.data.data; // 북마크 반환
} else {
throw new Error(response.data.message || "북마크 조회 실패");
}
} catch (error) {
console.error("북마크 조회 중 에러 발생:", error.message);
throw error;
}
};

// 3. 이름 수정
const updateUserInfo = async ({ token, userId, nickname }) => {
try {
const response = await apiClient.patch("api/mypage/updateInfo", {
userId, nickname // headers는 axios의 config로 처리
}, {
headers: { Authorization: `Bearer ${token}` }
});

if (response.data.code === "200") {
console.log("닉네임 수정 성공:", response.data.data);
return response.data.data;
} else {
throw new Error(response.data.message || "닉네임 수정 실패");
}
} catch (error) {
console.error("닉네임 수정 중 오류 발생:", error.message);
throw error;
}
};

// 4. 선호 도수 설정
const updateUserPreference = async ({ token, preferenceLevel }) => {
try {
// PATCH 요청 보내기
const response = await apiClient.patch("api/mypage/updatePreference", {
preferenceLevel // preferenceLevel만 요청 본문에 포함
}, {
headers: { Authorization: `Bearer ${token}` } // Authorization 헤더 포함
});

// 응답 코드가 200일 경우 성공 처리
if (response.data.code === "200") {
console.log("선호 도수 설정 성공:", response.data.data);
return response.data.data; // 수정된 데이터 반환
} else {
throw new Error(response.data.message || "선호 도수 설정 실패");
}
} catch (error) {
console.error("선호 도수 수정 중 오류 발생:", error.message);
throw error; // 에러 재전파
}
};



// 5. 프로필 사진 수정
const updateUserProfileImage = async (formData) => {
try {
const response = await apiClient.patch("api/mypage/updateProfileImage", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
withCredentials: true, // CORS가 설정된 서버와 쿠키를 공유할 경우 필요
});
if (response.data.code === "200") {
console.log("프로필 사진 수정 성공:", response.data.data);
return response.data.data; // 성공적으로 수정된 데이터 반환
} else {
throw new Error(response.data.message || "프로필 사진 수정 실패");
}
} catch (error) {
console.error("프로필 사진 수정 중 에러 발생:", error.message);
throw error;
}
};

// API 함수들을 export
export { getUserInfo, getUserBookmarks, updateUserInfo, updateUserPreference, updateUserProfileImage };
25 changes: 25 additions & 0 deletions client/src/api/searchapi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import axios from "axios";

// Axios 인스턴스 생성
const apiClient = axios.create({
baseURL: process.env.REACT_APP_API_ROUTE, // .env에 설정된 baseURL
});

const searchapi = async (drinkName) => {
try {
const response = await apiClient.get(`/api/post/search?drinkName=${drinkName}`);

// 성공 응답 처리
if (response.data.code === "200") {
console.log(response)
return response.data.data.searchResult.content; // 검색 결과 반환
} else {
throw new Error(response.data.message || "전통주 검색 실패");
}
} catch (error) {
console.error("API 호출 중 에러 발생:", error.message);
throw error; // 에러를 상위 호출로 전달
}
};

export default searchapi;
Loading