Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
2ab4a9a
Readme.md 깃 전략 추가
Tutankhannun Oct 28, 2025
ba29ef6
chore: next.js 설치
Tutankhannun Oct 29, 2025
7075e35
docs(convention.md): 컨벤션 문서 추가
Tutankhannun Oct 30, 2025
4d18414
chore: 파일 구조 세팅(1차)
Tutankhannun Oct 30, 2025
510fa94
chore: eslint-config-airbnb-extended 라이브러리 추가( Airbnb React Style Gui…
Tutankhannun Oct 30, 2025
8d29a32
레이아웃
codus02 Oct 31, 2025
a1c0056
api 불러와서 하는거 완료
codus02 Oct 31, 2025
d9d1cac
chore(src): 파일구조 변경 및 PR 반영
Tutankhannun Nov 1, 2025
0e8e6f5
feat(BrandSplash.tsx): 스플래쉬(lottie) 추가
Tutankhannun Nov 1, 2025
d5bd91b
fix(menu.tsx): path alias 적용, svgr 적용(fill property currentColor)
Tutankhannun Nov 1, 2025
db0c147
Merge pull request #3 from Tutankhannun/layout
Tutankhannun Nov 1, 2025
f1b45fe
fix(home/layout.tsx): import 경로 설정
Tutankhannun Nov 1, 2025
2eda989
fix(navbar): 아이콘 정렬
Tutankhannun Nov 1, 2025
df73c66
feat(header): 헤더 추가
Tutankhannun Nov 1, 2025
c4fe6cf
feat: 포스터 이미지 추가
Tutankhannun Nov 2, 2025
31a7f5b
fix: develop 충돌 방지
Tutankhannun Nov 2, 2025
f8ad975
fix: herooverlay 수정
Tutankhannun Nov 3, 2025
675b746
fix: lottie type error 수정
Tutankhannun Nov 3, 2025
11c2bfe
design: 배경 컬러 변경
Tutankhannun Nov 3, 2025
a907909
fix: lottie type 수정 2트
Tutankhannun Nov 3, 2025
0707199
fix: lottie.d.ts 삭제
Tutankhannun Nov 3, 2025
896540d
fix: react-lottie-player로 변경
Tutankhannun Nov 3, 2025
16e2a2c
design: 파비콘 추가
Tutankhannun Nov 3, 2025
c5b124b
chore: 폴더구조 수정
Tutankhannun Nov 6, 2025
f3caa87
Merge branch 'Tutankhannun:main' into codus02
codus02 Nov 6, 2025
9cd2659
검색창 거의다함
codus02 Nov 7, 2025
9206590
Merge pull request #4 from codus02/codus02
Tutankhannun Nov 7, 2025
f391696
feat: 무한스크롤 구현
Tutankhannun Nov 8, 2025
4a21459
fix: menu 초기값 재설정
Tutankhannun Nov 8, 2025
7040f5e
feat: 상세페이지
Tutankhannun Nov 8, 2025
3b25b59
fix: 배포 오류 수정
Tutankhannun Nov 8, 2025
8f81f6c
fix: 무한 스크롤 api 수정
Tutankhannun Nov 8, 2025
2bc8151
fix: 검색창 아이콘 수정
Tutankhannun Nov 8, 2025
a2eed46
fix: 디버그용 출력 추가
Tutankhannun Nov 8, 2025
cfdd0ab
fix: 무한 스크롤 수정
Tutankhannun Nov 8, 2025
69323ad
feat: 상세페이지
Tutankhannun Nov 8, 2025
157e84e
fix: 상세설명 페이지 수정
Tutankhannun Nov 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<<<<<<< HEAD
# 5주차 과제: Next Netflix 🎬🍿

## 서론
Expand Down Expand Up @@ -57,3 +58,82 @@
- [Next.js 14에서 변한 것들](https://velog.io/@lee_1124/Next.js-14-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8)
- [Git 협업 가이드](https://velog.io/@jinuku/Git-%ED%98%91%EC%97%85-%EA%B0%80%EC%9D%B4%EB%93%9C)
- [디자이너와 개발자가 협업하기 위한 피그마 기본 기능](https://chingguhl.tistory.com/entry/%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EA%BC%AD-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-%ED%94%BC%EA%B7%B8%EB%A7%88-10%EA%B0%80%EC%A7%80-%EA%B8%B0%EB%8A%A5-%EB%94%94%EC%9E%90%EC%9D%B4%EB%84%88%EC%99%80-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%ED%98%91%EC%97%85%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%ED%94%BC%EA%B7%B8%EB%A7%88-%EA%B8%B0%EB%B3%B8-%EA%B8%B0%EB%8A%A5)
---

# 📘 GitHub 협업 브랜치 전략 (next-netflix-22nd)

## 🏁 목적

- `main` 브랜치는 항상 **배포 가능한 안정 버전**으로 유지
- 팀원별로 **담당 페이지별 브랜치**를 생성하여 작업
- 완료되면 `main` 브랜치로 **Pull Request (PR)** 하여 병합

---

## 📁 브랜치 구조

| 브랜치 이름 | 담당 파일 | 담당자 |
|----------------|---------------------------------|----------------|
| `main` | 전체 (배포용) | 김윤성 |
| `기능1` | `-`, `-` | 김윤성 |
| `기능2` | `-`, `-` | 김윤성 |
| `기능3` | `-`, `-` | 이채연 |
| `기능4` | `-`, `-` | 이채연 |
| `기능5` | `-`, `-` | 이채연 |

---

## 🛠️ 브랜치 작업 순서 (팀원용)

```bash
# 1. main 기준 브랜치 생성 및 전환
git checkout main
git pull origin main
git checkout -b 기능1

# 2. 파일 수정 및 작업

# 3. 변경사항 커밋
git add .
git commit -m "기능1 작업"

# 4. 브랜치 푸시
git push origin 기능1
=======
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
>>>>>>> 93a5945 (검색창 거의다함)
41 changes: 41 additions & 0 deletions next-netflix-22nd/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
10 changes: 10 additions & 0 deletions next-netflix-22nd/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"tabWidth": 2,
"singleQuote": true,
"printWidth": 80,
"semi": true,
"bracketSpacing": true,
"objectWrap": "preserve",
"bracketSameLine": false,
"arrowParens": "always"
}
36 changes: 36 additions & 0 deletions next-netflix-22nd/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
50 changes: 50 additions & 0 deletions next-netflix-22nd/app/(frame)/detail/[media]/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// app/(frame)/detail/[media]/[id]/page.tsx
import Detail from '@components/detail/detail';

Choose a reason for hiding this comment

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

절대경로 이렇게 하면 오류 발생 하나요..?? @/components/detail/Detail 이런식으로 수정해야할 거 같습니다


type Params = {
params: Promise<{ media: 'movie' | 'tv'; id: string }>;
};

async function fetchTmdbDetail(media: 'movie' | 'tv', id: string) {
const token =
process.env.TMDB_ACCESS_TOKEN ||
process.env.NEXT_PUBLIC_TMDB_ACCESS_TOKEN ||
'';
const url = `https://api.themoviedb.org/3/${media}/${id}?language=eng-US`;

const res = await fetch(url, {
Copy link

Choose a reason for hiding this comment

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

보통 baseURL이나 엑세스 토큰을 HTTP 헤더에 넣은 기본 함수를 따로 생성하고,
그 기본 함수를 import해와서 API 경로나 쿼리 파라미터 등을 덧붙혀서 다른 API 함수를 생성하는 방식으로 API 관련 파일들을 구성합니다.
API 호출 관련 함수는 src/lib/api 폴더 아래에 정리해두시는게 좋을 거 같습니다.

Copy link

Choose a reason for hiding this comment

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

API 모듈 분리
API를 효율적으로 관리하는 방법
위 사이트의 폴더 구조가 정답은 아니지만 참고하시는 용도로 올려두겠습니다!

Copy link

Choose a reason for hiding this comment

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

확인해보니 tmdb.ts에 tmdbFetch라는 함수로 기본함수를 만들어 놓으셨는데,
tmdb.ts 파일 안에 정의를 하시거나 tmdbFetch함수를 이용하시면 좋을거 같습니다

headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json;charset=utf-8',
},
cache: 'no-store',
});
if (!res.ok) {
throw new Error(`TMDB detail ${media}/${id} ${res.status}`);
}
return res.json();

Choose a reason for hiding this comment

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

axios 라이브러리 적용하면 코드가 더 간단해질 수 있을 거 같아요

}
Comment on lines +8 to +26

Choose a reason for hiding this comment

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

주완 님이 리뷰남겨주신 것처럼 해당 API 호출은 api 폴더 내에서 함수로 정의해주시면 더 좋을 것 같습니다!


export default async function Page({ params }: Params) {
const { media: mediaParam, id } = await params;
const media = mediaParam === 'tv' ? 'tv' : 'movie';
try {
const item = await fetchTmdbDetail(media, id);
return (
<main>
<Detail item={item} />
</main>
);
} catch (err) {
return (
<main className="text-white p-6">
<h1 className="text-xl font-semibold">
상세 정보를 불러올 수 없습니다.
</h1>
<p className="mt-2 text-white/80 text-sm">
잠시 후 다시 시도해 주세요.
</p>
</main>
);
}
}
15 changes: 15 additions & 0 deletions next-netflix-22nd/app/(frame)/home/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// src/app/pages/home/layout.tsx
import type { ReactNode } from 'react';
import Header from '@home/header';

export default function HomeLayout({ children }: { children: ReactNode }) {
return (
<>
{/* 헤더: 오버레이 */}
<div className="absolute top-3 left-0 right-0 z-30">
<Header />
</div>
{children}
</>
);
}
101 changes: 101 additions & 0 deletions next-netflix-22nd/app/(frame)/home/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// src/app/pages/home/page.tsx
import Section from '@home/section';
import Hero from '@home/hero';
import CardRow from '@home/cardrow';
import HeroOverlay from '@home/herooverlay';
import Previews from '@home/previews';

// 기존 섹션들 + 새 섹션들 임포트
import {
getPopularOnNetflix,
getTrendingNow,
getTop10InKoreaToday,
getKoreanMovies,
getNetflixOriginals,
getNewReleases,
} from '@/lib/tmdb';

export default async function Page() {
// My List는 아직 저장소 연동이 없으니 비워둠(섹션은 보이되, 내용 없으면 표시문구가 나옴)
const myList: any[] = [];

const [pPopular, pTrending, pTop10, pKrMovies, pOriginals, pNew] =
await Promise.allSettled([
getPopularOnNetflix('movie'),
getTrendingNow(),
getTop10InKoreaToday(),
getKoreanMovies(),
getNetflixOriginals(),
getNewReleases(),
]);

const popularNetflix =
pPopular.status === 'fulfilled' ? (pPopular.value.results ?? []) : [];
const trending =
pTrending.status === 'fulfilled' ? (pTrending.value.results ?? []) : [];
const top10 =
pTop10.status === 'fulfilled' ? (pTop10.value.results ?? []) : [];
const koreanMovies =
pKrMovies.status === 'fulfilled' ? (pKrMovies.value.results ?? []) : [];
const netflixOriginals =
pOriginals.status === 'fulfilled' ? (pOriginals.value.results ?? []) : [];
const newReleases =
pNew.status === 'fulfilled' ? (pNew.value.results ?? []) : [];

// Hero 후보: 트렌딩에서 하나, 없으면 다른 섹션에서
const heroItem =
trending[0] ??
popularNetflix[0] ??
koreanMovies[0] ??
netflixOriginals[0] ??
newReleases[0] ??
top10[0];

const heroRank = heroItem
? top10.findIndex((x: any) => x.id === (heroItem as any).id) + 1 ||
undefined
: undefined;

return (
<>
{/* Hero는 이미지만 (상단부터 415px) */}
<Hero item={heroItem} />
<HeroOverlay rank={heroRank} />

{/* 헤더에 가리지 않도록 Hero 이후부터만 여백 */}
<div className="mt-14 px-6 space-y-10">
<Section title="Previews">
<Previews items={trending.length ? trending : popularNetflix} />
</Section>

<Section title="Continue Watching">
<CardRow items={trending} />
</Section>

<Section title="Popular on Netflix">
<CardRow items={popularNetflix} />
</Section>

<Section title="Trending Now">
<CardRow items={trending} />
</Section>

<Section title="Top 10 in Korea Today">
<CardRow items={top10} numbered />
</Section>

<Section title="Korean Movies">
<CardRow items={koreanMovies} />
</Section>

<Section title="Netflix Originals">
<CardRow items={netflixOriginals} />
</Section>

<Section title="New Releases">
<CardRow items={newReleases} />
</Section>
</div>
</>
);
}
41 changes: 41 additions & 0 deletions next-netflix-22nd/app/(frame)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import Menu from '@home/menu';

export default function FrameLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="w-full h-dvh bg-[gray] flex items-start justify-center">
<div className="relative w-[375px] h-dvh max-w-[100vh] text-white bg-black">
<section
aria-label="메인 화면"
className="
absolute inset-0
overflow-y-auto overscroll-y-contain
[-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden
pt-0
pb-[calc(48px+24px+env(safe-area-inset-bottom))]
"
>
{children}
</section>

<div className="absolute left-0 right-0 bottom-[24px] z-20 flex justify-center">
<Menu />
</div>

<div
className="absolute left-0 right-0 bottom-0 h-6 z-10 bg-black"
aria-hidden
>
<img
src="/home-indicator.svg"
alt=""
className="h-full w-auto mx-auto select-none pointer-events-none"
/>
</div>
</div>
</div>
);
}
Loading