반응형 헤더는 화면 크기에 따라 다른 UI를 보여줬다. 테일윈드 css와 next.js로 작업하고였고, 모바일, 테블릿, 데스크탑 등 다양한 기기에서 작동하게 만들었다.
먼저 헤더의 기본 구조를 만든다.. 여기에는 로고와 내비게이션 메뉴가 들어가요. 로고는
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>
데스크탑과 테블릿에서는 항상 보이게 하고, 모바일에서는 버튼을 눌렀을 때만 보이게 만들었다.
{/* 데스크탑, 테블릿 */}
<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>
)}
사용자가 로그인했을 때와 로그인하지 않았을 때를 구분해서, 각각 다른 버튼을 준다. 로그인된 사용자는 로그아웃 버튼과 마이페이지로 가는 버튼을, 로그인되지 않은 사용자는 로그인 버튼을 보여주게 구현했다.
{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>
)}
모바일에서는 마이페이지 모달을 통해 사용자 정보를 보여준다. 그리고 사용자가 로그인했을 때만 열리게 만들었다.
{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;