Next.js 프로젝트 분석

김동현·2022년 3월 7일
1

NextJS

목록 보기
3/12
post-thumbnail

NextJS 프로젝트 생성

NextJS 프로젝트를 생성하기 위해서 터미널에 아래 명령어로 NextJS 프로젝트를 생성합니다.

npx create-next-app@latest

NextJS 프로젝트의 기본 구조

생성된 NextJS 프로젝트의 초기 구조는 아래 그림과 같습니다.

프로젝트에는 기본적으로 styles, pages, public 폴더가 존재합니다.

1. style 폴더

"styles" 폴더에는 스타일 파일(CSS)을 저장합니다.

2. public 폴더

"public 폴더"에는 애플리케이션에서 사용되는 정적 파일들이 존재합니다.

React 프로젝트의 public 폴더에는 index.html 파일이 존재했었지만 NexJS 프로젝트의 public 폴더에는 index.html 파일이 존재하지 않습니다.
이는 NextJS가 pre-rendering으로 모든 페이지에 대한 각각의 HTML파일을 만들기 때문입니다.

❗️public 파일 사용시 주의사항

"public 폴더"에 존재하는 정적 파일들은 빌드시 서버 파일 시스템의 루트 경로에 존재하기 때문에 런타임에는 루트 경로("/")를 통해서 접근해야 합니다.

public 폴더에 저장된 정적 파일에 접근할 때는 파일 확장자도 모두 작성해주어야 합니다.

예를 들어, 만약 프로젝트 내 "/public/assets/image.png" 경로에 존재하는 이미지를 런타임에 접근할 때는 "/image.png"로 서버의 파일 시스템 루트 경로로 접근해야 하며, 파일 확장자도 모두 작성해주어야 합니다.

> public
    > assets
        - image.png
        

<img src="/assets/image.png" alt="image" />

서버의 파일 시스템 루트 경로로 접근하기 때문에 "public 폴더" 내 파일들은 "pages 폴더" 내 페이지 컴포넌트 파일 이름과 겹치지 않도록 해야 합니다. 그렇지 않으면 충돌이 발생하게 됩니다.

참고로 public 폴더에 존재하는 정적 파일들은 빌드시 public 폴더에 존재했던 파일들만 서버측에 존재하게 됩니다. 빌드 이후에 런타임에 추가된 정적 파일들에 대해서는 서버측에 존재하지 않기 때문에 접근 불가능합니다.

3. pages 폴더

"pages 폴더" 내에는 페이지를 나타내는 "페이지 컴포넌트 파일"들을 저장합니다.

각 페이지 컴포넌트 파일에서는 반드시 하나의 페이지 컴포넌트를 export default 해주어야 합니다. export default함으로써 NextJS가 각 페이지 컴포넌트들을 통해 HTML 문서를 pre-rendering하여 생성할 수 있습니다.

NextJS는 "pages 폴더 구조"를 기반으로 "라우팅 기능"을 제공합니다. 이때 추가적인 패키지나 코드를 필요로 하지 않습니다.

"pages 폴더" 내 존재하는 파일명이나 폴더명은 pre-rendering되어 생성될 HTML 문서 이름이 됩니다. 즉, 파일명과 폴더명이 URL의 경로값을 의미하게 됩니다.

pre-rendering되어 생성된 HTML 문서들은 빌드시 서버의 파일 시스템 루트 경로에 존재하므로 페이지 경로를 작성할 때는 서버의 파일 시스템 루트 경로를 통해 각 페이지에 접근할 수 있습니다.

참고로 pre-rendering된 HTML 문서는 서버의 파일 시스템 루트 경로에 존재하기 때문에 "public 폴더"에 존재하는 정적 파일명과 중복된다면 충돌을 발생하기 때문에 주의해야 합니다.

pages 폴더 구조

> nextjs 프로젝트
  > pages 폴더
      - index.js  -> "index.html" 문서 생성, "/" 요청시
      - apple.js  -> "apple.html" 문서 생성, "/apple" 요청시
      - banana.js -> "banana.html" 문서 생성, "/banana" 요청시
      
      > news 폴더
          - index.js  -> "news.html" 문서 생성, "/news" 요청시
          
      > user 폴더
          - index.js  -> "user.html" 문서 생성, "/user" 요청시
          - name.js   -> "user/name.html" 문서 생성, "/user/name" 요청시
          ,,,

