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 Buldog 김다희 & 이지인 미션 제출합니다. #15

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

Conversation

Dahn12
Copy link

@Dahn12 Dahn12 commented May 17, 2024

배포 링크

🔗

정적 라우팅과 동적 라우팅

정적 라우팅 (Static Routing)

URL 경로가 사전에 정의되고, 각 경로는 특정 페이지로 매핑되는 방식

/main, /search 등의 경로는 항상 각각 main, search 페이지로 매핑되고 경로는 static함

경로- 페이지 매핑은 애플리케이션 빌드 시점에 결정됨

동적 라우팅 (Dynamic Routing)

URL 경로의 일부가 동적으로 결정되는 방식

URL의 일부가 변수처럼 동작하여, 이를 통해 다양한 페이지를 렌더링 가능

Next.js에서는 대괄호([])를 사용해 동적 경로를 정의

이번 프로젝트에선

동적 라우팅을 사용하여 영화 상세 페이지를 구현함 detail[id]

사용자가 영화 선택 → 해당 영화의 id에 따라 상세 페이지로 이동

getMovieDetailsById를 통해 영화 세부 정보를 가져오는 api 요청을 보냄

이를 통해 사용자가 클릭/검색한 각각의 영화정보에 맞는 동적 페이지 구현

저번 과제에 추가 개선한 기능, 최적화

  • 변하는 캐러셀 영화에 맞게, 영화 소개도 업데이트 되도록 수정
    • 이를 위해 wrapper client- component인 movieindex 추가
    • 이유 - main 페이지는 서버 컴포넌트로 유지하기 위해서, hook을 사용하지 못하므로
    • 클라이언트 컴포넌트로 MovieINdex 컴포넌트 추가 (캐러셀, 컨트롤러 부모 컴포넌트)
  • middleware.ts 파일
    • search에서 api 요청보내는 것과 - 직접 주소창에 /api url 입력하는것 fetch 인자에 header 추가해서 구분
    • middleware에서 이 header에 따라 api 실행 or rewrite할지 처리

Untitled (1) Untitled
  • http 요청 200번대인 이미지만 필터링하는 checkImage 함수 구현

    • 접근 가능한 이미지인지 체크해서, 제대로 불러오기 실패한 영화는 아예 제외시킴
  • api 4개 → 1개로 수정(최적화), main 페이지 클라이언트 컴포넌트에서 서버 컴포넌트로 변경

    • 원래는 모든 api 요청을 tmdb 서버 → 프로젝트 서버 → 프로젝트 서버에 클라이언트에서 api 요청하는 방식으로 처리함

    • search 페이지는 검색 이벤트 때문에 클라이언트 컴포넌트여야 하므로, 프로젝트 서버 이용(search를 위한 api 1개만 남김) 필요

    • 반면, main 페이지는 클라이언트 컴포넌트일 필요 없음→ 클라이언트 측에서 tmdb 서버로 api 요청보내는 비동기 함수 utils 폴더 내에 구현해서 tmdb에 비동기 요청 직접 보냄

          const movies = await fetchAllCategories();
      
    • utils -> movieAPI에 fetch 함수 =이를 main 페이지에서 이용해서, 영화 정보 불러오기

    • 클라이언트 컴포넌트인 search 페이지에서 api를 통해, 검색어에 따른 영화정보 불러올 수 있도록 구현

        const fetchData = async () => {
            try {
              const response = await fetch('/api',{
                headers: {
                  'X-Client-Request': 'true' 
                  // 클라이언트에서 요청하는 것을 나타내는 헤더 추가(직접 주소창에 입력하는 것과 구분)
                }});
              const data: Movie[][] = await response.json();
      
              //중복 제거
              const flatData = data.flat();
              const filteredMovies = removeDuplicates(flatData);
      
              setAllMovies(filteredMovies);
            } catch (error) {
              console.error('Failed to load movies:', error);
            }
          };
      
          fetchData();
        }, []);
  • navbar 컴포넌트 공용 레이아웃으로 변경

  • utils로 API 관련 함수로 빼서 재사용

    • fetchMoviesData 함수를 fetchAllCategories()에서 promise.all로 api 요청 한번에 처리해서 Movie[][] 반환하도록 함
    • → 메인 페이지 api 요청보내는 코드 1줄로 해결
  • Image 레거시 코드 제거

    • 공식문서에서 layout과 objectFit 속성은 사용이 지양되는 것이라고 함
    • 기존의 앱 라우팅 방식에서는 layout과 objectFit 속성을 사용해도 괜찮았지만, 상위 버전에서 Image 컴포넌트는 이미지 렌더링 최적화에만 focusing을 하고 나머지는 css로 디자인 해야한다.
    • 따라서 상위 div를 추가해주고 fill 속성을 이용하여 수정함

