코딩/React

TailwindCSS 강좌 3편 - Card 만들기

드리프트 2021. 10. 16. 17:23
728x170

 

안녕하세요?

 

최근에 제 One-Pick이 된 TailwindCSS 강좌를 또하게 되었네요.

 

오늘은 Bootstrap 이나, Material-UI에는 다 있는 Card에 대해 직접 만들어 보겠습니다.

 

일단 완성형을 볼까요?

 

 

 

아래 동영상을 보시면 반응형 동작을 볼 수 있습니다.

반응형 동작

 

 

일단 지난 시간부터 이어져 오던 NextJS Template를 그대로 쓰겠습니다.

 

그리고 상단 Navbar 는 1편에 나온 걸 사용하도록 하겠습니다.

 

 

/pages/index.tsx

import Head from "next/head";

export default function Home() {
  return (
    <div>
      <Head>
        <title>Tailwind CSS Tutorial</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <div className="py-32 text-center">
        <div className="text-4xl  font-extrabold text-gray-700 mb-12">
          Navbar in Tailwind!
        </div>
        <div className="text-4xl  font-extrabold text-gray-700 mb-12">
          Responsive Cards in Tailwind!
        </div>
      </div>

      {/* 카드 전체를 반응형으로 만들기 위한 컨테이너 */}
      <div className="p-4 sm:w-1/2 lg:w-1/3">

      </div>
    </div>
  );
}

 

자, 우리의 index.tsx 페이지에 하나 만들어 보겠습니다.

 

먼저, 카드 한개 한개가 반응형으로 작동하도록 컨테이너 div 태그를 만들어 봅시다.

 

{/* 카드 전체를 반응형으로 만들기 위한 컨테이너 */}
      <div className="p-4 sm:w-1/2 lg:w-1/3">

      </div>

p-4 는 padding 을 4만큼 줬고 그다음에 나오는게 바로 width (폭) 인데요.

 

sm일경우 w-1/2 으로 지정해서 sm(small)일 경우 폭이 전체의 50%를 차지하도록 하고 있습니다.

 

그리고 lg일 경우 w-1/3으로 지정해서 lg(large)이상일 경우 폭이 전체의 1/3, 즉 33.3%를 차지하도록 하고 있습니다.

 

즉, 우리가 만들 카드는 작은 화면일 경우 2개씩, 큰 화면일 경우 3개씩 한줄에 나오도록 한다는 얘기죠.

 

이제 카드 자체를 만들어 볼까요?

 

아래 코드는 카드 전체를 border 지정하기 위한 div 태그입니다.

h-full 을 줘서 높이는 최대한 다 차지하도록 하고, border-2 라고 줘서 border 두께를 조금 줬습니다.

 

그리고 border-gray-200은 border 색깔입니다.

 

그리고 border-opacity-60은 border 투명도입니다.

 

그리고 rounded-lg 라고 해서 모서리를 둥글게 크게(lg) 했고 overflow 할 경우 보여지지 않게 hidden으로 처리 했습니다.

 

        <div className="h-full border-2 border-gray-200 border-opacity-60
                        rounded-lg overflow-hidden">
          test
        </div>

 

 

스크린샷을 볼까요?

 

 

왼쪽 아래 test 라고 박스가 보이네요.

 

일단 내용이 없기 때문에 박스의 높이가 작습니다.

 

그럼 본격적으로 이미지를 추가해 볼까요?

 

          <img
            className="lg:h-72 md:h-48 w-full 
                       object-cover object-center"
            src="https://picsum.photos/id/188/720/400/"
            alt="card image"
          />

 

이미지 src는 picsum.photos 에서 빌려왔습니다.

 

picsum.photos 는 코딩할 때 mockup 이미지를 제공해 주는데요.

 

unsplash 같은 사이트라고 보시면 됩니다.

 

img 태그의 CSS 는 어떻게 했냐면, lg일경우 h-72로 높이를 높게 지정했고, md일 경우 h-48로 높이를 조금 낮게 설정했습니다.

 

그리고 w-full로 폭은 최대한 차지하라고 했고, 그다음으로 중요한 TailwindCSS가 제공하는 반응형 이미지 속성입니다.

 

object-cover는 CSS 코드로는 object-fit: cover 입니다.

 

object-center는 object-position : center입니다.

 

두번째야 쉽게 이해 할 수 있는거고, object-fit: cover는 뭘까요?

 

이미지를 축소 확대할 때 비율대로 줄이고 확대하라는 뜻입니다.

 

TailwindCSS에는 여러가지를 제공해 주고 있으니까 각각 확인해 보시기 바랍니다.

 

이제 스크린샷을 볼까요?

 

 

