Front/Next.js

Next.js 기초 및 설정

oodada 2024. 5. 5. 01:31

Next.js 기초 및 설정

1. Next.js 란?

Next.js는 React 기반의 웹 프레임워크로, 서버 사이드 렌더링정적 파일 생성 등을 지원하여 개발자가 보다 효율적으로 웹 애플리케이션을 구축할 수 있도록 도와줍니다. 이 프레임워크는 React의 기능을 확장하여 더욱 강력한 기능을 제공하며, SSR(Server-Side Rendering), CSR(Client-Side Rendering), Static Site Generation 등의 다양한 렌더링 방식을 지원합니다.

2. Next.js 특징

  • 서버 사이드 렌더링(SSR): Next.js는 서버 사이드 렌더링을 지원하여 초기 로딩 시에 페이지를 서버에서 렌더링하여 사용자에게 보여줍니다. 이를 통해 SEO(Search Engine Optimization)를 개선하고, 초기 로딩 속도를 향상시킵니다.
  • 정적 파일 생성(Static Site Generation): Next.js는 페이지의 정적 HTML 파일을 빌드할 수 있는 정적 사이트 생성 기능을 제공합니다. 이를 통해 서버 부하를 줄이고, 사용자 경험을 개선할 수 있습니다.
  • 동적 라우팅(Dynamic Routing): Next.js는 동적 라우팅을 지원하여 페이지 경로를 동적으로 생성할 수 있습니다. 이를 통해 유연한 라우팅 구조를 구축할 수 있습니다.
  • API Routes: Next.js는 API Routes를 제공하여 서버리스(serverless) 함수를 생성할 수 있습니다. 이를 통해 서버리스 아키텍처를 쉽게 구현할 수 있습니다.
  • 개발 환경 설정 간소화: Next.js는 기본적으로 웹팩(Webpack)과 바벨(Babel)을 내장하고 있어 개발 환경 설정을 간소화합니다. 또한, 기본적으로 코드 스플리팅(Code Splitting), CSS 모듈화(CSS Modules) 등의 기능을 지원하여 개발자가 보다 효율적으로 개발할 수 있도록 돕습니다.

** 코드 스플리팅(Code Splitting)이란? **

코드 스플리팅(Code Splitting)은 웹 애플리케이션의 자바스크립트 코드를 여러 개의 번들로 분리하는 기술을 말합니다. 이를 통해 사용자가 방문한 페이지에 필요한 코드만 로드하여 초기 로딩 속도를 향상시킬 수 있습니다.

** CSS 모듈화(CSS Modules)이란? **

CSS 모듈화(CSS Modules)는 CSS 파일을 모듈화하여 컴포넌트 단위로 스타일을 적용할 수 있는 기술을 말합니다. 이를 통해 컴포넌트 간의 스타일 충돌을 방지하고, 코드의 일관성을 유지할 수 있습니다.

3. Next.js(Typesrcipt) 시작하기

Next.js 프로젝트를 Typesrcipt 버전으로 시작해보겠습니다. Typesrcipt는 정적 타입을 지원하여 코드의 안정성을 높이고, 개발 생산성을 향상시킬 수 있는 언어입니다. Next.js와 Typesrcipt를 함께 사용하면, 더욱 안정적이고 효율적인 웹 애플리케이션을 구축할 수 있습니다.

next-blog-app 디렉토리에 터미널을 열고 다음과 같이 명령어를 실행합니다.

  1. 프로젝트 생성: npx create-next-app ./ 명령어를 사용하여 Next.js 프로젝트를 생성합니다.
  2. 프로젝트 실행: npm run dev 명령어를 사용하여 Next.js 프로젝트를 실행합니다.
  3. 페이지 생성: pages 디렉토리에 페이지를 생성하여 Next.js 애플리케이션을 구축합니다.
npx create-next-app ./
cd next-blog-app
npm run dev # or yarn dev

