Skip to content

Commit

Permalink
전남대 FE_강호정 6주차 과제 (#113)
Browse files Browse the repository at this point in the history
* docs: step4 질문에 대한 답변 작성

* docs: 질문에 대한 답 보충

* feat: baseURL 수정

* refactor: 카멜케이스 전부 스네이크 케이스로 변경

* docs: README.md파일 수정

* feat: api명세에 따라 data구조 수정

* fix: 카테고리 페이지로 안넘어가는 오류 해결

* fix: 카테고리 페이지로 넘어가지 않는 오류 해결

* fix: 카테고리별 상품 목록 나오게 설정

* fix: 상품 상세페이지로 넘어가도록 수정

* fix: 결제 페이지로 넘어가지 않는 오류 수정

* fix: 위시 리스트 관련 api수정

* CI 추가

* ci수정

* deploy추가

* 들여쓰기 수정

* feat: gh-pages설치

* ci.yml파일 수정

* ci.yml파일 수정

* fix: error수정

* github.io로 수정

* https에서 http로 요청 에러 해결

* https에서 http로 요청 에러 해결
  • Loading branch information
hojeong26 authored Aug 7, 2024
1 parent 0be0b73 commit ebe3838
Show file tree
Hide file tree
Showing 29 changed files with 4,719 additions and 5,200 deletions.
33 changes: 33 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: CI

on:
push:
branches:
- step1
pull_request:
branches:
- hojeong26
# paths: // path는 특정 폴더에 변경사항이 생겼을 때 작동하도록 설정할 수 있음
# - "services/**"

jobs:
deploy:
runs-on: ubuntu-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v2

- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '18'

- name: Build
run: npm run build

- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.TOKEN }}
publish_dir: ./build
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ yarn-debug.log*
yarn-error.log*

.yaml

# Local Netlify folder
.netlify
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,13 @@
4. 페인팅 : 레이아웃을 이용하여 계산된 위치에 요소들을 표시하는 과정

=> 로더가 서버로부터 전달받은 리소스 스트림을 읽어오고 파서가 DOM Tree와 CSSOM Tree를 생성한다. 여기서 렌더링에 필요한 노드만 선택하여 페이지를 렌더링한다.


