Nextjs 13 with App directory

date
Jul 13, 2023
slug
nextjs13-app-router
author
status
Public
tags
Next.js
summary
Next.js 13에서 제공하는 App 라우터의 주요 기능들
type
Post
thumbnail
n13-app.png
updatedAt
Jul 26, 2023 11:15 AM
category
💡
Working with the app directory in Next.js 13 번역 원본 글 : https://blog.logrocket.com/next-js-13-app-directory/ 위의 글과 Nextjs 공식 페이지에 있는 내용에서 필요한 부분만을 정리했습니다.

📚 Working with the app directory in Next.js 13

📌 Next.js 13에서의 새로운 기능

pages diretory → app directory

  • pages directory 구조에서는 pages/home.jsx → site.com/home
  • app directory 구조에서는 app/profile/settings/page.jsx → site.com/profile/settings

loading.tsx

  • app 폴더 내에 만들 수 있는 선택적인 컴포넌트
  • 해당 loading.tsx 컴포넌트는 자동으로 React suspense boundary안에 페이지를 감싸게 됨

error.tsx

  • app에서 에러가 발생할 수 있는 가능한 작은 영역으로 나누어 분리시킬 수 있음
  • error.tsx 파일 생성 → React error boundary로 해당 페이지 영역을 감싸게 됨
  • error.tsx 파일이 위치한 곳에 어떤 에러가 발생하면 → 해당 컴포넌트로 대체 됨

layout.tsx

  • 여러 곳에서 공유되는 UI를 정의할 수 있음
  • layout 컴포넌트 안에 또 다른 layout 컴포넌트를 담을 수 있음
  • layout안에 있는 컴포넌트 안에서 route가 변경되면, layout의 state는 유지됨 (layout 컴포넌트가 unmounted되지 않음)

template.tsx

  • layout.tsx 파일과 유사, 그러나 페이지를 이동하면 상태가 유지되지 않음
 
💡
layout 과 template를 활용하면, partial rendering 으로 알려진 컨셉의 장점을 취할 수 있음
 
notion image
 

📌 app directory 를 사용하기

  • Next.js 13에서는 새로운 기능이 많이 추가되었음. pages directory에서 app directory로 이전하면서 알아야할 것들이 있음
 

[필수] root 레이아웃

  • app directory 의 루트 위치에 정의가 되어 있어야 함
  • app 안에 존재하는 모든 경로에 적용 가능
  • 추가로 root 레이아웃<html><body> 태그를 작성해줘야 함 - Next.js가 자동으로 추가해주지 않음
 

Head tag

  • app directory 내부에 head.js 파일이 속한 폴더에 <head> 태그의 콘텐츠를 정의할 수 있음
  • head.js<title> , <meta> , <link> , <script> 등과 같은 파일을 return 함
 

Route groups

  • 모든 app directory 내부 구조는 URL path에 영향을 끼침
  • 괄호로 폴더 이름을 감싸면 경로 그룹으로 포함되지 않음
notion image
 

Server component

  • 기본적으로 app directory 안에 생성된 컴포넌트는 React server components
  • RSC는 더 작은 번들사이즈로 더 좋은 성능을 제공함
  • 클라이언트 컴포넌트로 변경하고자 한다면, 파일의 최상단에 use client 라고 명시하면 됨
 

📌 Next.js 13 핸즈온!

page & layout

app/layout.tsx

export const metadata = { title: 'Areate Next App', description: 'Generated by create next app', }; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <body className={inter.className}>{children}</body> </html> ); }
  • export const metadata 는 static인지 dynamic인지에 따라서 달라짐 (참고링크)
 

app/page.tsx

export default function Page() { return ( <html lang="en"> <body className={inter.className}> <div className={styles.header}>from layout</div> <div>{children}</div> </body> </html> ); }

app/second/page.tsx

export default function Page() { return <h1>Second route!</h1>; }
 
notion image
 