위와 같은 방법을 사용하여 Next.js 프로젝트를 생성 시 질문에 따라 Typesrcipt 를 사용할 것인지, ESLint 와 Tailwind 를 사용할 것인지 등을 선택할 수 있습니다.

  • Typesrcipt: Typesrcipt 를 사용할 것인지 선택합니다.
  • ESLint: ESLint 를 사용하여 코드 스타일을 검사할 것인지 선택합니다. (yes)
  • Tailwind CSS: Tailwind CSS 를 사용하여 스타일을 작성할 것인지 선택합니다. (yes)
  • src 디렉토리에 컴포넌트를 생성하여 사용할 것인지 선택합니다. (yes)
  • App Router 를 사용하여 라우팅을 구현할 것인지 선택합니다. (yes)
  • import alias 를 사용하여 모듈을 더 쉽게 불러올 것인지 선택합니다. (yes)

- src/ 디렉토리 사용 여부

src/ 디렉토리를 사용할지 여부는 프로젝트의 구조와 개발 환경에 따라 다를 수 있지만 사용하는 것을 권장합니다.

  • 프로젝트 구조의 명확성: src/ 디렉토리를 사용하면 프로젝트의 주요 소스 코드가 담긴 디렉토리를 명확하게 구분할 수 있습니다. 이는 프로젝트의 구조를 더 쉽게 이해하고 유지보수하기 쉽게 만듭니다.
  • 코드의 일관성: src/ 디렉토리를 사용하면 프로젝트의 모든 소스 코드를 한 곳에 모아둘 수 있습니다. 이를 통해 코드의 일관성을 유지하고 관리하기 쉽게 만들 수 있습니다.
  • 프로젝트 확장성: src/ 디렉토리를 사용하면 향후 프로젝트를 확장할 때 더 유연하게 구조를 조정할 수 있습니다. 새로운 기능이나 모듈을 추가할 때 기존의 구조를 유지하면서 쉽게 통합할 수 있습니다.

4. Next.js 프로젝트 구조

next-blog-app/
├── src/
│   ├── pages/        # 라우팅과 관련된 페이지 컴포넌트
│   ├── components/   # 재사용 가능한 UI 컴포넌트
│   ├── styles/       # 스타일 관련 파일
│   └── utils/        # 유틸리티 함수
├── public/           # 정적 파일들이 저장되는 디렉토리
├── .next/            # Next.js 빌드 시 생성되는 파일들이 저장되는
├── node_modules/     # 프로젝트에 설치된 모듈들이 저장되는 디렉토리
├── package.json      # 프로젝트 정보와 의존성 모듈들이 정의된 파일
└── README.md         # 프로젝트의 설명이 작성된 파일

Next.js 프로젝트 구조화

next.js Routing

Next.js 페이지는 pages 디렉토리에 생성하여 사용할 수 있습니다. 페이지는 파일 이름에 따라 경로가 자동으로 생성되며, 동적 라우팅을 사용하여 동적 경로를 생성할 수 있습니다.

pages 디렉토리의 파일명은 소문자로 작성하며, 파일명에 따라 경로가 자동으로 생성됩니다.

next-blog-app/
├── src/
│   ├── pages/
│   │   ├── index.tsx
│   │   ├── post/
│   │   │   └── index.tsx
│   │   │   └── [id].tsx
│   │   └── about.tsx
└── ...

pages 디렉토리에 페이지를 생성하고, 파일명에 따라 경로가 자동으로 생성됩니다. index.tsx 파일은 / 경로로 접근할 수 있으며, [id].tsx 파일은 /post/:id 경로로 접근할 수 있습니다.

- 페이지 라우팅

// src/pages/index.tsx

export default function Home() {
    return (
        <div>
            <h1>Home</h1>
        </div>
    )
}
// src/pages/post/index.tsx

const Post = () => {
    return (
        <div>
            <h1>Post</h1>
        </div>
    )
}

export default Post
// src/pages/_app.tsx
import { AppProps } from 'next/app'

function MyApp({ Component, pageProps }: AppProps) {
    return <Component {...pageProps} />
}

export default MyApp

서버를 실행하고 http://localhost:3000 경로로 접속하면, Home 페이지가 렌더링됩니다. http://localhost:3000/post 경로로 접속하면, Post 페이지가 렌더링됩니다.

pages 디렉토리에 페이지를 생성하고, React 컴포넌트로 구성할 수 있습니다. 페이지는 export default 키워드를 사용하여 내보내며, pages 디렉토리의 파일명에 따라 경로가 자동으로 생성됩니다.