트러블 슈팅

  • search 페이지에서, 4개 카테고리 영화들 flat()으로 합침 → removeDuplicates 함수(utils/etc.ts)에서 Map을 사용해 중복 제거

    → 검색 시, 동일 영화 중복해서 뜨는 문제 해결

jinnyleeis and others added 25 commits May 16, 2024 12:44
feat : 이미지 클릭 시 해당 영화 detail로 이동
utils -> movieAPI에 fetch 함수
이를 main 페이지에서 이용해서, 영화 정보 불러오기
클라이언트 컴포넌트인 search 페이지에서 api를 통해, 검색어에 따른 영화정보 불러올 수 있도록 구현
feat : api 1개로, main 페이지 서버 컴포넌트로 변경
fix : 검색 페이지 영화 중복으로 뜨는 문제, 이미지 url 200번대 요청만 필터링
# Conflicts:
#	src/app/search/page.tsx
search에서 api 요청보내는 것과 - 직접 주소창에 /api url 입력하는것 fetch 인자에 header 추가해서 구분 -> middleware에서 이 header에 따라 api 실행 or rewrite할지 처리
feat : 미들웨어 추가, 404,403,각종 페이지
이를 위해 wrapper client- component인 movieindex 추가
이유 - main 페이지는 서버 컴포넌트로 유지하기 위해서, hook을 사용하지 못하므로
클라이언트 컴포넌트로 MovieINdex 컴포넌트 추가 (캐러셀, 컨트롤러 부모 컴포넌트)
feat : 변하는 캐러셀 영화에 맞게, 영화 소개도 업데이트 되도록 수정
# Conflicts:
#	src/app/components/Carousel.tsx
Copy link

@youdame youdame 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 +14 to +36
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('/api',{
headers: {
'X-Client-Request': 'true'
// 클라이언트에서 요청하는 것을 나타내는 헤더 추가(직접 주소창에 입력하는 것과 구분)
}});
const data: Movie[][] = await response.json();

//중복 제거
const flatData = data.flat();
const filteredMovies = removeDuplicates(flatData);

setAllMovies(filteredMovies);
} catch (error) {
console.error('Failed to load movies:', error);
}
};

fetchData();
}, []);

Copy link

Choose a reason for hiding this comment

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

search 페이지는 검색 이벤트 때문에 클라이언트 컴포넌트여야 하므로, 프로젝트 서버 이용(search를 위한 api 1개만 남김) 필요

라고 적어두셨는데 nextjs 서버를 이용하든 안하든 클라이언트 컴포넌트 서버 컴포넌트로 구현하는 거랑은 무관하다구 알고있는데 어떤 점에서 이렇게 말씀하신 건지 궁금합니다!

Copy link

Choose a reason for hiding this comment

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

Search 컴포넌트를 서버 컴포넌트로 둬서 데이터 패칭을 하고, 해당 데이터를 프롭으로 하위 컴포넌트인 클라이언트 컴포넌트에 넘겨준 뒤 그 클라이언트 컴포넌트에서 사용자 인터랙션이 있는 코드를 작성할 수도 있을 거 같아요~!

Comment on lines +55 to +60
<h2 className='text-[26.75px] font-bold my-[18px] ml-[10px]'>Top Searches</h2>
<div className='h-[573px] overflow-y-auto'>
{inputValue.trim() === ''
? allMovies.map((movie, index) => <SearchLists key={index} movie={movie} />)
: allMovies
.filter((movie) => movie.title.toLowerCase().includes(inputValue.toLowerCase()))
Copy link

Choose a reason for hiding this comment

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

index를 key로 사용하는 것보다 movie.id로 변경하는 게 좋을 거 같아요
[React] 배열의 index를 key로 쓰면 안되는 이유

Comment on lines +47 to +56
const accessibleMovies = await Promise.all(
data.results.map(async (movie: Movie) => {
const imagePath = `https://image.tmdb.org/t/p/original${movie.backdrop_path}`;
const isImageOk = await checkImage(imagePath); // 접근 가능한 이미지인지 체크하기
return isImageOk ? movie : null;
}),
);
// null이 아닌 것만 반환
return accessibleMovies.filter((movie: Movie | null) => movie !== null);
};
Copy link

