반응형 헤더 만들기

이보아·2024년 7월 29일
0

반응형 헤더는 화면 크기에 따라 다른 UI를 보여줬다. 테일윈드 css와 next.js로 작업하고였고, 모바일, 테블릿, 데스크탑 등 다양한 기기에서 작동하게 만들었다.

데스크탑

모바일



1. 기본 구조 만들기

먼저 헤더의 기본 구조를 만든다.. 여기에는 로고와 내비게이션 메뉴가 들어가요. 로고는 Link 컴포넌트를 사용해서 홈으로 가게 설정한다.

<header className="bg-background shadow-md relative">
  <div className="w-full mx-auto max-w-container-l m:max-w-container-m s:max-w-container-s flex justify-between items-center py-3 s:py-2">
    <Link href="/">
      <h1 className="text-lg font-bold">@gather_here</h1>
    </Link>
    <nav className="flex items-center gap-2">
      {/* 추가될 내용  */}
    </nav>
  </div>
</header>

2. 검색창 추가하기

데스크탑과 테블릿에서는 항상 보이게 하고, 모바일에서는 버튼을 눌렀을 때만 보이게 만들었다.

{/* 데스크탑, 테블릿 */}
<form className="relative s:hidden items-center overflow-hidden">
  <label htmlFor="search" className="sr-only">검색창</label>
  <input type="text" id="search" name="search" placeholder="검색어를 입력해 주세요." className="shared-input-gray rounded-lg" />
  <button className="absolute top-[10px] right-[8px]" type="submit">
    <Image src="/Common/Icons/search.png" alt="검색 아이콘" width={24} height={24} />
  </button>
</form>
{/* 모바일 */}
{isSearchOpen && (
  <div className="absolute top-0 left-0 w-full bg-background z-50 p-2 flex items-center">
    <label htmlFor="search" className="sr-only">검색창</label>
    <input type="text" id="search" name="search" placeholder="검색어를 입력해 주세요." className="shared-input-gray w-full" />
    <button type="button" onClick={toggleSearch} className="absolute right-4 top-1/2 transform -translate-y-1/2">
      <Image src="/Common/Icons/close.png" alt="닫기 버튼" width={21} height={21} />
    </button>
  </div>
)}

3. 사용자 정보와 로그아웃 버튼

사용자가 로그인했을 때와 로그인하지 않았을 때를 구분해서, 각각 다른 버튼을 준다. 로그인된 사용자는 로그아웃 버튼과 마이페이지로 가는 버튼을, 로그인되지 않은 사용자는 로그인 버튼을 보여주게 구현했다.

{user ? (
  <div className="flex items-center">
    {/* 마이페이지 버튼 (모바일) */}
    <button onClick={toggleModal} className="hidden s:flex items-center justify-center w-[42px] h-[42px] rounded-lg bg-fillLight hover:bg-fillLight text-white">
      <Image src="/Common/Icons/user.png" alt="유저 버튼 아이콘" width={24} height={24} />
    </button>
    {/* 마이페이지 버튼 (데스크탑, 테블릿) */}
    <Link href="/mypage" className="flex s:hidden items-center justify-center w-[42px] h-[42px] rounded-lg bg-fillLight hover:bg-fillLight text-white">
      <Image src="/Common/Icons/user.png" alt="유저 버튼 아이콘" width={24} height={24} />
    </Link>
    <button onClick={signOut} className="shared-button-gray ml-2 s:hidden">로그아웃</button>
  </div>
) : (
  <button onClick={handleOpenLoginModal} className="shared-button-gray">로그인</button>
)}

4. 마이페이지 모달

모바일에서는 마이페이지 모달을 통해 사용자 정보를 보여준다. 그리고 사용자가 로그인했을 때만 열리게 만들었다.

{isModalOpen && user && (
  <div className="absolute top-12 right-0 w-full max-w-[250px] bg-white shadow-lg rounded-lg p-4 z-50 s:block hidden">
    <div className="flex items-center mb-4 pb-4 border-b-[1px]">
      <div className="p-2 bg-slate-100 rounded-full flex items-center justify-center">
        <Image src="/Common/Icons/user.png" alt="User Icon" width={28} height={28} />
      </div>
      <div className="ml-4">
        <p className="text-fillStrong text-baseS">{user.email}</p>
        <p className="text-baseXs text-fillNeutral">프론트엔드 4</p>
      </div>
    </div>
    <ul className="space-y-2">
      <li><Link href="/mypage" className="block text-fillNormal hover:text-black">프로필 수정</Link></li>
      <li><Link href="/mypage/myinterests" className="block text-fillNormal hover:text-black">내 관심글</Link></li>
      <li><Link href="/mypage/myposts" className="block text-fillNormal hover:text-black">내 작성글</Link></li>
      <li><button onClick={signOut} className="block w-full text-left text-fillNormal hover:text-black">로그아웃</button></li>
    </ul>
  </div>
)}

전체코드

"use client";
import React, { useEffect, useState } from "react";
import Link from "next/link";
import { createClient } from "@/utils/supabase/client";
import { useRouter } from "next/navigation";
import Image from "next/image";
import { useModal } from "@/provider/ContextProvider";
import LoginForm from "@/components/Login/LoginForm";
import useSignupStore from "@/store/useSignupStore";

const supabase = createClient();