main 페이지가 렌더링되지 않을 경우, app 폴더의 page.tsx 을 삭제하면 해결됩니다.

Next.js 레이아웃 구성

Next.js 레이아웃은 components 디렉토리에 컴포넌트를 생성하여 사용할 수 있습니다. 레이아웃은 페이지의 공통 요소를 구성할 때 사용하며, outlet 컴포넌트를 사용하여 페이지 컨텐츠를 렌더링할 수 있습니다.

next-blog-app/
├── components/
│   └── layout/
│       └── Layout.tsx
│       └── Header.tsx
│       └── Footer.tsx

Next.js 레이아웃 적용

Next.js 레이아웃은 페이지에서 Layout 컴포넌트를 사용하여 적용할 수 있습니다. 페이지에서 Layout 컴포넌트를 사용하면, 페이지의 공통 요소를 효율적으로 구성할 수 있습니다.

// src/components/layout/Layout.tsx
import Link from 'next/link'
import React from 'react'

const Header = () => {
    return (
        <header>
            <h1>
                <Link href="/">logo</Link>
            </h1>

            <nav>
                <ul>
                    <li>
                        <Link href="/about">About</Link>
                    </li>
                    <li>
                        <Link href="/post">Post</Link>
                    </li>
                </ul>
            </nav>
        </header>
    )
}

export default Header
// src/components/layout/Header.tsx
export default function Header() {
    return (
        <header>
            <h1>Header</h1>
        </header>
    )
}
// src/components/layout/Footer.tsx
export default function Footer() {
    return (
        <footer>
            <h1>Footer</h1>
        </footer>
    )
}

Layout 컴포넌트를 생성하여 레이아웃을 구성하고, Header 컴포넌트와 Footer 컴포넌트를 생성하여 레이아웃에 적용할 수 있습니다. 이렇게 구성한 레이아웃은 React Router를 사용하여 페이지 간의 이동 시 공통 요소를 효율적으로 구성할 수 있습니다.

// src/pages/_app.tsx
import { AppProps } from 'next/app' // AppProps 타입을 불러옵니다.
import Layout from '@/components/layout/Layout'

function MyApp({ Component, pageProps }: AppProps) {
    return (
        <Layout>
            <Component {...pageProps} />
        </Layout>
    )
}

export default MyApp

_app.tsx 파일을 생성하여 레이아웃을 적용할 수 있습니다. _app.tsx 파일은 Next.js 애플리케이션의 전역 레이아웃을 설정할 때 사용되며, Layout 컴포넌트를 사용하여 페이지의 공통 요소를 구성할 수 있습니다.

// src/pages/_document.tsx
import Document, { Html, Head, Main, Nextsrcipt } from 'next/document'

class MyDocument extends Document {
    render() {
        return (
            <Html>
                <Head>
                    <link rel="icon" href="/favicon.ico" />
                </Head>
                <body>
                    <Main />
                    <Nextsrcipt />
                </body>
            </Html>
        )
    }
}

export default MyDocument

_document.tsx 파일을 생성하여 HTML 문서의 레이아웃을 설정할 수 있습니다. _document.tsx 파일은 모든 페이지에 공통으로 적용되는 HTML 문서의 레이아웃을 설정할 때 사용되며, Head 컴포넌트를 사용하여 메타 태그나 스타일 시트를 적용할 수 있습니다.

Next.js 페이지 링크

Next.js 페이지 간의 이동은 Link 컴포넌트를 사용하여 구현할 수 있습니다. Link 컴포넌트는 페이지 간의 이동을 자동으로 처리하며, 페이지를 미리 로드하여 사용자 경험을 향상시킵니다.

// pages/home/index.tsx

import Link from 'next/link'

export default const Home = () => {
    return (
        <div>
            <h1>Home</h1>
            <Link href="/post">
                Go to Post
            </Link>
        </div>
    )
}

Next.js 정적 파일 제공

Next.js는 public 디렉토리를 사용하여 정적 파일을 제공할 수 있습니다. public 디렉토리에 저장된 파일은 / 경로로 접근할 수 있으며, 이미지, CSS 파일, JS 파일 등을 저장할 수 있습니다.

next-blog-app/
├── public/
│   ├── images/
│   │   └── logo.png
│   ├── styles/
│   │   └── main.css

