Skip to content

Conversation

@ghost
Copy link

@ghost ghost commented Nov 30, 2024

요구사항

기본

  • 자유 게시판 페이지 주소는 /boards 입니다.
  • 전체 게시글에서 드롭 다운으로 '최신 순' 또는 '좋아요 순'을 선택해 정렬이 가능합니다.
  • 게시글 목록 조회 API를 사용하여 베스트 게시글 및 게시글 목록을 구현했습니다.
  • 게시글 title에 검색어가 일부 포함될 경우 검색할 수 있도록 구현했습니다.

심화

  • 반응형으로 보여지는 베스트 게시판 개수를 다르게 설정할 때 서버에 보내는 pageSize 값을 적절히 설정했습니다.

주요 변경사항

  • 자유 게시판 페이지(/boards) 추가
  • zustand를 사용하여 keyword(검색어), toggleState(토글 상태 최신 or 인기) 관리
  • 게시글 정렬 기능(최신 순, 좋아요 순) 구현
  • 검색 기능 추가 (게시글 제목 내 검색어 포함 시 조회)
  • 반응형 베스트 게시글 개수 설정
  • shadcn으로 toggleArticle.tsx component구현

스크린샷

image
image
image

멘토에게

  • shadcn을 사용했지만 tailwind css styling을 toggle component에 따로 했습니다. 다음 PR 때 수정하겠습니다.

@ghost ghost changed the title Next 김승준 sprint9 [김승준] spint9 Nov 30, 2024
@ghost ghost changed the title [김승준] spint9 [김승준] sprint9 Nov 30, 2024
@ghost ghost requested a review from arthurkimdev November 30, 2024 13:48
@ghost ghost added the 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. label Dec 1, 2024
- Split Article component into BestArticle and NormalArticle for better separation of concerns
- Move common props management to parent component
- Improve image resource handling
  - Pass likeIcon from parent component
  - Keep bestBadge within BestArticle component

BREAKING CHANG: Internal structure of Article component has been modified
Copy link
Collaborator

@arthurkimdev arthurkimdev left a comment

Choose a reason for hiding this comment

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

승준님 과제 제출하시느라 수고하셨습니다~ 👍

Comment on lines +21 to +36
const apiGetArticles = async (
params: ArticleParams
): Promise<ArticleResponse> => {
try {
const { data } = await axios.get<ArticleResponse>(
`${baseUrl}/articles?${createQueryParams(params)}`
);
return data;
} catch (error) {
const message = axios.isAxiosError(error)
? error.response?.data?.message ??
"error occurred & server error message is missing"
: "An unexpected error occurred(not axios error)";
throw new Error(message);
}
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

네이밍에 정답은 없지만, 현업 사례로 기준을 잡고 말씀드리면 API를 요청하는 부분은 fetchOOOO 통일하곤 해요. 즉 fetchArticles 가 되겠습니다. 또한 아래처럼 도메인 중심 네이밍 방법도 있는데요. 선택하셔서 수정하면 지금 보다 더 좋은 네이밍과 유연한 설계가 될 것 같아요. 😸

const articles = {
  list: () => axios.get('/articles'),
  detail: (id: string) => axios.get(`/articles/${id}`),
  create: (data: ArticleData) => axios.post('/articles', data),
  update: (id: string, data: ArticleData) => axios.put(`/articles/${id}`, data),
  delete: (id: string) => axios.delete(`/articles/${id}`)
};

Comment on lines +38 to +43
export const useArticles = (params: ArticleParams) =>
useQuery({
queryKey: ["articles", params],
queryFn: () => apiGetArticles(params),
});

Copy link
Collaborator

Choose a reason for hiding this comment

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

정답은 없지만 이러한 쿼리 요청 함수들은 fetch 파일에서 전부 관리하지 않고, hooks/queries 에 별도로 정리하는 방식도 있어요.

ㄴ api
    ㄴ fetchArticles.ts
ㄴ hooks
    ㄴ queries
        ㄴ useArticles.ts

Comment on lines +6 to +8
const containerStyles =
"flex flex-col mx-auto w-[343px] tablet:w-[696px] pc:w-[1200px]";

Copy link
Collaborator

Choose a reason for hiding this comment

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

이런 클래스명이 담긴 집함체들은 tailwind를 사용할 때, global.css 파일을 하나 만들어서 아래처럼 정의 후 사용할 수 있어요.

@layer components {
   . container-styles {
        @apply flex flex-col mx-auto w-[343px] tablet:w-[696px] pc:w-[1200px]
    }
export default function BoardsPage() {
  return (
    <div className={container-styles}>
      <BestSection />
      <NormalSection />
    </div>
  );
}
```

Comment on lines +52 to +67

return (
<section className="mb-6 pc:mb-10 w-full">
<h1 className="text-[20px] font-[700] mb-4 tablet:mb-6">베스트 게시글</h1>
<div className="flex gap-0 tablet:gap-4 pc:gap-6 w-full">
{isLoading ? (
<BestArticleSkeletons count={pageSize} />
) : (
<BestArticleList articles={data?.list ?? []} />
)}
</div>
</section>
);
};

export default BestSection;
Copy link
Collaborator

Choose a reason for hiding this comment

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

해당 부분은 Suspense와 ErrorBoundary를 사용하여 선언적 처리를 해볼 수 있어요. 즉 지금처럼

 if (error) {
    return <div>process of fetching articles failed</div>;
  }

  if (loading) {
    ...
  }

위와 같은 코드를 선언적으로 대체해주는 방법이예요.

Copy link
Collaborator

Choose a reason for hiding this comment

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

import { Suspense } from "react";
import { ErrorBoundary } from "react-error-boundary";

...

const BestSection = () => {
 return (
   <section className="mb-6 pc:mb-10 w-full">
     <h1 className="text-[20px] font-[700] mb-4 tablet:mb-6">베스트 게시글</h1>
     <ErrorBoundary FallbackComponent={ErrorFallback}>
       <Suspense fallback={<BestArticleSkeleton />}>
         <BestArticleContent />
       </Suspense>
     </ErrorBoundary>
   </section>
 );
};

export default BestSection;

Comment on lines +6 to +10
interface NormalArticleProps extends Omit<Article, "image"> {
imageUrl: string;
likeIcon: string;
isLoading: boolean;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Omit 사용 굳! 👍

Copy link
Collaborator

Choose a reason for hiding this comment

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

그 외로 많이 사용하는 Pick, Partial, Required 도 사용해보세요~

return (
<DropdownMenu>
<DropdownMenuTrigger
className="flex items-center justify-center gap-2 h-[42px] rounded-[12px] border bg-white hover:bg-gray-50
Copy link
Collaborator

Choose a reason for hiding this comment

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

정말 자주 사용하는 flex items-center justify-center 이런 것들은 global.css 에 아래처럼 클래스네임을 직접 커스텀하게 추가해서 사용해보세요. 예를들어 flex-center로 작명 후 조금 더 빠르게 스타일을 선언할 수 있습니다.

.flex-center {
  @apply flex items-center justify-center
}

이 후, 아래처럼 적용해볼 수 있겠어요.

className="flex-center gap-2 h-[42px] rounded-[12px] border bg-white hover:bg-gray-50"

@arthurkimdev arthurkimdev merged commit 082cd23 into codeit-bootcamp-frontend:Next-김승준 Dec 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants