UI 구현 (Header, NotificationTray)

애플리케이션 상단에 배치될 헤더 컴포넌트를 구성합니다.

Header.jsx

알림(notification) 상태를 관리하기 위한 데이터를 아래와 같이 구성합니다.

const initialNotifications = [
	{ id: 1, content: '"yamoo9"님 회원 가입을 환영합니다.' },
	{ id: 2, content: '"lovelyU"님이 새로운 글을 등록했습니다.' },
	{ id: 3, content: '"liketiger님이 "친구 요청" 하습니다.' },
  { id: 4, content: '"likelion"님이 "좋아요!"를 누르셨습니다.' },
];

알림 상태를 표시하거나 감추는 상태와 알림 내용 집합을 관리할 상태를 설정합니다. 그리고 조건부 렌더링이 되도록 JSX 마크업을 아래와 같이 구성합니다.

import { useState, useCallback } from 'react';
import NotificationTray from './NotificationTray';

function Header() {
	const [showNotifications, setShowNotifications] = useState(false);
	const [notificationContent, setNotificationContent] = useState(initialNotification);
	
	const handleToggleNotification = () => setShowNotifications((sn) => !sn);

	const handleDeleteNotification = useCallback(
		(deleteId) => () => {
			setNotificationContent(
				contents => contents.filter(({ id }) => id !== deleteId)
			);
		}, 
		[]
	);

	const label = `알림 ${showNotification ? '감춤' : '표시'}`;

  return (
		<header>
			<h1>
				<a href="/" className="homeLink">브랜드</a>
			</h1>
			<nav className="navigation">
				<h2 className="sr-only">페이지 탐색</h2>
				<ul>
					<li>
						<button 
							type="button" 
							aria-label={label}
              title={label}
							onClick={handleToggleNotification}
							className="notificationToggleButton"
						>
							<svg
					      fill="none"
					      viewBox="0 0 20 21"
					    >
					      <path
					        stroke="currentColor"
					        strokeLinecap="round"
					        strokeLinejoin="round"
					        d="M10 3.464V1.1m0 2.365a5.338 5.338 0 015.133 5.368v1.8c0 2.386 1.867 2.982 1.867 4.175C17 15.4 17 16 16.462 16H3.538C3 16 3 15.4 3 14.807c0-1.193 1.867-1.789 1.867-4.175v-1.8A5.338 5.338 0 0110 3.464zM4 3L3 2M2 7H1m15-4l1-1m1 5h1M6.54 16a3.48 3.48 0 006.92 0H6.54z"
					      />
					    </svg>
						</button>
					</li>
				</ul>
			</nav>
			{
				showNotifications && (
					<NotificationTray
						notificationContent={notificationContent}
						onDelete={handleDeleteNotification}
					/>
				)
			}
		</header>
	);
}

그리고 NotificationTray 컴포넌트를 작성합니다. 외부에서 전달된 속성(props)을 사용해 리스트 렌더링 하거나, 이벤트에 반응할 수 있도록 구성합니다.

NotificationTray.jsx

function NotificationTray({
	notificationContent,
	onDelete,
}) {
	return (
		<div className="NotificatonTray">
			<ul>
				{notificationContent.map(({ id, content }) => (
					<li key={id}>
						<span>{content}</span>
						<button
							type="button"
							className="clearButton"
							aria-label="알림 삭제"
							title="알림 삭제"
							onClick={onDelete(id)}
						>
							<svg
					      width={16}
					      height={16}
					      viewBox="0 0 512 512"
					      fill="currentColor"
					      strokeWidth={0}
					    >
					      <path
					        fill="none"
					        stroke="currentColor"
					        strokeLinecap="round"
					        strokeLinejoin="round"
					        strokeWidth="32"
					        d="M368 368L144 144m224 0L144 368"
					      />
					    </svg>
						</button>
					</li>	
				))}
			</ul>
		</div>
	);
}

마지막으로 App (또는 RootLayout) 컴포넌트에서 Header 컴포넌트를 눌러와 화면에 렌더링합니다.

App.jsx

import Header from './Header';

function App() {
	return (
		<>
			<Header />
		</>
	);
}

export default App;