Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
69b09ad
chore: project name 변경
y5037 Jan 17, 2025
93f3227
feat: Vite-react-app에서 next로 마이그레이션 완료
y5037 Jan 18, 2025
35d034d
refactor(mentor):types 파일은 타입스크립트 파일에서 import 하여 사용하므로 .d.ts에서 .ts로 확…
y5037 Jan 19, 2025
54fbb16
refactor(mentor): 다른 API 함수들과의 일관성을 위해 export 처리 변경
y5037 Jan 19, 2025
73b534a
chore: img 태그 Image 컴포넌트로 변경
y5037 Jan 19, 2025
41ca221
refactor(mentor): getProductData 함수의 리턴 타입 설정
y5037 Jan 19, 2025
2af43a5
chore: img 태그 Image 컴포넌트로 변경 후 발생한 타입 에러 수정
y5037 Jan 19, 2025
55b5af9
refactor(mentor): T prefix 삭제
y5037 Jan 19, 2025
bf67a40
refactor(mentor): props로 받은 상수명은 변수이므로 camelCase로 변경하여 사용
y5037 Jan 19, 2025
5117b2b
refactor(mentor): as 제거 후 ref가 null이 아닌 경우에 한정 하도록 수정
y5037 Jan 19, 2025
69cbf2e
chore: 코드리뷰대로 getProductData 함수의 파라미터와 리턴 타입을 설정하려 했으나 타입 오류로 인해 임시 원상복구
y5037 Jan 19, 2025
d886acd
chore: 오타 수정
y5037 Jan 19, 2025
d818031
chore: 상품 등록페이지 img 태그 image 컴포넌트로 변경
y5037 Jan 19, 2025
42f67ac
chore: 상품 등록페이지 Image alt 속성값 누락되어 추가
y5037 Jan 19, 2025
924cf70
chore: 상품 목록페이지 footer 추가
y5037 Jan 19, 2025
1e6ad5f
chore: api 디렉토리 위치 변경 및 상품 리스트 관련 페이지명 변경
y5037 Jan 20, 2025
8c8e4bd
chore: Component명 변경 및 hook 디렉토리 추가
y5037 Jan 20, 2025
fc88acf
chore: 직관적인 이름을 사용하기 위해 파일명과 컴포넌트명 변경
y5037 Jan 20, 2025
ed98b90
feat: 자유게시판 HTML, CSS 레이아웃 구현 완료
y5037 Jan 21, 2025
0dfa7db
chore: 반응형 CSS 수정 및 cursor 누락 코드 추가
y5037 Jan 21, 2025
ffc1f51
feat: 데이터 출력 완료
y5037 Jan 21, 2025
1997765
chore: 레이아웃 CSS 수정
y5037 Jan 21, 2025
9c4db88
feat: 타입 지정 완료
y5037 Jan 21, 2025
558caf0
feat: 스켈레톤 추가 완료
y5037 Jan 21, 2025
dd7e0ea
chore: 컴파일 에러 수정
y5037 Jan 21, 2025
d95bea7
chore: 컴파일 오류로 인해 pages의 types 이동
y5037 Jan 21, 2025
6bc9e78
feat: prefetch 추가
y5037 Jan 21, 2025
0deafd8
chore: 하단 여백 CSS 수정
y5037 Jan 21, 2025
3fb3ec7
chore: gitignore에 .next 추가 및 파일 삭제
y5037 Jan 21, 2025
968fd87
chore: 상품 디테일 페이지의 상품 썸네일 Error 발생시 대처 코드 추가
y5037 Jan 24, 2025
a3990a3
chore: 데이터가 로딩 되는 동안 화면이 비어보이는 문제가 발생하여 조건부 연산자 제거
y5037 Jan 26, 2025
9b2c765
chore: 데이터 유무에 따른 UI 출력의 && 연산자를 제거하니 스켈레톤 출력 이슈가 해결되어 setTimeout 제거
y5037 Jan 26, 2025
5befb40
feat: Suspense가 작동하지 않아 제거 후 페이지 이동의 로딩 커스팀 훅을 사용하여 적용
y5037 Jan 26, 2025
f9a53c3
chore: hook 디렉토리명 hooks로 변경
y5037 Jan 26, 2025
9fdae32
chore: useQueryBoard 커스텀 훅 이름 변경
y5037 Jan 26, 2025
6ebcc12
chore: 테스트 파일 삭제
y5037 Jan 26, 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
90 changes: 64 additions & 26 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,35 +1,73 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# Created by https://www.toptal.com/developers/gitignore/api/macos,git,react
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,git,react

