swimminginthecode DIVE!

Dazz6/ABOUT

모각 프로젝트 - #2-1. 홈 화면

dazz6 2025. 8. 1. 18:00

 

첫 진입시 출력되는 홈 화면으로, 모바일 대응 반응형도 구현했다.

"모여서 각자 공부하기" 라는 콘셉트를 보여 줄 수 있는 이미지를 위해 다양한 공부 및 작업하는 캐릭터를 삽입했다.

캐릭터 디자인은 디자이너 지인이 도와주었습니다 https://www.youtube.com/@티거-r9c

 

Next.js 의 장점, 서버 사이드 렌더링을 최대한 살리기 위해 메인은 당연히 server component 여야 한다고 생각했다.

그렇지만 모각존과 모각챌은 참여자가 많은 순으로 소개하는 기능이므로

실시간까지는 아니더라도 길지 않은 텀으로 업데이트되어야 할 것 같아 revalidate 를 10분으로 설정했다.

 

export async function MainZoneChallenge() {
  const res = await fetch(`${process.env.BACKEND_API_URL}`, {
    next: { revalidate: 60 * 10 },
  });

  if (!res.ok) {
    const err = await res.text();
    console.error("서버 응답:", err);
    throw new Error(`메인 불러오기 실패: ${res.status}`);
  }

  const data: MainResponse = await res.json();

  return data;
}

 

또한 이전 프로젝트에서 느꼈던 큰 문제점 중 하나인, "react 의 가장 큰 장점인 component 화를 살리지 못하고 있어 가독성 및 효율이 떨어짐" 을 개선하기 위해 메인에서 사용하는 ImageCard section 도 component 로 분리하여 props 로 값을 넘기는 방식을 사용했다.

 <ImageCard
          type="home"
          title="모여서 각자, 모각"
          description1="모각에 처음 오셨나요?"
          description2="간단한 챌린지로 시작해 보세요!"
          button="챌린지 시작하기"
          buttonUrl="/challenge"
          img1={student.src}
          img2={coding.src}
          img3={designer.src}
          bgImageLight={MainBgLight.src}
          bgImageDark={MainBgDark.src}
        />
"use client";

import Link from "next/link";
import Button from "../ui/button";
import { ImageCardProps } from "@/types/shared.type";

export default function ImageCard({
  title,
  description1,
  description2,
  button,
  buttonUrl,
  img1,
  img2,
  img3,
  bgImageLight,
  bgImageDark,
}: ImageCardProps) {
  return (
    <div className="flex justify-center w-full px-4">
      <div className="relative w-full max-w-7xl flex flex-col lg:flex-row items-center gap-6">
        <div
          className="relative w-full lg:w-4/5 aspect-[5/4] sm:aspect-[5/3] md:aspect-[5/2] bg-cover bg-left rounded-lg overflow-hidden"
          style={{ backgroundImage: `url(${bgImageLight})` }}
        >
          <div
            className="hidden dark:block absolute inset-0 z-0"
            style={{
              backgroundImage: `url(${bgImageDark})`,
              backgroundSize: "cover",
              backgroundPosition: "left",
            }}
          />

          {img1 && (
            <img
              src={img1}
              alt="img1"
              className="absolute top-10 right-6 w-20 sm:w-40 lg:w-52 z-10"
            />
          )}
          {img2 && (
            <img
              src={img2}
              alt="img2"
              className="absolute top-10 left-6 w-20 sm:w-40 lg:w-52 z-10"
            />
          )}
          {img3 && (
            <img
              src={img3}
              alt="img3"
              className="absolute bottom-6 left-1/2 transform -translate-x-1/2 w-20 sm:w-40 lg:w-56 z-10"
            />
          )}

          <div className="absolute inset-0 flex items-center justify-center lg:hidden z-10">
            <div className="backdrop-blur-md border border-borders text-gray-900 text-center p-6 rounded-lg max-w-md">
              <h2 className="text-2xl sm:text-3xl text-primary dark:text-primary-dark font-bold">
                {title}
              </h2>
              <div className="text-base text-text dark:text-borders sm:text-lg mt-2">
                <p>{description1}</p>
                {description2 && <p>{description2}</p>}
              </div>
              <Link href={buttonUrl}>
                <Button variant="secondary" className="mt-4">
                  {button}
                </Button>
              </Link>
            </div>
          </div>
        </div>

        <div className="hidden lg:flex flex-col justify-center gap-4 lg:w-1/5">
          <h2 className="text-3xl font-bold text-primary">{title}</h2>
          <div className="text-base text-gray-700 dark:text-gray-300">
            <p>{description1}</p>
            {description2 && <p>{description2}</p>}
          </div>
          <Link href={buttonUrl}>
            <Button variant="secondary">{button}</Button>
          </Link>
        </div>
      </div>
    </div>
  );
}