위 프로젝트 구조처럼 "pages 폴더" 내 작성된 파일명과 폴더명으로 동일한 이름의 HTML 문서를 생성(pre-rendering)하게 됩니다. 즉, 파일명과 폴더명이 URL의 경로값으로 사용됩니다.

참고로 "pages 폴더" 내 "index"라는 파일명은 해당 파일이 속한 폴더의 "루트 페이지", "루트 경로"를 의미합니다.

HTML 문서의 Content

브라우저에 로드된 페이지의 소스 코드를 살펴보면 HTML 문서는 콘텐츠를 포함한 HTML 문서가 로드된 것을 알 수 있습니다.

일반적인 React App의 경우 빈 HTML을 로드하지만 NextJS의 경우에는 빈 HTML 문서가 아닌 "콘텐츠를 갖는 HTML 파일"을 브라우저에게 응답으로 전달해줍니다. 이는 SEO 측면에서 기존 React보다 좋은 효과를 볼 수 있습니다.

이후 서버가 전달한 자바스크립트 파일 로드가 완료되면 전달받은 자바스크립트 파일을 실행하여 HTML 문서와 연결되며 자바스크립트 코드로 페이지를 조작하기 때문에 SPA이자 CSR로서 동작하게 됩니다.

// pages/index.js
import React from 'react';

const HomePage = () => {
    return <h1>Home Page!</h1>;
};

export default HomePage;

Next 프로젝트의 Pages 폴더에 위 코드와 같은 index.js 파일이 존재한다고 할 때 실제 페이지에 렌더링되는 HTML 코드는 아래 그림과 같습니다.

<h1>Home Page!</h1>가 HTML 파일에 포함되어 렌더링된 것을 확인할 수 있습니다. 이는 더이상 빈 HTML 파일이 아닌 콘텐츠가 포함된 HTML 파일을 렌더링하는 것을 확인할 수 있습니다.

_app.js 파일 역할

pages 폴더 바로 아래에 존재하는 "_app.js"의 MyApp 컴포넌트는 루트 컴포넌트 역할을 합니다.

pre-rendering을 위한 HTML 문서를 만들 때 가장 먼저 실행되는 파일이 바로 _app.js입니다.

_app.js 파일을 통해 pre-rendering될 HTML 파일에 포함될 콘텐츠를 생성해주는 파일입니다. 이때 페이지 컴포넌트의 첫 실행 결과를 콘텐츠로서 생성하게 됩니다.

즉, 모든 페이지 컴포넌트들은 NextJS가 _app.js 파일을 통해 실행하여 HTML 문서의 콘텐츠를 생성하게 됩니다.

// pages/_app.js
import '../styles/globals.css';  // -> 모든 페이지에 적용될 전역 스타일 적용 가능

function MyApp({ Component, pageProps }) {
    // Component는 렌더링될 페이지 컴포넌트
    // pagePorps는 페이지 컴포넌트에게 props로 전달될 데이터
    
    // 반환문에 작성한 페이지 컴포넌트 자체는 생성될 HTML 문서의 body 영역에 포함될 콘텐츠
    return <Component {...pageProps} />;
}

export default MyApp;  // -> 생성될 HTML 문서의 body 영역 콘텐츠

MyApp 컴포넌트의 "props.Component"에는 페이지 컴포넌트가 전달됩니다. 전달받은 페이지 컴포넌트를 실행하여 반환된 결과를 콘텐츠로서 사용하게 됩니다.

_app.js 파일 실행 과정

아래는 페이지 컴포넌트가 _app.js에게 전달되어 HTML 문서의 콘텐츠를 생성하는 과정입니다.

// pages/index.js
function HomePage() {
    return <h1>This is HomePage!</h1>;
};

export default HomePage;
  1. 서버는 pages/index.js에 대응하는 HTML 문서를 만들고자 할 때 가장 먼저 pages폴더 내 _app.js 파일을 실행합니다.

  2. _app.js 파일의 MyApp 컴포넌트는 props로 Component와 pagesProps을 전달받습니다. "props.Component"는 pages/index.js가 export한 HomePage 컴포넌트 함수가 전달됩니다(pagesProps prop은 현재 사용되지 않습니다).

pages 폴더 내 모든 페이지 컴포넌트들은 _app.js 파일을 거쳐 HTML 문서의 콘텐츠를 생성하기 때문에 모든 페이지에 적용될 레이아웃이나 스타일들을 적용할 수 있습니다.

