학습내용
- _document.js
- Web Vitals
- Middleware
Next.js 프로젝트의 실행 순서
_app.js ➡️ _document.js ➡️ _app.js
import { Html, Head, Main, NextScript } from "next/document"; const Document = () => { return ( <Html lang="en"> <Head /> <body> <Main /> <NextScript /> </body> </Html> ); }; export default Document;
_document.js의
<Main />
컴포넌트가 _app.js에 해당된다.
Next.js 프로젝트에 styled-components를 적용하면 새로고침시 적용했던 스타일이 사라지고 초기화 된다. 스타일이 적용되기도 전에 렌더링이 완료됐기 때문이다. 아래와 같이 _document.js에서 ServerStyleSheet
를 사용하면 초기 렌더링시 스타일을 포함해서 렌더링 한다.
import Document from 'next/document'
import { ServerStyleSheet } from 'styled-components'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet()
const originalRenderPage = ctx.renderPage
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: App => props => sheet.collectStyles(<App {...props} />)
})
const initialProps = await Document.getInitialProps(ctx)
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
)
}
} finally {
sheet.seal()
}
}
}
export default MyDocument
babel-plugin-styled-components
를 사용하는 방법도 있지만 나의 경우엔 babel 뭐시기로는 해결이 되지 않았고, 이 방법을 사용하자 바로 해결되었다.
웹사이트 품질을 결정하는 지표로 LCP, FID, CLS 등이 있다.
- | LCP | FID | CLS |
---|---|---|---|
의미 | Largest Contentful Paint | First Input Delay | Cumulative Layout Shift |
측정 항목 | 페이지 로딩 성능(화면에 보이는 부분 중 최대 크기의 element가 렌더링 되는 속도) | 페이지 응답성(특정 버튼을 눌렀을 때 상호작용 속도) | 시각적 안정성 |
개선 방안 | priority(이미지 컴포넌트 우선 렌더링) | Link의 prefetch(Link를 적용한 컴포넌트가 화면에 보이면 해당 페이지의 데이터를 미리 다운) | 스켈레톤 UI, next/image |
크롬 개발자도구의 Lighthouse 항목에서 Analyze page load 실행
캡쳐 아래 항목으로 내려보면 개선 사항이 적혀있는데 해당 항목을 참조해서 Web Vitals를 개선할 수 있다.
redux와 마찬가지로 요청과 응답 사이에서 수행하는 작업을 의미한다.
yarn add next@latest
middleware.js
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
// This function can be marked `async` if using `await` inside
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/about-2', request.url))
}
// See "Matching Paths" below to learn more
export const config = {
matcher: '/about/:path*',
}
middleware를 설치하면 위 파일이 자동으로 생성된다. 이 중 맨 아래 matcher
에 경로가 설정되면 해당 경로에서만 middleware가 실행되고, 아무 경로도 없을시엔 모든 경로에서 실행된다.
export function middleware(request: NextRequest) {
const { cookies } = request;
if(!cookies["firebaseSomething"]{
return NextResponse.redirect("/login);
}
}
특정 쿠기가 없다면 로그인 불가
export function middleware(request: NextRequest) {
const url = request.nextUrl.clone();
const { pathname } = request.nextUrl;
if(pathname === "/jamescameron"){
url.pathname = "/titanic";
return NextResponse.redirect(url);
}
}
/jamescameron
으로 접속하면 경로가 /titanic
으로 재설정 된다. 이 밖에도 에러 페이지나 랜딩 페이지로 redirect
할 수도 있다.
export function middleware(request: NextRequest) {
const url = request.nextUrl.clone();
const { pathname } = request.nextUrl;
if(pathname === "/jamescameron"){
url.pathname = "/titanic";
return NextResponse.rewrite(url);
}
}
redirect
와 같지만 경로가 유지된다는 차이점이 있다. 즉, /jamescameron
으로 접속하면 해당 경로는 유지하되 /titanic
경로에 해당하는 내용이 렌더링 된다.
if(pathname === "/jamescameron"){
url.pathname = "/titanic";
url.pathname = "/avartar";
}
이것을 응용하면 같은 경로에서 조건에 따라서 다른 내용을 렌더링 할 수도 있다.