2023. 4. 30. 16:18ㆍReact/Next.js
Next13
작년 10월에 Next13이 업데이트 될 때, 다들 '다시 배워야한다.' '맨날 업데이트 된다.' 이랬던 것 같은데 미루고 미루다가 Next 13 베타를 써보고 느낀 점을 기록해보고자 작성하게 되었다. (근데 tailwind를 곁들인...)
[only for IntelliJ] tailwind defaultStyle 버그
IntelliJ 에디터에서 tailwindcss의 autocomplete가 적용되지 않는 버그이다.
물론 자동완성이 되지 않더라도 스타일은 적용되지만, margin, padding, text 등에 대한 spacing도 다르기도 하고, 자동완성이 안되는 것 때문에 매번 공식문서를 방문하는 것도 쉽지않다. 구글링을 해도 마땅한 이유가 나오지 않다가 몇 번의 셀프 테스트케이스를 만들어서 정리하다가 원인을 찾아냈는데 의외로 허무하다.
import defaultTheme from 'tailwindcss/defaultTheme'; // X
const defaultTheme = require('tailwindcss/defaultTheme') // O
module.exports = {
//*...
theme: {
extend: {
fontFamily: {
sans: ['var(--font-multima)', 'var(--font-roboto)', 'sans-serif', ...defaultTheme.fontFamily.sans],
roboto: ['var(--font-roboto)', 'sans-serif', ...defaultTheme.fontFamily.sans],
},
//*...
}
모듈을 불러오는 방식에서 ES6 방식인 import로 호출하면 autocomplete가 동작하지 않았던 것. 재밌는 건 VSCode에서는 방식과 관계없이 잘 나온다는 점이다. intelliJ에서만 발생하는 버그인 걸로!
app 디렉토리구조는 pages와 뭐가 다를까?
전 버전에서 사용했던 pages는 서버사이드 컴포넌트, 클라이언트사이드 컴포넌트의 구분 없이 사용되곤 했는데 이때 re-rendering 측면에서 비용이 큰 컴포넌트에 대한 대응이 쉽지 않았다. 컴포넌트를 사용해서 명시적으로 페이지를 구분하긴 했지만, 명확한 가이드가 없는 상황이라 팀 컨벤션에 맞춰서 개발하는 것이 일반적인 방법이었다.
app 디렉토리의 경우에는 기존 대비 2가지 장점을 가진다.
- SSR 컴포넌트 / CSR 컴포넌트를 명확히 나눌 수 있다.
- 같은 라우터 내부에서 Hierarchy에 대한 가이드를 제공한다.
SSR 컴포넌트 vs CSR 컴포넌트
Next 13 beta 버전을 사용할 때는 SSR 컴포넌트와 CSR 컴포넌트를 구분지어야한다. (구분할 수 있다가 아니다)
모든 컴포넌트는 기본적으로 SSR 컴포넌트로 다뤄지며, CSR 컴포넌트로 사용하고 싶다면 아래 컨벤션을 따라야한다.
'use client' // CSR 컴포넌트로 사용하겠다는 메타데이터 명시
import Link from 'next/link';
import { usePathname } from 'next/navigation';
interface Props {
to: string;
value: string;
hasSubpage?: boolean;
}
const NavLink = ({ to, value, hasSubpage }: Props) => {
const router = usePathname();
return (
<Link className={`relative font-regular-16 ${router === to && 'nav-selected'}`} href={to}>{value}</Link>
)
}
export default NavLink;
문서에 따르면, 서버 컴포넌트와 클라이언트 컴포넌트를 구분지을 수 있는 가이드가 잘 정의되어 있다.
요약하자면, url 파라미터나 payload가 없는 GET 요청 시 혹은 UI 이벤트 없이 단순 정보를 표시해주는 컴포넌트는 서버 컴포넌트, 상태를 사용하거나 UI 이벤트가 발생, 이 이벤트로 url params나 payload가 바뀌는 경우는 클라이언트 컴포넌트를 사용한다.
기준 | Server Component | Client Component |
데이터를 Fetch 해야할 때 | ✅ | ⚠️ (UI로 url params나 payload가 바뀔 때) |
백엔드 리소스에 접근해야할 때 | ✅ | ❌ |
API 키나 Access token 처럼 민감 정보를 저장할 때 | ✅ | ❌ |
서버에 더 큰 의존성을 유지할 때 | ✅ | ❌ |
onClick이나 onChange 처럼 DOM 이벤트 리스너를 사용할 때 | ❌ | ✅ |
상태나 사이드 이펙트를 사용할 때 | ❌ | ✅ |
브라우저 API를 사용할 때 | ❌ | ✅ |
custom hooks를 써야할 때 | ❌ | ✅ |
React Class 컴포넌트를 쓸 때 | ❌ | ✅ |
이렇게 컴포넌트를 나눠놓으면 서버 컴포넌트에 대해서는 리렌더링이 필요없어지기 때문에 원하는 컴포넌트에서만 렌더링을 시켜줄 수 있다.
Component Hierarchy
pages 때는 index에서 모든 페이지를 담당했지만, Next 13부터는 컴포넌트간 계층구조가 생기게 되었다.
- layout
- template
- error
- loading
- not-found
- page
레이아웃 컴포넌트 기준으로 분리하는 전략은 과거부터도 많이 사용하곤 했었는데, 리렌더링 측면에서 많이 유리해졌다.
CRA에서는 레이아웃 컴포넌트 하단에 라우터를 네스팅해두어서 대응했던 걸 내장에서 처리할 수 있는 아주 유용한 기능이다. (문서에서는 이 전략을 Partial rendering이라고 한다.)
사용해보면서 유용하다고 느낀 점은 SEO 부분인데,
별도로 SEO 컴포넌트를 만들어서 index에 넣어줬던 과거의 Next와 달리 Layout에서 대응하는게 가능하다.
사용도 엄청 간단하다. 이제 사라진 getServerSideProps처럼 metadata 객체를 export 변수로 선언해놓으면 끝.
LightHouse 점수에 중요한 페이지별 메타데이터 대응도 너무 쉽게 가능하다.
// @app/layout.tsx
export const metadata = {
title: 'Jake.log',
description: '프론트엔드 전반적인 영역에서 고민합니다.',
icons: {
icon: '/favicon.ico'
},
openGraph: {
type: 'website',
siteName: 'Jake.log',
}
}
export default function RootLayout({ children } : { children: React.ReactNode }) {
return (
//..
)
}
next/font의 탄생!
구글폰트와 로컬폰트에 대해서 이제 더 이상 webfont 링크를 가져다 쓰지 않아도 된다!
이제 next/font를 설치하면 너무 쉽게 대응할 수 있다.
npm install next/font
or
yarn add next/font
// 구글 폰트에서 쓰고 싶은 폰트가 있으면?
const roboto = Noto_Sans_KR({
weight: ['400', '500', '700'],
subsets: ['latin'],
variable: '--font-roboto' // 이 부분을 선언해줘야 tailwind에서 따로 override가 가능하다.
});
// 로컬에서 쓰고 싶은 폰트가 있다면?
const myCustomFont = localFont({
{
path: '../../public/fonts/Multima-Bold.woff',
weight: '700',
},
{
path: '../../public/fonts/Multima-BoldItalic.woff',
weight: '700',
style: 'italic'
},
{
path: '../../public/fonts/Multima-Medium.woff',
weight: '500',
},
{
path: '../../public/fonts/Multima-MediumItalic.woff',
weight: '500',
style: 'italic'
},
{
path: '../../public/fonts/Multima-Regular.woff',
weight: '400',
},
{
path: '../../public/fonts/Multima-RegularItalic.woff',
weight: '400',
style: 'italic'
},
],
fallback: ['var(--font-roboto)', 'sans-serif'], // unicode-range 대응 가능
variable: '--font-multima'
})
fallback이라는 프로퍼티를 사용하면 fallback(영어만 지원되는 폰트에서 한글을 쓰고 싶다던가..)에 대해서 너무 편하게 대응이 가능하다.
과거에는 따로 global style을 담당하는 파일에서 일일히 @font-face를 사용해서 유니코드를 적어서 대응해야했지만, 이제 더이상 그럴 필요는 없을 것 같다.
각 폰트를 생성한 다음에는 변수명을 body의 className으로 넣어주면 tailwind.config.js에서 사용이 가능하다.
// @app/layout.tsx
//..
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="kr">
<body className={`${multima.className} ${roboto.variable}`}>
{children}
</body>
</html>
)
}
// tailwind.config.js
module.exports = {
content: [
'./src/**/*.{js,ts,jsx,tsx}',
],
theme: {
extend: {
fontFamily: {
sans: ['var(--font-multima)', 'var(--font-roboto)', 'sans-serif', ...defaultTheme.fontFamily.sans],
roboto: ['var(--font-roboto)', 'sans-serif', ...defaultTheme.fontFamily.sans],
}
//...
}
Next 13 beta 후기 총평
레거시로 남아버린 지식과 conflict가 나면서 Next 팀을 욕하면서 beta 버전을 익혔지만, 포스트를 정리해가는 과정에서 이전버전보다 훨씬 생산성이 높아졌다는 점을 다시 알아볼 수 있었다. (코드 생산성 측면이나, 리렌더링 측면이나)
여담으로 개인적으로 가장 고통스러운 부분은 'use client'를 정의해주는 부분이다. 빨리 에디터 차원에서 클라이언트 컴포넌트 보일러플레이트를 만들어주는 플러그인이 나왔으면 좋겠다.
'React > Next.js' 카테고리의 다른 글
AWS EC2에 Next 프로젝트 배포해보기 A to Z (0) | 2023.04.02 |
---|---|
Next에서 정적 미디어파일 사용하기 (0) | 2023.03.19 |
한땀한땀 Next로 프로젝트 마이그레이션 하기 (1) | 2023.03.17 |
Next.js로 배포해보는 gh-pages (1) | 2022.11.01 |
[TIL] NextJS는 window를 바로 접근할 수 없다. (window is not defined) (0) | 2022.10.28 |