Choose a reason for hiding this comment

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

이미지 에러가 나는 영화들에 대해 이런 식으로 처리를 해주셨군요! 그런데 에러가 나는 이미지에 대한 검색 결과는 아예 보여주지 않아서 기본 이미지를 보여주는 식으로도 처리할 수 있을 거 같아요~

Copy link

Choose a reason for hiding this comment

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

const BASE_URL = 'https://api.themoviedb.org/3/movie';
const IMAGE_BASE_URL = 'https://image.tmdb.org/t/p/original';

이렇게 상수로 관리해두면 url이 바뀌었을 때 이 값만 고치면 돼서 편하답니다~ 다른 곳에서도 이 값을 사용해서 constant 폴더에 따로 둬도 좋을 거 같아요 ㅎㅎ

Comment on lines +19 to +28
//image URL 접근 가능 여부 체크 함수
const checkImage = async (url: string): Promise<boolean> => {
try {
const res = await fetch(url, { method: 'HEAD' });
return res.ok; // code가 200번대면, 성공적 - >해당 이미지 반환
} catch (error) {
console.error(' 이미지 접근 불가 ', error);
return false; // 이미지 접근 불가
}
};
Copy link

Choose a reason for hiding this comment

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

덕분에 head 메소드가 의미하는 바에 대해 처음 알게됐어요 !

Copy link

Choose a reason for hiding this comment

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

환경변수 냠

Copy link

Choose a reason for hiding this comment

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

.gitignore.env 파일을 입력해서 push를 방지해주시면 될 것 같아요.
저희팀이 이번 과제에서 참고한 글 남길게용

NextJS에서 환경변수 .env 사용하는 방법

Choose a reason for hiding this comment

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

저도 관련 내용 공부한거 남기고 갑니다!!

  • 타인에게 보여주기 민감한 정보를 담고 있는 .env.local 파일
  • .gitignore에 이 파일이 자동으로 등록되어 있다. 즉 깃허브에 push 해도 .env.local 파일은 올라가지 않는다.
  • .gitignore에 등록되어 있는 파일이기 때문에 깃허브 협업 시 누군가가 .env.local을 작성하고 깃허브에 push해도 다른 팀원들은 확인할 수 없다. 각자 로컬에서 .env.local 파일을 작성해주어야 한다.
  • vercel로 배포 시 vercel은 .gitignore 파일을 적용하지 않기 때문에 .env.local 파일도 적용되지 않는다. 따라서 vercel에서 따로 환경 변수를 등록해주어야 한다. (참고 자료)

src={movieDetail.backdrop_path ? url : ''}
alt="movie poster"
fill
sizes="100vw"
Copy link

Choose a reason for hiding this comment

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

vw는 뷰포트, 전체 화면 너비를 따라가는 거라 sizes를 100vw로 설정해두는 것보다 375px로 지정해두는 게 적절할 거 같아요!

Copy link

@noeyeyh noeyeyh left a comment

Choose a reason for hiding this comment

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

Buldog팀 안녕하세요!

이번 과제에서 섬세하게 에러 페이지와, 나머지 구현하지 않아도 되는 페이지들을 이모티콘과 함께 띄워주시는 등.. 메인페이지에서 영화 rank에 따라 바뀌는 부분도 재밌게 봤어요! 주석도 쉽게 작성해주셔서 코드 보는 데 도움이 되었습니다... 👍

시험기간 화이팅하시규.. 다음 과제도 화이팅해용!!

Copy link

Choose a reason for hiding this comment

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

.gitignore.env 파일을 입력해서 push를 방지해주시면 될 것 같아요.
저희팀이 이번 과제에서 참고한 글 남길게용

NextJS에서 환경변수 .env 사용하는 방법

];

const pathname = usePathname();
console.log(pathname);
Copy link

Choose a reason for hiding this comment

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

보통 커밋할 때 console.log는 삭제하고 하는 것이 일반적이라고 합니다!


export default CommingSoon;


Copy link

Choose a reason for hiding this comment

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