멋진 풍경사진이 아주 예쁘게 카드 형태로 보이네요.

 

이제 다음으로 카드 사진 밑에 내용이 들어와야 되는데요.

 

내용에는 날짜, 타이틀, 설명, 그리고 링크 및 기타 항목인데요.

 

이걸 다 감싸는 div 컨테이너가 있어야 합니다.

 

    {/* 카드 전체를 반응형으로 만들기 위한 컨테이너 */}
      <div className="p-4 sm:w-1/2 lg:w-1/3">
        <div className="h-full border-2 border-gray-200 border-opacity-60 rounded-lg overflow-hidden">
          <img
            className="lg:h-72 md:h-48 w-full object-cover object-center"
            src="https://picsum.photos/id/188/720/400/"
            alt="card image"
          />
          <div className="p-6 hover:bg-indigo-600 hover:text-white
                         transition duration-300 ease-in">
            날짜, 타이틀, 내용, 링크 및 기타
          </div>
        </div>
      </div>

 

지금까지 코드 전부 넣었습니다.

 

맨 아래 날짜, 타이틀 등등이 보이는 부분이 이미지 밑에 우리가 쓸 내용입니다.

 

p-6으로 하고,  hover일 경우 bg-indigo-600으로 주고, hover일 경우 text-white로 했습니다.

 

그리고 transition duration-300 ease-in으로 멋있는 애니메이션도 줬습니다.

 

한번 볼까요?

 

마우스를 내용에 가져대면 hover에 의해 배경색과 텍스트 색이 바뀝니다.

 

자, 이제 50%까지는 왔네요.

 

그럼 날짜, 타이틀 등등을 멋지게 꾸며볼까요?

 

먼저 날짜입니다.

 

            <h2 className="text-base font-medium text-indigo-300 mb-1">
              Oct 16, 2021
            </h2>

 

날짜는 text-base 로 font-size: 1rem; line-height: 1.5rem; 으로 지정했습니다.

 

그리고, font-medium 으로 font-weight : 500으로 줬고 text-indigo-300으로 파란색 비슷하게 그리고 mb-1을 줘서 다음에 나올 것과 마진을 조금 줬습니다.

 

다음은 타이틀입니다.

 

<h1 className="text-2xl font-semibold mb-3">타이틀</h1>

text-2xl 로 좀 크게 했고 font-semibold로 두껍게 타이틀처럼 보이게 했고 mb-3 으로 마진 bottom을 조금 두었습니다.

 

다음은 내용 부분입니다.

 

            <p className="leading-relaxed mb-3">
              Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam
              modi, expedita quos doloremque autem ipsum itaque incidunt ipsam
              reprehenderit fuga! Dolores quisquam eius cum accusamus?
            </p>

 

내용은 Lorem ipsum으로 채워 넣었습니다.

 

p 태그에 leading-relaxed 를 넣었는데요. 이게 뭐냐면 줄간격을 1.625로 지정하는 겁니다.

 

이름이 relaxed인데 이게 보기 편하다고 하나봐요.

 

위 사진 보시면 줄 간격 관련해서 많은게 있습니다.

 

그럼 지금까지의 스크린샷을 볼까요?

 

어떤가요? 조금 멋있게 보이시죠?

 

이제 마지막 링크 및 기타 부분입니다.

 

이 부분은 각자 만들려고 하는 사이트의 특성에 맞게 추가하시면 됩니다.

 

            <div className="flex items-center flex-wrap ">
              <a className="text-indigo-300 inline-flex items-center md:mb-2 lg:mb-0">
                Read More
                <ArrowRightIcon className="w-4 h-4 ml-2" />
              </a>
              <span className="text-gray-400 mr-3 inline-flex items-center lg:ml-auto md:ml-0 ml-auto leading-none text-sm pr-3 py-1 border-r-2 border-gray-200">
                <EyeIcon className="w-4 h-4 mr-1" />
                1.2K
              </span>
              <span className="text-gray-400 inline-flex items-center leading-none text-sm">
                <ChatIcon className="w-4 h-4 mr-1" />6
              </span>
            </div>

마지막은 Read More 과 각종 아이콘으로 구성되어 있습니다.

 

그래서 div 태그로 flex 로 구성했는데요. items-center로 가운데 정렬, flex-wrap을 줬습니다.

 

그리고 아이콘은 지난 시간에 이용한 Heroicons인데요.

 

지난 시간에는 Heroicons가 npm 에 있는지 모르고 그냥 복사했는데요.

 

다음과 같이 설치하시면 됩니다.

 

npm install @heroicons/react

그리고 다음과 같이 아이콘을 불러 오면 됩니다.

 

import { ArrowRightIcon, EyeIcon, ChatIcon } from "@heroicons/react/outline";

 

