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

경북대 FE_이정민 6주차 과제 Step1,2 #76

Open
wants to merge 19 commits into
base: userjmmm
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
4c8e346
chore: 로그인 및 관심목록 코드 준비
Jul 31, 2024
9730e7a
refactor: useGetCategories 파일명 오타 수정
Jul 31, 2024
59cc0c8
refactor: CategoryResponseData 타입을 Pageable에 맞추어 수정
Jul 31, 2024
e403736
refactor: 옵션 선택 시 총 결제 금액이 계산되도록 수정
Jul 31, 2024
0f58ca5
refactor: 옵션 핸들링 로직 수정
Jul 31, 2024
a8c2d1c
feat: 상단 네비게이션 바에서 백엔드 API 선택하는 기능 추가
Jul 31, 2024
bfef5aa
refactor: baseUrl이 변경될 때 실시간으로 반영하도록 수정
Aug 1, 2024
9b3af7e
feat: 모든 카테고리를 받아올 수 있도록 하는 로직 추가
Aug 1, 2024
6117daf
refactor: useGetProducts.ts에서 getProductsPath의 쿼리 매개변수 순서 변경
Aug 1, 2024
c4baf5e
feat: 서버에서 받아온 token을 저장해서 로그인 상태 유지되는 로직 추가
Aug 1, 2024
8f1bb84
feat: 위시 조회시 Page<WishList>로 return 받을 수 있도록 Data 타입 추가
Aug 1, 2024
cc36364
refactor: localStorage로 로그아웃 기능 가능하도록 수정
Aug 1, 2024
8999b15
feat: 변경된 API에 맞춰 위시 삭제 기능 추가
Aug 2, 2024
d611714
feat: 토큰 디코딩해서 사용자 이름 받아오는 기능 추가
Aug 2, 2024
08a112b
feat: 주문 페이지에서 상품 옵션까지 받아오는 기능 추가
Aug 2, 2024
b8a7096
chore(ui): 카카오로 로그인 하는 로그인 페이지 UI 수정
Aug 2, 2024
1b337dc
refactor: 옵션마다 post 요청 각각 진행하도록 수정
Aug 2, 2024
11940e7
refactor: 재용님 BASE_URL 변경
Aug 2, 2024
d168557
refactor(ui): 주문하기 페이지에서 선택한 옵션을 모두 확인할 수 있게 UI 변경
Aug 2, 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
15 changes: 15 additions & 0 deletions package-lock.json

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

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@emotion/styled": "^11.11.0",
"@tanstack/react-query": "^5.24.1",
"axios": "^1.6.7",
"base-64": "^1.0.0",
"framer-motion": "^11.0.6",
"glob": "^11.0.0",
"react": "^18.2.0",
Expand All @@ -54,6 +55,7 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/base-64": "^1.0.2",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.82",
"@types/react": "^18.2.57",
Expand Down Expand Up @@ -87,4 +89,4 @@
"overrides": {
"react-refresh": "0.11.0"
}
}
}
33 changes: 33 additions & 0 deletions public/token.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OAuth Callback</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
margin-top: 50px;
}
</style>
</head>
<body>
<h1>OAuth Callback</h1>
<p id="tokenDisplay">토큰을 기다리고 있습니다...</p>
<script>
const params = new URLSearchParams(window.location.search);
const token = params.get('tokenValue');

if (token) {
// 토큰이 존재하면 로컬 스토리지에 저장
console.log('Received token:', token);
localStorage.setItem('authToken', token);
window.location.href = '/';
} else {
console.error('Token not found in query parameters.');
tokenDisplay.textContent = 'Token not found in query parameters.';
}
</script>
</body>
</html>
59 changes: 43 additions & 16 deletions src/api/hooks/categories.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,48 @@ export const categoriesMockHandler = [
}),
];

