🐨CoalaCoding
DocsExamplesTry itBoardB반
🐨CoalaCoding

개발자를 위한 한국어 웹 기술 문서

문서

  • JavaScript
  • Web Publishing
  • React
  • Python

커뮤니티

  • 게시판
  • 예제 모음
  • Try it 에디터

기타

  • GitHub
  • 관리자
© 2026 CoalaCoding. All rights reserved.
  • 22_무비앱-완료본
  • 01. GOFLIX 프로젝트 소개와 개발환경 설정
  • 02. React 진입점과 라우팅 설정
  • 03. Axios로 TMDB API 연결하기
  • 04. 공통 UI 컴포넌트 만들기 (UI.jsx)
  • 05. App.jsx — 레이아웃 구성과 데이터 가져오기
  • 06. Header와 Footer 만들기
  • 07. Home.jsx — 메인 페이지 완성하기
  • 08. Section과 Card — 영화 카드 목록 만들기
  • 09. MovieDetail — 영화 상세 페이지 만들기
  • 10. Category, ErrorPage 완성하기
  • 11. AI 챗봇 연동하기
  • 12_Swiper_캐러셀_적용과_프로젝트_마무리
  • 13. GOFLEX Gemini CLI 바이브코딩 프롬프트 템플릿
  • 00_시작하기
  • 01_App
  • 02_CSS
  • 03_Nav
  • 04_Hero
  • 05_AboutMe
  • 06_Projects
  • 07_Contact
  • 08_Footer
  • 09_완성_정리
  • 10_바이브코딩
  1. 홈
  2. 문서
  3. React
  4. 실전 프로젝트
  5. 08. Section과 Card — 영화 카드 목록 만들기

08. Section과 Card — 영화 카드 목록 만들기

더보기 링크가 있는 섹션, 호버 오버레이 카드 컴포넌트를 만든다

코드 블록의 Try it Yourself 버튼으로 직접 실행할 수 있다.

구문

💡TIP

Gemini CLI로 구현하기 — Section & Card 컴포넌트

  • 프롬프트: gemini "src/components/Section.jsx와 Card.jsx를 작성해줘. Section은 title, items, category props를 받아서 제목, 더보기 링크(/category/[category]), [열 수]열 그리드 카드 목록을 렌더링해줘. Card는 item prop을 받아서 TMDB 포스터 이미지(https://image.tmdb.org/t/p/w500/), 호버 시 줄거리+평점 오버레이, 하단에 제목+평점+개봉일을 표시해줘. Tailwind CSS group-hover를 활용해줘."
    • 사용 가이드: [열 수]를 원하는 그리드 열 개수(기본: 4)로 바꾼다. 카드에 기능을 추가하려면 프롬프트 끝에 요구사항을 덧붙인다.

1. 이번 편에서 만들 것

컴포넌트역할
Section제목 + "더보기" 링크 + 영화 카드 그리드
Card포스터 + 호버 시 줄거리/평점 오버레이 + 하단 정보

2. Section.jsx 작성

src/components/Section.jsx 파일을 열고 임시 코드를 모두 지운 뒤 아래를 작성한다. Section은 제목, "더보기" 링크, 카드 그리드를 하나의 단위로 묶는 레이아웃 컴포넌트이다. title과 items prop만 바꿔서 세 가지 카테고리에 재사용한다.

import { Link } from "react-router";
import { Card } from "./Card.jsx";
import { Container } from "./UI.jsx";

export function Section({ title, items, category }) {
  return (
    <Container className="py-24">
      <div className="flex items-center justify-between pt-10 pb-5 px-3">
        <h2 className="text-4xl font-bold text-white">{title}</h2>
        {category && (
          <Link to={`/category/${category}`} className="text-yellow-400 hover:text-yellow-300 text-sm font-bold">
            더보기 &rarr;
          </Link>
        )}
      </div>
      <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-6">
        {items.map((el) => (
          <Card key={el.id} item={el} />
        ))}
      </div>
    </Container>
  );
}
줄설명
34편에서 만든 Container 컴포넌트를 가져온다. 공통 레이아웃 컴포넌트이다.
5title(제목), items(영화 배열), category(카테고리 키)를 받는다.
8flex — 자식 요소를 가로로 나란히 배치하는 Tailwind 클래스이다. justify-between — 자식들을 양쪽 끝으로 밀어낸다. 결과적으로 제목은 왼쪽, "더보기"는 오른쪽에 배치된다.
10-14category가 있을 때만 "더보기" 링크를 표시한다. 클릭하면 /category/popular 같은 페이지로 이동한다. &rarr;는 → 화살표이다.
16grid — CSS Grid 레이아웃이다. grid-cols-4는 4열 구성이다. gap-6은 그리드 아이템 사이의 간격이다. sm:, md: 반응형 접두사로 뷰포트 크기에 따라 열 수가 변경된다.
17-19.map()(맵)은 배열의 각 요소에 콜백 함수를 실행하고, 그 반환값으로 구성된 새 배열을 만드는 메서드이다. items에 영화가 20개 있으면 Card 컴포넌트 20개로 이루어진 새 배열이 만들어지고, React가 이를 화면에 렌더링한다.

