오늘을 모든 페이지에 공동으로 들어가는 헤더를 만들었습니다. 작업 속도가 거북이보다 느렸던게 아주 큰 흠이지만, 이전 프로젝트에서 정확히 이해하지 못하고 넘어갔던 부분을 조금씩 알아가는 느낌이 들어 다행이라는 생각이 듭니다. 오늘 공부한 부분을 내일 개발할 부분에 적용할 수 있겠죠?!
이번 프로젝트는 지금까지와는 다르게 개발기간도 길고, 디자이너님도 함께합니다. 그래서 우리 팀이 아주아주 정성으로 작성한 와이어프레임을 가지고 멋지게 작업해주고 계십니다. 어제 메인페이지 와이어프레임 디자이너.ver을 받아볼 수 있었는데요. 고것을 가지고 헤더를 만들어보았습니다.
중 일부입니다.
깔끔하고 멋지죠?!
footer 부분은 팀원분이 만드셨습니닷
좌상단에서부터 보면
사이트 로고가 들어갈 부분은 일단 text로 처리했습니다. 클릭하면 메인페이지로 링크됩니다.
아직 색상팔레트가 정확히 정해지지 않은 것 같아 검은색으로 남겨두었습니다.
카테고리를 클릭하면 상품 카테고리 드롭다운이 나옵니다.
이렇게. (css는 일단 흐린눈을,,👀)
그리고 해당 카테고리를 클릭하면 카테고리 페이지로 링크되도록 했습니다.
검색창은 input만 있습니다. 나중에 검색기능을 완성되면 input 처리와 검색 결과 처리를 추가할 계획입니다.
이 부분은 로그인 여부에 따라 달라지는데 로그인하지 않았다면 클릭했을 때 로그인/회원 가입 페이지로 연결됩니다.
만약 로그인 한 상태라면
클릭했을 때 드롭다운이 나옵니다. 마이페이지를 클릭하면 user 페이지로 이동하고 로그아웃 버튼을 누르면 로그아웃하며 홈으로 이동합니다.
로그인 상태는 zustand를 이용해 전역상태 관리합니다.
장바구니 아이콘을 클릭하면 장바구니 페이지로 링크됩니다.
로그인 하지 않았을 때는 보이지 않도록 해야하는데 방금 생각났습니다. 수정해야겠네요.
수정하려다가 다시 생각해보니 로그인 상태는 zustand를 이용해 관리하는데 header에는 use client가 없습니다. header를 그냥 csr으로 관리할 지 팀원들과 상의해봐야겠네요.
++ 모든 드롭다운은 영역밖을 클릭하면 닫히도록 만들었습니다.
카테고리 목록은 supabase public schema에 category라는 이름의 table로 저장되어있습니다. 물론 헤더의 카테고리 목록 부분을 하드코딩해도 되지만 휴먼 에러가 발생할 수 있고, 추후 카테고리의 확장성을 고려했을 때 db에서 불러오기로 했습니다.
import { supabase } from "@/service/supabase";
import { NextRequest, NextResponse } from "next/server";
export const GET = async () => {
let { data: category, error } = await supabase.from("category").select("*");
if (error) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
return NextResponse.json(category);
};
import { CategoryTable } from "@/types/db";
import { useQuery } from "@tanstack/react-query";
const useCategory = () => {
const {
data: category,
isLoading,
isError
} = useQuery<CategoryTable[] | CategoryTable>({
queryFn: async (): Promise<CategoryTable[] | CategoryTable> => {
const response = await fetch(`/api/category`, { method: "GET" });
const data = await response.json();
return data;
},
queryKey: ["category"]
});
return { category, isLoading };
};
export default useCategory;
"use client";
import React, { useState, useEffect } from "react";
import Link from "next/link";
import { RxHamburgerMenu } from "react-icons/rx";
import useCategory from "@/hooks/useCategory";
import { CategoryTable } from "@/types/db";
interface Props {
category: CategoryTable;
}
const CategoryName = ({ category: { id, category_name } }: Props) => {
return (
<Link href={`/category/${id}`}>
<li>{category_name}</li>
</Link>
);
};
const HeadCategory = () => {
//카테고리 메뉴 열기
const [open, setOpen] = useState(false);
const { category, isLoading } = useCategory();
// console.log(category);
...
return (
<>
<div className="flex flex-row space-x-4 cursor-pointer relative" onClick={() => setOpen(!open)}>
<RxHamburgerMenu size="24" />
<p>카테고리</p>
{open && !isLoading && (
<ul className="absolute p-1 mt-7 ml-7 text-right w-32 z-50 right-0 bg-white shadow-md cursor-pointer">
{(category as CategoryTable[]).map((category) => (
<CategoryName category={category} key={category.id} />
))}
</ul>
)}
</div>
</>
);
};
export default HeadCategory;
useCategory에서 가져온 데이터에 map함수를 적용하는게 쉽지 않았습니다.
코드가 크게 맘에 들지는 않습니다. 너무 지저분해요.
프로젝트를 진행하면서 시간 여유가 있다면 깔끔하게 정리하고 싶습니다.
...
const Header = () => {
return (
<header className="py-2">
<div className="container m-auto flex flex-col max-w-[1200px] min-h-[128px] w-[90%] space-y-4">
<div className="py-4">
<Link href={"/"} className="font-black text-3xl">
YOLOCEAN
</Link>
</div>
<div className="flex flex-row justify-between pb-4">
<div>
<HeadCategory /> //이렇게 삽입해줬습니다.
</div>
<div className="flex flex-row space-x-6">
<div className="w-72 border border-black rounded-xl">
<input type="text" className="mx-2 w-56 focus:outline-none" />
<AiOutlineSearch className="inline mr-3 ml-2" size="20" />
</div>
<div>
<AuthBtn />
</div>
<div>
<Link href={"/cart"}>
<AiOutlineShopping size="22" />
</Link>
</div>
</div>
</div>
</div>
</header>
);
};
db에서 데이터를 잘 가져왔던 경험을 가지고 앞으로 프로젝트에 잘 써먹어 보도록 하겠습니다. 오늘도 수고했다!!