다른 페이지들도 센스 있게 아이콘으로 띄워주시네용 👍

function Carousel({ movies, currentMovieIndex }: CarouselProps) {
return (
<div className="relative mb-[20px] h-[395px] w-[375px] ">
<div className="w-[375px] h-[415px] overflow-hidden relative">
Copy link

Choose a reason for hiding this comment

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

최외곽 div랑 내부 이미지 감싸는 div가 height이 오타난 것 같아요. 어차피 fill 속성으로 부모 크기에 따라 맞춰지게 지정해주셔서 이미지를 감싸는 div에 따로 width와 height을 지정해주지 않아도 될 것 같아요!

return (
<div className="mr-[7px] flex-shrink-0" key={movie.id}>
<div style={{ width: 103, height: imageSize }}>
{movie.backdrop_path && (
Copy link

Choose a reason for hiding this comment

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

backdrop_path가 존재하는지 위에 if문과 함께 두 번 검사하고 있는 것 같아요.
이미 위에서 if문으로 만약 backdrop_path가 없는 경우, 해당 아이템을 렌더링하지 않고 넘어가도록 해서 movie.backdrop_path $$ ( 이 부분은 없어도 될 것 같아요!

</MovieIndex>
)}
{movies.map((movieList, index) => (
<div key={index}>
Copy link

Choose a reason for hiding this comment

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

배열의 인덱스를 키 값으로 사용하는 것은 지양해야 한다고 합니다!

배열의 index를 key로 쓰면 안되는 이유

Copy link

Choose a reason for hiding this comment

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

보통 movie.id를 많이 쓰는 것 같아요 !

Copy link

@Rose-my Rose-my left a comment

Choose a reason for hiding this comment

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

이번주 과제도 수고많으셨어요 !! UI도 깔끔하고 리팩토링도 열심히 하신것 같아요 ㅎㅎ 트러블 슈팅도 잘 봤습니당 !! 👍

isCircular: boolean;
}

const MainLists: React.FC<MainListProps> = ({ movies, itemClass, isCircular }) => {
Copy link

Choose a reason for hiding this comment

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

React.FC의 사용을 최소화하기 위해서

기존의 React.FC<MainListProps> = (props) => {};
(props: MainListProps) => {}; 요렇게 바꿀 수도 있답니다 !!

</MovieIndex>
)}
{movies.map((movieList, index) => (
<div key={index}>
Copy link

Choose a reason for hiding this comment

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

보통 movie.id를 많이 쓰는 것 같아요 !

Copy link

Choose a reason for hiding this comment

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

404에러 처리까지 !! 멋져요

"react": "^18",
"react-dom": "^18",
"react-lottie-player": "^2.0.0",
"react-slick": "^0.30.2",
Copy link

Choose a reason for hiding this comment

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

react-slick라이브러리를 쓰셨구나 !! 최고입니다 !! 저도 다음에 써보려고요 ㅎㅎ

Comment on lines +16 to +27
<div className='flex space-x-[25px]'>
{
Menu.map((menu) => {
return(
<span className='font-normal text-header'key={menu}>
{menu}
</span>
)
}
)
}
</div>
Copy link

Choose a reason for hiding this comment

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

.prettierrc파일은 잘 있는데 indent 사이즈가 설정한 것과 다른것 같아요 ! prettierrc가 잘 적용이 안됐을까요?

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기 멘토 김현민 이라고합니다~!!

이미 많은 분들이 코드 리뷰를 남겨준 후여서 약간의 제 의견들만 남겨봤습니다 ㅎㅎ
정답이라고 생각하시지 마시고 쭉 읽어보며 참고만 해주시면 좋을 것 같아요~~

이번 과제도 너무 고생 많으셨습니다!!! 👍

Comment on lines +1 to +34
name: git push into another repo to deploy to vercel

on:
push:
branches: [master]

jobs:
build:
runs-on: ubuntu-latest
container: pandoc/latex
steps:
- uses: actions/checkout@v3
- name: Install mustache (to update the date)
run: apk add ruby && gem install mustache
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20.x' # Ensuring compatibility with Node.js 20
- name: creates output
run: sh ./build.sh
- name: Pushes to another repository
id: push_directory
uses: cpina/github-action-push-to-another-repository@master
env:
API_TOKEN_GITHUB: ${{ secrets.DEPLOY_TOKEN }}
with:
source-directory: 'output'
destination-github-username: jinnyleeis
destination-repository-name: next-netflix-19th-deploy
user-email: ${{ secrets.OFFICIAL_ACCOUNT_EMAIL }}
commit-message: ${{ github.event.commits[0].message }}
target-branch: master
- name: Test get variable exported by push-to-another-repository
run: echo $DESTINATION_CLONED_DIRECTORY
Copy link

Choose a reason for hiding this comment

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

팀 레포에 대한 버셀의 요금 부과 정책에 대비하여 Github Actions를 활용해 개인 레포로 옮긴 것 멋집니다 ㅎㅎ

Choose a reason for hiding this comment

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

오 자동배포 설정해주셨군요!! 저희도 설정해줬는데 너무 편한 것 같아요

Comment on lines +24 to +43
{ /* 그라데이션 */}
<div
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: '40%',
backgroundImage: 'linear-gradient(to bottom, rgba(0, 0, 0, 0.7), transparent)',
}}
></div>
<div
style={{
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: '40%',
backgroundImage: 'linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent)',
}}
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
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: '40%',
backgroundImage: 'linear-gradient(to bottom, rgba(0, 0, 0, 0.7), transparent)',
}}
></div>
<div
style={{
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: '40%',
backgroundImage: 'linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent)',
}}
<GradientStyle />

Comment on lines +39 to +41
<button className="flex h-11 w-28 cursor-pointer items-center justify-center gap-2.5 rounded-md border-none bg-[#C4C4C4]">
<Image src={Play} alt="play" width={18} height={21.6} />
<span className="text-play font-semibold text-[#000000]">Play</span>
Copy link

Choose a reason for hiding this comment

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

자주 사용되는 색깔에 대해서는 쉬운 네이밍으로 세팅하는 게 좋을 것 같아요 ㅎㅎ

https://doyourbestcode.tistory.com/131

제가 Next + Tailwind 를 세팅하는 방식도 살짝 참고만 해보세요~~

import logo from '../../public/icons/logos_netflix-icon.svg'

function Header() {
const Menu = ['Tv Shows' ,'Movies', 'My List']
Copy link

@wokbjso wokbjso May 19, 2024

Choose a reason for hiding this comment

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

헤더 텍스트들을 배열로 관리해주신 덕분에 추후 유지보수에도 좋겠네요 ㅎㅎㅎ

Choose a reason for hiding this comment

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

그러게요! 배열로 관리할 생각은 따로 못해봤는데 좋은 방법인 것 같습니다

</div>
<h2 className='text-[26.75px] font-bold my-[18px] ml-[10px]'>Top Searches</h2>
<div className='h-[573px] overflow-y-auto'>
{inputValue.trim() === ''
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 +2 to +13

export default function ApiError() {
return (
<div>
<Head>
<title>API Access Denied</title>
<meta name="description" content="Access to the API is restricted" />
</Head>
<main>
<h1 className='text-[150px] mt-[300px] flex justify-center'>☠️</h1>
<h2 className="ml-[16px] mt-[20px] text-[20.92px] font-bold flex justify-center"> 403 - API Access Denied</h2>
<p className="ml-[16px] text-[20.92px] flex justify-center">You do not have permission to access this API.</p>
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 +31 to +39
<h2 className="ml-[16px] mt-[22px] text-[20.92px] font-bold">
{mainListsTitle[index]}
</h2>
<MovieList
movies={movieList}
itemClass={index === 0 ? 'rounded-full' : ''}
isCircular={index === 0}
/>
</div>
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 +60 to +63
const categories = ['top_rated', 'popular', 'upcoming', 'now_playing'];

try {
const movies = await Promise.all(categories.map(fetchMoviesData));
Copy link

Choose a reason for hiding this comment

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

병렬적으로 처리해서 netword-waterfall 방지하신 점 좋네요 ㅎㅎ

react-intersection-observer 라이브러리를 활용하여 우선 Previews, NewReleases 2개의 api만 호출해온 후, Popular on Netflix 슬라이더가 view에 보이는 순간 나머지 2개의 api를 호출하는 방식도 재밌을 것 같고 초기 성능에도 효과적일 수 있을 것 같네요~~

function Header() {
const Menu = ['Tv Shows' ,'Movies', 'My List']
return (
<div className='fixed top-[24px] flex justify-center items-center w-[375px] h-[57px] z-50'>
Copy link

Choose a reason for hiding this comment

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

어느정도 스크롤 하면 헤더의 background-color를 설정해준다면 스크롤이 내려왔을 때도 헤더가 더 잘 보일 것 같아요!!

Comment on lines +5 to +8
export const metadata: Metadata = {
title: 'next-netflix-19',
description: 'ceos19',
};
Copy link

Choose a reason for hiding this comment

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

메타데이터 설정까지 너무 좋습니다 ㅎㅎ

Copy link

@CSE-pebble CSE-pebble 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 +34
name: git push into another repo to deploy to vercel

on:
push:
branches: [master]

jobs:
build:
runs-on: ubuntu-latest
container: pandoc/latex
steps:
- uses: actions/checkout@v3
- name: Install mustache (to update the date)
run: apk add ruby && gem install mustache
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20.x' # Ensuring compatibility with Node.js 20
- name: creates output
run: sh ./build.sh
- name: Pushes to another repository
id: push_directory
uses: cpina/github-action-push-to-another-repository@master
env:
API_TOKEN_GITHUB: ${{ secrets.DEPLOY_TOKEN }}
with:
source-directory: 'output'
destination-github-username: jinnyleeis
destination-repository-name: next-netflix-19th-deploy
user-email: ${{ secrets.OFFICIAL_ACCOUNT_EMAIL }}
commit-message: ${{ github.event.commits[0].message }}
target-branch: master
- name: Test get variable exported by push-to-another-repository
run: echo $DESTINATION_CLONED_DIRECTORY

Choose a reason for hiding this comment

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

오 자동배포 설정해주셨군요!! 저희도 설정해줬는데 너무 편한 것 같아요

Choose a reason for hiding this comment

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

저도 관련 내용 공부한거 남기고 갑니다!!

  • 타인에게 보여주기 민감한 정보를 담고 있는 .env.local 파일
  • .gitignore에 이 파일이 자동으로 등록되어 있다. 즉 깃허브에 push 해도 .env.local 파일은 올라가지 않는다.
  • .gitignore에 등록되어 있는 파일이기 때문에 깃허브 협업 시 누군가가 .env.local을 작성하고 깃허브에 push해도 다른 팀원들은 확인할 수 없다. 각자 로컬에서 .env.local 파일을 작성해주어야 한다.
  • vercel로 배포 시 vercel은 .gitignore 파일을 적용하지 않기 때문에 .env.local 파일도 적용되지 않는다. 따라서 vercel에서 따로 환경 변수를 등록해주어야 한다. (참고 자료)

Choose a reason for hiding this comment

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

svg를 불러와서 next/image의 Image의 src에 넣어주는 방법도 좋지만 svg 파일을 컴포넌트처럼 사용할 수 있는 방법이 있더라구요!! 저도 이번에 첨 알았습니다 쓰면서 편하고 좋았어서 한번 참고해보시는거 추천드려요 ㅎㅎ

https://react-svgr.com/docs/next/

import logo from '../../public/icons/logos_netflix-icon.svg'

function Header() {
const Menu = ['Tv Shows' ,'Movies', 'My List']

Choose a reason for hiding this comment

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

그러게요! 배열로 관리할 생각은 따로 못해봤는데 좋은 방법인 것 같습니다

return (
<Lottie
animationData={netflix}
style={{ width: "60%" }}

Choose a reason for hiding this comment

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

중간중간 이렇게 style을 지정해주신 부분이 있던데 이유가 궁금해요!

<Link href={link} key={id} className="flex flex-col items-center">
{/* 이미지 색상은 안 변하므로 이 부분 해결 필요 */}
<Image
src={pathname === link ? clicked : src }

Choose a reason for hiding this comment

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

pathname === link를 확인하는 부분이 반복되고 있으니까 isCurrentPath와 같은 변수로 따로 빼서 관리해줘도 좋을 것 같아요!

Comment on lines +30 to +51
<div
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: '40%',
backgroundImage: 'linear-gradient(to bottom, rgba(0, 0, 0, 0.7), transparent)',
}}
></div>
<div
style={{
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: '40%',
backgroundImage: 'linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent)',
}}
></div>
</>
</div>

Choose a reason for hiding this comment

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

이 부붕느 공통 스타일로 따로 빼서 관리해주는게 가독성에 좋을 것 같아요!

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