3. Card.jsx — 1단계: import + 포스터

src/components/Card.jsx 파일을 열고 임시 코드를 모두 지운 뒤 작성한다. Card는 영화 포스터, 호버 오버레이(줄거리·평점), 하단 정보를 표시하는 컴포넌트이다. 클릭하면 영화 상세 페이지로 이동한다.

import { Link } from "react-router";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faHeart, faStar } from "@fortawesome/free-solid-svg-icons";

export function Card({ item }) {
  const poster = `https://image.tmdb.org/t/p/w500/${item.poster_path}`;
줄설명
3하트(faHeart)와 별(faStar) 아이콘을 가져온다.
5props 이름이 item이다. (이전 교안에서는 movie였다.)
6TMDB 포스터 이미지 URL을 조합한다.

4. Card.jsx — 2단계: JSX 렌더링

  return (
    <div className="card py-10 group">
      <Link to={`/movie/${item.id}`}>
        <div className="relative overflow-hidden rounded-md aspect-[2/3]">
          <img
            className="object-cover w-full h-full transition-transform duration-300 group-hover:scale-110"
            src={poster}
            alt={item.title}
          />
          <div className="absolute inset-0 bg-black/60 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex flex-col justify-end p-4">
            <p className="text-white text-sm line-clamp-3">{item.overview}</p>
            <div className="flex items-center gap-1 mt-2 text-yellow-400">
              <FontAwesomeIcon icon={faStar} className="text-xs" />
              <span className="text-sm font-bold">{(item.vote_average || 0).toFixed(1)}</span>
            </div>
          </div>
        </div>
줄설명
2group 클래스를 선언한다. 자식 요소에서 group-hover:를 사용할 수 있다.
6group-hover:scale-110 — 카드에 마우스를 올리면 이미지가 1.1배 확대된다.
10호버 오버레이 — absolute inset-0은 top:0, right:0, bottom:0, left:0을 한 번에 설정하는 Tailwind 단축 클래스이다. 부모 요소를 완전히 덮는다. 평소에는 opacity-0(투명), 마우스를 올리면 group-hover:opacity-100(표시)으로 전환된다.
11line-clamp-3은 줄거리를 3줄까지만 표시하고 나머지는 말줄임표로 처리한다.
14(item.vote_average || 0).toFixed(1) — ||(OR 연산자)는 왼쪽이 거짓 값이면 오른쪽을 반환한다. 평점이 없으면 0으로 대체하고, .toFixed(1)로 소수점 1자리로 고정한다.

5. 동작 확인

확인 항목기대 결과
메인 페이지 스크롤영화 포스터가 4열 그리드로 나열됨
카드에 마우스 올림포스터가 확대되고, 어두운 오버레이에 줄거리+평점 표시
"더보기 →" 클릭/category/popular 등 카테고리 페이지로 이동
카드 클릭/movie/숫자로 이동
모바일 크기1열 → 2열 → 4열로 반응형 변화

6. 전체 코드

import { Link } from "react-router";
import { Card } from "./Card.jsx";
import { Container } from "./UI.jsx";

export function Section({ title, items, category }) {
  return (
    <Container className="py-24">
      <div className="flex items-center justify-between pt-10 pb-5 px-3">
        <h2 className="text-4xl font-bold text-white">{title}</h2>
        {category && (
          <Link to={`/category/${category}`} className="text-yellow-400 hover:text-yellow-300 text-sm font-bold">
            더보기 &rarr;
          </Link>
        )}
      </div>
      <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-6">
        {items.map((el) => (
          <Card key={el.id} item={el} />
        ))}
      </div>
    </Container>
  );
}

목차

  • 구문