# dependencies
/node_modules
/.pnp
.pnp.js
### Git ###
# Created by git for backups. To disable backups in Git:
# $ git config --global mergetool.keepBackup false
*.orig

# testing
/coverage
# Created by git when using merge tools for conflicts
*.BACKUP.*
*.BASE.*
*.LOCAL.*
*.REMOTE.*
*_BACKUP_*.txt
*_BASE_*.txt
*_LOCAL_*.txt
*_REMOTE_*.txt

# next.js
/.next/
/out/
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride

# production
/build
# Icon must end with two \r
Icon

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### macOS Patch ###
# iCloud generated files
*.icloud

### react ###
.DS_*
*.log
logs
**/*.backup.*
**/*.back.*

node_modules
node_modules/
node_modules/.cache
bower_components

*.sublime*

# local env files
.env*.local
psd
thumb
sketch

# vercel
.vercel
### next ###
.next

# typescript
*.tsbuildinfo
next-env.d.ts
# End of https://www.toptal.com/developers/gitignore/api/macos,git,react
41 changes: 1 addition & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,40 +1 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.

[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.

The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
## Next.js Project 시작
60 changes: 60 additions & 0 deletions api/api.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { CommentDataProps, ProductDataProps } from "../api/types";
const BASE_URL = "https://panda-market-api.vercel.app";

// 베스트/전체 상품 리스트
export async function getProductData(params = {}) {
const query = new URLSearchParams(params).toString();
const response = await fetch(`${BASE_URL}/products?${query}`);

if (!response.ok) {
throw new Error("상품을 불러오는 데 실패했습니다.");
}
const body = await response.json();
return body;
}

// 디테일 상품 정보
export async function getProductId(
productId: string | string[],
setProductData: React.Dispatch<
React.SetStateAction<ProductDataProps | undefined>
>,
setLoading: React.Dispatch<React.SetStateAction<boolean>>
) {
const response = await fetch(`${BASE_URL}/products/${productId}`);

try {
const body = await response.json();
setProductData(body);
} catch (error) {
console.log(error);
}

if (!response.ok) {
throw new Error("정보를 불러오는 데 실패했습니다.");
}

setLoading(false);
}

// 디테일 댓글
export async function getComments(
productId: string | string[],
setCommentsData: React.Dispatch<
React.SetStateAction<CommentDataProps | undefined>
>
) {
const response = await fetch(
`${BASE_URL}/products/${productId}/comments?limit=10`
);
try {
const body = await response.json();
setCommentsData(body);
} catch (error) {
console.log(error);
}

if (!response.ok) {
throw new Error("정보를 불러오는 데 실패했습니다.");
}
}
35 changes: 35 additions & 0 deletions api/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { StaticImport, StaticRequire } from "next/dist/shared/lib/get-img-props";

export interface ProductDataProps {
Copy link
Contributor

Choose a reason for hiding this comment

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

items에 정의된 타입이랑 다른 건가요?(링크) 다르게 유지해야 할 필요가 있는게 아니라 동일한 api 데이터 받는 거라면 하나를 보도록 하는게 좋을 것 같아요.

createdAt: string;
description: string;
favoriteCount: number;
id: number;
images:string | StaticImport;
isFavorite: boolean;
name: string;
ownerId: number;
ownerNickname: string;
price: number;
tags: string[];
updatedAt: string;
}

interface ListWriter {
id: number;
image?: string | null;
nickname: string;
}

interface ListComment {
content: string;
createdAt: string;
id: number;
updatedAt: string;
writer: ListWriter;
}

export interface CommentDataProps {
list: ListComment[];
nextCursor: number;
}
31 changes: 31 additions & 0 deletions components/app/Dayjs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import dayjs from "dayjs";
import duration, { Duration } from "dayjs/plugin/duration";
dayjs.extend(duration);

export function getTimeDiff(timeToCompare: string) {
const timeDiffDuration: Duration = dayjs.duration(
dayjs().diff(timeToCompare)
);
const yearDiff: number = parseInt(timeDiffDuration.format("Y"));
const monthDiff: number = parseInt(timeDiffDuration.format("M"));
const dateDiff: number = parseInt(timeDiffDuration.format("D"));
const hourDiff: number = parseInt(timeDiffDuration.format("H"));
const minuteDiff: number = parseInt(timeDiffDuration.format("m"));
const secondDiff: number = parseInt(timeDiffDuration.format("s"));

if (yearDiff > 0) {
return `${yearDiff}년 전`;
} else if (monthDiff > 0) {
return `${monthDiff}달 전`;
} else if (dateDiff > 0) {
return `${dateDiff}일 전`;
} else if (hourDiff > 0) {
return `${hourDiff}시간 전`;
} else if (minuteDiff > 0) {
return `${minuteDiff}분 전`;
} else if (secondDiff > 0) {
return `${secondDiff}초 전`;
} else {
return "";
}
}
60 changes: 60 additions & 0 deletions components/app/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import styles from "../../styles/app/footer.module.css";
import IconInsta from "@/public/assets/images/app/home/ic_instagram.svg";
import IconFacebook from "@/public/assets/images/app/home/ic_facebook.svg";
import IconYoutube from "@/public/assets/images/app/home/ic_youtube.svg";
import IconTwitter from "@/public/assets/images/app/home/ic_twitter.svg";
import Link from "next/link";

function Footer() {
return (
<footer className={styles.footer}>
<div className={styles.footerWrap}>
<p className={styles.copyright}>&copy;codeit - 2024</p>
<div className={styles.btnLink}>
<Link href="/privacy">Privacy Policy</Link>
<Link href="/faq">FAQ</Link>
</div>
<ul className={styles.socialBox}>
<li>
<Link
href="https://www.facebook.com/"
rel="noopener noreferrer"
target="_blank"
>
<IconFacebook />
</Link>
</li>
<li>
<Link
href="https://www.twitter.com/"
rel="noopener noreferrer"
target="_blank"
>
<IconTwitter />
</Link>
</li>
<li>
<Link
href="https://www.youtube.com/"
rel="noopener noreferrer"
target="_blank"
>
<IconYoutube />
</Link>
</li>
<li>
<Link
href="https://www.instagram.com/"
rel="noopener noreferrer"
target="_blank"
>
<IconInsta />
</Link>
</li>
</ul>
</div>
</footer>
);
}

export default Footer;
54 changes: 54 additions & 0 deletions components/app/ItemListNav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useRouter } from "next/router";
import Link from "next/link";
import Image from "next/image";
import styles from "../../styles/app/navi.module.css";
import NavLogoImg from "@/public/assets/images/app/navi/logo.svg";
import profileDefaultImg from "@/public/assets/images/app/navi/profile_default.png";

