Skip to content

Commit

Permalink
충남대 FE_강병현_3주차 과제 Step4 (#77)
Browse files Browse the repository at this point in the history
* init

* docs(README): update feature list

* chore: install axios

* feat: define separated request and response types and improve file structure

* chore: update absolute path

* feat: add useFetch custom hooks

* feat: add Spinner component

* feat: add ErrorMessage component

* chore: add .env file with REACT_APP_BASE_URL

* feat: implement SectionHeader component using /api/v1/themes API

* feat: Add real-time trending gift ranking section

* design: change ErrorMessage component

* feat: display 'No products to show!' message when there are no products

* feat: redirect to home page if themeKey is invalid

* feat: implement product list API with pagination for 20 items

* feat: add FilterProvider context and useFilter hook to Storybook stories

* docs: update feature list

* feat: add StatusHandler component

* feat: add UI handling for empty data state

* feat: handle different error messages based on HTTP status codes

* docs: update feature list

* docs: update feature list

* refactor: simplify isEmpty check

* refactor: change SectionHeader component to ThemeHeader component

* refactor: remove mocks

* docs: update feature list

* feat: implement API calls using react-query

* feat: add useGoodsItemListQuery hooks

* feat: add useInfiniteScroll hooks

* feat: add infinite scroll with pagination for theme-based gift recommendations

* feat(storybook): add QueryClientProvider to Storybook

* docs: add Q&A section to README

* docs: remove QUESTION.md

* docs(README): update with code review feedback and changes

* chore: add .env file to .gitignore

* refactor: remove unnecessary rest operator

* refactor: avoid unnecessary barrel file pattern

* feat: enhance useInfiniteScroll to accept scroll condition and fetchNextPage as arguments

* fix: correct typo in queryKey in useGoodsItemListQuery

* refactor: remove separation of queryKey and queryFn in useGoodsItemListQuery

* fix: resolve type errors in useInfiniteQuery in useGoodsItemListQuery

* refactor: aliasing absolute path namespaces to internalTypes for types

* docs(README): removed spread operator for props passing

* refactor: removed spread operator for props passing

* refactor: use Pick to specify themeKey type in useParams in GoodsItemList

* refactor: refactoring useGoodsItemListQuery hook
  • Loading branch information
kang-kibong authored Jul 17, 2024
1 parent c961262 commit 4ee257c
Show file tree
Hide file tree
Showing 154 changed files with 12,839 additions and 16,254 deletions.
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": ["@emotion"]
}
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
REACT_APP_BASE_URL=https://kakao-tech-campus-mock-server.vercel.app
63 changes: 63 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": ["react-app", "eslint:recommended", "plugin:import/typescript", "airbnb", "prettier"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": "latest",
"sourceType": "module"
},
"rules": {
"no-var": "error",
"no-multiple-empty-lines": "error",
"no-console": ["error", { "allow": ["warn", "error", "info"] }],
"eqeqeq": "error",
"dot-notation": "error",
"import/extensions": ["error", "ignorePackages", {
"js": "never",
"jsx": "never",
"ts": "never",
"tsx": "never"
}],
"react/jsx-props-no-spreading": "off",
"import/prefer-default-export": "off",
"react/jsx-filename-extension": ["error", { "extensions": [".tsx"] }],
"import/no-extraneous-dependencies": "off",
"import/no-unresolved": "off",
"react/require-default-props": "off",
"no-use-before-define": "off",
"@typescript-eslint/no-use-before-define": ["error", { "variables": false , "functions": false,"classes": false}],
"react/prop-types": "off",
"import/no-cycle": "off",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "error",
"jsx-a11y/label-has-associated-control": [ 2, {
"some": [ "nesting", "id" ]
}],
"react-hooks/exhaustive-deps": "off"
},
"settings": {
"import/resolver": {
"alias": {
"map": [
["@", "./src"],
["@components", "./src/components"],
["@assets", "./src/assets"],
["@hooks", "./src/hooks"],
["@pages", "./src/pages"],
["@routes", "./src/routes"],
["@utils", "./src/utils"],
["@context", "./src/context"],
["@internalTypes", "./src/types"],
["@apis", "./src/apis"]
],
"extensions": [".js", ".jsx", ".ts", ".tsx"]
}
}
}
}
59 changes: 0 additions & 59 deletions .eslintrc.js

This file was deleted.

28 changes: 28 additions & 0 deletions .github/workflows/chromatic.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Workflow name
name: 'Chromatic Deployment'

# Event for the workflow
on: push

# List of jobs
jobs:
test:
# Operating System
runs-on: ubuntu-latest
# Job steps
steps:
- uses: actions/checkout@v1
- run: yarn
#👇 Adds Chromatic as a step in the workflow
- uses: chromaui/action@v1
# Options required for Chromatic's GitHub Action
with:
#👇 Chromatic projectToken, see https://storybook.js.org/tutorials/intro-to-storybook/react/ko/deploy/ to obtain it
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}

- name: Create comment PR
uses: thollander/actions-comment-pull-request@v1
env:
TOKEN: ${{ secrets.TOKEN }}
with:
message: "🚀storybook: ${{ steps.chromatic.outputs.storybookUrl }}"
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

# misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
Expand All @@ -22,4 +23,7 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*

.eslintcache
.yaml

*storybook.log
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npx lint-staged
3 changes: 0 additions & 3 deletions .prettierignore

This file was deleted.

5 changes: 3 additions & 2 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"singleQuote": true,
"parser": "typescript",
"semi": true,
"useTabs": false,
"tabWidth": 2,
"trailingComma": "all",
"printWidth": 100,
"printWidth": 120,
"arrowParens": "always"
}
35 changes: 15 additions & 20 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,31 @@
import type { StorybookConfig } from '@storybook/react-webpack5';
import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
import path from 'path';