📌 중첩된 레이아웃 구조

  • 각 레이아웃 컴포넌트들은 app 최상단 뿐 아니라, 각 폴더 레벨에서도 작동함
notion image
 

layout.tsx의 중첩

import styles from './layout.module.css'; export default function Layout({ children }: { children: React.ReactNode }) { return ( <div className={styles.header}> <div> <h1 style={{ marginTop: 0 }}>From layout</h1> </div> <div> <Link href="/profile/teacher">navigate to teacher profile</Link> </div> <div> <Link href="/profile/student">navigate to student profile</Link> </div> </div> }
  • 해당 layout.tsx/student/teacher 에 영향을 끼침
 

중첩된 라우팅

app/profile/student/page.tsx

const students = { 1: { name: 'John', age: 10, subjects: ['Math', 'English'], }, }; export default function Page() { const student = students[1]; return ( <React.Fragment> <h1>Student profile</h1> <div>Name: {student.name}</div> <div>Age: {student.age}</div> <div>Studies: {student.subjects.join(', ')}</div> </React.Fragment> ); }

app/profile/teacher/page.tsx

import React from 'react'; const teachers = { 1: { name: 'Mr Smith', age: 30, subjects: ['Math', 'English'], }, }; export default function Page() { const teacher = teachers[1]; return ( <React.Fragment> <h1>Teacher profile</h1> <div>Name: {teacher.name}</div> <div>Age: {teacher.age}</div> <div>Teaches: {teacher.subjects.join(', ')}</div> </React.Fragment> ); }
 

app/layout.tsx → 링크 추가

<Link href="/profile/teacher">navigate to teacher profile</Link> <Link href="/profile/student">navigate to student profile</Link>
  • 중첩된 레이아웃 확인
 
💡
app은 서버에서 렌더링 → 클라이언트 훅 사용불가
 
 

📌 에러 처리 - Errorboundary

app/breaking/page.tsx

'use client'; import { useEffect, useReducer, useState } from 'react'; import styles from './breaking.module.css'; export default function Page() { const [error, setError] = useState(false); useEffect(() => { if (error) throw new Error(); }, [error]); return ( <div className={styles.component}> <div>BREAKING</div> <div> <button onClick={(e) => setError(true)}>break this</button> </div> </div> ); }
  • React Error Boundary와 마찬가지로 이벤트 핸들러에서 발생하는 에러는 잡지 못함 (렌더링 과정 중에 발생하는 에러를 잡음 → 더 찾아보자)
    • notion image

📌 로딩 컴포넌트 - Suspense

페이지 이동 중 로딩 (like Lazy loading)

app/breaking/loading.tsx

export default function Loading() { return <h1>Loading...</h1> }
  • /breaking 로 이동하는 순간 발생하는 컴포넌트의 로딩 시간동안 해당 컴포넌트가 보여짐
 
 

데이터 페칭에서 발생하는 로딩

app/second/page.tsx

async function getData() { const index = Math.floor(Math.random() * 10); const res = await fetch( `https://jsonplaceholder.typicode.com/posts/${index}` ); return res.json(); } const page = async () => { const data = await getData(); return <p>{data.body}</p>; }; export default page;

app/second/loading.tsx

const Loading = () => { return <div>로딩 중</div>; }; export default Loading;

결과

  • [리마인드] app 폴더에 있는 컴포넌트는 기본적으로 “서버 컴포넌트”
  • 마찬가지로 네트워크 요청이 발생하는 위치에 loading파일이 있으면 자동으로 Suspense Fallback 으로 작동
    • notion image

📌 추가 자료

 
chat
Chat으로 물어보세요!
chat-bubble
채팅으로 물어보세요
안녕하세요!
프론트엔드 개발자, 봉승우입니다.
저에 대해 궁금하신 것이 있나요?
너는 어떤 사람이야?
너가 진행한 프로젝트를 간략히 소개해줘
너의 학업은 어때?
가장 어려웠던 프로젝트는 뭐였어?
어떤 특허를 가지고 있어?