[MainProject] Header
Header
웹 페이지에서 공통 컴포넌트로 사용되는 Header
구현
Header style
styled-components를 사용하여 Header 컴포넌트의 스타일을 정의
//Header.style.ts
import { Link } from "react-router-dom";
import styled, { css } from "styled-components";
import { dropDown, dropUp, fadeIn, fadeOut } from "../../styles/keyframes";
export const LinkText = styled(Link)`
display: flex;
font-size: 1.3rem;
font-family: "Yaldevi", sans-serif;
font-weight: 700;
color: #6d4924;
transition: 0.3s;
&:hover {
transform: scale(110%, 110%);
color: var(--purple);
cursor: pointer;
}
`;
export const SmallLinkText = styled.div`
font-family: "Yaldevi", sans-serif;
font-weight: 700;
line-height: 15px;
display: flex;
height: 1.5rem;
font-size: 14px;
color: #6d4924;
transition: 0.3s;
`;
export const Icon = styled(Link)`
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 80px;
min-width: 60px;
margin-top: 15px;
&:hover > div {
transform: scale(110%, 110%);
color: var(--purple);
cursor: pointer;
}
& > img {
transition: 0.3s;
}
&:hover > img {
transform: scale(110%, 110%);
}
`;
export const IconDiv = styled.div`
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 80px;
min-width: 60px;
margin-top: 15px;
&:hover > div {
transform: scale(110%, 110%);
color: var(--purple);
cursor: pointer;
}
& > img {
transition: 0.3s;
}
&:hover > img {
transform: scale(110%, 110%);
}
`;
export const HeaderContainer =
styled.div <
{ animation: string } >
`
position: fixed;
top: 0;
left: 0;
right: 0;
display: flex;
width: 100%;
height: 80px;
z-index: 1;
align-items: center;
justify-content: space-between;
background-color: #fcfcffaa;
animation: 0.3s ${({ animation }) =>
animation === "fadeIn" ? fadeIn : fadeOut} forwards;
`;
export const AuthRelativeContainer = styled.div`
display: flex;
width: 18%;
justify-content: center;
@media screen and (max-width: 700px) {
display: none;
}
`;
export const DropDownContainer =
styled.div <
{ isOpenMenu: boolean } >
`
display: none;
position: relative;
width: 10%;
height: 100%;
justify-content: center;
align-items: center;
@media screen and (max-width: 700px) {
display: flex;
}
& > ul {
${({ isOpenMenu }) =>
isOpenMenu
? css`
visibility: visible;
animation: ${dropDown} 0.3s forwards;
`
: css`
animation: ${dropUp} 0.3s forwards;
`}
}
`;
export const DropDownContent = styled.ul`
visibility: hidden;
background-color: white;
position: absolute;
top: 100%;
right: 0px;
width: 150px;
border-radius: 0px 0px 15px 15px;
overflow: hidden;
& > li,
a > li {
padding: 1rem;
text-align: center;
transition: 0.3s;
display: flex;
align-items: center;
justify-content: space-around;
&:hover {
background-color: var(--normal-gray);
}
}
`;
export const HeaderLogo = styled.div`
font-family: Just Another Hand, cursive;
font-size: 2.5rem;
cursor: pointer;
display: flex;
width: 18%;
justify-content: flex-end;
& > a {
transition: 0.3s;
color: #6d4924;
}
&:hover > a {
transform: scale(110%, 110%);
color: var(--purple);
}
`;
export const HamburgerMenuStyle =
styled.a <
{ isOpenModal: boolean } >
`
position: relative;
width: 50px;
height: 44px;
cursor: pointer;
& > span {
width: 75%;
height: 3px;
left: 25%;
background-color: #6d4924;
position: absolute;
border-radius: 4px;
transition: all 0.3s;
}
& > span:nth-of-type(1) {
top: 25%;
${({ isOpenModal }) =>
isOpenModal ? "transform: translateY(11.5px) rotate(-45deg);" : ""}
}
& > span:nth-of-type(2) {
top: 50%;
${({ isOpenModal }) => (isOpenModal ? "opacity: 0" : "")}
}
& > span:nth-of-type(3) {
top: 75%;
${({ isOpenModal }) =>
isOpenModal ? "transform: translateY(-11.5px) rotate(45deg);" : ""}
}
`;
LinkText: styled-components를 사용하여 스타일이 적용된 Link 컴포넌트를 생성 이 컴포넌트는 링크를 표현하며, hover 시에 색상과 크기가 변경되는 애니메이션 효과가 적용된다.
Icon: 아이콘을 표현하기 위한 컴포넌트 아이콘과 관련된 이미지 및 스타일이 적용되며, hover 시에 크기와 색상이 변경되는 애니메이션 효과가 적용된다.
HeaderContainer: 헤더 컨테이너를 스타일링하는 컴포넌트입니다. 헤더의 크기, 배경색, 애니메이션 효과가 정의되어 있습니다.
position: fixed; //헤더 상단 고정
AuthRelativeContainer: 인증 관련 컨테이너를 스타일링하는 컴포넌트 미디어 쿼리를 사용하여 화면 크기에 따라 표시 여부가 조절된다.
DropDownContainer: 드롭다운 메뉴 컨테이너를 스타일링하는 컴포넌트 미디어 쿼리를 사용하여 화면 크기에 따라 표시 여부가 조절되며, 드롭다운 메뉴의 애니메이션 효과가 지정된다.
DropDownContent: 드롭다운 메뉴의 내용을 스타일링하는 컴포넌트 드롭다운 메뉴 아이템의 스타일이 정의되어 있다.
HamburgerMenuStyle: 햄버거 메뉴 아이콘을 스타일링하는 컴포넌트 햄버거 아이콘의 스타일 및 상태에 따른 애니메이션 효과가 정의되어 있다.
export const HamburgerMenuStyle = styled.a<{ isOpenModal: boolean }>`
isOpenModal이라는 프롭스를 받아와서 햄버거 메뉴를 오픈
& > span:nth-of-type(1) {
top: 25%;
${({ isOpenModal }) => (isOpenModal ? 'transform: translateY(11.5px) rotate(-45deg);' : '')}
}
& > span:nth-of-type(2) {
top: 50%;
${({ isOpenModal }) => (isOpenModal ? 'opacity: 0' : '')}
}
& > span:nth-of-type(3) {
top: 75%;
${({ isOpenModal }) => (isOpenModal ? 'transform: translateY(-11.5px) rotate(45deg);' : '')}
}
`;
각각 첫 번째, 두 번째, 세 번째 span 요소에 대한 스타일을 지정
Header 컴포넌트
useLocation
을 이용하여 현재 경로를 가져온다.useNavigate
를 이용하여 라우팅을 수행하는 함수를 가져온다.useState
를 이용하여 메뉴의 열림/닫힘 상태를 관리한다.useAuthAnimation
커스텀 훅을 사용하여 인증 관련 애니메이션 상태와 함수를 가져온다.
//Header.tsx
import { Link, useLocation, useNavigate } from "react-router-dom";
import { useEffect, useState } from "react";
import login_img from "../../assets/images/login_img.png";
import logout_img from "../../assets/images/logout_img.png";
import cart_img from "../../assets/images/cart_img.png";
import chatlist_img from "../../assets/images/chatlist_img.png";
import { LocalStorage } from "../../utils/browserStorage";
import {
BASE_ANIMATION_TIME,
LOCAL_STORAGE_KEY_LIST,
} from "../../assets/constantValue/constantValue";
import { postLogout } from "../../api/authApis";
import useScreenResize from "../../hooks/useScreenResize";
import useAuthAnimation from "../../hooks/useAuthAnimation";
import {
AuthRelativeContainer,
DropDownContainer,
DropDownContent,
HeaderContainer,
HeaderLogo,
Icon,
IconDiv,
LinkText,
SmallLinkText,
} from "./Header.style";
import HamburgerMenu from "./HamburgerMenu";
function Header() {
const location = useLocation();
const navigate = useNavigate();
const [isOpenMenu, setIsOpenMenu] = useState < boolean > false;
const { animation, setAnimation } = useAuthAnimation();
const handleResize = () => {
if (window.innerWidth > 700) {
setIsOpenMenu(false);
}
};
const clickLogout = () => {
postLogout()
.then(() => {
alert("로그아웃 되었습니다.");
LocalStorage.clear();
navigate("/");
})
.catch(() => console.log("로그아웃을 할 수 없습니다: noCookie"));
};
useScreenResize(handleResize);
useEffect(() => {
if (location.pathname === "/auth" && animation === "fadeIn") {
setTimeout(() => setAnimation("fadeOut"));
return;
}
if (location.pathname !== "/auth") {
setTimeout(() => setAnimation("fadeIn"), BASE_ANIMATION_TIME);
return;
}
}, [location]);
return (
<HeaderContainer animation={animation}>
<HeaderLogo>
<Link to="/">BUYTE</Link>
</HeaderLogo>
<div
style=
>
<LinkText to="/map">Map</LinkText>
<LinkText to="/select">Order</LinkText>
<LinkText to="/mypage">Mypage</LinkText>
</div>
{LocalStorage.get(LOCAL_STORAGE_KEY_LIST.AccessToken) ? (
<>
<AuthRelativeContainer>
{LocalStorage.get(LOCAL_STORAGE_KEY_LIST.MemberRole) ===
"SELLER" && (
<Icon to="/chatList">
<img
src={chatlist_img}
alt="ChatList"
style=
/>
<SmallLinkText>채팅목록</SmallLinkText>
</Icon>
)}
<Icon to="/cart">
<img src={cart_img} alt="Cart" style= />
<SmallLinkText>장바구니</SmallLinkText>
</Icon>
<IconDiv onClick={clickLogout}>
<img src={logout_img} alt="Login" style= />
<SmallLinkText>LOGOUT</SmallLinkText>
</IconDiv>
</AuthRelativeContainer>
<DropDownContainer
onClick={() => setIsOpenMenu(!isOpenMenu)}
isOpenMenu={isOpenMenu}
>
<HamburgerMenu isOpenMenu={isOpenMenu} />
<DropDownContent>
{LocalStorage.get(LOCAL_STORAGE_KEY_LIST.MemberRole) ===
"SELLER" && (
<Link to="/chatList">
<li>
<img
src={chatlist_img}
alt="ChatList"
style=
/>
<div>채팅목록</div>
</li>
</Link>
)}
<Link to="/cart">
<li>
<img src={cart_img} alt="Cart" style= />
<div>장바구니</div>
</li>
</Link>
<li onClick={clickLogout}>
<img src={login_img} alt="Login" style= />
<div>LOGOUT</div>
</li>
</DropDownContent>
</DropDownContainer>
</>
) : (
<>
<AuthRelativeContainer>
<Icon to="/auth">
<img src={login_img} alt="Login" style= />
<SmallLinkText>LOGIN</SmallLinkText>
</Icon>
</AuthRelativeContainer>
<DropDownContainer
onClick={() => setIsOpenMenu(!isOpenMenu)}
isOpenMenu={isOpenMenu}
>
<HamburgerMenu isOpenMenu={isOpenMenu} />
<DropDownContent>
<Link to="/auth">
<li>
<img src={login_img} alt="Login" style= />
<div>LOGIN</div>
</li>
</Link>
</DropDownContent>
</DropDownContainer>
</>
)}
</HeaderContainer>
);
}
export default Header;
handleResize 함수
const handleResize = () => {
if (window.innerWidth > 700) {
setIsOpenMenu(false);
}
};
innerWidth
에 따라 앞에서 정의한 햄버거 메뉴를 오픈한다.
clickLogout 함수
const clickLogout = () => {
postLogout()
.then(() => {
alert("로그아웃 되었습니다.");
LocalStorage.clear();
navigate("/");
})
.catch(() => console.log("로그아웃을 할 수 없습니다: noCookie"));
};
- 로그아웃 버튼을 클릭했을 때 호출
- 서버로 로그아웃 요청을 보내고, 성공적으로 로그아웃되면
LocalStorage
를 초기화하고 홈 페이지로 이동한다.
useEffect 훅
useEffect(() => {
if (location.pathname === "/auth" && animation === "fadeIn") {
setTimeout(() => setAnimation("fadeOut"));
return;
}
if (location.pathname !== "/auth") {
setTimeout(() => setAnimation("fadeIn"), BASE_ANIMATION_TIME);
return;
}
}, [location]);
- 현재 경로가
/auth
이고 인증 애니메이션이fadeIn
상태일 때, 로그인 페이지로 이동하는 경우에 애니메이션을 페이드아웃으로 변경 - 그 외의 경우에는
/auth
경로가 아니면서 다른 경로로 이동할 때 애니메이션을 페이드인으로 변경
구현 이미지
Login X 처음 화면 Login O 화면 장바구니 페이지 접근 가능
댓글남기기