public 디렉토리에 images 디렉토리와 styles 디렉토리를 생성하고, 이미지 파일과 CSS 파일을 저장할 수 있습니다. 이렇게 저장된 파일은 /images/logo.png, /styles/main.css 경로로 접근할 수 있습니다.

Typesrcipt Type 정의

Next.js 프로젝트에서 타입을 정의할 때는 types 디렉토리를 사용하여 타입을 정의할 수 있습니다. types 디렉토리에 타입을 정의하고, 필요한 파일에서 타입을 불러와 사용할 수 있습니다.

next-blog-app/
src/
├── types/
│   └── types.ts       # 타입 정의를 모아둔 디렉토리와 파일
|   └── utils.ts       # 유틸리티 함수를 모아둔 디렉토리와 파일
├── data/
│   └── posts.ts       # 데이터를 모아둔 디렉토리와 파일
  • Typesrcipt를 사용하여 타입을 정의
// src/types/types.ts
// Post 타입을 정의합니다.
export interface Post {
    id: number
    title: string
    content: string
    author: string
    date: string // 날짜는 ISO 형식 (예: "2024-01-01")으로 나타냅니다.
    tags?: string[] // 태그는 선택적으로 배열 형태로 나타냅니다.
    // undefined일 수 있기 때문에 옵셔널 체이닝 연산자(?.)를 사용합니다.
}
  • Post 타입을 사용하여 데이터를 정의
// src/data/posts.ts
import { Post } from '@/types/types'

// Post 타입을 사용하여 데이터를 정의합니다.
// posts 배열에 Post 타입의 데이터를 저장합니다.
export const posts: Post[] = [
    {
        id: 1,
        title: 'Javasrcipt 클로저 이해하기',
        content: `
        클로저는 자바스크립트에서 중요한 개념입니다. 클로저는 함수가 선언된 시점의 스코프(변수 범위)를 기억하여, 나중에 그 함수가 호출될 때 해당 스코프에 접근할 수 있게 합니다. 
        예를 들어, 내부 함수가 외부 함수의 변수를 기억하고 있는 경우를 생각해 봅시다. 클로저를 잘 이해하면, 고급 자바스크립트 프로그래밍 기법을 쉽게 다룰 수 있습니다. 이 글에서는 클로저의 기초 개념과, 클로저를 활용한 실전 예제를 알아보겠습니다.
        `,
        author: '홍길동',
        date: '2024-05-10',
        tags: ['Javasrcipt', '클로저', '프로그래밍'],
    },
    {
        id: 2,
        title: 'CSS Grid 레이아웃 가이드',
        content: `
        CSS Grid 레이아웃은 반응형 디자인을 구현하기 위한 강력한 도구입니다. 그리드 시스템을 사용하면 여러 개의 행과 열을 활용하여 복잡한 레이아웃을 손쉽게 구성할 수 있습니다. 
        이 가이드에서는 Grid의 기본 개념을 소개하고, 실제 프로젝트에서 그리드 레이아웃을 활용하는 방법을 단계별로 설명합니다. 그리드 아이템 간의 간격 조절부터 다양한 행과 열의 크기 조절에 이르기까지, 이 글을 통해 여러분은 더욱 유연한 웹 디자인을 만들 수 있을 것입니다.
        `,
        author: '이영희',
        date: '2024-05-08',
        tags: ['CSS', '웹 디자인', '그리드 레이아웃'],
    },
    {
        id: 3,
        title: 'Python 데이터 클래스 활용하기',
        content: `
        데이터 클래스는 파이썬에서 클래스 구조를 간단하게 정의할 수 있는 방법을 제공합니다. 일반 클래스를 사용할 때보다 코드가 훨씬 간결해지며, 기본적인 기능(예: __init__, __repr__)도 자동으로 제공됩니다.
        이 글에서는 데이터 클래스의 사용법과 주요 기능을 살펴보고, 이를 통해 파이썬 코드의 효율성을 높이는 방법을 알아보겠습니다. 또한, 데이터 클래스의 커스터마이징 방법과 기존 클래스와의 비교를 통해 다양한 활용 사례를 소개합니다.
        `,
        author: '김철수',
        date: '2024-05-05',
        tags: ['Python', '데이터 클래스', '프로그래밍'],
    },
]

