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

[6주차] 로컬무드팀 미션 제출합니다. #13

Open
wants to merge 111 commits into
base: master
Choose a base branch
from

Conversation

geeoneee
Copy link

🔍 서론

안녕하세요 로컬무드팀 김현민 김지원입니다~ 👀
저희는 이번 과제 추가 기능으로 검색 페이지 무한스크롤, 검색 페이지 스켈레톤 컴포넌트 구현해보았습니다:)
열심히 했으니 잘 봐주세용:) 😊😊😊



🔍 배포링크

https://next-netflix-18th-woad.vercel.app/


🔍 Key Features

  • 결과화면의 상세 페이지와 검색 페이지
  • 상세 페이지 : 동적 라우팅으로 구현
  • 검색 페이지 : 실시간 키워드 검색, 무한 스크롤, 스켈레톤 컴포넌트
  • Figma의 디자인 그대로 구현
  • SSR 적용
  • 반응형 고려



🔍 KEY QUESTIONS

1. 정적 라우팅(Static Routing)/동적 라우팅(Dynamic Routing)이란?

Next.js는 파일 시스템 기반 정적 라우팅과 동적라우팅을 지원합니다.

  • 정적 라우팅
    -> 직접 경로를 설정
스크린샷 2023-11-17 오후 7 30 10


  • 동적 라우팅
    -> 한번 경로를 설정하면 라우팅 프로토콜을 통해 알아서 계산되어 경로가 설정.
    -> 예를 들어, 보여줄 화면이 너무 많아 버리게 되면 각각의 상품에 따라 페이지를 만들어 정적라우팅을 해주기는 어렵기 때문에 효과적으로 처리하기 위해서 동적 라우팅 을 사용합니다.
    -> 동일한 UI를 보여주면서, 이미지 경로나 제목만 바뀌게 할 수 있습니다.
스크린샷 2023-11-17 오후 7 30 48


2.성능 최적화를 위해 사용한 방법

  1. Next jsImage 컴포넌트 활용
  • 이미지 최적화를 통한 성능 개선
  1. 데이터 fetch 시 적절한 캐싱처리
  2. ssr 과 서버컴포넌트, 클라이언트 컴포넌트의 분리를 통한 사용자 경험 개선

wokbjso and others added 30 commits November 6, 2023 20:36
wokbjso and others added 25 commits November 16, 2023 00:39
Feat:스켈레톤 애니메이션 추가
Copy link

@oooppq oooppq left a comment

Choose a reason for hiding this comment

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

한주동안 수고하셨습니다~~ 정말 다양한 라이브러리를 사용하여 기능을 구현해주신 것 같아요! 저도 사용해보지 않은 기능들을 많이 보게 됐는데, 잘 배워서 나중에 사용해볼게요~~

import { twMerge } from "tailwind-merge";
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import Slider from "react-slick";
Copy link

Choose a reason for hiding this comment

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

react-slick 라이브러리르 사용하셨네용 저도 다른 프로젝트 할 때 썼었는데 간단하게 슬라이더 만들기 편리한 라이브러리인 것 같아요~~