이렇게 쉽게 설치 할 수 있는 걸 모르고 지난 시간에는 헛고생했었네요.

 

 

이제 완성되었습니다.

 

한번 볼까요?

 

자 어떤가요?

 

멋진 Card 컴포넌트가 완성되었습니다.

 

그런데 우리는 지금까지 너무 Hard Coding으로 Card 컴포넌트를 작성했는데요.

 

이걸 계속 Re-use 할려면 컴포넌트로 만들어야 겠죠.

 

 

/components/Card.tsx 파일을 만듭시다.

 

import { ArrowRightIcon, EyeIcon, ChatIcon } from "@heroicons/react/outline";

export interface CardProps {
  imageSrc: string;
  date: string;
  title: string;
  desc: string;
  viewed: string;
  reply: number;
}

const Card = (props: CardProps) => {
  return (
    <div className="p-4 sm:w-1/2 lg:w-1/3">
      <div className="h-full border-2 border-gray-200 border-opacity-60 rounded-lg overflow-hidden">
        <img
          className="lg:h-72 md:h-48 w-full object-cover object-center"
          src={props.imageSrc}
          alt="blog"
        />
        <div className="p-6 hover:bg-indigo-600 hover:text-white transition duration-300 ease-in">
          <h2 className="text-base font-medium text-indigo-300 mb-1">
            {props.date}
          </h2>
          <h1 className="text-2xl font-semibold mb-3">{props.title}</h1>
          <p className="leading-relaxed mb-3">{props.desc}</p>
          <div className="flex items-center flex-wrap ">
            <a className="text-indigo-300 inline-flex items-center md:mb-2 lg:mb-0">
              Read More
              <ArrowRightIcon className="w-4 h-4 ml-2" />
            </a>
            <span className="text-gray-400 mr-3 inline-flex items-center lg:ml-auto md:ml-0 ml-auto leading-none text-sm pr-3 py-1 border-r-2 border-gray-200">
              <EyeIcon className="w-4 h-4 mr-1" />
              {props.viewed}
            </span>
            <span className="text-gray-400 inline-flex items-center leading-none text-sm">
              <ChatIcon className="w-4 h-4 mr-1" />
              {props.reply}
            </span>
          </div>
        </div>
      </div>
    </div>
  );
};

export default Card;

이렇게 컴포넌트로 만들면 Card 컴포넌트에 들어갈 CardProps 만 주면 똑같은걸 재 사용 할 수 있어 나중에 유용하게 쓸 수 있을 겁니다.

 

이제 이 Card 컴포넌트를 활용하는 페이지를 만들어야 하는데

 

우리 Navbar에 보시면 첫번째 메뉴가 바로 Features 입니다.

 

이 링크에 해당되는 Features 링크에 Card 이미지 세개를 보여주는 사이트를 만들어 볼까요?

 

/pages/features.tsx 파일을 만들어 볼까요?

 

import Head from "next/head";
import Card from "../components/Card";

export default function Features() {
  return (
    <div>
      <Head>
        <title>Tailwind CSS Tutorial - Features</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

          {/* First Card */}
          <Card
            imageSrc="https://picsum.photos/id/188/720/400/"
            date="Oct 15, 2021"
            title="Cities are crowded"
            desc="Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam
              modi, expedita quos doloremque autem ipsum itaque incidunt ipsam
              reprehenderit fuga! Dolores quisquam eius cum accusamus?"
            viewed="1.2K"
            reply={6}
          />

          {/* Second Card */}
          <Card
            imageSrc="https://picsum.photos/id/1016/720/400"
            date="Oct 16, 2021"
            title="Mountains are alone"
            desc="Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam
              modi, expedita quos doloremque autem ipsum itaque incidunt ipsam
              reprehenderit fuga! Dolores quisquam eius cum accusamus?"
            viewed="1.1K"
            reply={5}
          />

          {/* Third Card */}
          <Card
            imageSrc="https://picsum.photos/id/1011/720/400"
            date="Oct 17, 2021"
            title="Lakes are silent"
            desc="Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam
              modi, expedita quos doloremque autem ipsum itaque incidunt ipsam
              reprehenderit fuga! Dolores quisquam eius cum accusamus?"
            viewed="1.0K"
            reply={3}
          />
      </div>
  
  );
}

 

Card 컴포넌트를 이용해서 3개의 Card를 만들었습니다.

 

 

Card 컴포넌트가 두서없이 그냥 밑으로 쭉 나열되었네요.

 

이제 Card 객체를 반응형으로 만드는 Container를 만들어 보도록 하겠습니다.

 

 <div className="container px-5 py-10 mx-auto">

 

