프로그라피 어드민 페이지
사이드바를 구현했다.
import { Heading, Stack, Text } from '@chakra-ui/react'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
type Item = {
label: string
href: string
subItems?: Omit<Item, 'subItems'>[]
}
type Props = {
title: string
items: Item[]
}
const Sidebar = ({ title, items }: Props) => {
return (
<Stack w="280px" h="100%" bg="gray.50" p="36px">
<Text fontSize="14px" fontWeight="700" mb="36px" color="black">
{title}
</Text>
<Stack spacing="25px">
{items.map((item) => (
<SideItem key={item.label} {...item} />
))}
</Stack>
</Stack>
)
}
export default Sidebar
const SideItem = ({ label, href, subItems }: Item) => {
const pathname = usePathname()
const isActive = href === pathname || subItems?.some((subItem) => subItem.href === pathname)
return (
<Stack spacing="12px">
<Link href={href}>
<Heading size="sm" color={isActive ? 'black' : 'gray.500'}>
{label}
</Heading>
</Link>
{subItems?.map((subItem) => <SideSubItem key={subItem.label} {...subItem} />)}
</Stack>
)
}
const SideSubItem = ({ label, href }: Item) => {
const pathname = usePathname()
const isActive = href === pathname
return (
<Link href={href}>
<Text
ml="32px"
color={isActive ? 'black' : 'gray.500'}
bg={isActive ? 'gray.100' : 'transparent'}
p="4px 8px"
>
{label}
</Text>
</Link>
)
}
Sidebar를 사용하는 곳에 Layout이기 때문에 pathname으로 props를 다르게 넘거주도록 했다.
import { Box, Flex } from '@chakra-ui/react'
import Header from './Header'
import Sidebar from './Sidebar'
import type { PropsWithChildren } from 'react'
import { usePathname } from 'next/navigation'
import { sidebarList } from '@/constants/sidebar'
const Layout = ({ children }: PropsWithChildren) => {
const pathname = usePathname()
const sidebarProps = sidebarList.find((sidebar) => pathname.startsWith(sidebar.href))
if (!sidebarProps) {
return children
}
return (
<>
<Header currentPath={sidebarProps.href} />
<Flex pt="80px" h="100%">
{sidebarProps && <Sidebar {...sidebarProps} />}
<Box as="main" flex="1" p="36px">
{children}
</Box>
</Flex>
</>
)
}
export default Layout
여기서 sidebarList는 sidebar에 들어갈 item들을 담아 배열로 저장해놓은 것이다.
type SidebarProps = ComponentProps<typeof Sidebar> & { href: string }
const memberSidebar: SidebarProps = {
title: '회원 관리',
href: '/member',
items: [
{
label: '현 기수 관리',
href: '/member',
subItems: [
{
label: '대시보드',
href: '/member/dashboard',
},
{
label: '출석체크',
href: '/member/attendance',
},
],
},
{
label: '이전 기수 데이터',
href: '/member/previous',
},
],
}
//...
export const sidebarList: SidebarProps[] = [
memberSidebar,
//...
]
이렇게 nesting 가능한 구조로 사이드 바를 구현했다. 이렇게 구현함으로써 매 페이지마다 사이드바를 구현하는 것을 방지할 수 있게 되었다.
리액트 중간고사
오늘 학교 리액트 중간고사를 봤다.
시험 문제는 객관식, 빈칸, 단답형 이렇게 나왔는데, 문제가 불친절하게 나왔다..
빈칸이 다음과 같이 나왔다.
const root = document.getElementById(____)
render(<App />, root)
문제는 별다른 얘기 없이 저 빈칸에 들어갈 코드를 적으라는 것이었다.
뭐 답은 'root'지만, index.html도 보여주지 않은 상태에서 이런 문제를 내는 것은 너무 불친절한 것 같다. 그리고 아무리 CRA를 쓰더라도 index.html을 보여주지 않는 것은 좀 그렇다.
마치 CRA가 정답인거처럼... CRA를 쓰지 않는다면 어떻게 해야하는지는 알려주지 않는다.