const menuData = [
{ id: 1, name: "자유게시판", path: "/boards" },
{ id: 2, name: "중고마켓", path: "/items" },
];

function ItemListNav() {
const router = useRouter();

return (
<div className={styles.fixContainer}>
<nav className={styles.navCover}>
<div className={styles.pageControl}>
<Link href="/" prefetch={true}>
<div className={styles.btnLogo}>
<NavLogoImg />
<p className={styles.companyName}>판다마켓</p>
</div>
</Link>
<div className={styles.btnWrap}>
{menuData.map((menu) => {
return (
<Link
key={menu.id}
href={menu.path}
prefetch={true}
className={`${styles.btnNavMenu} ${
menu.path === router.pathname ? styles.isActive : ""
}`}
>
{menu.name}
</Link>
);
})}
</div>
</div>
{/* 사용자 정보를 담은 프로필 또는 아바타이므로 이 기능만 제공해주는 콤포넌트를 분리하는 것도 추후 고려 */}
<div className={styles.userControl}>
<div className={styles.circle}>
<Image src={profileDefaultImg} alt="기본프로필이미지" />
</div>
</div>
</nav>
</div>
);
}

export default ItemListNav;
11 changes: 11 additions & 0 deletions components/app/Loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import styles from "../../styles/app/loading.module.css";

export const Loading = () => {
return (
<div className={styles.loaderContainer}>
<span className={styles.loader}></span>
</div>
);
};

export default Loading;
Loading
Loading