먼저 Card 세개를 포함하는 Features 페이지의 전체적인 틀을 가지고 있는 div 객체를 위와 같이 만듭시다.

 

TailwindCSS의 container 속성은 width 부분이 100%로 반응형이며 media-query에 따라 width를 100%로 주고 있습니다.

 

반응형 페이지를 만들때는 container를 쓰면 아주 좋습니다.

 

그리고 px-5 py-10 mx-auto로 마진과 패딩을 줬고요.

 

        <div className="text-center mb-12">
          <h1 className="text-4xl md:text-6xl text-gray-700 font-semibold">
            Tailwind Css Responsive Cards
          </h1>
        </div>

제목 부분입니다. div 태그안에 h1 태그로 제목을 크고 멋지게 만들었습니다.

 

이제 Card 세개를 묶는 div 태그를 만들어야 하는데요.

 

당연히 flex를 이용할 겁니다.

 

<div className="flex flex-wrap">
          {/* First Card */}
          <Card
          .....
          .....
          ....
          ....
          
          {/* Second Card */}
          <Card
          ....
          ....
          ....
          ....
          
          {/* Third Card */}
          <Card
          ....
          ....
          ...
          ....
          
 </div>

Card 세개를 그냥 flex flex-wrap으로 감싸 줬습니다.

 

어떻게 보이는지 볼까요?

 

 

 

아주 멋지게 md 화면일 경우 세개가 나란히 놓여져 있네요.

 

세개 Card 가 너무 떨어져 있네요.

 

마진을 좀 줄여 볼까요?

 

<div className="flex flex-wrap -m-4">

 

-m-4 를 줬습니다.

 

m-4 의 마이너스 버전입니다.

 

그러면 Card 세개의 마진이 약간 겹쳐 지겠죠.

 

 

뭔가 꽉 차 보이는 거 같습니다.

 

 

flex-wrap에 의해 화면이 좁아지면 옆으로 나열되는게 점점 하나씩 다음칸으로 나열됩니다.

 

최종 코드입니다.

 

/pages/featurs.tsx

 

import Head from "next/head";
import Card from "../components/Card";

export default function Features() {
  return (
    <div>
      <Head>
        <title>Tailwind CSS Tutorial - Features</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <div className="container px-5 py-10 mx-auto">
        {/* Cards Page - Title */}
        <div className="text-center mb-12">
          <h1 className="text-4xl md:text-6xl text-gray-700 font-semibold">
            Tailwind Css Responsive Cards
          </h1>
        </div>

        <div className="flex flex-wrap -m-4">
          {/* First Card */}
          <Card
            imageSrc="https://picsum.photos/id/188/720/400/"
            date="Oct 15, 2021"
            title="Cities are crowded"
            desc="Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam
              modi, expedita quos doloremque autem ipsum itaque incidunt ipsam
              reprehenderit fuga! Dolores quisquam eius cum accusamus?"
            viewed="1.2K"
            reply={6}
          />

          {/* Second Card */}
          <Card
            imageSrc="https://picsum.photos/id/1016/720/400"
            date="Oct 16, 2021"
            title="Mountains are alone"
            desc="Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam
              modi, expedita quos doloremque autem ipsum itaque incidunt ipsam
              reprehenderit fuga! Dolores quisquam eius cum accusamus?"
            viewed="1.1K"
            reply={5}
          />

          {/* Third Card */}
          <Card
            imageSrc="https://picsum.photos/id/1011/720/400"
            date="Oct 17, 2021"
            title="Lakes are silent"
            desc="Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam
              modi, expedita quos doloremque autem ipsum itaque incidunt ipsam
              reprehenderit fuga! Dolores quisquam eius cum accusamus?"
            viewed="1.0K"
            reply={3}
          />
        </div>
      </div>
    </div>
  );
}

 

 

아참 그리고 Navbar.tsx 부분에 Features 링크를 수정해야 겠죠.

 

           {/* primary nav */}
            <div className="hidden md:flex items-center space-x-1">
              <a
                href="/features"
                className="py-5 px-3 text-gray-700 hover:text-gray-900"
              >
                Features
              </a>
       .........
       .......
       .....
       ...
       .
              
      {/* mobile menu items */}
      <div className={`${!menuToggle ? "hidden" : ""} md:hidden`}>
        <a
          href="/features"
          className="block py-2 px-4 text-sm hover:bg-gray-200"
        >
          Features
        </a>

 

a 태그에 href="/features"라고 주면 됩니다.

 

NextJS는 page 방식의 라우팅이기 때문에 react-route-dom 같은게 필요없이 정말 간단하게 라우팅 할 수 있죠.

 

지금까지 TailwindCSS 카드 컴포넌트 작성과 반응형 페이지를 만들어봤습니다.

 

그럼.

 

그리드형