Type something to search...

07. Home.jsx — 메인 페이지 완성하기

요약

Gemini CLI로 구현하기 — Home 메인 페이지

  1. npx gemini
  2. 1
    src/components/Home.jsx를 작성해줘. useOutletContext로 now, popular, topRated, loading 데이터를 받아야 해. 상단에는 배경 영상([영상 파일명])과 인기영화 첫 번째 항목을 히어로로 표시하고, 하단에는 현재 상영작/인기 영화/최고 평점 3개의 Section 컴포넌트를 보여줘. 로딩 중에는 Spinner를 표시해줘.
  3. 사용 가이드: [영상 파일명]public 폴더에 넣은 동영상 파일 이름으로 바꾼다(예: video.mp4).

1. Home 페이지의 구조

GOFLEX의 메인 페이지는 두 가지 영역으로 나뉜다.

  • 히어로(Hero) 영역 — 배경 영상 + 인기 영화 하이라이트 + “자세히 보기” 버튼
  • 영화 목록 영역 — 현재 상영작, 인기 영화, 최고 평점 3개 섹션
순서만들 기능핵심 개념
1단계import + 데이터 받기useOutletContext
2단계히어로 영역비디오 배경, 조건부 렌더링
3단계영화 목록 영역Section 재사용, loading 처리

2. 1단계 — import + 데이터 받기

src/components/Home.jsx 파일을 열고 임시 코드를 모두 지운 뒤 아래를 작성한다.

src/components/Home.jsx
1
import { useOutletContext, Link } from "react-router";
2
import { Section } from "./Section.jsx";
3
import { Spinner } from "./UI.jsx";
4
5
export function Home() {
6
const { now, popular, topRated, loading } = useOutletContext();
7
8
// 인기영화 첫 번째를 히어로에 표시
9
const hero = popular.length > 0 ? popular[0] : null;
설명
1useOutletContext(유즈아울렛컨텍스트)로 5편에서 App.jsxOutletcontext prop으로 전달한 데이터를 받는다. Link는 히어로의 “자세히 보기” 버튼에 사용한다.
34편에서 만든 Spinner 컴포넌트를 가져온다. 데이터가 아직 도착하지 않았을 때 “불러오는 중…” 메시지를 표시한다.
6객체 구조 분해(Destructuring, 디스트럭처링) — 객체에서 필요한 프로퍼티를 개별 변수로 추출하는 문법이다. useOutletContext()가 반환한 객체에서 now, popular, topRated, loading을 각각 변수로 꺼낸다.
8삼항 연산자(조건 ? 참 : 거짓)이다. popular 배열에 요소가 있으면 첫 번째 요소(popular[0])를 hero에 저장하고, 없으면 null을 저장한다.

3. 2단계 — 히어로 영역

같은 Home.jsx 파일에서, 1단계의 hero 변수 선언 바로 아래에 return을 작성한다. 히어로 영역은 세 겹의 레이어(배경 영상 → 오버레이 → 텍스트)로 구성된다. position: absolute 요소를 겹쳐서 레이어를 구성하는 CSS 포지셔닝 기법이다.


4. 3단계 — 영화 목록 영역

같은 Home.jsx 파일에서, 히어로 </section> 태그 바로 아래에 이어서 작성한다. loading 상태에 따라 스피너 또는 영화 목록을 조건부 렌더링한다.

src/components/Home.jsx
1
{loading && <Spinner className="text-center py-20 bg-black" />}
2
3
{!loading && (
4
<>
5
<Section title="현재 상영작" items={now} category="now_playing" />
6
<Section title="인기 영화" items={popular} category="popular" />
7
<Section title="최고 평점" items={topRated} category="top_rated" />
8
</>
9
)}
10
</>
11
);
12
}
설명
1loadingtrue이면 Spinner 컴포넌트로 “불러오는 중…” 텍스트를 표시한다.
3-8!(NOT 연산자)는 불린 값을 반전시킨다. loadingtrue이면 !loadingfalse, loadingfalse이면 !loadingtrue가 된다. 로딩이 끝났을 때(false) 3개의 Section을 표시한다. category prop은 “더보기” 링크에 사용된다.
5category="now_playing"Section에서 /category/now_playing 링크를 생성한다.

5. 동작 확인

확인 항목기대 결과
히어로 영역배경 영상 위에 “GOFLEX” + 인기 영화 제목 + “자세히 보기” 버튼
”자세히 보기” 클릭해당 영화 상세 페이지로 이동
스크롤 아래”현재 상영작”, “인기 영화”, “최고 평점” 섹션 표시

주의

아직 SectionCard 컴포넌트를 만들지 않았으므로 에러가 발생할 수 있다. 바로 다음 편에서 만든다.


6. 전체 코드

src/components/Home.jsx
1
import { useOutletContext, Link } from "react-router";
2
import { Section } from "./Section.jsx";
3
import { Spinner } from "./UI.jsx";
4
5
export function Home() {
6
const { now, popular, topRated, loading } = useOutletContext();
7
8
// 인기영화 첫 번째를 히어로에 표시
9
const hero = popular.length > 0 ? popular[0] : null;
10
11
return (
12
<>
13
{/* 상단 비디오 영역 */}
14
<section className="relative h-screen overflow-hidden">
15
<video src="video.mp4" className="absolute top-0 left-0 w-full h-full object-cover" autoPlay muted loop playsInline />
16
<div className="absolute bg-black/50 w-full h-full top-0 left-0"></div>
17
<div className="relative container mx-auto flex flex-col justify-center items-center h-full text-center px-6">
18
<h2 className="text-5xl md:text-7xl lg:text-9xl font-bold text-yellow-400">GOFLEX</h2>
19
<p className="text-xl md:text-2xl text-white mt-4">최신 영화와 인기 작품을 만나보세요.</p>
20
{hero && (
21
<div className="mt-8 flex flex-col items-center gap-3">
22
<p className="text-gray-300 text-lg">지금 인기 있는 영화</p>
23
<h3 className="text-3xl md:text-4xl font-bold text-white">{hero.title}</h3>
24
<Link to={`/movie/${hero.id}`} className="mt-2 bg-yellow-400 text-black px-8 py-3 rounded-lg font-bold hover:bg-yellow-300 transition-colors">
25
자세히 보기
26
</Link>
27
</div>
28
)}
29
</div>
30
</section>
31
32
{/* 영화 목록 */}
33
{loading && <Spinner className="text-center py-20 bg-black" />}
34
35
{!loading && (
36
<>
37
<Section title="현재 상영작" items={now} category="now_playing" />
38
<Section title="인기 TV" items={popular} category="popular" />
39
<Section title="최고 평점" items={topRated} category="top_rated" />
40
</>
41
)}
42
</>
43
);
44
}