const CATEGORIES_RESPONSE_DATA = [
{
id: 2920,
name: '생일',
description: '감동을 높여줄 생일 선물 리스트',
color: '#5949a3',
imageUrl:
'https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fgift%2Fhome%2Ftheme%2F292020231106_MXMUB.png',
const CATEGORIES_RESPONSE_DATA = {
totalElements: 2,
totalPages: 1,
first: true,
last: true,
size: 10,
content: [
{
id: 2920,
name: '생일',
description: '감동을 높여줄 생일 선물 리스트',
color: '#5949a3',
imageUrl:
'https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fgift%2Fhome%2Ftheme%2F292020231106_MXMUB.png',
},
{
id: 2930,
name: '교환권',
description: '놓치면 후회할 교환권 특가',
color: '#9290C3',
imageUrl:
'https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20240131153049_5a22b137a8d346e9beb020a7a7f4254a.jpg',
},
],
number: 0,
sort: {
empty: true,
sorted: false,
unsorted: true,
},
{
id: 2930,
name: '교환권',
description: '놓치면 후회할 교환권 특가',
color: '#9290C3',
imageUrl:
'https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20240131153049_5a22b137a8d346e9beb020a7a7f4254a.jpg',
numberOfElements: 2,
pageable: {
pageNumber: 0,
pageSize: 10,
sort: {
empty: true,
sorted: false,
unsorted: true,
},
offset: 0,
paged: true,
unpaged: false,
},
];
empty: false,
};
54 changes: 46 additions & 8 deletions src/api/hooks/useGetCategories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,58 @@ import { useQuery } from '@tanstack/react-query';

import type { CategoryData } from '@/types';

import { BASE_URL, fetchInstance } from '../instance';
import { getBaseUrl, fetchInstance } from '../instance';

export type CategoryResponseData = CategoryData[];
export type CategoryResponseData = {
totalElements: number;
totalPages: number;
first: boolean;
last: boolean;
size: number;
content: CategoryData[];
number: number;
sort: {
empty: boolean;
sorted: boolean;
unsorted: boolean;
};
numberOfElements: number;
pageable: {
pageNumber: number;
pageSize: number;
sort: {
empty: boolean;
sorted: boolean;
unsorted: boolean;
};
offset: number;
paged: boolean;
unpaged: boolean;
};
empty: boolean;
};

export const getCategoriesPath = () => `${getBaseUrl()}/api/categories`;

export const getCategories = async (): Promise<CategoryData[]> => {
let allCategories: CategoryData[] = [];
let currentPage = 0;
let hasNextPage = true;

export const getCategoriesPath = () => `${BASE_URL}/api/categories`;
const categoriesQueryKey = [getCategoriesPath()];
while (hasNextPage) {
const response = await fetchInstance.get<CategoryResponseData>(
`${getCategoriesPath()}?page=${currentPage}&size=100`
);
allCategories = [...allCategories, ...response.data.content];
hasNextPage = !response.data.last;
currentPage++;
}
Comment on lines +38 to +50
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currentPage, hasNextPage가 getCategories 함수의 책임일까요?


export const getCategories = async () => {
const response = await fetchInstance.get<CategoryResponseData>(getCategoriesPath());
return response.data;
return allCategories;
};

export const useGetCategories = () =>
useQuery({
queryKey: categoriesQueryKey,
queryKey: ['categories'],
queryFn: getCategories,
});
4 changes: 2 additions & 2 deletions src/api/hooks/useGetProductDetail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useSuspenseQuery } from '@tanstack/react-query';

import type { ProductData } from '@/types';

import { BASE_URL, fetchInstance } from '../instance';
import { getBaseUrl, fetchInstance } from '../instance';

export type ProductDetailRequestParams = {
productId: string;
Expand All @@ -12,7 +12,7 @@ type Props = ProductDetailRequestParams;

export type GoodsDetailResponseData = ProductData;

export const getProductDetailPath = (productId: string) => `${BASE_URL}/api/products/${productId}`;
export const getProductDetailPath = (productId: string) => `${getBaseUrl()}/api/products/${productId}`;

export const getProductDetail = async (params: ProductDetailRequestParams) => {
const response = await fetchInstance.get<GoodsDetailResponseData>(
Expand Down
4 changes: 2 additions & 2 deletions src/api/hooks/useGetProductOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { useSuspenseQuery } from '@tanstack/react-query';

import type { ProductOptionsData } from '@/types';

import { BASE_URL, fetchInstance } from '../instance';
import {getBaseUrl, fetchInstance } from '../instance';
import type { ProductDetailRequestParams } from './useGetProductDetail';

type Props = ProductDetailRequestParams;

export type ProductOptionsResponseData = ProductOptionsData[];

export const getProductOptionsPath = (productId: string) =>
`${BASE_URL}/api/products/${productId}/options`;
`${getBaseUrl()}/api/products/${productId}/options`;

export const getProductOptions = async (params: ProductDetailRequestParams) => {
const response = await fetchInstance.get<ProductOptionsResponseData>(
Expand Down
15 changes: 9 additions & 6 deletions src/api/hooks/useGetProducts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {

import type { ProductData } from '@/types';

import { BASE_URL } from '../instance';
import { getBaseUrl } from '../instance';
import { fetchInstance } from './../instance/index';

type RequestParams = {
Expand Down Expand Up @@ -35,12 +35,14 @@ type ProductsResponseRawData = {
export const getProductsPath = ({ categoryId, pageToken, maxResults }: RequestParams) => {
const params = new URLSearchParams();

params.append('categoryId', categoryId);
if (pageToken)
params.append('page', pageToken);
if (maxResults)
params.append('size', maxResults.toString());
params.append('sort', 'name,asc');
if (pageToken) params.append('page', pageToken);
if (maxResults) params.append('size', maxResults.toString());
params.append('categoryId', categoryId);

return `${BASE_URL}/api/products?${params.toString()}`;
return `${getBaseUrl()}/api/products?${params.toString()}`;
};

export const getProducts = async (params: RequestParams): Promise<ProductsResponseData> => {
Expand All @@ -58,6 +60,7 @@ export const getProducts = async (params: RequestParams): Promise<ProductsRespon
};

type Params = Pick<RequestParams, 'maxResults' | 'categoryId'> & { initPageToken?: string };

export const useGetProducts = ({
categoryId,
maxResults = 20,
Expand All @@ -71,4 +74,4 @@ export const useGetProducts = ({
initialPageParam: initPageToken,
getNextPageParam: (lastPage) => lastPage.nextPageToken,
});
};
};
22 changes: 18 additions & 4 deletions src/api/instance/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { QueryClient } from '@tanstack/react-query';
import type { AxiosInstance, AxiosRequestConfig } from 'axios';
import axios from 'axios';

let BASE_URL = localStorage.getItem('baseURL') || 'https://api.example.com';

Comment on lines +5 to +6
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let으로 선언하는 이유가 무엇일까요?
BASE_URL이 변경될 수 있는건가요?
해당 파일 내에서 let이 자주 사용되는데, 변경의 추적이 어려우므로 const를 사용해 주세요!

const initInstance = (config: AxiosRequestConfig): AxiosInstance => {
const instance = axios.create({
timeout: 5000,
Expand All @@ -16,10 +18,8 @@ const initInstance = (config: AxiosRequestConfig): AxiosInstance => {
return instance;
};

export const BASE_URL = 'https://api.example.com';
// TODO: 추후 서버 API 주소 변경 필요
export const fetchInstance = initInstance({
baseURL: 'https://api.example.com',
export let fetchInstance = initInstance({
baseURL: BASE_URL,
});

export const queryClient = new QueryClient({
Expand All @@ -32,3 +32,17 @@ export const queryClient = new QueryClient({
},
},
});

export const updateBaseUrl = (newBaseUrl: string) => {
BASE_URL = newBaseUrl;
localStorage.setItem('baseURL', newBaseUrl);
fetchInstance = initInstance({
baseURL: BASE_URL,
});
// 선택한 이름마다 잘 받아오는지 확인용, 추후 삭제 예정
console.log('Updated fetchInstance:', fetchInstance.defaults.baseURL);
};

export const getBaseUrl = () => {
return localStorage.getItem('baseURL') || 'https://api.example.com';
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ export const CategoryProductsSection = ({ categoryId }: Props) => {
categoryId,
});

if (isLoading) return <LoadingView />;
if (isError) return <TextView>에러가 발생했습니다.</TextView>;
if (!data) return <></>;
if (data.pages[0].products.length <= 0) return <TextView>상품이 없어요.</TextView>;
if (isLoading)
return <LoadingView />;
if (isError)
return <TextView>에러가 발생했습니다.</TextView>;
if (!data)
return <></>;
if (data.pages[0].products.length <= 0)
return <TextView>상품이 없어요.</TextView>;

const flattenGoodsList = data.pages.map((page) => page?.products ?? []).flat();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type Props = {

export const CountOptionItem = ({
name,
minValues = 1,
minValues = 0,
maxValues = 100,
value,
onChange,
Expand Down
Loading