Skip to content
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

[이동훈] Sprint9 #312

Conversation

Donghunn-Lee
Copy link
Collaborator

@Donghunn-Lee Donghunn-Lee commented Nov 30, 2024

요구사항

기본

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

심화

  • 반응형으로 보여지는 베스트 게시판 개수를 다르게 설정할때 서버에 보내는 pageSize값을 적절하게 설정합니다.
  • next의 data prefetch 기능을 사용해봅니다.

이미지

  • 반응형 디자인 구현

    • image
    • image
    • image
  • 검색 기능

    • image
    • 요구 사항에선 title에 검색어가 포함되면 가져온다고 되어있는데, content에 포함되어있어도 가져오도록 백엔드에서 구현된 것 같습니다.
  • 필터링 기능

    • image

멘토에게

  • Next.js로 새롭게 프로젝트를 생성하는 만큼 스타일링도 새 기술을 사용하고 싶어 tailwind css를 사용했습니다.
  • 심화 요구사항으로 베스트 게시글을 반응형으로 다르게 요청한다고 되어 있는데, 그냥 처음에 3개를 가져오고 화면에 따라 보이도록 하면 한 번의 요청으로 가능하기 때문에 그렇게 구현했습니다. 대신 일반 게시글을 모바일에서 7개, 테블릿 이상에서 10개씩 가져오도록 구현했습니다.
  • 이미지가 없을 때 대체 이미지를 설정하였습니다. 다만 example.com같은 값이 들어있는 경우엔 alt값이 출력됩니다. 이 경우엔 어떻게 해야 할 지 잘 모르겠습니다.

@Donghunn-Lee Donghunn-Lee added 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. 미완성🫠 죄송합니다.. labels Dec 3, 2024
@Donghunn-Lee Donghunn-Lee removed the 미완성🫠 죄송합니다.. label Dec 5, 2024
Comment on lines +26 to +61
const fetchBestArticles = useCallback(async () => {
const response = await getArticles({
pageSize: BEST_ARTICLE_SIZE,
orderBy: 'like',
});
setBestArticles(response);
}, []);

const fetchArticles = useCallback(async () => {
const pageSize = isMobile
? ARTICLES_PER_MOBILE_PAGE
: ARTICLES_PER_DESCKTOP_PAGE;

if (searchQuery === '') {
const response = await getArticles({
pageSize,
orderBy: sort,
});
setArticles(response);
return;
}

const response = await getArticles({
page,
pageSize,
orderBy: sort,
keyword: searchQuery,
});

setArticles(response);
}, [page, sort, searchQuery, isMobile]);

useEffect(() => {
fetchBestArticles();
fetchArticles();
}, [fetchBestArticles, fetchArticles]);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

지난 코드리뷰에서 자주 쓰이는 fetch 함수 부분을 훅으로 만들어 사용할 수 있다고 알려주셨습니다.
이번에 적용해보려고 했는데, 함수 형태는 비슷하지만 의존성을 가지는 parameter가 여러 개라서 어떻게 적용해야할 지 잠 모르겠습니다!

Copy link
Collaborator

Choose a reason for hiding this comment

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

아래 처럼 어떤 의존성을 주더라도 useFetch 훅에서는 fetch 함수에게 받아온 parameter 값을 그대로 전달하는 방법으로 설계하면 어떨까요?

예를들면 fetch 는 아래처럼 되야할거예요. axios의 params 옵션은 자동으로 query string으로 변환되기 때문에, 파라미터를 그대로 전달하면 됩니다.

interface FetchParams extends Record<string, any> {
 page?: number;
 pageSize?: number;
 orderBy?: 'recent' | 'like';
 keyword?: string;
}

const getArticles = async (params: FetchParams) => {
 const response = await axios.get('/articles', { params });
 return response.data;
};

Copy link
Collaborator

Choose a reason for hiding this comment

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

가칭 useFetch는 아래 아디이어 같은 방법이 있겠어요. 동작할 수 있게 디테일한 내용은 수정해야겠지만요!

interface FetchState<T> {
 data: T | null;
 error: Error | null;
 isLoading: boolean;
}

const useFetch = <T, P extends Record<string, any>>(
 fetcher: (params: P) => Promise<T>,
 params: P,
 deps?: any[]
) => {
 const [state, setState] = useState<FetchState<T>>({
   data: null,
   error: null,
   isLoading: false
 });

 useEffect(() => {
   const fetchData = async () => {
     setState(prev => ({ ...prev, isLoading: true }));
     try {
       const data = await fetcher(params);
       setState({ data, error: null, isLoading: false });
     } catch (err) {
       setState({ data: null, error: err as Error, isLoading: false });
     }
   };

   fetchData();
 }, deps ? [...deps] : [JSON.stringify(params)]);

 return state;
};

Copy link
Collaborator

@arthurkimdev arthurkimdev Dec 8, 2024

Choose a reason for hiding this comment

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

그렇담 실제로 사용할 때, 아래처럼 어떤 parameter 값이 전달되어서 무방할 것 같아요~ 😸

// articles
const { data: articles } = useFetch(getArticles, {
 page: 1,
 pageSize: 10,
 orderBy: 'recent'
});

// products
const { data: products } = useFetch(getProducts, {
 category: 'electronics',
 limit: 20,
 sort: 'desc'
});

Comment on lines +1 to +3
export const BEST_ARTICLE_SIZE = 3;
export const ARTICLES_PER_MOBILE_PAGE = 7;
export const ARTICLES_PER_DESCKTOP_PAGE = 10;
Copy link
Collaborator

Choose a reason for hiding this comment

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

상수를 한 파일에 정리하는 습관 좋습니다. 👍


export default function Document() {
return (
<Html lang="en">
<Html lang='en'>
Copy link
Collaborator

Choose a reason for hiding this comment

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

en 이 아니라 kr로 변경해주셔야 되겠죠!? 🙂

}) {
return (
<section className=''>
<div className='flex justify-between items-center'>
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로 작명 후 조금 더 빠르게 스타일을 선언할 수 있습니다.

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

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

<div className='flex-center'>

Comment on lines +12 to +15
import BestArticleContainer from './BestArticleContainer';
import Header from './Header';
import ArticleContainer from './ArticleContainer';
import SearchBar from './SearchBar';
Copy link
Collaborator

Choose a reason for hiding this comment

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

이러한 컴포넌트들도 @hooks/ 처럼 절대주소를 사용하는걸로 통일 하면 더 좋습니다. 🙂

const [isOpen, setIsOpen] = useState(false);

const handleOptionClick = (option: sort) => {
console.log(option);
Copy link
Collaborator

Choose a reason for hiding this comment

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

콘솔 제거 필요합니다. 😎

height={28}
/>
</div>
<div className='hidden sm:block'>
Copy link
Collaborator

Choose a reason for hiding this comment

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

보통 반응형 코드를 작성할 때 모바일 > 태블릿 > 데스크탑 기준으로 클래스명을 작성해요. 이 순서로 하게 되면 lg는 굳이 붙이지 않아도 도 되겠죠!?

<div className='sm:block md:0000 hidden'>

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.

수고하셨습니다~ 👍

@arthurkimdev arthurkimdev merged commit 21d28ae 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