slidesToScroll: 1,
};
return (
<div className={twMerge("bg-background-main", className)}>
Copy link

Choose a reason for hiding this comment

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

tailwind에서 이런 기능도 제공하는군요 담에 써봐야겠어요!

Comment on lines +86 to +87
placeholder="blur"
blurDataURL="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFklEQVR42mN8//HLfwYiAOOoQvoqBABbWyZJf74GZgAAAABJRU5ErkJggg=="
Copy link

Choose a reason for hiding this comment

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

blur로 속성 지정하고 원하는 이미지를 보여주는 방식으로 구현한 것 같네여 Image 컴포넌트를 잘 활용하신 것 같습니다~~

data={await getMovies(sliderInfo[0].url)}
/>
<PlayBar />
{sliderInfo.map(async (slider, index) => (
Copy link

Choose a reason for hiding this comment

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

아예 각각의 슬라이더 정보를 배열로 만들어서 map함수로 간단하게 표현한 부분 좋은 것 같습니다~~

@@ -0,0 +1,45 @@
import { BiPlayCircle } from "react-icons/bi";
import InfiniteScroll from "react-infinite-scroller";
Copy link

Choose a reason for hiding this comment

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

아예 무한스크롤 기능을 제공하는 라이브러리를 사용하셨네요! 저는 react-intersection-observer을 사용해서 간단하게 구현했는데, 사용하신 라이브러리가 개발할 때에는 훨씬 편할 것 같아요! 제가 사용한건 직접 기능을 구현해줘야하긴 하거든요..

Comment on lines +39 to +41
<Suspense fallback={<SearchMovieSkeletonList />}>
<MovieList searchText={searchText} />
</Suspense>
Copy link

Choose a reason for hiding this comment

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

Suspense 기능을 이용해서 로딩중일 때에는 skeleton component가 표현되도록 구현하셨네요! 저도 suspense를 이용하려다가 검색어 입력에 따른 검색결과 출력 과정에서의 로딩의 skeleton을 표현하기엔 한계가 있는 것 같아서 사용하지 않게 되었습니다.. 페이지를 전환할 때에 suspense를 적극적으로 사용한다면 ux적으로 견고한 웹을 만들 수 있을 것 같아요!

Copy link

@silverain02 silverain02 left a comment

Choose a reason for hiding this comment

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

이번 주도 고생하셨습니다~! ssr로 데이터 불러오는 방식 많이 배워가요!!

Comment on lines +2 to +3
const apiKey =
"eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIzZDJjYWU5NGI2Yzk1MWNiY2EyMmVjMTc5Y2JmZDM3ZSIsInN1YiI6IjYzZDhiNDYzM2RjMzEzMDA4MjMyZTkyMSIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.fMlQjoW-zgChmdjBWD4oB0mHbaJ4fRjmXL8XAGougHY";

Choose a reason for hiding this comment

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

axios instance생성하셨을 때 처럼 API key는 env파일로 빼는 것도 좋을 것 같아요!

Comment on lines +4 to +9
const headers = {
Authorization: `Bearer ${apiKey}`,
};

const res = await fetch(url, { headers });
const data = await res.json();

Choose a reason for hiding this comment

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

요기서 생성해둔 axios instance를 활용하지 않고 fetch를 쓴 이유가 있을까요?!

Copy link

Choose a reason for hiding this comment

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

저도 궁금합니다! next 13으로 오면서 getServersideProps 대신 fetch 방식으로 불러오면 데이터 fetching을 자동적으로 해준다고 해서 사용해보려고 했다가... 데이터가 undefined로 넘어오더라구요ㅜ 해당 부분 염두에 두시고 fetch로 사용하신 것일까요? 그렇다면 저희 코드에도 반영해보고 싶습니다..😅

Comment on lines +13 to +18
<Image
alt="영화 이미지"
src={getMoviePoster(movieInfo.poster_path)}
sizes="100vw"
fill={true}
placeholder="blur"

Choose a reason for hiding this comment

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

Next Image활용하셨군요! 저희는 쓰다가 size설정이 안되는 오류때문에 그냥 img태그를 썼는데 참고해갑니다~!

Copy link

Choose a reason for hiding this comment

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

width, height 에 매몰되어 있었는데... sizes 설정도 할 수 있었군요🥲 참고하겠습니다!!!

</div>
)}
<Slider {...settings}>
{data.map((movie) => (

Choose a reason for hiding this comment

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

slider 이미지들 map처리하니까 코드 가독성이 높아진 것 같아요!

<Header />
<CustomSlider<getMainSliderMovieResponse>
type="big"
data={await getMovies(sliderInfo[0].url)}

Choose a reason for hiding this comment

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

async-await으로 server-side component에서 데이터 불러오는 방식 좋은 것 같아요!

Copy link

@mod-siw mod-siw left a comment

Choose a reason for hiding this comment

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

이번 주차 과제도 수고 많으셨습니다!!
저도 이번에 처음 tailwind CSS를 사용해봤는데, 코드 리뷰하면서 tailwind를 어떻게 하면 보다 잘 사용할 수 있는지 많이 배웠어요. 몰랐던 속성들도 많이 배워가고, 무한 스크롤 관련해서도 보면서 많이 익혀갑니다!😄
스터디 시간에 뵈어요!

{HeaderState.map((state) => (
<a
key={state}
className="hover:text-button-main cursor-pointer transition duration-300 ease-in-out"
Copy link

Choose a reason for hiding this comment

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

단순 cursor-pointer만 준 게 아니라 hover했을 때 색 변경 디테일 좋은 것 같습니다👍👍

@@ -0,0 +1,21 @@
import { ReactNode } from "react";
import { ImPlay3 } from "react-icons/im";
import { twMerge } from "tailwind-merge";
Copy link

Choose a reason for hiding this comment

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

저번 주차 스터디에서 스타일 컴포넌트 단점과 비교하여 tailwind 장점으로 컴포넌트별 디테일 설정을 들어주셨었는데 이렇게 쓸 수 있었군요! twMerge를 몰랐었는데 덕분에 리뷰하면서 알아갑니다. 잘 활용하면 굉장히 효율적일 것 같아요😊😊 리뷰하며 예시 코드까지 함께 배워갑니다!

Comment on lines +13 to +18
<Image
alt="영화 이미지"
src={getMoviePoster(movieInfo.poster_path)}
sizes="100vw"
fill={true}
placeholder="blur"
Copy link

Choose a reason for hiding this comment

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

width, height 에 매몰되어 있었는데... sizes 설정도 할 수 있었군요🥲 참고하겠습니다!!!

Comment on lines +4 to +9
const headers = {
Authorization: `Bearer ${apiKey}`,
};

const res = await fetch(url, { headers });
const data = await res.json();
Copy link

Choose a reason for hiding this comment

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

저도 궁금합니다! next 13으로 오면서 getServersideProps 대신 fetch 방식으로 불러오면 데이터 fetching을 자동적으로 해준다고 해서 사용해보려고 했다가... 데이터가 undefined로 넘어오더라구요ㅜ 해당 부분 염두에 두시고 fetch로 사용하신 것일까요? 그렇다면 저희 코드에도 반영해보고 싶습니다..😅


export default function MovieList({ searchText }: { searchText: string }) {
const { getByFarMovieData, hasNextPage, fetchNextPage } = useGetSearchMovies({
api: searchText === "" ? "/movie/now_playing" : "/search/movie",
Copy link

Choose a reason for hiding this comment

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

아무것도 입력하지 않았을 때도 아이템 띄워주는 디테일 좋네요!!👍👍

@@ -0,0 +1,10 @@
export default function SearchMovieSkeleton() {
Copy link

Choose a reason for hiding this comment

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

스켈레톤 컴포넌트까지 구현하신 거 너무 좋은 것 같습니다...🥹 배포 페이지에서 애니메이션까지 같이 들어가는 거 보고 감탄했네요!!

export default function SearchMovieSkeletonList() {
return (
<ul
className="hide-scrollbar flex-1 overflow-y-scroll pt-[0.9rem]"
Copy link

Choose a reason for hiding this comment

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

tailwind에서 스크롤 숨기는 설정이 자기 멋대로 된다 싶었더니... hide-scollbar로 입력하는 거였군요 저는 scollbar-hide로 입력하고 있었네요...^^ 안되는 이유가 있었군요....

return (
<ul
className="hide-scrollbar flex-1 overflow-y-scroll pt-[0.9rem]"
style={{ scrollSnapType: "y proximity", scrollSnapAlign: "start" }}
Copy link

Choose a reason for hiding this comment

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

scrollSnapType 이라는 속성도 있었네요!! 이렇게 따로 설정해줄 수 있는 속성이 있는 줄 몰랐었는데 덕분에 배워갑니다~~!!

};

return (
<div className="bg-background-main pt-[4.4rem]">
Copy link

Choose a reason for hiding this comment

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

배포 페이지에서 결과가 없는 검색어를 입력하면 TopSearches 밑으로 흰색 배경이 떠서 배경 설정이 안되어있나 싶었는데 되어있네요..! 확인해보시고 height 등을 통해 배경색이 전체적으로 입혀질 수 있도록 해주셔도 좋을 것 같습니다!

searchText?: string;
api: string;
}) {
const { data, hasNextPage, fetchNextPage } = useInfiniteQuery({
Copy link

Choose a reason for hiding this comment

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

무한 스크롤에서 useInfiniteQuery 사용하는 방법 배워갑니다👍👍

<div className="font-main text-white mb-[2.4rem]">Previews</div>
<p className="text-white movie-detail-sub">{movieInfo.overview}</p>
</div>
</div>
Copy link

Choose a reason for hiding this comment

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

상세 페이지에서도 footer 넣어주시면 좋을 것 같아요!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants