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주차] Team Couplelog 김민영 & 안혜연 미션 제출합니다. #13

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

Conversation

Rose-my
Copy link

@Rose-my Rose-my commented May 17, 2024

배포링크

6주차 미션: Next-Netflix

🌱 필수, 선택 구현

  • 상세 페이지는 동적 라우팅을 이용해 구현합니다.
  • 검색 페이지는 실시간 키워드 검색으로 구현합니다.
  • Open api를 사용해서 데이터 패칭을 진행합니다. (ex. themoviedb API)
  • 성능 최적화를 위한 방법을 적용해봅니다.
  • SSR(Server Side Rendering)을 적용해서 구현합니다.
  • 검색 페이지 무한스크롤을 구현합니다.
  • 검색 페이지 스켈레톤 컴포넌트를 구현합니다.

🪴 Team

👨‍💻 팀원 정보 및 역할

김민영 안혜연
WEB Frontend WEB Frontend

👨‍💻 팀원 역할 분담

▶️ View & API

View API
🧑🏻‍🎨 김민영 SearchPage Search 영화사진 불러오기 GET
🐰 안혜연 DetailsPage Details 영화사진 불러오기 GET

📄 브랜치 전략

[Issue 먼저 생성하고 해당 이슈 번호 브랜치 생성]

▶️ 브랜치명

  • main: 최종 Merge를 하는 곳 (배포 브랜치)
  • develop : 개발할때 Merge하는 곳
  • feature : 기능을 개발하면서 각자 페이지별로 사용할 브랜치
  • test : 개인 연습 브랜치

▶️ 브랜치 전략

  • feature/#이슈번호/페이지/기능설명
develop
ㄴ feature/#이슈번호/home/headerUI
  • ↩️ PR은 1명 이상이 확인하면 merge

📚 커밋 컨밴션

커밋 단위는 반드시 최소한의 작업 단위로 쪼개서, 한 PR당 10커밋 이상 넘어가지 않도록 합니다.

커밋 역할
feat 기능 구현과 관련된 커밋
style 코드 순서, css등의 포맷에 관한 커밋 (기능에 변화X)
design UI 구현 (css 구체화) 커밋
fix 버그를 고친 경우
refactor 더 좋은 코드로 개선한 경우 (기능에 변화가 없는 경우) ex-코드리뷰 반영
docs README.md 등 문서를 작성한 경우
chore 주석 추가, 자잘한 문서 수정

📄 리팩토링

[layout]

root layout

  • root layout에는 html, body (head) 태그만 있게 한다
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
} 

(common layout)

  • 공통 하단 바는 common layout 파일에 위치
export default function CommonLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <section className="children-container">
      {children}
      <FooterLine />
    </section>
  );
}
  • 🫨 최종 app 폴더 구조

📦app
┣ 📂(commonLayout)
┃ ┣ 📂details
┃ ┃ ┗ 📂[id]
┃ ┃ ┃ ┗ 📜page.tsx
┃ ┣ 📂home
┃ ┃ ┗ 📜page.tsx
┃ ┣ 📂search
┃ ┃ ┗ 📜page.tsx
┃ ┗ 📜layout.tsx
┣ 📜layout.tsx
┗ 📜page.tsx



[env]

  • Next.js에서는 환경 변수 이름이 NEXT_PUBLIC_으로 시작해야 클라이언트 사이드에서도 환경 변수에 접근할 수 있다

  • 배포 시 웹서버에서 발생하는 문제 : 배포할 때 프로젝트 설정에 env 넣어주어야 한다

Error: An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.


[코드 리팩토링]

  • 중복된 코드는 배열화해서 map함수로 수정

[SSR]

  • api 호출 함수를 useEffect 없이 SSR 방식으로 하려고 했는데 에러가 떠서 기능구현이라도 일단 끝내기로 했습니다. ㅜ

🛠 기술 스택

역할 종류
Library NextJS
Programming Language TypeScript
Styling Tailwind
Data Fetching Axios
Formatting ESLint Prettier
Package Manager Npm
Version Control Git GitHub

🎀 pr 상세

🔗 PR Full ver.

🧨 KEY QUESTIONS

[정적 라우팅(Static Routing)/동적 라우팅(Dynamic Routing)이란?]

  1. 정적 라우팅은 하나의 고정된 페이지로 라우팅되는 것을 의미한다. 사용자의 요청에 따라 해당 경로에 정의된 페이지를 제공한다.

  2. 동적 라우팅은 미리 정의된 URL 주소로만 라우팅하는 것이 아니라 사용자가 접근한 경로 혹은 특정 값에 따라 동적인 라우팅을 제공하고 싶을 때 사용할 수 있는 방식이다. 단일 페이지, 하나의 컴포넌트를 사용하여 변화하는 데이터를 수용할 수 있는 페이지를 제작할 수 있다. 동적 라우팅은 URL의 일부를 변수로 사용하여 같은 페이지 레이아웃을 다양한 데이터와 함께 재사용할 수 있는 큰 장점이 있다.

파일 경로 이미지

위의 이미지와 같이 대괄호 []로 시작하는 폴더를 생성하여 그 안에 page.tsx를 만들어 '/details/123' '/details/456' 과 같은 url을 만들 수 있다. '/details/1/565544'와 같은 주소도 만들 수 있다.

[성능 최적화를 위해 사용한 방법]

  • Next.js에서 제공하는 Image 컴포넌트 사용
    • 이미지를 자동으로 압축하고, 적절한 크기로 리사이즈하며, 필요에 따라 지연 로딩을 적용한다.

이번주가 둘 다 너무 바빴습니다 ㅜ 시간 내서 둘이 다시한번 SSR 구현해보도록 하겠습니다

Rose-my and others added 22 commits May 15, 2024 15:17
"images.domains" configuration is deprecated. Please use "images.remotePatterns" configuration instead
@Rose-my Rose-my changed the title . [6주차] Team Couplelog 김민영 & 안혜연 미션 제출합니다. May 17, 2024
Copy link

@wokbjso wokbjso left a comment

Choose a reason for hiding this comment

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

안녕하세요~~ 19기 프론트 멘토 김현민 이라고합니다!!!

전체적으로 너무 깔끔하고 정돈된 코드 잘 봤습니다 ㅎㅎ
너무 잘 정리된 코드들을 보며 저도 배워가는 점이 있었던 것 같습니다.

이번 과제 너무 수고 많으셨어요~!!! 👍

@@ -0,0 +1,30 @@
name: git push into another repo to deploy to vercel
Copy link

Choose a reason for hiding this comment

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

요금 부과를 막기 위해 개인 레포로 이동하는 과정을 Github Actions로 작성하신 점 멋있네요 ㅎㅎ

/>
)}
</div>
<div className="w-full h-full absolute bg-gradient-to-b from-transparent via-transparent to-black"></div>
Copy link

Choose a reason for hiding this comment

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

요 부분 컴포넌트로 생성해 주면 좀 더 코드가 깔끔해질 것 같아요 ㅎㅎ
Today.tsx 에서도 동일한 코드를 사용중이여서 재사용성도 좋아질 것 같습니다~~

Suggested change
<div className="w-full h-full absolute bg-gradient-to-b from-transparent via-transparent to-black"></div>
<GradientStyle />

Comment on lines +13 to +48
return (
<div>
<div className="w-full h-[415px] relative">
<div className="absolute inset-0">
{randomMovie && (
<Image
fill
src={`https://image.tmdb.org/t/p/original${randomMovie.poster_path}`}
alt={randomMovie.title}
style={{ objectFit: 'cover' }}
priority
/>
)}
</div>
<div className="w-full h-full absolute bg-gradient-to-b from-transparent via-transparent to-black"></div>
</div>
<section className="flex flex-row justify-center gap-[5px] pt-0.5">
<TopNImg />
{randomIndex !== null && (
<div className="fonts-today">Top {randomIndex + 1} in Korea Today</div>
)}
</section>
<section className="flex justify-between w-full h-[45px] mt-[11px] pl-[54px] pr-[62px]">
<div className="flex flex-col items-center">
<PlusImg />
<div className="fonts-mainicon">My List</div>
</div>
<PlayBtn width={6.91406} />
<div className="flex flex-col items-center">
<InfoImg />
<div className="fonts-mainicon">Info</div>
</div>
</section>
</div>
);
}
Copy link

Choose a reason for hiding this comment

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

Today.tsx 컴포넌트에서 이미지 띄우기 / ...in Korea Today 텍스트 띄우기 /버튼 바 띄우기 이렇게 여러 성격의 기능들이 들어가 있어서 컴포넌트를 좀만 더 생성해줘서 세분화해주면 더 가독성에 좋을 것 같아요~!!

Suggested change
return (
<div>
<div className="w-full h-[415px] relative">
<div className="absolute inset-0">
{randomMovie && (
<Image
fill
src={`https://image.tmdb.org/t/p/original${randomMovie.poster_path}`}
alt={randomMovie.title}
style={{ objectFit: 'cover' }}
priority
/>
)}
</div>
<div className="w-full h-full absolute bg-gradient-to-b from-transparent via-transparent to-black"></div>
</div>
<section className="flex flex-row justify-center gap-[5px] pt-0.5">
<TopNImg />
{randomIndex !== null && (
<div className="fonts-today">Top {randomIndex + 1} in Korea Today</div>
)}
</section>
<section className="flex justify-between w-full h-[45px] mt-[11px] pl-[54px] pr-[62px]">
<div className="flex flex-col items-center">
<PlusImg />
<div className="fonts-mainicon">My List</div>
</div>
<PlayBtn width={6.91406} />
<div className="flex flex-col items-center">
<InfoImg />
<div className="fonts-mainicon">Info</div>
</div>
</section>
</div>
);
}
return <div>
<BigMovieImage /> -> 이미지 띄우기
<Text text={`Top ${randomIndex + 1} in Korea Today`} className="fonts-today" /> -> ...in Korea Today 텍스트
<ButtonBar /> -> MyList, Play, info 가지고 있는 버튼
</div>

( in Korea Today 부분은 이미 짜주신 것 처럼 div 태그에 해주셔도 상관 없는데, 저는 개인적으로 Text 컴포넌트를 하나 파서 저런식으로 넘겨주면 가독성 측면에서 더 좋아지는 거 같더라구여 ㅎㅎ)

Text 컴포넌트 예시(twMerge 라이브러리 사용) ->

   return <p className={twMerge("초기 설정해 준 style", className)}>
      {text}
   </p>

Comment on lines +36 to +44
<div className="flex flex-col items-center">
<PlusImg />
<div className="fonts-mainicon">My List</div>
</div>
<PlayBtn width={6.91406} />
<div className="flex flex-col items-center">
<InfoImg />
<div className="fonts-mainicon">Info</div>
</div>
Copy link

Choose a reason for hiding this comment

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

여기서 My List와 Info 도 PlayBtn 처럼 특정 기능을 수행하고, 디자인도 겹치네요!!
버튼을 파악하기에 중요한 데이터인 버튼의 텍스트들을 외부에서 넘겨서 컴포넌트로 생성해주면 좋을거 같아요 ㅎㅎ

Suggested change
<div className="flex flex-col items-center">
<PlusImg />
<div className="fonts-mainicon">My List</div>
</div>
<PlayBtn width={6.91406} />
<div className="flex flex-col items-center">
<InfoImg />
<div className="fonts-mainicon">Info</div>
</div>
<BarDarkBtn icon={<PlusImg>} text="My List" />
<PlayBtn width={6.91406} />
<BarDarkBtn icon={<InfoImg>} text="Info" />

BarDarkBtn 컴포넌트 예시 ->

  return <button className={twMerge("flex flex-col items-center", className)}>
       {icon}
       <div className={twMerge("fonts-mainicon", textClassName)}>My List</div>
  </button>

title: string;
}

export const trendingMovies: trendingMoviesTypes[] = [
Copy link

Choose a reason for hiding this comment

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

디테일 너무 고생하셨네여 ㅋㅋㅋㅋㅋㅋㅋ 짱입니다

Comment on lines +10 to +34
export default function Page() {
const [searchedMovies, setSearchedMovies] = useState([]);

const handleSearch = async (query: string) => {
try {
const data = await getSearchedMovies(query);
setSearchedMovies(data.results);
} catch (error) {
console.error('에러 발생:', error);
}
};

return (
<section className="flex flex-col pt-11 h-full">
<div className="sticky top-0 z-20">
<SearchBar onSearch={handleSearch} />
<p className="py-5 pl-2.5 fonts-bigtitle">Top Searches</p>
</div>
<div className="flex-1 overflow-auto">
{searchedMovies.length > 0 ? <Movies searchedMovies={searchedMovies} /> : <Trending />}
</div>
<FooterNav />
</section>
);
}
Copy link

Choose a reason for hiding this comment

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

현재는 query 가 바뀔 때 마다 바로바로 api 호출을 모두 시도하고, 그때마다 페이지 전체가 리렌더링되는 상황인거 같아요!!

query 가 바뀌더라도 어느정도 api 호출을 줄여줄 수 있는 debounce 및 thottle 사용 / 과도한 리렌더링 방지를 위한 로직(useMemo, useCallback, React.memo) 에 대해 고민해보시면 좋을 것 같아요~!!


export default function Header() {
// 메뉴 항목을 배열로 정의
const menuItems = ["TV Shows", "Movies", "My List"];
Copy link

Choose a reason for hiding this comment

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

메뉴 항목을 배열로 정의한 후 map 으로 처리하신 로직 좋은거 같아요 ㅎㅎ 유지보수에도 좋은 코드 같습니다!!

{poster_path && (
<Image
fill
src={`https://image.tmdb.org/t/p/original${poster_path}`}
Copy link

Choose a reason for hiding this comment

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

https://image.tmdb.org/t/p/original

해당 엔드포인트가 많은 파일에서 사용되고 있어서 따로 변수로 생성해준 뒤 export 해주면 더 좋을 거 같아요~!!

Comment on lines +3 to +15
interface PlayBtnProps {
width: number;
handlePlayBtn?: () => void;
}

export default function PlayBtn(props: PlayBtnProps) {
const { width, handlePlayBtn } = props;

return (
<button
style={{ width: `${width}rem` }}
className={`flex justify-center items-center h-[45px] gap-4 rounded-md bg-btn-gray`}
onClick={handlePlayBtn}>
Copy link

Choose a reason for hiding this comment

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

width 처럼 css 스타일을 외부에서 받아야 할 때 twMerge 라이브러리를 사용하시면 더 편리할 것 같아요!! 혹시 추가적인 css 스타일을 더 받아와야 할 때에도 더욱 유연하게 대처도 가능할 거 같아요 ㅎㅎ

Suggested change
interface PlayBtnProps {
width: number;
handlePlayBtn?: () => void;
}
export default function PlayBtn(props: PlayBtnProps) {
const { width, handlePlayBtn } = props;
return (
<button
style={{ width: `${width}rem` }}
className={`flex justify-center items-center h-[45px] gap-4 rounded-md bg-btn-gray`}
onClick={handlePlayBtn}>
interface PlayBtnProps {
handlePlayBtn?: () => void;
className?: string;
}
export default function PlayBtn(props: PlayBtnProps) {
const { handlePlayBtn, className } = props;
return (
<button
className={twMerge(`flex justify-center items-center h-[45px] gap-4 rounded-md bg-btn-gray`, className)}
onClick={handlePlayBtn}>

외부에서 className prop 에 예를들어 w-[20rem] 이렇게만 적어주셔도 돼서 편리하고, 추가적인 스타일도 넘겨야 할 때 유연하게 대처할 수 있을 것 같습니다 ㅎㅎ

passHref
>
<div className="min-w-[102px] h-[102px] relative object-cover rounded-full overflow-hidden cursor-pointer">
<Image
Copy link

Choose a reason for hiding this comment

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

Next/Image 를 적극적으로 사용하시는 점 너무 좋네요~~

Copy link

@Shunamo Shunamo left a comment

Choose a reason for hiding this comment

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

안녕하세요! 이번 과제도 너무 수고 많으셨어요! 항상 pr을 정성스럽게 올려주셔서 pr 읽는 맛이 쏠쏠합니당 ㅎㅎ 브랜치 전략 보고 본격적으로 협업 연습하신 것 같아서 멋있었어요! 좋은 코드 보고 많이 배워가요 감사합니다 👍 ❤️

<div className="flex flex-col w-full">
<DetailTop poster_path={movie.poster_path} />
<div className="pt-3.5 pl-8 pr-8">
<PlayBtn width={18.9375}/>
Copy link

Choose a reason for hiding this comment

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

버튼이 모바일 뷰에서는 예쁘게 위치해 있는데, 전체화면에서는 한쪽으로 치우쳐 있어요..!

저는 그래서 화면 비율에 따라서 가로나 세로로 쭉 늘릴 수 있는 컴포넌트들은 너비나 높이에 상수 값을 주는 대신에, padding을 주고 너비를 100%로 해서 스타일링 하는데, 이렇게 하면 크게 신경쓰지 않아도 돼서 편하더라구요! 이 방법도 좋은 방법이라고 생각해요! :)

<div>
<div className="flex flex-col w-full">
<DetailTop poster_path={movie.poster_path} />
<div className="pt-3.5 pl-8 pr-8">
Copy link

Choose a reason for hiding this comment

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

Suggested change
<div className="pt-3.5 pl-8 pr-8">
<div className="pt-3.5 pl-8 pr-8">

여기에서는 이렇게 고쳐주면 가운데로 정렬돼요!
그런데 이렇게 바꾸면 밑에 overview 부분도 조정해야 될 것 같아요 😿

import dynamic from 'next/dynamic';

export default function page() {
const Landing = dynamic(() => import('@/components/lottie/Landing'), {
Copy link

Choose a reason for hiding this comment

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

dynamic import 사용하신 거 멋있어요! 👍

</FooterBtn>
<FooterBtn text="More" isCurrentPage={pathname === '/more'}>
<MoreIcon />
</FooterBtn>
Copy link

Choose a reason for hiding this comment

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

저도 처음에 푸터 요소들을 나열해서 구현했었는데, 이번 과제 리팩토링하면서 요소들을 배열로 처리해서 map 함수로 랜더링하도록 바꿔봤어요! 이렇게 하니까 중복되는 코드를 줄일 수 있었어요!
이런 방법도 생각해보시면 좋을 것 같아요! 👍

fill
src={`https://image.tmdb.org/t/p/original${randomMovie.poster_path}`}
alt={randomMovie.title}
style={{ objectFit: 'cover' }}
Copy link

Choose a reason for hiding this comment

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

테일윈드에서 objectFit: 'cover' 을 사용할 때 이렇게 바꿔서 사용하면 된다구 합니다!!
저도 승완님이 알려주셨어요 ㅎㅎ 👍 💯
승완님의 리뷰

Copy link

Choose a reason for hiding this comment

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

깜짝 놀랐어여 ㅎㅎ 디테일 최고.... 👍

{searchedMovies &&
searchedMovies.map((movie: MovieTypes) => (
<div key={movie.id} className="flex bg-search-gray">
<article className={'w-[300px] h-[140px] overflow-hidden relative shrink-0 rounded-r-lg'}>
Copy link

Choose a reason for hiding this comment

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

모바일뷰에서 searchedMovie가 잘려서 보여요..!! 아무래도 이미지 크기 때문인 것 같은데,,, 한 번 확인해보시면 좋을 것 같아요!!

Copy link

Choose a reason for hiding this comment

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

스크린샷 2024-05-19 오후 4 34 03 이런식으로 보입니다...!

Copy link
Member

@ddhelop ddhelop 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 +1 to +9
// now playing 영화 데이터 반환하는 함수.
export async function getNowPlayingMovie() {
const url = `https://api.themoviedb.org/3/movie/now_playing?api_key=${process.env.NEXT_PUBLIC_MOVIE_API_KEY}`;
const nowPlayingMovieResponse = await fetch(url, { cache: 'no-store' });

const nowPlayingMovieData = await nowPlayingMovieResponse.json();

return nowPlayingMovieData.results;
}
Copy link
Member

Choose a reason for hiding this comment

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

API 호출 함수들의 중복되는 fetch 호출을 fetchMovies 함수로 추출하여 중복 코드를 제거하면 좋을 것 같습니다.

Suggested change
// now playing 영화 데이터 반환하는 함수.
export async function getNowPlayingMovie() {
const url = `https://api.themoviedb.org/3/movie/now_playing?api_key=${process.env.NEXT_PUBLIC_MOVIE_API_KEY}`;
const nowPlayingMovieResponse = await fetch(url, { cache: 'no-store' });
const nowPlayingMovieData = await nowPlayingMovieResponse.json();
return nowPlayingMovieData.results;
}
async function fetchMovies(url: string) {
const response = await fetch(url, { cache: 'no-store' });
if (!response.ok) {
throw new Error(``);
}
const data = await response.json();
return data.results;
}
export const getNowPlayingMovie = () =>
fetchMovies(`https://api.themoviedb.org/3/movie/now_playing?api_key=${API_KEY}`);

Comment on lines +59 to +63
const res = await fetch(url);
const data = await res.json();

return data;
};
Copy link
Member

Choose a reason for hiding this comment

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

fetch 호출 후 응답이 성공적인지 확인하고, 실패 시 에러를 던지도록 짜면 안정성을 높이는데 도움이 될 것 같습니다.

Suggested change
const res = await fetch(url);
const data = await res.json();
return data;
};
const res = await fetch(url);
if (!res.ok) {
throw new Error(``);
}
return await res.json();

Comment on lines +22 to +27
{wrapperItems.map((item, index) => (
<Wrapper
key={index}
title={item.title}
fetchType={item.fetchType}
/>
Copy link
Member

Choose a reason for hiding this comment

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

저번과제에서 이 부분을 리팩토링했네요..!

<div>
<Lottie
animationData={netflixAnimation}
style={{ display: 'flex', width: '100vw', height: '80vh', paddingTop: '8rem' }}
Copy link
Member

Choose a reason for hiding this comment

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

이부분도 tailwindcss로 충분히 구현가능한 부분이예요!

Suggested change
style={{ display: 'flex', width: '100vw', height: '80vh', paddingTop: '8rem' }}
className="flex w-screen h-[80vh] pt-32"

Copy link

@Dahn12 Dahn12 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 +7 to +9
<section>
<p className="fonts-details mt-6">{overview}</p>
</section>
Copy link

Choose a reason for hiding this comment

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

이부분 오버플로우 처리해줘도 좋을거 같아요...!

passHref
>
<div className="min-w-[103px] h-[161px] relative object-cover cursor-pointer">
<Image
Copy link

Choose a reason for hiding this comment

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

스크린샷 2024-05-19 오후 4 33 24 이렇게 사진이 안뜨는 부분에 대한 처리가 있으면 더 좋을거 같아요!

{searchedMovies &&
searchedMovies.map((movie: MovieTypes) => (
<div key={movie.id} className="flex bg-search-gray">
<article className={'w-[300px] h-[140px] overflow-hidden relative shrink-0 rounded-r-lg'}>
Copy link

Choose a reason for hiding this comment

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

스크린샷 2024-05-19 오후 4 34 03 이런식으로 보입니다...!

<input
type="text"
className="block fonts-input bg-transparent px-2 focus:outline-none w-screen"
placeholder="Search for a show, movie, genre, etc."
Copy link

Choose a reason for hiding this comment

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

스크린샷 2024-05-19 오후 4 34 20 이부분도 모바일 뷰에서는 아이콘이 잘려보여요!

@jinnyleeis
Copy link

제가 노트북을 안가져와서 코드에 일일이 코멘트 남기기가 어려워 한번에 리뷰 달아요ㅠㅠ

trendingMovies 파일까지 만들어주셔서 검색어 없을 시 트랜딩 무비가 뜨게 하신 것 너무 인상깊어요!

무한 스크롤도 구현하느라 고생 많으셨엉요!

무한스크롤때문인지 검색어에 해당하지 않는 영화들도 표시되는 것 같은데, 검색어에따라 영화가 알맞게 필터링 되도록 수정하명 좋을 것 같아요!image

또 이렇게 이미지가 재대로 불러와지지 못한 영화들이 이렇게 뜨는데,

따로 필터링하거나 기본이미지로 보여주는 로직을 추가하는 것도 좋을 것 같아요!image

반응형 구현하는게 어렵던데,, 반응형도 잘 구현하시고 너무 인상깊게 봤어요!

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.

7 participants