_app.js 파일의 기능

아래는 _app.js 파일을 통해 모든 페이지에 적용될 공통 스타일, 공통 레이아웃을 적용하는 방법입니다.

  1. 모든 페이지에서 적용될 "공통적인 레이아웃"
// pages/_app.js
import Layout from '../components/layout/Layout';

functino MyApp({ Component, pagesProps }) {
    return (
        <Layout>  // -> 모든 페이지에 공통적으로 적용될 Layout(공통 레이아웃)
            <Component {...pagesProps} />
        </Layout>
    );
};

export default MyApp;
  1. 모든 페이지에서 "공통적으로 사용될 스타일"
// pages/_app.js
import '../styles/globals.css'; // -> 모든 페이지에 적용될 CSS 파일(공통 스타일)

functino MyApp({ Component, pagesProps }) {
    return <Component {...pagesProps} />;
};

export default MyApp;

즉, 모든 페이지에서 공통적으로 적용될 것들을 _app.js 파일에서 작업이 가능합니다. 이렇게 적용한 공통 레이아웃이나 컴포넌트, 공통 스타일 등은 다른 URL의 경로로 라우팅이 되더라도 계속해서 유지가 됩니다.

_document.js 파일 역할

서버는 _app.js를 실행한 직후 _document.js를 실행합니다. _document.js는 Pre-rendering을 위한 "실질적은 HTML 문서를 생성"하는 역할을 합니다.

즉, 서버는 모든 페이지 컴포넌트에 대해서 _app.js를 먼저 실행하여 HTML 문서의 body 영역에 포함될 콘텐츠를 생성하고, 이후에 _document.js를 실행하여 pre-rendering을 위한 실질적인 HTML 문서를 생성합니다.

// _document.js  // -> 서버에서 _app.js를 실행한 뒤에 실행할 파일
import Document, { Html, Head, Main, NextScript } from 'next/document'

class MyDocument extends Document {

    render() {
        return (
            <Html>
                <Head />
                <body>
                    // Main 컴포넌트는 _app.js가 export default한 MyApp 컴포넌트를 의미(실제 렌더링될 콘텐츠)
                    <Main />
          			// NextScript 컴포넌트는 페이지의 스크립트 요소들을 처리하고 페이지 로딩 성능을 향상시키는 역할
                    <NextScript />
                </body>
            </Html>
        );
     }
}

export default MyDoucment;

_app.js에서 export default한 MyApp 컴포넌트가 _document.js의 "Main 컴포넌트 위치"에 렌더링됩니다.

참고로 pages 폴더 내 _document.js를 생략할 수 있으며, 생략한 경우 NextJS가 기본값을 사용하게 됩니다. 단, _app.js는 생략할 수 없습니다.

_document.js 파일 기능

_document.js는 NextJS에서 사용될 "마크업 보강"을 위해 사용합니다. HTML 문서의 head 태그에 작성될 메타 데이터(title, meta, link, style,,)들을 _document.js의 Head 컴포넌트 내부에 작성하면 생성되는 모든 HTML 문서의 head 태그에 그대로 작성되어 생성이 됩니다.

즉, _document.js는 공통적으로 활용할 head(Ex. 메타 태그)나 body 태그 안에 들어갈 내용들을 커스텀할때 활용합니다.

// _document.js  // -> 서버에서 _app.js를 실행한 뒤에 실행할 파일
import Document, { Html, Head, Main, NextScript } from 'next/document'

class MyDocument extends Document {

    render() {
        return (
            <Html>
                <Head>
                    // 모든 페이지에 적용될 메타 데이터 설정
                    <title>페이지 제목</title>
                    <meta charset="UTF-8" />
                    <meta name="author" content="페이지 제작자" />
                    <meta name="description" content="페이지 설명" />
                    <meta name="viewport" content="widht=device-width, initial-scale= 1.0" />
                    ,,,
                </Head>
                <body>
                    <Main />
                    <NextScript />
                </body>
            </Html>
        );
    }
};

export default MyDocument;

Head 컴포넌트에 작성한 모든 태그들은 생성될 모든 HTML 문서의 head 영역에 포함됩니다. 만약 각 페이지마다 개별적으로 포함될 메타데이터들은 다른 방식으로 작성해야합니다. 이는 추후에 다루겠습니다.

profile
Frontend Dev

0개의 댓글