Components 구성

next-blog-app/
├── src/
├── ├── components/
│   ├── ├── post/
│   │   │   ├── PostList.tsx
│   │   │   ├── PostItem.tsx
│   │   │   ├── PostDetail.tsx
  • PostItem 컴포넌트를 생성하여 Post 데이터를 렌더링
// components/post/PostItem.tsx
// Type-Only Import 문법을 사용하여 타입만 불러옵니다.
import type { Post } from '@/type/types'
import Link from 'next/link' // Link 컴포넌트를 임포트합니다.

// PostProps 타입을 정의합니다.
interface PostProps {
    post: Post
}

const PostItem = ({ post }: PostProps) => {
    return (
        <div>
            {/* 제목을 Link 컴포넌트로 감싸고 href에 동적 경로를 지정합니다. */}
            <h2>
                <Link href={`/post/${post.id}`}>{post.title}</Link>
            </h2>
            <p style={{ whiteSpace: 'nowrap', width: '80%', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                {post.content}
            </p>
            <p>{post.date}</p>
        </div>
    )
}

export default PostItem
// components/Post/PostList.tsx
import React from 'react'
import { posts } from '@/data/posts'
import PostItem from '@/components/post/PostItem'

const PostList = () => {
    return (
        <div>
            {posts.map((post) => (
                <PostItem key={post.id} post={post} />
            ))}
        </div>
    )
}

export default PostList

- 페이지에 데이터 렌더링

// pages/post/index.tsx
import PostList from '@/components/post/PostList'

const PostPage = () => {
    return (
        <div>
            <h1>Post</h1>
            <PostList />
        </div>
    )
}

export default PostPage

Post 컴포넌트를 사용하여 데이터를 렌더링할 수 있습니다. Post 컴포넌트에 Post 타입의 데이터를 전달하고, posts 배열을 사용하여 데이터를 렌더링합니다.

- 페이지에 동적 라우팅 적용

// components/post/PostDetail.tsx
import type { Post } from '@/type/types'
import Link from 'next/link' // Link 컴포넌트를 임포트합니다.

// PostProps 타입을 정의합니다.
interface PostProps {
    post: Post
}

const PostDetail = ({ post }: PostProps) => {
    return (
        <div>
            <h2>{post.title}</h2>
            <p>{post.content}</p>
            <p>{post.author}</p>
            <p>{post.date}</p>
            {/* && 연산자를 사용하여 tags가 존재하는 경우에만 출력합니다. */}
            <ul>{post.tags && post.tags.map((tag, index) => <li key={index}>{tag}</li>)}</ul>
        </div>
    )
}

export default PostDetail
// pages/post/[id].tsx
import { GetStaticProps, GetStaticPaths } from 'next'
import { posts } from '@/data/posts' // 가정한 경로
import PostDetail from '@/components/post/PostDetail'

// PostType 타입을 정의합니다.
interface PostType {
    id: number
    title: string
    content: string
    author: string
    date: string
    tags?: string[]
}

// PostType 타입을 사용하여 PostProps 타입을 정의합니다.
interface PostProps {
    post: PostType
}

// getStaticPaths 함수를 사용하여 동적 경로를 생성합니다.
// async 함수를 사용하여 비동기 처리를 수행합니다.
export const getStaticPaths: GetStaticPaths = async () => {
    // posts 배열의 id 값을 사용하여 동적 경로를 생성합니다.
    const paths = posts.map((post) => ({
        params: { id: post.id.toString() },
        // id 값을 문자열로 변환하여 params에 저장합니다.
        // 예: { params: { id: '1' } }
    }))

    return {
        paths, // 생성된 동적 경로를 반환합니다.
        fallback: false, // fallback을 false로 설정하여 없는 페이지는 404 페이지를 반환합니다.
    }
}

// getStaticProps 함수를 사용하여 각 동적 경로에 필요한 데이터를 전달합니다.
// async 함수를 사용하여 비동기 처리를 수행합니다.
export const getStaticProps: GetStaticProps<PostProps, { id: string }> = async ({ params }) => {
    // params.id 값을 사용하여 해당 포스트를 찾습니다.
    const post = posts.find((post) => post.id === Number(params?.id))
    // params.id는 문자열이므로 Number로 변환합니다.
    // params가 undefined인 경우를 대비하여 옵셔널 체이닝 연산자(?.)를 사용해 에러를 방지합니다.

    if (!post) {
        return { notFound: true } // 포스트가 없는 경우 404 페이지를 반환
    }

    return {
        props: {
            post,
        },
    }
}

// Post 컴포넌트를 정의합니다.
const Post: React.FC<PostProps> = ({ post }) => {
    return (
        <div>
            <h1>Post</h1>
            <PostDetail post={post} />
        </div>
    )
}

export default Post

Tailwind CSS 적용

next-blog-app/
src/
├── styles/
│   ├── globals.css
│   ├── Home.module.css
│   ├── Post.module.css
/* styles/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* styles/Home.module.css */
.container {
    max-width: 960px;
    margin: 0 auto;
    padding: 0 1rem;
}

.title {
    font-size: 2rem;
    font-weight: bold;
    color: #333;
}

/* styles/Post.module.css */
.post {
    margin-bottom: 2rem;
    border-bottom: 1px solid #ddd;
}

.postTitle {
    font-size: 1.5rem;
    font-weight: bold;
    color: #333;
}

.postContent {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    width: 80%;
    margin-top: 1rem;
    color: #666;
}

.postAuthor {
    margin-top: 0.5rem;
    color: #999;
}

.postDate {
    margin-top: 0.5rem;
    color: #999;
}

.postTags {
    margin-top: 0.5rem;
    color: #999;
}

컴포넌트에 스타일 적용

// components/Post/Post.tsx
import type { Post } from '@/type/types'
import Link from 'next/link'
import styles from '@/styles/Post.module.css'

// PostProps 타입을 정의합니다.
interface PostProps {
    post: Post
}

const PostItem = ({ post }: PostProps) => {
    return (
        <div className={styles.post}>
            {/* 제목을 Link 컴포넌트로 감싸고 href에 동적 경로를 지정합니다. */}
            <h2>
                <Link href={`/post/${post.id}`}>
                    <span className={styles.postTitle}>{post.title}</span>
                </Link>
            </h2>
            <p className={styles.postContent}>{post.content}</p>
            <p className={styles.postDate}>{post.date}</p>
        </div>
    )
}

export default PostItem

Post 컴포넌트에 Post.module.css 파일을 적용하여 스타일을 적용할 수 있습니다. Post.module.css 파일을 사용하여 컴포넌트에 스타일을 적용하고, styles 객체를 사용하여 클래스 이름을 지정할 수 있습니다.

Next.js 빌드 및 실행

next-blog-app/
├── .next/
└── ...
  • Next.js 프로젝트를 빌드하고 실행하는 방법
# Next.js 프로젝트 빌드
npm run build # or yarn build

# Next.js 프로젝트 실행
npm run start # or yarn start

npm run build 명령어를 사용하여 Next.js 프로젝트를 빌드하고, npm run start 명령어를 사용하여 Next.js 프로젝트를 실행할 수 있습니다. 빌드된 파일은 .next 디렉토리에 저장되며, 실행 시 .next 디렉토리의 파일을 사용하여 Next.js 애플리케이션을 실행합니다.

Next.js 프로젝트 배포

Next.js 프로젝트를 배포할 때는 Vercel, Netlify, AWS, Heroku 등의 클라우드 서비스를 사용하여 배포할 수 있습니다. 이 중에서 Vercel은 Next.js를 만든 회사이며, Next.js 프로젝트를 쉽게 배포할 수 있는 플랫폼을 제공합니다.

  • Vercel: Next.js를 만든 회사이며, Next.js 프로젝트를 쉽게 배포할 수 있는 플랫폼을 제공합니다.
  • Netlify: 정적 사이트를 무료로 호스팅할 수 있는 플랫폼을 제공합니다.
  • AWS: 클라우드 서비스를 제공하는 아마존 웹 서비스입니다.
  • Heroku: 클라우드 플랫폼을 제공하는 회사로, 다양한 언어와 프레임워크를 지원합니다.

'Front > Next.js' 카테고리의 다른 글

firebase를 이용한 Next.js 블로그 앱 만들기  (0) 2024.05.05
티스토리 친구하기