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주차] Therapease 과제 제출합니다. #14

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

Conversation

flowersayo
Copy link

@flowersayo flowersayo commented May 19, 2023

배포링크

https://ceos-6th-next-netflix-17th.netlify.app/

필수 요건

  • 결과화면의 상세 페이지와 검색 페이지를 구현합니다.
  • 상세 페이지는 동적 라우팅을 이용해 구현합니다.
    • 동적 라우팅을 통한 url 파라미터 ( 영화 id ) 로 특정 영화 정보를 가져옵니다. (SSR)
  • 검색 페이지는 실시간 키워드 검색으로 구현합니다.
  • Figma의 디자인을 그대로 구현합니다.
  • SSR을 적용해서 구현합니다.
  • Open api를 사용해서 데이터 패칭을 진행합니다. (ex. themoviedb API)

추가 구현 🧩

선택 사항

  • 검색 페이지 무한스크롤을 구현합니다.
  • 검색 페이지 스켈레톤 컴포넌트를 구현합니다.
  • 성능 최적화를 위한 방법을 적용해봅니다.

Key Question

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

정적 라우팅(Static Routing)

  • 라우팅 경로와 해당 경로에 대한 페이지를 사전에 정의하는 방식
  • 즉, 프로젝트에서 사용할 모든 경로를 미리 알고 있고, 각 경로에 대해 페이지 컴포넌트를 매핑하여 정적으로 설정
  • Next.js에서는 pages 디렉토리에 파일을 생성하고 파일 구조를 통해 경로가 자동으로 설정됨.
    • 예를 들어, pages/about.js 파일은 "/about" 경로에 대한 페이지를 나타냅니다.
  • 정적 라우팅은 빌드 시점에 페이지를 사전 렌더링하여 정적 HTML 파일로 제공 => 성능 향상과 검색 엔진 최적화(SEO)

동적 라우팅(Dynamic Routing)

  • 런타임 중에 경로 매개변수를 사용하여 페이지를 동적으로 생성하는 방식
  • 동일한 페이지 컴포넌트를 여러 경로에 대해 재사용할 수 있다.
    • 예를 들어, 블로그 게시물을 나타내는 페이지 컴포넌트가 있다면, "/blog/post1", "/blog/post2"와 같이 다양한 경로에 대해 동적으로 페이지를 생성할 수 있다.
  • 런타임에 요청이 발생할 때 페이지를 렌더링하므로 외부 데이터 소스에서 데이터를 가져와 페이지를 구성하는 데 사용할 수 있다.
  • Next.js에서는 동적 경로를 지정하기 위해 파일 이름에 대괄호([])를 사용하고, 경로 매개변수를 통해 동적으로 생성되는 페이지를 처리할 수 있다. 예를 들어, detail/[id].js 파일은 "/detail/:id" 경로에 대한 동적 페이지를 나타낸다. id는 실제 요청 경로에서 사용되는 동적 매개변수를 뜻한다.

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

<useRef 를 활용한 렌더링 성능 향상>

: 리액트 전 생애주기 동안 상태가 유지되어야하지만, 화면 변화와는 직접적으로 관계없는 값을 다뤄야하는 경우 useState 대신
useRef를 사용하였습니다. 유튜브 api 를 통한 검색 결과 영상 링크를 저장할때 useRef 를 활용하였습니다.

const youtubeId = useRef(null); //주목할 부분

  const getYoutubeLink = async () => {
    let results = await (
      await fetch(
        `https://www.googleapis.com/youtube/v3/search?type=video&q=${query.name}&key=${process.env.YOUTUBE_API_KEY}`
      )
    ).json();

    youtubeId.current = results.items[0].id.videoId;

    console.log(results, youtubeId.current);
  };

  useEffect(() => {
    getYoutubeLink();
  }, []);

  const handleClickPlayBtn = () => {
    if (youtubeId.current) {
      window.open(`https://www.youtube.com/watch?v=${youtubeId.current}`);
    }
  };

만일 해당 youtubeId를 state 변수로 저장했다면 youtube api 로 검색 결과를 가져온 후 의미 없는 화면 재 렌더링이 일어났겠지만, useRef 변수로 처리하였기 때문에 불필요한 화면 재 렌더링 없이 링크 값을 업데이트 할 수 있게 되었습니다.

Gaeun-Kwon and others added 30 commits May 10, 2023 03:52
Add: 프로젝트 초기 세팅
* feat: splash

* add : add tab icons

* feat: navigations ( Bottom Tab )
* Feat: Header 컴포넌트 생성

* Style: theme 스타일 생성
스크롤 안보이도록 커스텀
* Feat: Header 컴포넌트 생성

* Style: theme 스타일 생성

* Feat: PlayBar 컴포넌트 생성

* Fix: 무한 리렌더링 수정
Feat: 메인페이지 영화리스트 구현
* Feat: Header 컴포넌트 생성

* Style: theme 스타일 생성

* Feat: PlayBar 컴포넌트 생성

* Fix: 무한 리렌더링 수정

* Fix: localhost -> 127.0.0.1
@flowersayo flowersayo changed the title Therapease 과제 제출합니다! [6주차] Therapease 과제 제출합니다. May 19, 2023
Copy link