const Header: React.FC = () => {
  const { user, setUser, resetUser } = useSignupStore();
  const router = useRouter();
  const [isSearchOpen, setIsSearchOpen] = useState(false); // 검색창 열림/닫힘 상태
  const [isModalOpen, setIsModalOpen] = useState(false); // 마이페이지 모달 열림/닫힘 상태
  const { openModal, closeModal } = useModal();

  // 로그아웃 함수
  const signOut = async () => {
    const { error } = await supabase.auth.signOut();
    if (!error) {
      resetUser();
      router.push("/");
    } else {
      console.error("Error logging out:", error);
    }
  };

  // 검색창 토글 함수
  const toggleSearch = () => {
    setIsSearchOpen(!isSearchOpen);
  };

  // 마이페이지 모달 토글 함수
  const toggleModal = () => {
    setIsModalOpen(!isModalOpen);
  };

  // 로그인 모달 열기 함수
  const handleOpenLoginModal = () => {
    openModal(<LoginForm />);
  };

  // 사용자 정보를 가져오는 함수
  useEffect(() => {
    const getUser = async () => {
      const { data, error } = await supabase.auth.getUser();
      if (data?.user) {
        setUser(data.user);
      }
    };
    getUser();
  }, [setUser]);

  return (
    <header className="bg-background shadow-md relative">
      <div className="w-full mx-auto max-w-container-l m:max-w-container-m s:max-w-container-s flex justify-between items-center py-3 s:py-2">
        {/* 로고와 홈으로 가는 링크 */}
        <Link href="/">
          <h1 className="text-lg font-bold">@gather_here</h1>
        </Link>
        <nav className="flex items-center gap-2">
          {/* 검색창 (데스크탑, 테블릿) */}
          <form className="relative s:hidden items-center overflow-hidden">
            <label htmlFor="search" className="sr-only">
              검색창
            </label>
            <input
              type="text"
              id="search"
              name="search"
              placeholder="검색어를 입력해 주세요."
              className="shared-input-gray rounded-lg"
            />
            <button className="absolute top-[10px] right-[8px]" type="submit">
              <Image src="/Common/Icons/search.png" alt="검색 아이콘" width={24} height={24} />
            </button>
          </form>
          <div className="flex items-center s:space-x-2">
            {/* 검색창 열기 버튼 (모바일) */}
            <button
              onClick={toggleSearch}
              className="hidden s:flex items-center justify-center w-[42px] h-[42px] rounded-lg bg-fillLight hover:bg-fillLight text-white"
            >
              <Image src="/Common/Icons/search.png" alt="검색 아이콘" width={24} height={24} />
            </button>
            {/* 글 작성 버튼 */}
            <Link href="/post" passHref>
              <button className="hidden s:flex items-center justify-center w-[42px] h-[42px] rounded-lg bg-fillLight hover:bg-fillLight text-white">
                <Image src="/Common/Icons/write.png" alt="글작성 버튼 아이콘" width={21} height={21} />
              </button>
            </Link>
            {user ? (
              <div className="flex items-center">
                {/* 마이페이지 버튼 (모바일) */}
                <button
                  onClick={toggleModal}
                  className="hidden s:flex items-center justify-center w-[42px] h-[42px] rounded-lg bg-fillLight hover:bg-fillLight text-white"
                >
                  <Image src="/Common/Icons/user.png" alt="유저 버튼 아이콘" width={24} height={24} />
                </button>
                {/* 마이페이지 버튼 (데스크탑, 테블릿) */}
                <Link
                  href="/mypage"
                  className="flex s:hidden items-center justify-center w-[42px] h-[42px] rounded-lg bg-fillLight hover:bg-fillLight text-white"
                >
                  <Image src="/Common/Icons/user.png" alt="유저 버튼 아이콘" width={24} height={24} />
                </Link>
                <button onClick={signOut} className="shared-button-gray ml-2 s:hidden">
                  로그아웃
                </button>
              </div>
            ) : (
              <button onClick={handleOpenLoginModal} className="shared-button-gray">
                로그인
              </button>
            )}
          </div>
        </nav>
      </div>
      {/* 검색창 (모바일) */}
      {isSearchOpen && (
        <div className="absolute top-0 left-0 w-full bg-background z-50 p-2 flex items-center">
          <label htmlFor="search" className="sr-only">
            검색창
          </label>
          <input
            type="text"
            id="search"
            name="search"
            placeholder="검색어를 입력해 주세요."
            className="shared-input-gray w-full"
          />
          <button type="button" onClick={toggleSearch} className="absolute right-4 top-1/2 transform -translate-y-1/2">
            <Image src="/Common/Icons/close.png" alt="닫기 버튼" width={21} height={21} />
          </button>
        </div>
      )}
      {/* 마이페이지 모달 (모바일) */}
      {isModalOpen && user && (
        <div className="absolute top-12 right-0 w-full max-w-[250px] bg-white shadow-lg rounded-lg p-4 z-50 s:block hidden">
          <div className="flex items-center mb-4 pb-4 border-b-[1px]">
            <div className="p-2 bg-slate-100 rounded-full flex items-center justify-center">
              <Image src="/Common/Icons/user.png" alt="User Icon" width={28} height={28} />
            </div>
            <div className="ml-4">
              <p className="text-fillStrong text-baseS">{user.email}</p>
              <p className="text-baseXs text-fillNeutral">프론트엔드 4</p>
            </div>
          </div>
          <ul className="space-y-2">
            <li>
              <Link href="/mypage" className="block text-fillNormal hover:text-black">
                프로필 수정
              </Link>
            </li>
            <li>
              <Link href="/mypage/myinterests" className="block text-fillNormal hover:text-black">
                내 관심글
              </Link>
            </li>
            <li>
              <Link href="/mypage/myposts" className="block text-fillNormal hover:text-black">
                내 작성글
              </Link>
            </li>
            <li>
              <button onClick={signOut} className="block w-full text-left text-fillNormal hover:text-black">
                로그아웃
              </button>
            </li>
          </ul>
        </div>
      )}
    </header>
  );
};

export default Header;
profile
매일매일 틀깨기

0개의 댓글