const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/preset-create-react-app',
'@storybook/addon-onboarding',
'@storybook/addon-links',
'@storybook/addon-essentials',
'@chromatic-com/storybook',
'@storybook/addon-interactions',
],
webpackFinal: async (config) => {
config.resolve?.plugins?.push(
new TsconfigPathsPlugin({
configFile: path.resolve(__dirname, '../tsconfig.json'),
}),
);

return config;
},
framework: {
name: '@storybook/react-webpack5',
options: {
builder: {
useSWC: true,
},
},
},
docs: {
autodocs: 'tag',
options: {},
},
staticDirs: ['../public'],
webpackFinal: async (config) => {
if (config.resolve) {
config.resolve.plugins = [
...(config.resolve.plugins || []),
new TsconfigPathsPlugin({
extensions: config.resolve.extensions,
}),
];
}
return config;
},
};
export default config;
2 changes: 0 additions & 2 deletions .storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import type { Preview } from '@storybook/react';
import '@/styles';

const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
Expand Down
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# 3️⃣ 3주차 API
## 📡 1단계 - API 적용하기
### ✅ 기능 목록
- [x] `oas.yaml` 파일을 토대로 Request, Response Type을 정의
- [x] axios 설치
- [x] `oas.yaml` 파일과 목 API URL을 사용하여 API를 구현
- 메인 페이지 - Theme 카테고리 섹션
- [x] `/api/v1/themes` API를 사용하여 Section을 구현
- [x] API는 Axios 또는 React Query 등을 모두 활용해서 구현
- 메인 페이지 - 실시간 급상승 선물랭킹 섹션
- [x] `/api/v1/ranking/products` API를 사용하여 Section을 구현 (Axios 사용 가능)
- [x] 필터 조건을 선택 하면 해당 조건에 맞게 API를 요청하여 보여지게 구현
- Theme 페이지 - header
- [x] url의 pathParams와 `/api/v1/themes` API를 사용하여 Section을 구현
- [x] `themeKey`가 잘못 된 경우 메인 페이지로 연결
- Theme 페이지 - 상품 목록 섹션
- [x] `/api/v1/themes/{themeKey}/products` API를 사용하여 상품 목록을 구현
- [x] API 요청 시 한번에 20개의 상품 목록이 내려오도록 구현

## ⚠️ 2단계 - Error, Loading Status 핸들링 하기
### ✅ 기능 목록
- [x] Loading 상태에 대한 UI 대응
- [x] 데이터가 없는 경우에 대한 UI 대응
- [x] Http Status에 따라 Error를 다르게 처리

## 🎁 3단계 - 테마 별 선물 추천 API에 페이지네이션 구현하기 & React Query 사용해보기
### ✅ 기능 목록
- [x] 스크롤을 내리면 추가로 데이터를 요청
- [x] 1단계에서 구현한 API를 react-query로 구현

## 🤔 4단계 - 질문의 답변을 README에 작성
### 질문 1. CORS 에러
CORS 에러는 웹 애플리케이션이 다른 도메인에서 리소스를 요청할 때 발생합니다. 해결 방법으로는 서버에서 CORS 헤더 추가, 프록시 서버 사용, 개발 중 브라우저 설정 변경 등이 있습니다.

### 질문 2. 비동기 처리 방법
Callback은 간단하지만 콜백 지옥이 발생할 수 있습니다. Promise는 체이닝을 통해 가독성이 좋지만 코드가 길어질 수 있습니다. Async/Await는 동기식 코드처럼 읽혀 가독성이 뛰어나지만 예외 처리를 위해 try/catch를 사용해야 합니다.

### 질문 3. React Query와 queryKey
React Query는 서버 상태 관리, 캐싱, 비동기 데이터 페칭을 간소화합니다. queryKey는 각 쿼리를 고유하게 식별하여 캐싱, 업데이트, 무효화를 효율적으로 관리하는 데 사용됩니다.


## 🛠️ 코드 리뷰 반영
### 📄 요구 사항
- [x] .env파일 .gitignore에 추가
- [x] 불필요한 rest operator 제거
- [x] 불필요한 barrel file pattern 지양하도록 리팩토링
- [x] useInfiniteScroll scroll 조건과 fetchNextPage를 인자로 받아 hook을 개선
- [x] useGoodsItemListQuery hook
- [x] queryKey 오타 수정
- [x] queryKey, queryFn 분리 제거
- [x] useInfiniteQuery 타입 지정 오류 개선
- [x] type 절대경로 네임스페이스 지정
- [x] spread operator을 통한 props 전달 제거
11 changes: 10 additions & 1 deletion craco.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,16 @@ const path = require('path');
module.exports = {
webpack: {
alias: {
'@': path.resolve(__dirname, 'src'),
'@': path.resolve(__dirname, 'src/'),
'@components': path.resolve(__dirname, 'src/components'),
'@assets': path.resolve(__dirname, 'src/assets'),
'@hooks': path.resolve(__dirname, 'src/hooks'),
'@pages': path.resolve(__dirname, 'src/pages'),
'@routes': path.resolve(__dirname, 'src/routes'),
'@utils': path.resolve(__dirname, 'src/utils'),
'@context': path.resolve(__dirname, 'src/context'),
'@internalTypes': path.resolve(__dirname, 'src/types'),
'@apis': path.resolve(__dirname, 'src/apis'),
},
},
};
Loading

0 comments on commit 4ee257c

Please sign in to comment.