next.js + sanity를 활용한 포트폴리오 사이트를 만들겠다고 얘기한 이후인 2월의 어느날, 포폴을 제출해야 했다. 2월 3째주에 몰아서 작업한 기록을 이제서야 포스팅해본다.
시리즈의 첫번째 포스팅은 1월 말이었는데...벗...그 후로 상황이 많이 달라졌다.
난 분명 야금야금 만들고 싶었는데...
1)포폴 제출 요청이 들어왔고, 2)링크로 포폴을 제출하고 싶었고, 3)제출하려면 만들어야 했다.
🤦♀️
제출하려면 해야지!
2월 셋째주에 몰아서 작업했다.
제출하고 지쳐서 한동안 안건드리다가... 저번달부터 다시 야금야금 고쳐보고 있다.
5개월만의 포스팅이니 만큼, 이번 포스팅은
2월 셋째주에 작업한 깃헙 커밋...되새김질이 되겠다.
기존 schema에서 인풋값은 년-월-일-시-분 까지 다 받고 있었으나 아웃풋은 2019 - 2020
처럼 year만 표시하고 있었다. 하지만 난 2020.03 - 2020.11
이렇게 표시하고 싶었지.
그래서 다음과 같이 수정했다.
코드를 들고와서 다시 보자면
(나는 디자이너라 챗 gpt의 도움을 받았다...^^)
date
object의 month
는 0부터 시작하므로 +1을 더해서 표시한다. duration
이 존재하지 않으면 undefined
로 표시duration.start
가 존재하지 않는다면 undefined
로 표시duration.end
가 존재하지 않는다면, endYear
은 Now
로, endMonth
는 undefined
로 표시startYear
와 endYear
가 모두 존재할 때만 렌더링, startMonth
와 endMonth
가 undefined
인 경우는 월을 표시하지 않음const startYear = new Date(duration?.start).getFullYear()
const startMonth = duration?.start? new Date(duration.start).getMonth() + 1 : undefined
const endYear = duration?.end? new Date(duration?.end).getFullYear() : 'Now'
const endMonth = duration?.end? new Date(duration.end).getMonth() + 1 : undefined
return (
<div>
<div className="mb-20">
...
{!!(startYear && endYear) && (
<div className="p-3 lg:p-4">
<div className="text-xs md:text-sm">Duration</div>
<div className="text-md md:text-lg">{`${startYear}.${startMonth} - ${endYear}.${endMonth}`}</div>
</div>
)}
스위프트 배울 시절 옵셔널 체이닝에 정신이 아득해졌던 기억이 있는데, 타입스크립트에도... 있네....!
next.js + sanity의 다른 템플릿을 참고해서 작업했던 것 같은데 아니었다. 챗gpt랑 작업했네.
the navbar that I code like this. but I want to add responsible menu for mobile, like hamburger menu. my code is like this. could you edit it? = 지금 내코드 이런데 모바일 대응하게 수정해줄래?
그래서 챗gpt랑 합의 본 코드는 다음과 같다.
'use client'
& useState
넣기
: 왜 넣었는지 기억이 안나서 다시 빼봤는데 다음과 같은 에러가 떴다.
You're importing a component that needs useState. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
공식 설명 링크
-> next.js는 SSR(서버 사이드 렌더링)과 CSR(클라이언트 사이드 렌더링)을 처리를 다르게 하는데, SSR되는 컴포넌트 중에 CSR로 되어야하는 부분이 생긴다면 (e.g. useState
) 파일의 맨 위에 'use client'
를 정의해서 클라이언트 번들의 일부로 인식되게끔 한다.
isMenuOpen
으로 상태 관리
:
'use client'
import { MenuIcon, CloseIcon } from '@sanity/icons'
import { resolveHref } from 'lib/sanity.links'
import Link from 'next/link'
import { useState } from 'react'
import { MenuItem } from 'types'
interface NavbarProps {
export function Navbar({ menuItems }: NavbarProps) {
const [isMenuOpen, setIsMenuOpen] = useState(false)
return (
...
{/* Hamburger menu for mobile*/}
<div className="flex md:hidden">
<button
type="button"
className="text-gray-600 hover:text-black focus:outline-none"
onClick={() => setIsMenuOpen(!isMenuOpen)}
>
{isMenuOpen ? (
<CloseIcon className="h-6 w-6" />
) : (
<MenuIcon className="h-6 w-6" />
)}
</button>
</div>
{/* Menu items */}
<div
className={`${
isMenuOpen ? 'block' : 'hidden'
} md:flex md:items-center w-full md:w-auto`}
>
{menuItems &&
menuItems.map((menuItem, key) => {
const href = resolveHref(menuItem?._type, menuItem?.slug)
if (!href || menuItem?._type === 'home') {
return null
}
return (
...
)
})}
</div>
</div>
)
}