Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
8d73d8c
[Refactor] ItemsList style 분리
Feb 18, 2025
409ee54
[Chore] ItemsList 폴더 이동, HomePage->ItemsPage로 이름변경
Feb 18, 2025
78c3078
[Chore] globalstyles 분리
Feb 18, 2025
86e69a5
[Refactor] ItemsPage mobile 반응형 수정
Feb 19, 2025
930dba9
[Style] 이전 작업 style.jsx font 적용
Feb 19, 2025
addb51f
[Refactor] edit heartBtn
Feb 19, 2025
fecbd64
[Refactor]edit pageCount
Feb 19, 2025
f8d651c
[Style]ItemList style edit
Feb 20, 2025
b87b91b
[Chore]i typescript and edit tsconfig.json options
Feb 20, 2025
46d3ce6
[Chore] i types/styled-compoenents
Feb 20, 2025
1cb5a08
[Types] add declaration file for SVG imports and select componenet type
Feb 20, 2025
167dcc0
[Type] button componente type
Feb 22, 2025
212edd8
[Type] input component type
Feb 22, 2025
a04e631
[Type] BtnHeart type
Feb 22, 2025
3210bd4
[Type] Convert JSX to TSX and add type for AddItem
Feb 23, 2025
9dac8e0
[Type]Convert JSX to TSX and add type for Tag
Feb 23, 2025
8f838ca
[Type] edit type AddItem and Tag
Feb 23, 2025
b960234
[Type]Convert JSX to TSX ItemsList and ItemsList.style and edit type …
Feb 23, 2025
7bb8e24
[Type] add type for ItemsPage and ItemsPage.style
Feb 23, 2025
3f77bf3
[Type]Convert JSX to TSX CommentCard , Comment, Product
Feb 23, 2025
ef38e4e
[Type] add type for Comments
Feb 23, 2025
a1639f6
[Type] add type for Product
Feb 23, 2025
3731caa
[Type] add type for CommentCard
Feb 23, 2025
5fdcc0e
[Type]Convert JSX to TSX ProductInfo
Feb 24, 2025
0c34f84
[Chore] global.d.ts created
Feb 24, 2025
a8fd50d
[Type] global.d.ts에 타입이랑 interface 싹다 모음
Feb 24, 2025
8186da1
[Type] create global type Params and ProductInfo
Feb 24, 2025
c5fea26
[Type] add type for pageCount
Feb 24, 2025
de53246
[Type] create Item type with Omit
Feb 24, 2025
6352933
[Type] Comment interface 전역 타입 설정
Feb 24, 2025
d0b343e
[Type] onClick type 생성
Feb 24, 2025
7e56e15
[Refactor] fetch ->axios
Feb 24, 2025
a4011fe
[Type] product.api interface
Feb 24, 2025
3ee9850
[Chore] 단순 오타 에러 수정
Feb 24, 2025
6dc1fab
[Chore] type items -> item 으로 이름 변경
Feb 24, 2025
b4c2a29
[Chore] 잘 모르겠는 부분 주석 남김
Feb 24, 2025
b562007
[Chore] npm run start 에러 경로문제 수정
Feb 24, 2025
cd7f584
[Fix] 개발 환경 실행시 이벤트 핸들러 동작 오류 수정
Feb 24, 2025
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
61 changes: 51 additions & 10 deletions package-lock.json

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