@westofsky westofsky left a comment

Choose a reason for hiding this comment

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

안녕하세요 Repick팀 배성준입니다!
이번 과제하느라 정말 고생하셨어요~~~~
코드리뷰하면서 type을 잘 지정하시는 것 배우고 갑니다! css적으로 디테일까지 배웠던 것 같아요
몇가지 간단한 comment 작성했습니다!
혹시 근데 저만 netflix-logo.gif가 안보이나요 .. ?

Comment on lines +18 to +23
const getYoutubeLink = async () => {
let results = await (
await fetch(
`https://www.googleapis.com/youtube/v3/search?type=video&q=${query.name}&key=${process.env.YOUTUBE_API_KEY}`
)
).json();

Choose a reason for hiding this comment

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

key를 가져올 때
image
오류가 있는데 혹시 배포에서 오류가 난 걸까요 ??

Choose a reason for hiding this comment

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

혹시 YOUTUBE_API_KEY 앞에 NEXT_PUBLIC_~~ 붙여보시는건 어떨까요? 저도 .env 파일에서 API_KEY 이런 식으로 선언하니깐 undefined로 떴던 기억이 있어서요!

Comment on lines +17 to +22
<Image
src={imgPath}
width={400}
height={415}
alt="main"
style={{ objectFit: 'cover' }}

Choose a reason for hiding this comment

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

전체 home container은 375px로 주었는데 이미지에 400px을 주어서 사이즈가 안 맞는 것 같아요

.container {
display: flex;
gap: 0.5rem;
width: 40rem;

Choose a reason for hiding this comment

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

40rem을 주신 이유가 있을까요 ?? 이렇게되면 화면이 전체 container 크기인 375px을 넘게될 수 있어서 100%로 부모 크기 맞춰가는 게 좋아 보입니다!

Suggested change
width: 40rem;
width : 100%;

Comment on lines +8 to +39
const tabLists = [
{
id: 0,
name: 'Home',
src: ['/icons/home.svg', '/icons/home_selected.svg'],
link: '/home',
},
{
id: 1,
name: 'Search',
src: ['/icons/search.svg', '/icons/search_selected.svg'],
link: '/search',
},
{
id: 2,
name: 'Coming Soon',
src: ['/icons/play.svg', '/icons/play_selected.svg'],
link: '/home',
},
{
id: 3,
name: 'Downloads',
src: ['/icons/download.svg', '/icons/download_selected.svg'],
link: '/home',
},
{
id: 4,
name: 'More',
src: ['/icons/more.svg', '/icons/more_selected.svg'],
link: '/home',
},
];

Choose a reason for hiding this comment

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

아이콘 수가 많을수록 svg파일을 다운 받는 것은 성능 저하의 가능성이 있어서 react-icons 를 사용해보시는 건 어떤가요 ??

Copy link
Member

Choose a reason for hiding this comment

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

저는 css자식 선택자를 통해 svg의 fill속성을 바꿔줌으로서 svg파일을 import하지만, 다른 색을 칠해주도록 구현하였어요!! 이 방식도 괜찮은것같아 추천드립니다~!

Comment on lines +19 to +25
<Image
src={`${IMAGE_BASE_URL}/${movie.poster_path}`}
width={146}
height={76}
alt="image"
style={{ objectFit: 'cover' }}
/>

Choose a reason for hiding this comment

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

image
에서처럼 image가 없는 경우에 Netflix 로고를 띄워두거나 하면 좋은 것 같아요!

Suggested change
<Image
src={`${IMAGE_BASE_URL}/${movie.poster_path}`}
width={146}
height={76}
alt="image"
style={{ objectFit: 'cover' }}
/>
{movie.poster_path === null ? (
<Image
src="/netflix-logo.png"
width={146}
height={76}
alt={movie.title}
style={{ objectFit: 'cover' }}
/>
) : (
<Image
src={`${IMAGE_BASE_URL}/${movie.poster_path}`}
width={146}
height={76}
alt={movie.title}
style={{ objectFit: 'cover' }}
/>
)}

Comment on lines +17 to +22
<Image
src="/images/netflix-logo.gif"
width={700}
height={500}
alt="netflix-logo"
/>

Choose a reason for hiding this comment

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

혹시 gif는 저만 안 보이는 걸까요 ...??

Choose a reason for hiding this comment

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

저두요ㅠㅠ

Copy link
Member

Choose a reason for hiding this comment

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

gif파일이라 용량이 너무 커서 렌더링이 안되는것인가...싶긴하네요

}

.section-input input {
width: calc(100% - 40px - 20px - 15px);

Choose a reason for hiding this comment

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

저는 calc를 잘 쓸 줄 몰라서 보통 flex로 간격 조정하면 그에 맞는 크기를 주려고 하는데요 이 방법도 한번 참고하시면 좋을 것 같아요!

Suggested change
width: calc(100% - 40px - 20px - 15px);
flex-grow: 2;
padding-left: 20px;

Copy link

@oyatplum oyatplum left a comment

Choose a reason for hiding this comment

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

안녕하세요~ 6주차 코드 리뷰 파트너 Repick의 이예지입니다!!😆
이번 주도 수고 많으셨어요ㅎㅎ!!

깔끔하게 컴포넌트 관리해주신 부분과 디테일한 css 보고 많이 배울 수 있었던 코드인 것 같아요!! 두 분의 고민한 흔적이 많이 보인 과제인 것 같습니다!👍🏻👍🏻

제가 하고싶은 말을 성준님이 이미 해주셔서... 몇 마디만 추가했어요^^...!!

넷플릭스 과제 마무리 하시느라 고생하셨고 스터디 때 봬요~💗

Comment on lines +17 to +19
{MENU_LIST.map((menu) => {
return <span key={menu}>{menu}</span>;
})}

Choose a reason for hiding this comment

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

Header 부분을 배열로 처리하는 아이디어!! 좋은 것 같네요ㅎㅎ👍🏻

Comment on lines +18 to +23
const getYoutubeLink = async () => {
let results = await (
await fetch(
`https://www.googleapis.com/youtube/v3/search?type=video&q=${query.name}&key=${process.env.YOUTUBE_API_KEY}`
)
).json();

Choose a reason for hiding this comment

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

Youtube search api v3 로 비디오 정보를 가져와 주셨군요!! 혹시 TMDB가 아닌 유튜브에서 가지고 오신 이유가 있으실까요??

Comment on lines +17 to +22
<Image
src="/images/netflix-logo.gif"
width={700}
height={500}
alt="netflix-logo"
/>

Choose a reason for hiding this comment

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

저두요ㅠㅠ

Copy link
Member

@moong23 moong23 left a comment

Choose a reason for hiding this comment

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

안녕하세요 Hooking팀의 김문기입니다~!

useRef를 통해 불필요한 렌더링을 방지하신것과, 여러 신선한 코드들을 통해 새로운 방식을 시도하려고 하신것이 눈에 많이 띄었습니다...!

이번주 과제두 고생 많으셨어요!!! 담주부터 시작하는 협동 과제도 화이팅입니다~!!!

Comment on lines +15 to +17
{movieLists.map((movie, idx) => (
<FlatListItem item={movie} key={idx} round={round} />
))}
Copy link
Member

Choose a reason for hiding this comment

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

key값은 될 수 있으면 movie의 특정 id값이나 date값을 넣어주는것..이 좋다고 합니다~!!

Comment on lines +47 to +56
<Image
src={`${IMAGE_BASE_URL}${poster_path}`}
width={sizes[size].width}
height={sizes[size].height}
alt={title || 'movie_img'}
style={{
objectFit: 'cover',
borderRadius: round ? '10rem' : 0,
}}
/>
Copy link
Member

Choose a reason for hiding this comment

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

Image태그에 placeholder를 통해 이미지가 받아와지기 전에 다른 이미지를 먼저 넣어둘 수 있습니다! 이를 확인해보셔도 좋을 것 같아요!

Comment on lines +8 to +39
const tabLists = [
{
id: 0,
name: 'Home',
src: ['/icons/home.svg', '/icons/home_selected.svg'],
link: '/home',
},
{
id: 1,
name: 'Search',
src: ['/icons/search.svg', '/icons/search_selected.svg'],
link: '/search',
},
{
id: 2,
name: 'Coming Soon',
src: ['/icons/play.svg', '/icons/play_selected.svg'],
link: '/home',
},
{
id: 3,
name: 'Downloads',
src: ['/icons/download.svg', '/icons/download_selected.svg'],
link: '/home',
},
{
id: 4,
name: 'More',
src: ['/icons/more.svg', '/icons/more_selected.svg'],
link: '/home',
},
];
Copy link
Member

Choose a reason for hiding this comment

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

저는 css자식 선택자를 통해 svg의 fill속성을 바꿔줌으로서 svg파일을 import하지만, 다른 색을 칠해주도록 구현하였어요!! 이 방식도 괜찮은것같아 추천드립니다~!

&:hover {
${transformScale}
transform: scale(1.01);
-webkit-transform: scale(1.01);
Copy link
Member

Choose a reason for hiding this comment

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

webkit을 통해 여러 브라우저에도 동일하게 적용될 수 있게 신경써주신 부분이 인상깊네요~!

Comment on lines +9 to +11
const myFont = localFont({
src: '../assets/sanfranciscodisplay-regular-webfont.woff',
});
Copy link
Member

Choose a reason for hiding this comment

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

웹폰트 적용까지..!

const router = useRouter();
const { query } = router;

const youtubeId = useRef(null);
Copy link
Member

Choose a reason for hiding this comment

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

youtubeId를 useRef값으로 관리하여서 불필요한 rerendering이 발생하는것을 방지하는것 좋은 것 같습니다!

Comment on lines +17 to +22
<Image
src="/images/netflix-logo.gif"
width={700}
height={500}
alt="netflix-logo"
/>
Copy link
Member

Choose a reason for hiding this comment

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

gif파일이라 용량이 너무 커서 렌더링이 안되는것인가...싶긴하네요

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.

6 participants