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 이은진 3주차 과제 Step2 #43

Open
wants to merge 8 commits into
base: eunjin210
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 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
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# 3주차 상품리스트 구현 - api
## step1 📝구현사항
+ 첨부된 oas.yaml 파일을 토대로 Request, Response Type을 정의하기
+ axios 라이브러리 설치
* 메인페이지 - theme
* /api/v1/themes API를 사용하여 Section을 구현
* API는 Axios또는 React Query 등을 모두 활용해서 구현 가능
* 메인 페이지 - 실시간 급상승 선물랭킹
* /api/v1/ranking/products API를 사용하여 Section을 구현
* 필터 조건을 선택 하면 해당 조건에 맞게 API를 요청하여 보여지게 해야함
* Theme 페이지 - header
* url의 pathParams와 /api/v1/themes API를 사용하여 Section을 구현
* themeKey가 잘못 된 경우 메인 페이지로 연결
* Theme 페이지 - 상품 목록 섹션
* /api/v1/themes/{themeKey}/products API를 사용하여 상품 목록을 구현
*API 요청 시 한번에 20개의 상품 목록이 내려오게 구현
29 changes: 16 additions & 13 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"dependencies": {
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"axios": "^1.7.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.1"
Expand Down
38 changes: 38 additions & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// src/api/index.ts
import axios from 'axios';

import type { GoodsData, RankingFilterOption, ThemeData } from '@/types';

const api = axios.create({
baseURL: 'https://react-gift-mock-api-eunjin.vercel.app',
});

export const fetchTheme = async (): Promise<ThemeData[]> => {
const response = await api.get<{ themes: ThemeData[] }>('/api/v1/themes');
return response.data.themes;
};

export const fetchThemes = async (themeKey: string): Promise<ThemeData> => {
console.log('Fetching theme with key:', themeKey);
const response = await api.get<ThemeData>(`/api/v1/themes/${themeKey}`);
console.log('Fetched theme data:', response.data);
return response.data;
};

export const getThemeProducts = async (themeKey: string): Promise<GoodsData[]> => {
const response = await api.get<{ products: GoodsData[] }>(`/api/v1/themes/${themeKey}/products`, {
params: {
maxResults: 20,
},
});
return response.data.products;
};

export const getRankingGoods = async (filterOption: RankingFilterOption): Promise<GoodsData[]> => {
const response = await api.get<{ products: GoodsData[] }>('/api/v1/ranking/products', {
params: filterOption,
});
return response.data.products;
};

export default api;
37 changes: 31 additions & 6 deletions src/components/features/Home/GoodsRankingSection/index.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,46 @@
import styled from '@emotion/styled';
import { useState } from 'react';
import { useEffect, useState } from 'react';

import { getRankingGoods } from '@/api';
import { Container } from '@/components/common/layouts/Container';
import { breakpoints } from '@/styles/variants';
import type { RankingFilterOption } from '@/types';
import { GoodsMockList } from '@/types/mock';
import type { GoodsData, RankingFilterOption } from '@/types';

import { GoodsRankingFilter } from './Filter';
import { GoodsRankingList } from './List';

export const GoodsRankingSection = () => {
export const GoodsRankingSection: React.FC = () => {
const [filterOption, setFilterOption] = useState<RankingFilterOption>({
targetType: 'ALL',
rankType: 'MANY_WISH',
});
const [goodsList, setGoodsList] = useState<GoodsData[]>([]);
const [errorMessage, setErrorMessage] = useState<string | null>(null); // 변수 이름 변경

// GoodsMockData를 21번 반복 생성
useEffect(() => {
const fetchGoods = async () => {
setErrorMessage(null);

Choose a reason for hiding this comment

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

이미 null 인 상태라 null 로 초기화 할 필요 없을거 같습니다.

Suggested change
setErrorMessage(null);

Copy link
Author

Choose a reason for hiding this comment

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

시간 내주셔서 리뷰해주심에 항상 감사드립니다 !

실시간 급상승 랭킹에서 남성이 위시로 받은 페이지에서, 아이템을 가져올 때 오류가 발생하여
catch (err) {
setErrorMessage('데이터를 불러오는데 실패하였습니다.');
} 코드가 실행됩니다.
이후 다른 페이지를 누르면 불러 올 상품 데이터가 있더라도, ErrorMessage에 '데이터를 불러오는데 실패하였습니다.' 이 문자열이 들어있어 제대로 상품들이 출력 되지 않아 코드에 setErrorMessage(null); 이 문장을 넣어 해결하였습니다!

위 방식으로 문제를 해결하였는데, 혹시 이 방법이 setErrorMessage(null); 이 코드를 없애고도 위 문제를 해결 할 수 있거나, 더 좋은 방식이 있다면 답변 부탁드립니다

try {
const data = await getRankingGoods(filterOption);
setGoodsList(data);
} catch (err) {
setErrorMessage('데이터를 불러오는데 실패하였습니다.');
}
};

fetchGoods();
}, [filterOption]);

return (
<Wrapper>
<Container>
<Title>실시간 급상승 선물랭킹</Title>
<GoodsRankingFilter filterOption={filterOption} onFilterOptionChange={setFilterOption} />
<GoodsRankingList goodsList={GoodsMockList} />
{errorMessage ? (
<ErrorMessage>{errorMessage}</ErrorMessage>
) : (
<GoodsRankingList goodsList={goodsList} />
)}
</Container>
</Wrapper>
);
Expand Down Expand Up @@ -50,3 +68,10 @@ const Title = styled.h2`
line-height: 50px;
}
`;

const ErrorMessage = styled.div`
color: #ff0000;
text-align: center;
font-size: 16px;
margin-top: 20px;
`;
101 changes: 28 additions & 73 deletions src/components/features/Home/ThemeCategorySection/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
import styled from '@emotion/styled';
import { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';

import { fetchTheme } from '@/api';
import { Container } from '@/components/common/layouts/Container';
import { Grid } from '@/components/common/layouts/Grid';
import { getDynamicPath } from '@/routes/path';
import { breakpoints } from '@/styles/variants';
import type { ThemeData } from '@/types';

import { ThemeCategoryItem } from './ThemeCategoryItem';

export const ThemeCategorySection = () => {
export const ThemeCategorySection: React.FC = () => {
const [themes, setThemes] = useState<ThemeData[]>([]);

useEffect(() => {
const fetchThemesData = async () => {
try {
const data = await fetchTheme();
setThemes(data);
} catch (error) {
console.error('Failed to fetch themes:', error);
}
};

fetchThemesData();
}, []);

themes.map((theme)=> console.log(theme.key));
return (
<Wrapper>
<Container>
Expand All @@ -18,78 +37,14 @@ export const ThemeCategorySection = () => {
md: 6,
}}
>
<Link to={getDynamicPath.theme('life_small_gift')}>
<ThemeCategoryItem
image="https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fgift%2Fhome%2Ftheme%2F292020231106_MXMUB.png"
label="생일"
/>
</Link>
<Link to={getDynamicPath.theme('life_small_gift')}>
<ThemeCategoryItem
image="https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fgift%2Fhome%2Ftheme%2F292020231106_MXMUB.png"
label="졸업선물"
/>
</Link>
<Link to={getDynamicPath.theme('life_small_gift')}>
<ThemeCategoryItem
image="https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fgift%2Fhome%2Ftheme%2F292020231106_MXMUB.png"
label="스몰럭셔리"
/>
</Link>
<Link to={getDynamicPath.theme('life_small_gift')}>
<ThemeCategoryItem
image="https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fgift%2Fhome%2Ftheme%2F292020231106_MXMUB.png"
label="명품선물"
/>
</Link>
<Link to={getDynamicPath.theme('life_small_gift')}>
<ThemeCategoryItem
image="https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fgift%2Fhome%2Ftheme%2F292020231106_MXMUB.png"
label="결혼/집들이"
/>
</Link>
<Link to={getDynamicPath.theme('life_small_gift')}>
<ThemeCategoryItem
image="https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fgift%2Fhome%2Ftheme%2F292020231106_MXMUB.png"
label="따뜻한선물"
/>
</Link>
<Link to={getDynamicPath.theme('life_small_gift')}>
<ThemeCategoryItem
image="https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fgift%2Fhome%2Ftheme%2F292020231106_MXMUB.png"
label="가벼운선물"
/>
</Link>
<Link to={getDynamicPath.theme('life_small_gift')}>
<ThemeCategoryItem
image="https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fgift%2Fhome%2Ftheme%2F292020231106_MXMUB.png"
label="팬심저격"
/>
</Link>
<Link to={getDynamicPath.theme('life_small_gift')}>
<ThemeCategoryItem
image="https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fgift%2Fhome%2Ftheme%2F292020231106_MXMUB.png"
label="교환권"
/>
</Link>
<Link to={getDynamicPath.theme('life_small_gift')}>
<ThemeCategoryItem
image="https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fgift%2Fhome%2Ftheme%2F292020231106_MXMUB.png"
label="건강/비타민"
/>
</Link>
<Link to={getDynamicPath.theme('life_small_gift')}>
<ThemeCategoryItem
image="https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fgift%2Fhome%2Ftheme%2F292020231106_MXMUB.png"
label="과일/한우"
/>
</Link>
<Link to={getDynamicPath.theme('life_small_gift')}>
<ThemeCategoryItem
image="https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fgift%2Fhome%2Ftheme%2F292020231106_MXMUB.png"
label="출산/키즈"
/>
</Link>
{themes.map((theme) => (
<Link key={theme.id} to={getDynamicPath.theme(theme.key)}>
<ThemeCategoryItem
image={theme.imageURL} // API에서 가져온 이미지 URL 사용
label={theme.label}
/>
</Link>
))}
</Grid>
</Container>
</Wrapper>
Expand Down
Loading