**[api명세](https://alive-tail-1fa.notion.site/API-778e7fee2b6c45f4bf19c06ac1e15461?pvs=4)**

**<백엔드 분이 주신 url>**

- 아래 endpoint로 접속해볼 수 있습니다!
- 52.78.81.37:8080
- ex: http://52.78.81.37:8080/api/products?sort=name,asc&category_id=1

9,458 changes: 4,483 additions & 4,975 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
"^axios$": "axios/dist/node/axios.cjs"
}
},
"homepage": "https://hojeong26.github.io/react-deploy",
"scripts": {
"start:mock": "cross-env REACT_APP_RUN_MSW=true npm run start",
"start": "craco start",
"build": "craco build",
"test": "craco test",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
"build-storybook": "storybook build",
"predeploy": "npm run build",
"deploy": "gh-pages -d build"
},
"browserslist": {
"production": [
Expand Down Expand Up @@ -45,6 +48,7 @@
"react-router-dom": "^6.22.1"
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@craco/craco": "^7.1.0",
"@emotion/eslint-plugin": "^11.11.0",
"@storybook/addon-essentials": "^7.6.17",
Expand Down Expand Up @@ -79,6 +83,7 @@
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-simple-import-sort": "^12.0.0",
"eslint-plugin-storybook": "^0.8.0",
"gh-pages": "^6.1.1",
"jest": "^29.7.0",
"msw": "^1.3.3",
"prettier": "^3.2.5",
Expand Down
7 changes: 6 additions & 1 deletion src/api/hooks/useGetCategorys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import type { CategoryData } from '@/types';

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

export type CategoryResponseData = CategoryData[];
export type CategoryResponseData = {
status: number;
categories: CategoryData[];
timestamp: string;
success: boolean;
};

export const getCategoriesPath = () => `${BASE_URL}/api/categories`;
const categoriesQueryKey = [getCategoriesPath()];
Expand Down
23 changes: 12 additions & 11 deletions src/api/hooks/useGetProductDetail.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { useSuspenseQuery } from '@tanstack/react-query';

import { useQuery } from '@tanstack/react-query';
import type { ProductData } from '@/types';

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

export type ProductDetailRequestParams = {
Expand All @@ -10,21 +8,24 @@ export type ProductDetailRequestParams = {

type Props = ProductDetailRequestParams;

export type GoodsDetailResponseData = ProductData;
export type GoodsDetailResponseData = {
products: ProductData;
};

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

export const getProductDetail = async (params: ProductDetailRequestParams) => {
const response = await fetchInstance.get<GoodsDetailResponseData>(
getProductDetailPath(params.productId),
);
const url = getProductDetailPath(params.productId);
const response = await fetchInstance.get<GoodsDetailResponseData>(url);

return response.data;
return response.data.products;
};

export const useGetProductDetail = ({ productId }: Props) => {
return useSuspenseQuery({
queryKey: [getProductDetailPath(productId)],
queryFn: () => getProductDetail({ productId }),
return useQuery({
queryKey: ['productDetail', productId],
queryFn: async () => {
return getProductDetail({ productId });
},
});
};
4 changes: 3 additions & 1 deletion src/api/hooks/useGetProductOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import type { ProductDetailRequestParams } from './useGetProductDetail';

type Props = ProductDetailRequestParams;

export type ProductOptionsResponseData = ProductOptionsData[];
export type ProductOptionsResponseData = {
options: ProductOptionsData[];
};

export const getProductOptionsPath = (productId: string) =>
`${BASE_URL}/api/products/${productId}/options`;
Expand Down
57 changes: 28 additions & 29 deletions src/api/hooks/useGetProducts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,64 +11,63 @@ import { fetchInstance } from './../instance/index';

type RequestParams = {
categoryId: string;
pageToken?: string;
maxResults?: number;
page_token?: string;
max_results?: number;
};

type ProductsResponseData = {
products: ProductData[];
nextPageToken?: string;
pageInfo: {
totalResults: number;
resultsPerPage: number;
next_page_token?: string;
page_info: {
total_results: number;
results_per_page: number;
};
};

type ProductsResponseRawData = {
content: ProductData[];
number: number;
totalElements: number;
size: number;
last: boolean;
products: ProductData[];
};

export const getProductsPath = ({ categoryId, pageToken, maxResults }: RequestParams) => {
export const getProductsPath = ({ categoryId, page_token, max_results }: RequestParams) => {
const params = new URLSearchParams();

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

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

export const getProducts = async (params: RequestParams): Promise<ProductsResponseData> => {
const response = await fetchInstance.get<ProductsResponseRawData>(getProductsPath(params));
const url = getProductsPath(params);
const response = await fetchInstance.get<ProductsResponseRawData>(url);
const data = response.data;

console.log('Fetched Data:', data);

return {
products: data.content,
nextPageToken: data.last === false ? (data.number + 1).toString() : undefined,
pageInfo: {
totalResults: data.totalElements,
resultsPerPage: data.size,
products: data.products.filter((product) => product.id && product.name && product.price),
next_page_token: undefined,
page_info: {
total_results: data.products.length,
results_per_page: data.products.length,
},
};
};

type Params = Pick<RequestParams, 'maxResults' | 'categoryId'> & { initPageToken?: string };
type Params = Pick<RequestParams, 'max_results' | 'categoryId'> & { init_page_token?: string };
export const useGetProducts = ({
categoryId,
maxResults = 20,
initPageToken,
max_results = 20,
init_page_token,
}: Params): UseInfiniteQueryResult<InfiniteData<ProductsResponseData>> => {
return useInfiniteQuery({
queryKey: ['products', categoryId, maxResults, initPageToken],
queryFn: async ({ pageParam = initPageToken }) => {
return getProducts({ categoryId, pageToken: pageParam, maxResults });
queryKey: ['products', categoryId, max_results, init_page_token],
queryFn: async ({ pageParam = init_page_token }) => {
return getProducts({ categoryId, page_token: pageParam, max_results });
},
initialPageParam: initPageToken,
getNextPageParam: (lastPage) => lastPage.nextPageToken,
initialPageParam: init_page_token,
getNextPageParam: (lastPage) => lastPage.next_page_token,
});
};
4 changes: 2 additions & 2 deletions src/api/instance/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ const initInstance = (config: AxiosRequestConfig): AxiosInstance => {
return instance;
};

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

export const queryClient = new QueryClient({
Expand Down
8 changes: 5 additions & 3 deletions src/api/wish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { fetchInstance } from './instance';

const BASE_PATH = '/api/wishes';

export const addWish = async (productId: number) => {
export const addWish = async (product_id: number) => {
try {
const response = await fetchInstance.post(`${BASE_PATH}`, { productId });
const response = await fetchInstance.post(`${BASE_PATH}`, { product_id });
console.log('addWish response:', response); // 추가된 로그
return response.status === 201;
} catch (error) {
console.error('Error:', error);
Expand All @@ -15,7 +16,8 @@ export const addWish = async (productId: number) => {
export const getWishList = async () => {
try {
const response = await fetchInstance.get(`${BASE_PATH}`);
return response.data;
console.log('Fetched Wish List:', response.data); // 데이터 확인용 로그 추가
return response.data.wishlist;
} catch (error) {
console.error('Error:', error);
throw new Error('관심 상품 목록 조회 중 오류가 발생했습니다.');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,6 @@ const Title = styled.h1`
}
`;

export const getCurrentCategory = (categoryId: string, categoryList: CategoryData[]) => {
return categoryList.find((category) => category.id.toString() === categoryId);
export const getCurrentCategory = (category_id: string, categoryList: CategoryData[]) => {
return categoryList.find((category) => category.id.toString() === category_id);
};
21 changes: 15 additions & 6 deletions src/components/features/Category/CategoryProductsSection/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import styled from '@emotion/styled';
import { Link } from 'react-router-dom';

import { useGetProducts } from '@/api/hooks/useGetProducts';
import { DefaultGoodsItems } from '@/components/common/GoodsItem/Default';
import { Container } from '@/components/common/layouts/Container';
Expand All @@ -20,12 +19,22 @@ export const CategoryProductsSection = ({ categoryId }: Props) => {
categoryId,
});

// console.log('Fetched Data:', data);
// console.log('isError:', isError);
// console.log('isLoading:', isLoading);

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

const flattenGoodsList = data.pages
.flatMap((page) => page?.products ?? [])
.filter((product) => product && product.id && product.name && product.price);

console.log('Flatten Goods List:', flattenGoodsList);

const flattenGoodsList = data.pages.map((page) => page?.products ?? []).flat();
if (flattenGoodsList.length === 0) return <TextView>유효한 상품이 없어요.</TextView>;

return (
<Wrapper>
Expand All @@ -37,11 +46,11 @@ export const CategoryProductsSection = ({ categoryId }: Props) => {
}}
gap={16}
>
{flattenGoodsList.map(({ id, imageUrl, name, price }) => (
{flattenGoodsList.map(({ id, image_url, name, price }) => (
<Link key={id} to={getDynamicPath.productsDetail(id)}>
<DefaultGoodsItems
key={id}
imageSrc={imageUrl}
imageSrc={image_url}
title={name}
amount={price}
subtitle={''}
Expand Down
4 changes: 3 additions & 1 deletion src/components/features/Goods/Detail/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ type Props = ProductDetailRequestParams;
export const GoodsDetailHeader = ({ productId }: Props) => {
const { data: detail } = useGetProductDetail({ productId });

if (!detail) return <div>No product details found.</div>;

return (
<Wrapper>
<GoodsImage src={detail.imageUrl} alt={detail.name} />
<GoodsImage src={detail.image_url} alt={detail.name} />
<InfoWrapper>
<Title>{detail.name}</Title>
<Price>{detail.price}</Price>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,25 @@ import { addWish } from '@/api/wish';

type Props = {
name: string;
productId: number;
minValues?: number;
maxValues?: number;
product_id: number;
min_values?: number;
max_values?: number;
value: string;
onChange: (value: string) => void;
};

export const CountOptionItem = ({
name,
productId,
minValues = 1,
maxValues = 100,
product_id,
min_values = 1,
max_values = 100,
value,
onChange,
}: Props) => {
const { getInputProps, getIncrementButtonProps, getDecrementButtonProps } = useNumberInput({
step: 1,
min: minValues,
max: maxValues,
min: min_values,
max: max_values,
defaultValue: value,
onChange: (valueAsString) => {
onChange(valueAsString);
Expand All @@ -40,7 +40,8 @@ export const CountOptionItem = ({
const wishhandler = async () => {
setIsChecked((prev) => !prev);
try {
const success = await addWish(productId);
const success = await addWish(product_id);
console.log('addWish success:', success);
if (success) {
alert('관심 등록 완료');
} else {
Expand Down
Loading

0 comments on commit ebe3838

Please sign in to comment.