6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/styled-components": "^5.1.34",
"axios": "^1.7.9",
"firebase": "^11.1.0",
"lodash.throttle": "^4.1.1",
Expand Down Expand Up @@ -40,5 +41,10 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"typescript": "^4.9.5"
}
}
53 changes: 5 additions & 48 deletions src/Main.jsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,11 @@
import { Route, BrowserRouter, Routes } from "react-router-dom";
import { createGlobalStyle } from "styled-components";
import GlobalStyle from "./style/globalStyle.js";
//
import LandingPage from "./pages/LandingPage/LandingPage.jsx";
import App from "./App.js";
import HomePage from "./pages/HomePage/HomePage.jsx";
import AddItem from "./pages/AddItem/AddItem.jsx";
import Product from "./pages/ProductPage/Product.jsx";
import Test from "./components/TestPage.jsx";
//
//
const GlobalStyle = createGlobalStyle`
* {
box-sizing: border-box;
}
body {
font-family: 'Pretendard', sans-serif;
font-display: swap;
margin: 0;
padding: 0;
}
html {
margin: 0;
padding: 0;
}
a {
text-decoration: none;
color: #ffffff;
}
p{
margin: 0px;
}
@font-face {
font-family: 'Pretendard';
src: url('https://fastly.jsdelivr.net/gh/Project-Noonnu/[email protected]/Pretendard-Regular.woff2') format('woff2');
src: url('https://fastly.jsdelivr.net/gh/Project-Noonnu/[email protected]/Pretendard-Regular.woff') format('woff');
font-display: swap;
font-weight: 400;
font-style: normal;
}

@font-face {
font-family: "Pretendard";
src: url('https://fastly.jsdelivr.net/gh/Project-Noonnu/[email protected]/Pretendard-Bold.woff2') format('woff2');
src: url('https://fastly.jsdelivr.net/gh/Project-Noonnu/[email protected]/Pretendard-Bold.woff') format('woff');
font-display: swap;
font-weight: 600;
font-style: normal;
}
`;
import ItemsPage from "./pages/ItemsPage/ItemsPage.tsx";
import AddItem from "./pages/AddItem/AddItem.tsx";
import Product from "./pages/ProductPage/Product.tsx";
//
function Main() {
return (
Expand All @@ -57,8 +15,7 @@ function Main() {
<Routes>
<Route path="/" element={<LandingPage />} />
<Route element={<App />}>
<Route path="/test" element={<Test />} />
<Route path="/items" element={<HomePage />} />
<Route path="/items" element={<ItemsPage />} />
<Route path="/items/:productId" element={<Product />} />
<Route path="/additem" element={<AddItem />} />
</Route>
Expand Down
6 changes: 3 additions & 3 deletions src/api/comment.api.jsx → src/api/comment.api.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import axios from "axios";
const BASE_URL = "https://panda-market-api.vercel.app";

export async function getProductComments(productId, limit = 3) {
export async function getProductComments({ productId }: Params, limit = 3) {
try {
const res = await axios.get(
`${BASE_URL}/products/${productId}/comments?limit=${limit}`
);
Comment on lines 6 to 8
Copy link
Collaborator

Choose a reason for hiding this comment

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

또한 axios를 사용중이시라면 instnace를 생성하셔서 사용해보실 것을 권장드립니다 !

BASE_URL을 매번 입력하셔야 하며, 각기 다른 엔드포인트와 그에 따른 설정이 필요할 때에 유지보수가 어려울 수 있어요:

어떻게 세팅하면 될까? 🤔

instance를 만들어서 export를 하고 사용해보는 것 정도로 시도해보면 좋을 것 같아요. axios-instance 파일을 만들어서 instance를 생성하고 export한 후 사용해보는건 어떨까요?
다음과 같이 만들어볼 수 있어요:

const baseURL = process.env.NEXT_PUBLIC_LINKBRARY_BaseURL;

const instance = axios.create({
  baseURL: baseURL,
  headers: {
    'Content-Type': 'application/json',
  },
});

export default instance

axios instance

인가에 필요한 accessTokenlocalStorage가 있다면 axios의 인터셉터를 활용할 수 있습니다 !

인터셉터는 혼자 해결해보시는 것을 권장드립니다. 혹시 모르시겠으면 다음 위클리 미션에 질문해주세요. 😊

사용 방법 🚀

사용 방법은 정말 간단해요. 다음과 같이 사용할 수 있습니다:

instance.get(`/user/${userId}`)

딱 보니. 마이그레이션도 정말 쉽게 할 수 있겠죠? 😊

axios API

if (!res) {
throw new Error("리뷰 불러오기 실패");
}
return res.data;
const data: Comment[] = res.data.list;
return data;
Comment on lines +4 to +13
Copy link
Collaborator

Choose a reason for hiding this comment

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

다음과 같이 제네릭을 통해서 타입을 정의할 수 있어요 !

Suggested change
export async function getProductComments({ productId }: Params, limit = 3) {
try {
const res = await axios.get(
`${BASE_URL}/products/${productId}/comments?limit=${limit}`
);
if (!res) {
throw new Error("리뷰 불러오기 실패");
}
return res.data;
const data: Comment[] = res.data.list;
return data;
export async function getProductComments({ productId }: Params, limit = 3) {
try {
const res = await axios.get<Comment[]>( // <-- 여기요 !
`${BASE_URL}/products/${productId}/comments?limit=${limit}`
);
if (!res) {
throw new Error("리뷰 불러오기 실패");
}
const data = res.data.list;
return data;

} catch (error) {
console.error(error);
return null;
}
}
42 changes: 0 additions & 42 deletions src/api/product.api.jsx

This file was deleted.

60 changes: 60 additions & 0 deletions src/api/product.api.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import axios from "axios";
const BASE_URL = "https://panda-market-api.vercel.app";
//

export interface ResponseData extends Product {
createdAt: string;
favoriteCount: number;
ownerNickname: string;
ownerId: number;
id: number;
isFavorite: boolean;
}
export type Item = Omit<ResponseData, "ownernickname" | "isFavorite">;

export async function getProducts({
device = "desktop",
page = 1,
selectedOrder = "최신순",
}) {
const order = selectedOrder === "최신순" ? "recent" : "favorite";
const pageSize = device === "mobile" ? 4 : device === "tablet" ? 6 : 10;
const query = `?orderBy=${order}&page=${page}&pageSize=${pageSize}`;
Comment on lines +20 to +22
Copy link
Collaborator

Choose a reason for hiding this comment

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

쿼리는 URLSearchParams로 손쉽게 사용할 수 있어요 !

Suggested change
const order = selectedOrder === "최신순" ? "recent" : "favorite";
const pageSize = device === "mobile" ? 4 : device === "tablet" ? 6 : 10;
const query = `?orderBy=${order}&page=${page}&pageSize=${pageSize}`;
const order = selectedOrder === "최신순" ? "recent" : "favorite";
const pageSize = device === "mobile" ? 4 : device === "tablet" ? 6 : 10;
const query = new URLSearchParams({order, page, pageSize}).toString();

URLSearchParams와 함께 객체로 손쉽게 핸들링할 수 있습니다 !
객체로 구성할 수 있어 가독성이 좋고, URL 인코딩을 자동으로 처리하여 특수 문자나 공백이 포함된 값에서도 안전하게 동작합니다 !

URLSearchParams: URLSearchParams 인터페이스는 URL의 쿼리 문자열을 대상으로 작업할 수 있는 유틸리티 메서드를 정의합니다.

쿼리를 생성하실 때에 참고해서 사용해보세요 😊


try {
const res = await axios.get(`${BASE_URL}/products${query}`);
if (!res) throw new Error("상품 불러오기 실패");
const data: Item[] = res.data.list;
return data;
} catch (error) {
console.error(error, "상품 불러오기 실패");
}
}

export async function bestProducts({ device }: Device) {
const pageSize = device === "mobile" ? 1 : device === "tablet" ? 2 : 4;
try {
const res = await axios.get(
`${BASE_URL}/products?orderBy=favorite&pageSize=${pageSize}`
);
if (!res) throw new Error("베스트상품 api 실패");
const data: Item[] = res.data.list;
return data;
} catch (error) {
console.error(
error,
"베스트 상품을 불러오지 못했습니다. 다시 시도해주세요"
);
}
}

export async function getProductInfo({ productId }: Params) {
try {
const response = await axios.get(`${BASE_URL}/products/${productId}`);
if (!response) throw new Error("제품정보 get api 실패");
const data: ResponseData = response.data;
return data;
} catch (error) {
console.error(error, "제품정보 api 실패");
}
}
3 changes: 3 additions & 0 deletions src/assets/icons/nextPage.icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/icons/previousPage.icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/icons/search.icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions src/assets/icons/sort.icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading