-
Notifications
You must be signed in to change notification settings - Fork 20
[김희진] sprint 9 #78
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
The head ref may contain hidden characters: "Next-\uAE40\uD76C\uC9C4-sprint1"
[김희진] sprint 9 #78
Changes from all commits
c6b9a32
30627cf
8f74df5
faa11f4
b06fcc8
443ac33
97d05e9
17add08
40bdab8
2fffc17
8cd34f9
f4419c0
eb090ee
677f49d
2304bfd
80e8a7d
7c620e3
92b328a
98c37d2
93d9858
cdbd2da
ad4a93a
1a9c7a8
7b96172
b17975e
5b35cdc
d1f5b68
1d749f9
3c00f6a
371173e
fd9e4b9
bc98c38
2475e61
346e271
7cc5f45
6bad784
465674b
0fc7edf
a9749a6
46e04b2
7efcd7f
a19a52c
2f41af1
032c967
2a6bf14
4659c8f
b468da8
bf12624
6c5d9cd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
| /node_modules | ||
| /.pnp | ||
| .pnp.js | ||
| .env | ||
|
|
||
| # testing | ||
| /coverage | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import api from "./index"; | ||
|
|
||
| export interface BoardItem { | ||
| updatedAt: string; | ||
| createdAt: string; | ||
| likeCount: number; | ||
| writer: { | ||
| nickname: string; | ||
| id: number; | ||
| }; | ||
| image: string; | ||
| content: string; | ||
| title: string; | ||
| id: number; | ||
| } | ||
|
|
||
| export interface Boards { | ||
| totalCount?: 0; | ||
| list: BoardItem[]; | ||
| } | ||
|
|
||
| interface Params { | ||
| page: number; | ||
| pageSize: number; | ||
| orderBy: string; | ||
| keyword: string; | ||
| } | ||
|
|
||
| export async function getBoards(params: Params): Promise<Boards> { | ||
| const { page, pageSize, orderBy, keyword } = params; | ||
| const response = await api.get("/articles", { | ||
| params: { page, pageSize, orderBy, keyword }, | ||
| }); | ||
|
|
||
| return response.data; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import axios from "axios"; | ||
|
|
||
| const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL; | ||
|
|
||
| const api = axios.create({ | ||
| baseURL: BASE_URL, | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| }, | ||
| }); | ||
|
|
||
| export default api; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import { getBoards } from "@/apis/boards"; | ||
|
|
||
| export default async function getBoardsData() { | ||
| const allData = await getBoards({ | ||
| page: 1, | ||
| pageSize: 10, | ||
| orderBy: "recent", | ||
| keyword: "", | ||
| }); | ||
|
|
||
| const initialAllData = allData.list; | ||
|
|
||
| const bestData = await getBoards({ | ||
| page: 1, | ||
| pageSize: 3, | ||
| orderBy: "like", | ||
| keyword: "", | ||
| }); | ||
|
|
||
| const initialBestData = bestData?.list ?? []; | ||
|
|
||
| return { initialAllData, initialBestData }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import Best from "@/components/BestBoards/Best"; | ||
| import All from "@/components/AllBoards/All"; | ||
| import getBoardsData from "./getBoardsData"; | ||
| import { Metadata } from "next"; | ||
|
|
||
| export const metadata: Metadata = { | ||
| title: "판다마켓 | 자유게시판", | ||
| }; | ||
|
|
||
| export const revalidate = 60; | ||
|
|
||
| export default async function Boards() { | ||
| const { initialAllData, initialBestData } = await getBoardsData(); | ||
|
|
||
| return ( | ||
| <div className="flex justify-center items-center my-10 lg:my-0"> | ||
| <div className="max-w-[1200px] min-w-[343px] px-6 lg:p-4 md:p-6 flex flex-col gap-10"> | ||
| <Best initialData={initialBestData} /> | ||
| <All initialData={initialAllData} /> | ||
| </div> | ||
|
Comment on lines
+13
to
+20
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오호.. 초기 데이터를 받아와서 TTV를 빠르게 하셨군요 ..? 🫢🫢훌륭합니다. ㄷㄷㄷ 첫 코드부터 기대되는데요 ?
|
||
| </div> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| @tailwind base; | ||
| @tailwind components; | ||
| @tailwind utilities; | ||
|
|
||
| @layer base { | ||
| * { | ||
| box-sizing: border-box; | ||
| text-decoration: none; | ||
| list-style: none; | ||
| } | ||
|
|
||
| body { | ||
| font-family: "Pretendard", sans-serif; | ||
| } | ||
|
|
||
| body, | ||
| p, | ||
| h2 { | ||
| margin: 0; | ||
| } | ||
|
|
||
| ul, | ||
| li { | ||
| margin: 0; | ||
| padding: 0; | ||
| } | ||
|
|
||
| button, | ||
| input, | ||
| textarea { | ||
| border: none; | ||
| } | ||
| } | ||
|
|
||
| @layer utilities { | ||
| .line-break { | ||
| display: -webkit-box; | ||
| -webkit-box-orient: vertical; | ||
| -webkit-line-clamp: 2; | ||
| text-align: left; | ||
| overflow: hidden; | ||
| } | ||
| } | ||
|
|
||
| @font-face { | ||
| font-family: "ROKAF Sans"; | ||
| src: url("/font/ROKAF.ttf"); | ||
| font-weight: 700; | ||
| font-style: normal; | ||
| } | ||
|
|
||
| @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-SemiBold.woff2") | ||
| format("woff2"); | ||
| src: url("https://fastly.jsdelivr.net/gh/Project-Noonnu/[email protected]/Pretendard-SemiBold.woff") | ||
| format("woff"); | ||
| font-display: swap; | ||
| font-weight: 600; | ||
| 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: 700; | ||
| font-style: normal; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { Metadata } from "next"; | ||
|
|
||
| export const metadata: Metadata = { | ||
| title: "판다마켓 | 중고마켓", | ||
| }; | ||
|
|
||
| export default function Home() { | ||
| return <>items page</>; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import Header from "@/components/common/Header/Header"; | ||
| import "./globals.css"; | ||
| import { Metadata } from "next"; | ||
|
|
||
| export const metadata: Metadata = { | ||
| title: "판다마켓", | ||
| }; | ||
|
|
||
| export default function RootLayout({ | ||
| children, | ||
| }: { | ||
| children: React.ReactNode; | ||
| }) { | ||
| return ( | ||
| <html lang="ko"> | ||
| <body> | ||
| <Header /> | ||
| {children} | ||
| </body> | ||
| </html> | ||
| ); | ||
| } |
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 크으 ~ not found까지 꼼꼼하네요 👍👍👍 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import { Metadata } from "next"; | ||
| import Image from "next/image"; | ||
| import panda from "@/public/icons/panda.svg"; | ||
|
|
||
| export const metadata: Metadata = { | ||
| title: "판다마켓 | not found", | ||
| }; | ||
|
|
||
| export default function NotFound() { | ||
| return ( | ||
| <div className="w-screen min-h-[calc(100vh-70px)] flex flex-col justify-center items-center gap-10"> | ||
| <Image width={280} height={280} src={panda} alt="panda" /> | ||
| <p className="text-7xl text-blue font-bold">404 | NOT FOUND</p> | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| export default function Home() { | ||
| return <>home page</>; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| "use client"; | ||
|
|
||
| import AllItem from "./AllItem"; | ||
| import Dropdown from "../common/Dropdown/Dropdown"; | ||
| import Search from "../Search/Search"; | ||
| import Pagination from "../Pagination/Pagination"; | ||
| import useParams from "@/hooks/useParams"; | ||
| import useAllData from "./useAllData"; | ||
| import { BoardItem } from "@/apis/boards"; | ||
|
|
||
| const FilterList = ["recent", "like"]; | ||
| const PAGE_SIZE = 10; | ||
|
|
||
| interface AllProps { | ||
| initialData: BoardItem[]; | ||
| } | ||
|
|
||
| export default function All({ initialData }: AllProps) { | ||
| const { page, orderBy, keyword, handleParamsUpdate } = useParams(); | ||
| const { all, totalBoards } = useAllData({ initialData, PAGE_SIZE }); | ||
|
|
||
| return ( | ||
| <div className="w-full flex flex-col gap-6"> | ||
| <div className="flex justify-between items-center"> | ||
| <h2 className="text-gray900 text-Bold20 font-bold">게시글</h2> | ||
| <button className="py-2 px-6 bg-blue text-white text-Bold16 rounded-lg cursor-pointer"> | ||
| 글쓰기 | ||
| </button> | ||
| </div> | ||
| <div className="w-full flex flex-col gap-6"> | ||
| <div className="flex justify-center items-center gap-3"> | ||
| <Search /> | ||
| <Dropdown | ||
| list={FilterList} | ||
| orderBy={orderBy} | ||
| onChange={(filter) => handleParamsUpdate({ orderBy: filter })} | ||
| /> | ||
| </div> | ||
| <div className="w-full flex flex-col gap-6"> | ||
| {all.map((item) => ( | ||
| <AllItem key={item.id} all={item} /> | ||
| ))} | ||
| </div> | ||
| {!keyword && ( | ||
| <Pagination | ||
| totalBoards={totalBoards ?? 0} | ||
| currentPage={page} | ||
| pageSize={PAGE_SIZE} | ||
| /> | ||
| )} | ||
| </div> | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| import Image from "next/image"; | ||
| import user from "@/public/icons/user.svg"; | ||
| import heart from "@/public/icons/emptyHeart.svg"; | ||
| import formattedDate from "@/utils/formattedDate"; | ||
| import { BoardItem } from "@/apis/boards"; | ||
| import Link from "next/link"; | ||
| import BoardImage from "../BoardImage/BoardImage"; | ||
|
|
||
| interface AllItemProps { | ||
| all: BoardItem; | ||
| } | ||
|
|
||
| export default function AllItem({ all }: AllItemProps) { | ||
| return ( | ||
| <Link | ||
| className="w-full h-[138px] flex flex-col gap-4 pb-6 bg-bg border-b border-gray200 cursor-pointer" | ||
| href="/" | ||
| > | ||
| <div className="w-full flex justify-between items-start"> | ||
| <p className="flex-1 h-[60px] break-words text-gray800 text-Bold20"> | ||
| {all.content} | ||
| </p> | ||
| <BoardImage image={all.image} /> | ||
| </div> | ||
| <div className="w-full flex justify-between items-center"> | ||
| <div className=" flex justify-center items-center gap-2"> | ||
| <Image src={user} width={24} height={24} alt="user" /> | ||
| <span className="text-gray600 text-Regular14"> | ||
| {all.writer.nickname} | ||
| </span> | ||
| <span className="text-gray400 text-Regular14"> | ||
| {formattedDate(all.createdAt)} | ||
| </span> | ||
| </div> | ||
| <div className="flex justify-center items-center gap-1"> | ||
| <Image src={heart} width={16} height={16} alt="like" /> | ||
| <span className="text-gray500 text-Regular14">{all.likeCount}</span> | ||
| </div> | ||
| </div> | ||
| </Link> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import { BoardItem, getBoards } from "@/apis/boards"; | ||
| import { useEffect, useState } from "react"; | ||
| import useParams from "@/hooks/useParams"; | ||
|
|
||
| interface AllDataProps { | ||
| initialData: BoardItem[]; | ||
| PAGE_SIZE: number; | ||
| } | ||
|
|
||
| export default function useAllData({ initialData, PAGE_SIZE }: AllDataProps) { | ||
| const [all, setAll] = useState<BoardItem[]>(initialData); | ||
| const [totalBoards, setTotalBoards] = useState<number>(0); | ||
| const { page, orderBy, keyword } = useParams(); | ||
|
|
||
| useEffect(() => { | ||
| getBoards({ | ||
| page, | ||
| pageSize: keyword ? 1000 : PAGE_SIZE, | ||
| orderBy, | ||
| keyword, | ||
| }) | ||
| .then((result) => { | ||
| if (!result) return; | ||
| setAll(result.list); | ||
| if (result.totalCount) { | ||
| setTotalBoards(result.totalCount); | ||
| } | ||
| }) | ||
| .catch((error) => console.error(error)); | ||
| }, [page, orderBy, keyword, PAGE_SIZE]); | ||
|
|
||
| return { all, totalBoards }; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
각기 다른 예외 처리를 하기에 어려울 것 같아요.
서로 다른 쿼리로 통신 하고 있기에 다른 에러가 발생할 여지가 있으나(물론 해당 코드에는 예외처리가 없긴 하지만), 이렇게 처리하면 특정 API의 예외처리를 하기 어려울 것 같군요 !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
또한, 현재 직렬처리로 되어있는데 병렬로 처리하시면 성능에 도움이 됩니다 !