Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
324ee73
refactor: Add variables to determine authentication and home pages ba…
hyeonjiroh Feb 20, 2025
b6b0163
feat: Add customizable debounce delay to useWindowSize hook
hyeonjiroh Feb 20, 2025
bdb8b5d
refactor: Move handleLoad function inside useEffect
hyeonjiroh Feb 20, 2025
e3b3943
refactor: Replace magic numbers with constants
hyeonjiroh Feb 20, 2025
da1a388
chore: Add alt="" for decorative images
hyeonjiroh Feb 20, 2025
0b89c72
feat: Seperate ImageUploader component
hyeonjiroh Feb 20, 2025
e4e5fbc
feat: Seperate DeleteButton component
hyeonjiroh Feb 20, 2025
1edefa7
feat: Enable register button when image is uploaded
hyeonjiroh Feb 21, 2025
662aed1
feat: navigate to item detail page when clicking on an item in the list
hyeonjiroh Feb 21, 2025
24c7059
feat: add PrimaryButton component
hyeonjiroh Feb 21, 2025
633e8e4
feat: add breakpoints utility file for responsive design
hyeonjiroh Feb 21, 2025
6428a66
feat: complete itemInfoSection component
hyeonjiroh Feb 21, 2025
03b7bfc
feat: Seperate MenuButton component
hyeonjiroh Feb 22, 2025
c8caed0
feat: Add comment data fetching
hyeonjiroh Feb 22, 2025
f55c062
feat: Complete comment section layout
hyeonjiroh Feb 23, 2025
55bd12e
feat: Show time elapsed since comment update if less than 1 day
hyeonjiroh Feb 23, 2025
b763621
feat: Add edit mode functionality for comment editing
hyeonjiroh Feb 23, 2025
19750c3
feat: Add empty comment screen when no comments are available
hyeonjiroh Feb 23, 2025
94c36cd
feat: Add back to items page button
hyeonjiroh Feb 23, 2025
3706efe
feat: Migrate landing page to React
hyeonjiroh Feb 23, 2025
2235af0
feat: Migrate login page to React
hyeonjiroh Feb 24, 2025
62ab22c
feat: Migrate signup page to React
hyeonjiroh 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
11 changes: 5 additions & 6 deletions React/panda-market/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,16 @@ import "./styles/App.color.css";
function App() {
const location = useLocation();

const isAuthPage = ["/login", "/signup"].includes(location.pathname);
const isHomePage = location.pathname === "/";

return (
<>
{!["/signin", "/signup"].includes(location.pathname) && (
<Header className={styles.nav} />
)}
{!isAuthPage && <Header className={styles.nav} />}
<div className={styles.body}>
<Outlet />
</div>
{["/"].includes(location.pathname) && (
<Footer className={styles.footer} />
)}
{isHomePage && <Footer className={styles.footer} />}
</>
);
}
Expand Down
4 changes: 3 additions & 1 deletion React/panda-market/src/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { BrowserRouter, Routes, Route } from "react-router-dom";
import App from "./App";
import HomePage from "./pages/HomePage/HomePage";
import LoginPage from "./pages/LoginPage/LoginPage";
import SignupPage from "./pages/SignupPage/SignupPage";
import CommunityPage from "./pages/CommunityPage/CommunityPage";
import MarketPage from "./pages/MarketPage/MarketPage";
import ItemPage from "./pages/ItemPage/ItemPage";
Expand All @@ -13,7 +14,8 @@ function Main() {
<Routes>
<Route path="/" element={<App />}>
<Route index element={<HomePage />} />
<Route path="signin" element={<LoginPage />} />
<Route path="login" element={<LoginPage />} />
<Route path="signup" element={<SignupPage />} />
<Route path="community" element={<CommunityPage />} />
<Route path="items">
<Route index element={<MarketPage />} />
Expand Down
23 changes: 23 additions & 0 deletions React/panda-market/src/apis/itemApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const instance = axios.create({
baseURL: "https://panda-market-api.vercel.app",
});

const COMMENTS_LIMIT = 3;

export async function getItems({ page = "", pageSize = "", order = "" }) {
const query = `page=${page}&pageSize=${pageSize}&orderBy=${order}`;
try {
Expand All @@ -14,3 +16,24 @@ export async function getItems({ page = "", pageSize = "", order = "" }) {
throw error;
}
}

export async function getItemById(productId = "") {
try {
const res = await instance.get(`/products/${productId}`);
return res.data;
} catch (error) {
console.error(error);
throw error;
}
}

export async function getItemComments(productId = "", { cursor = 0 }) {
const query = `limit=${COMMENTS_LIMIT}&cursor=${cursor}`;
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 query = `limit=${COMMENTS_LIMIT}&cursor=${cursor}`;
const query = new URLSearchParams({cursor, limit: COMMENTS_LIMIT, });

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

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

try {
const res = await instance.get(`/products/${productId}/comments?${query}`);
return res.data;
} catch (error) {
console.error(error);
throw error;
}
}
5 changes: 3 additions & 2 deletions React/panda-market/src/assets/icon/ic_back.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions React/panda-market/src/assets/icon/ic_menu.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 React/panda-market/src/assets/icon/ic_prev.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
17 changes: 17 additions & 0 deletions React/panda-market/src/assets/image/Img_inquiry_empty.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 7 additions & 5 deletions React/panda-market/src/components/Layout/Header.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Link, NavLink, useLocation } from "react-router-dom";
import useWindowSize from "../..//hooks/useWindowSize";
import BREAKPOINTS from "../../utils/breakpoints";
import PrimaryButton from "../UI/PrimaryButton";
import logoImg from "../../assets/logo/panda-market-logo.svg";
import logoWordImg from "../../assets/logo/panda-market-logo-only-word.svg";
import UserIcon from "../../assets/user/default-profile.png";
import UserIcon from "../../assets/image/default-profile.png";
import styles from "./Header.module.css";

function Header() {
Expand All @@ -29,7 +31,7 @@ function Header() {
<div className={styles.header}>
<div className={styles.leftSide}>
<Link to="/">
{width >= 768 ? (
{width >= BREAKPOINTS.TABLET ? (
<img src={logoImg} alt="판다마켓 로고" />
) : (
<img src={logoWordImg} alt="판다마켓 로고" />
Expand All @@ -54,10 +56,10 @@ function Header() {
<img src={UserIcon} className={styles.user} alt="유저 메뉴" />
)}
{["/"].includes(location.pathname) && (
<Link to="/signin">
<button type="button" className={styles.loginButton}>
<Link to="/login">
<PrimaryButton type="button" className={styles.loginButton}>
로그인
</button>
</PrimaryButton>
</Link>
)}
</div>
Expand Down
12 changes: 12 additions & 0 deletions React/panda-market/src/components/UI/DeleteButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import DeleteIcon from "../../assets/icon/ic_delete.svg";
import styles from "./DeleteButton.module.css";

function DeleteButton({ className = "", onClick = () => {} }) {
return (
<button type="button" onClick={onClick} className={className}>
<img src={DeleteIcon} alt="" className={styles.deleteIcon} />
</button>
);
}

export default DeleteButton;
5 changes: 5 additions & 0 deletions React/panda-market/src/components/UI/DeleteButton.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.deleteIcon {
padding: 0.5rem;
border-radius: 100%;
background-color: var(--gray400);
}
16 changes: 16 additions & 0 deletions React/panda-market/src/components/UI/MenuButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import MenuIcon from "../../assets/icon/ic_menu.svg";
import styles from "./MenuButton.module.css";

function MenuButton({ className = "", onClick = () => {} }) {
return (
<button
type="button"
onClick={onClick}
className={`${className} ${styles.menuButton}`}
>
<img src={MenuIcon} alt="" />
</button>
);
}

export default MenuButton;
7 changes: 7 additions & 0 deletions React/panda-market/src/components/UI/MenuButton.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.menuButton {
border-radius: 100%;
}

.menuButton:hover {
background-color: var(--gray100);
}
18 changes: 18 additions & 0 deletions React/panda-market/src/components/UI/PrimaryButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from "react";
import styles from "./PrimaryButton.module.css";

function PrimaryButton({
children,
className = "",
onClick = () => {},
disabled = false,
}) {
const buttonClass = `${styles.button} ${className}`;
return (
<button className={buttonClass} onClick={onClick} disabled={disabled}>
{children}
</button>
);
}
Comment on lines +4 to +16
Copy link
Collaborator

Choose a reason for hiding this comment

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

(심화/제안/고민해보기) 혹시 버튼 컴포넌트들을 공통 버튼 컴포넌트로 리팩토링 할 수는 없을까요?

MenuButton, DeleteButton, PrimaryButton.. 더 나아가서 SecondaryButton, UpdateButton 등등 목적에 따라 버튼이 생겨날 수 있을 것 같아요.
만약 디자인 시스템이 바뀌어서 일괄적으로 버튼의 radius를 바꿔야하는 상화이 온다면 유지보수하기가 어려워질 수 있겠지요?

예를 들어서 다음과 같은 컴포넌트를 만들어볼 수 있을 것 같아요:

import styles from "./Button.module.css";

function Button({
  children,
  variant = "primary",
  icon,
//  onClick = () => {},
  className = "",
//  disabled = false,
  ...rest
}) {
  return (
    <button
      className={`${styles.button} ${styles[variant]} ${className}`}
//      onClick={onClick}
//      disabled={disabled} 해당 내용들은 `rest`에 자동 등재
      {...rest}
    >
      {icon && <img src={icon} alt="" className={styles.icon} />}
      {children}
    </button>
  );
}

export default Button;

Copy link
Collaborator

Choose a reason for hiding this comment

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

방금 제안드린 내용에서 추가

사실, 여기서 icon 또한 buttonchildren으로 포함시킬 수도 있긴 하겠네요 😊
예를 들어서 다음과 같이요 !:

<Button variant="error" className="flex justify-between w-24 items-center">삭제<DeleteIcon /></Button>


export default PrimaryButton;
16 changes: 16 additions & 0 deletions React/panda-market/src/components/UI/PrimaryButton.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.button {
background-color: var(--blue);
color: var(--gray100);
}

.button:hover {
background-color: #1967d6;
}

.button:focus {
background-color: #1251aa;
}

.button:disabled {
background-color: var(--gray400);
}
4 changes: 2 additions & 2 deletions React/panda-market/src/hooks/useWindowSize.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState, useEffect } from "react";
import debounce from "lodash.debounce";

function useWindowSize() {
function useWindowSize(delay = 300) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

크으 ~ 피드백 반영 좋습니다 현지님 👍

const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
Expand All @@ -13,7 +13,7 @@ function useWindowSize() {
width: window.innerWidth,
height: window.innerHeight,
});
}, 300);
}, delay);

window.addEventListener("resize", handleResize);
return () => {
Expand Down
81 changes: 81 additions & 0 deletions React/panda-market/src/pages/HomePage/HomePage.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { Link } from "react-router-dom";
import { Helmet } from "react-helmet";
import PrimaryButton from "../../components/UI/PrimaryButton";
import BannerTopImg from "../../assets/image/Img_home_top.svg";
import BannerBottomImg from "../../assets/image/Img_home_bottom.svg";
import HomeImg1 from "../../assets/image/Img_home_01.png";
import HomeImg2 from "../../assets/image/Img_home_02.png";
import HomeImg3 from "../../assets/image/Img_home_03.png";
import styles from "./HomePage.module.css";

function HomePage() {
Expand All @@ -7,6 +14,80 @@ function HomePage() {
<Helmet>
<title>판다마켓 - 일상의 모든 물건을 거래해 보세요</title>
</Helmet>
<div className={`${styles.banner} ${styles.top}`}>
<div className={styles.wrapper}>
<div className={styles.bannerContainer}>
<div className={styles.bannerLeft}>
<h1 className={styles.title}>
일상의 모든 물건을
<br className={styles.mobile} /> 거래해 보세요
</h1>
<Link to="/items">
<PrimaryButton className={styles.itemsLinkButton}>
구경하러 가기
</PrimaryButton>
</Link>
</div>
<div className={styles.bannerRight}>
<img src={BannerTopImg} alt="상단 배너 이미지" />
</div>
</div>
</div>
</div>
<div className={`${styles.features} ${styles.wrapper}`}>
<div className={styles.feature}>
<img src={HomeImg1} width="68.5%" alt="인기 상품" />
<div className={styles.featureContent}>
<h2 className={styles.featureTag}>Hot item</h2>
<h3 className={styles.bold}>인기 상품을 확인해 보세요</h3>
<p className={styles.featureDescription}>
가장 HOT한 중고거래 물품을
<br />
판다 마켓에서 확인해 보세요
</p>
</div>
</div>
Comment on lines +37 to +49
Copy link
Collaborator

Choose a reason for hiding this comment

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

(제안/선택) section을 사용하셔도 될 것 같아요.

Suggested change
<div className={`${styles.features} ${styles.wrapper}`}>
<div className={styles.feature}>
<img src={HomeImg1} width="68.5%" alt="인기 상품" />
<div className={styles.featureContent}>
<h2 className={styles.featureTag}>Hot item</h2>
<h3 className={styles.bold}>인기 상품을 확인해 보세요</h3>
<p className={styles.featureDescription}>
가장 HOT한 중고거래 물품을
<br />
판다 마켓에서 확인해 보세요
</p>
</div>
</div>
<section className={`${styles.features} ${styles.wrapper}`}>
<div className={styles.feature}>
<img src={HomeImg1} width="68.5%" alt="인기 상품" />
<div className={styles.featureContent}>
<h2 className={styles.featureTag}>Hot item</h2>
<h3 className={styles.bold}>인기 상품을 확인해 보세요</h3>
<p className={styles.featureDescription}>
가장 HOT한 중고거래 물품을
<br />
판다 마켓에서 확인해 보세요
</p>
</div>
</section>

영역이 구분되며 h로 각 섹션의 정체성이 명확하므로 section태그도 괜찮은 의미일 것 같아서 제안드려요:

다음은 MDN<section>에 대한 설명 중 첫 문장입니다.

The <section> HTML element represents a generic standalone section of a document, which doesn't have a more specific semantic element to represent it. Sections should always have a heading, with very few exceptions.

<section> HTML 요소는 문서의 일반적인 독립형 섹션을 나타내며 이를 나타내는 더 구체적인 의미 요소가 없습니다. 섹션에는 거의 예외를 제외하고 항상 제목이 있어야 합니다.

<div className={`${styles.feature} ${styles.columnReverse}`}>
<div className={styles.featureContent}>
<h2 className={styles.featureTag}>Search</h2>
<h3 className={styles.bold}>구매를 원하는 상품을 검색하세요</h3>
<p className={styles.featureDescription}>
구매하고 싶은 물품은 검색해서
<br />
쉽게 찾아보세요
</p>
Comment on lines +52 to +58
Copy link
Collaborator

Choose a reason for hiding this comment

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

h 시리즈가 순차적으로 잘 들어갔네요 👍

</div>
<img src={HomeImg2} width="68.5%" alt="검색 기능" />
</div>
<div className={styles.feature}>
<img src={HomeImg3} width="68.5%" alt="판매 상품 등록" />
<div className={styles.featureContent}>
<h2 className={styles.featureTag}>Register</h2>
<h3 className={styles.bold}>판매를 원하는 상품을 등록하세요</h3>
<p className={styles.featureDescription}>
어떤 물건이든 판매하고 싶은 상품을
<br />
쉽게 등록하세요
</p>
</div>
</div>
</div>
<div className={`${styles.banner} ${styles.bottom}`}>
<div className={styles.wrapper}>
<div className={styles.bannerContainer}>
<div className={styles.bannerLeft}>
<h1 className={styles.title}>
믿을 수 있는
<br />
판다마켓 중고 거래
</h1>
</div>
<div className={styles.bannerRight}>
<img src={BannerBottomImg} alt="하단 배너 이미지" />
</div>
</div>
</div>
</div>
</>
);
}
Expand Down
Loading
Loading