처음에 구상했던 Navbar의 디자인이다. 해당 디자인의 Navbar 구현 자체는 어렵지 않았다. 하지만 프로필을 눌렀을 때 드롭다운 메뉴들을 보여주는 부분은 신경쓸 필요가 있었다.
semantic-ui Dropdown 공식문서
https://react.semantic-ui.com/modules/dropdown/
Dropdown 구현 방법은 여러가지가 있지만 나는 그 중에서 semantic-ui에 있는 Dropdown을 이용했다. 공식문서를 참고하면 어떤 식으로 Dropdown 메뉴들을 구현하는지 예시도 나와있고, 해당 컴포넌트에 전달할 수 있는 props 목록들도 잘 설명되어있다.
Dropdown 전체를 구성한다. trigger가 따로 없으면 그냥 작은 드롭다운 아이콘을 누를 때 드롭다운이 된다. 하지만 우리 페이지의 디자인은 아이콘을 누를 때도 드롭다운이 되도록 만들어야했다. 이때 이 trigger라는 인자를 이용해 이를 해결했다.
const trigger = (
<span>
<Avatar
src={user?.user.avatar ? user.user.avatar : `${process.env.PUBLIC_URL}/images/missing.png`}
style={{ cursor: 'pointer' }}
/>
</span>
);
다음과 같이 trigger을 선언하고 props로 전달하면 된다.
<Dropdown
direction="left"
trigger={trigger}
style={{ display: 'flex', alignItems: 'center' }}
>
...
Dropdown.Menu의 하위 컴포넌트에 드롭다운 메뉴에 들어갈 Dropdown.Item 아이템들이 구성된다. 해당 아이템들의 텍스트가 DropText로 구성된다.
나는 정말 간단하게 사용한 정도지만 페이지를 참고하면 거의 원하는 드롭다운 메뉴들은 다 구현할 수 있어보였다.
Navbar는 fixed를 이용해 제일 맨 위 상위로 고정시켰다.
// src/components/Navbar/DesktopNabar.jsx
import React, { useRef, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { useSelector, useDispatch } from 'react-redux';
import styled, { css } from 'styled-components';
import { Icon, Dropdown } from 'semantic-ui-react';
import COLOR from 'constants/color.constant';
import { darken } from 'polished';
import { SET_TYPE } from 'reducers/postListType';
const Nav = styled.nav`
position: fixed;
top: 0;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
background-color: white;
padding: 0.85rem 2rem;
margin-bottom: 4rem;
z-index: 1;
box-shadow: 1px 1px 10px -5px black;
`;
const Avatar = styled.img`
width: 2rem;
height: 2rem;
margin-left: 0.5rem;
border-radius: 50%;
`;
const DropText = styled.div`
font-weight: 500 !important;
`;
const Login = styled.button`
background: ${COLOR.PRIMARY} !important;
color: white !important;
font-family: 'NS-R' !important;
height: 2rem;
font-size: 14px !important;
letter-spacing: 0.1rem;
border-radius: 0.3rem;
padding: 0 1rem;
cursor: pointer;
&:hover {
background: ${darken(0.03, COLOR.PRIMARY)} !important;
}
`;
const Logo = styled.img`
cursor: pointer;
width: 7rem;
height: auto;
`;
const DesktopNavbar = () => {
const navigate = useNavigate();
const { user } = useSelector((state) => state.authentication);
const handleSignOut = () => {
localStorage.removeItem('user');
window.location.replace('/');
};
const trigger = (
<span>
<Avatar
src={user?.user.avatar ? user.user.avatar : `${process.env.PUBLIC_URL}/images/missing.png`}
style={{ cursor: 'pointer' }}
/>
</span>
);
return (
<Nav>
// ...
<Icon
name="search"
style={{ fontSize: '1.5rem', marginRight: '0.5rem', cursor: 'pointer' }}
/>
{user ? (
<>
<Icon
name="pencil alternate"
style={{ fontSize: '1.5rem', marginLeft: '0.5rem', cursor: 'pointer' }}
onClick={() => {
navigate('/post');
}}
/>
<Dropdown
direction="left"
trigger={trigger}
style={{ display: 'flex', alignItems: 'center' }}
>
<Dropdown.Menu style={{ marginTop: '1.3rem' }}>
<Dropdown.Item
onClick={() => {
navigate('/mypage');
}}
>
<DropText>프로필</DropText>
</Dropdown.Item>
<Dropdown.Item
onClick={() => {
navigate('/post');
}}
>
<DropText>글쓰기</DropText>
</Dropdown.Item>
<Dropdown.Item
onClick={() => {
navigate('/setting');
}}
>
<DropText>설정</DropText>
</Dropdown.Item>
<Dropdown.Item onClick={handleSignOut}>
<DropText>로그아웃</DropText>
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>{' '}
</>
) : (
<Link to="/signin">
<Login>로그인</Login>
</Link>
)}
</div>
</Nav>
);
};
export default DesktopNavbar;