NextJS 쉽게 알아보자 -2-

Nanotube·2022년 5월 16일
0

NextJS

목록 보기
2/3

tsconfig.json 셋팅

{
  "compilerOptions": {
    "target": "es2022", // 컴파일 할 ECMAScript 버전 ES6, ES2017, ES2018....
    "lib": ["dom", "dom.iterable", "esnext"], // 컴파일 과정 사용될 라이브러리 파일 목록
    "allowJs": true, // JS 파일 컴파일 허용 설정
    "skipLibCheck": true /**/,
    "baseUrl": "./", //프로젝트 내 모듈이 위치한 기준 디렉토리 설정
    "path": {}, //baseUrl 기준으로 모듈을 불러올 위치 설정
    "strict": true, // use-strict 엄격타입 설정
    "typeRoot": ["./src/types/"], //사용자 설정 타입을 전역에서 사용 가능하도록 루트 폴더로 설정
    "forceConsistentCasingInFileNames": true, //파일 참조시 대소문자 철저히 구별 설정
    "noEmit": true /* 결과 파일을 Emit 여부 */,
    "esModuleInterop": true, // 모든 imports에 대한 namespace 생성을 통해 CommonJS와 ES Modules 간의 상호 운용성이 생기게할 지 여부,
    "module": "esnext", // 모듈을 위한 코드 생성 설정, amd, commonjs, none
    "moduleResolution": "node", // 모듈 해석 방법 설정: 'node' (Node.js) 혹은
    "resolveJsonModule": true, // json파일 Import시 설정
    "isolatedModules": true, // 각 파일을 별도 모듈로 변환
    "jsx": "preserve", //Next는 react대신 preserve로 설정
    "incremental": true // 증분 컴파일 설정 여부
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], // 컴파일에 포함할 파일 목록
  "exclude": ["node_modules"] //컴파일 제외 목록
}

next.config.js 셋팅

이부분은 따로 설명할게 없다.. 공식문서를 확인하는 수밖에..

NextJS config

그래도 기본적인걸 몇가지 알아보자면 다음과 같다.

/** @type {import('next').NextConfig} */

module.exports = async (phase, { defaultConfig }) => ({
  ...defaultConfig,
  /* 렌더링 가능한 페이지 확장 */
  pageExtensions: ['ts', 'tsx', 'mdx', 'md', 'js', 'jsx'],
  /* react Strict Mode */
  reactStrictMode: true,
  /* CSS-In-JS, 스타일드 컴포넌트 사용 시 */
  compiler: {
    styledComponents: true,
  },
  /* env 환경변수 사용시, process.env.customKey */
  env: {
    customKey: 'my-value',
  },
  /* 내장 image Optimization API 대신 외부 이미지 로더를 사용할때 */
  images: {
    loader: 'imgix, akami, cloudinary (택1, default: vercel)',
    path: 'url...',
  },
  distDir: 'build', // 빌드시 .next폴더 대신 build 폴더 생성
  /* 빌드 시 fs모듈 import 에러 문제 해결*/
  /* 1. NextJS 12는 Webpack5사용, fs 모듈 Import 에러때문에 빡칠때 사용 */
  /* 2. 기타 플러그인 필요시 사용 */
  webpack5: true,
  webpack: (config) => {
    config.resolve.fallback = { fs: false };
    config.module.rules.push({
      test: /\.mdx/,
      use: [
        options.defaultLoaders.babel,
        {
          loader: '@mdx-js/loader',
          options: pluginOptions.options,
        },
      ],
    });

    return config;
  },

  devIndicators: {
    buildActivityPosition: 'bottom-right',
  },
});

파일구조

_app.tsx

_app.tsx는 모든 페이지 컴포넌트를 감싸고있는 최상위(root) 공통 컴포넌트이다. 그렇기 때문에 NextJS 앱을 실앻아면 가장 먼저 불러오는 파일이다.

사용 목적

  1. 페이지 전환 시 레이아웃 유지
  2. 페이지 전환 시 상태(state) 유지(redux, recoil...)
  3. 글로벌 CSS 적용
  4. 페이지에 추가적인 데이터 삽입 시

규칙

  1. 페이지 전환 시 레이아웃 유지
  2. 페이지 전환 시 상태(state) 유지(redux, recoil...)
  3. 글로벌 CSS 적용
  4. 페이지에 추가적인 데이터 삽입 시
  • 예시 _app.tsx
import '../styles/globals.css';
import type { AppProps } from 'next/app';

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

export default MyApp;

Custom App, 공통 레이아웃

모든 페이지를 하나의 레이아웃 디자인으로 적용하기 위해선 아래와 같이 작성한다.

  • ./Components/Layout.tsx
import Head from 'next/head';
import { ReactNode } from 'react';

type LayoutProps = {
  children?: ReactNode;
};

const Layout = ({ children }: LayoutProps) => {
  return (
    <div>
      <Head>
        <title>{'공통 레이아웃'}</title>
      </Head>
      <section>
        <header></header>
        <main>
          <div></div>
          {children}
          <div></div>
        </main>
        <footer></footer>
      </section>
    </div>
  );
};

export default Layout;
  • _app.tsx

_app.tsx에서 레이아웃 컴포넌트를 가져와서 감싸주면 된다.

import '../styles/globals.css';
import type { AppProps } from 'next/app';
import Layout from '../components/Layout';

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

export default MyApp;

Custom App, 다중 레이아웃

다중 레이아웃은 About, Post, Contact 등 다양한 페이지에서 서로 다른 디자인의 레이아웃으로 적용하려면, getLayout이라는 기능을 추가해주기만 하면 된다. 공식문서에서 나온 방식으로 작성해준다.

import type { ReactElement, ReactNode } from 'react';
import type { NextPage } from 'next';
import type { AppProps } from 'next/app';

/* 각 페이지별 적용된 레이아웃 입력 및 반환 타입 지정 */
type NextPageWithLayout = NextPage & {
  getLayout?: (page: ReactElement) => ReactNode;
};

/* 각 페이지를 컴포넌트 단위로 받을 커스텀 타입 */
type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout;
};

export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
  /* 페이지 전환시 서로 다른 레이아웃 적용(Nullish Coalescing)  */
  const getLayout = Component.getLayout ?? ((page) => page);

  return getLayout(<Component {...pageProps} />);
}

_document.tsx

_document.tsx 파일을 생성해주면 _app파일 다음으로 실행된다. 이 파일을 굳이 왜 생성해주는지 이유는 아래와 같다.

사용 목적

  1. CSS-in-JS와 같은 라이브러리 지원
  2. 모든 페이지의 Head 속성들을 조작해야할 때(carset, 웹 접근 관련 태그 설정...)
  3. 다양한 폰트를 필요로할 때
  4. Bootstrap, material UI등 글로벌 커스텀 스타일을 적용하려할때

규칙

  1. _Document는 오직 서버(server)에서만 실행되므로 브라우저의 속성인 onClick() 같은 이벤트는 작동이 되지 않는다.
  2. Document Class를 상속받기 때문에, Class 문법으로 작성해야한다.
  3. <HTML>,<HEAD>,<MAIN>,<NextScript>는 필수로 들어가야한다.
  4. <HEAD>를 넣을 떄 모든 페이지에 Head를 적용하므로 반드시 next/documentimport한다.
  5. Head태그 내부의 title태그 같은 페이지별 변동될 만한 속성은 추가하지 않는다.
  • 예시 _document.tsx
import Document, {
  DocumentContext,
  Head,
  Html,
  Main,
  NextScript,
} from 'next/document';

class MyDocument extends Document {
  static async getInitialProps(ctx: DocumentContext) {
    const initialProps = await Document.getInitialProps(ctx);

    return initialProps;
  }
  render() {
    return (
      <Html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default MyDocument;

예전 방식 - Styeled-Components 적용(사용시)

Scss,tailwind,Emotion 등.. 자주 쓰이는 스타일링 라이브러리들이 존재하지만 얘네들은 딱히 _document에서 설정할 것 없이 컨픽 파일(....json)을 생성해주면 되지만, Styled-Component를 사용해서 바로 스타일에 적용하기 위해서는 귀찮은 과정들을 거쳐야 했었다.

  • _Document 소스코드 수정
import Document, {
  Html,
  Head,
  Main,
  NextScript,
  DocumentContext,
} from 'next/document';
import { ServerStyleSheet } from 'styled-components';

class MyDocument extends Document {
  static async getInitialProps(ctx: DocumentContext) {
    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();
    }
  }

  render() {
    return (
      <Html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default MyDocument;
  • .babelrc 추가

NextJS를 제작한 Vercel에선 기본 컴파일로러 해석하기 위해 옵션을 추가하고 있지만, 직접 적용해보니 완벽하게 적용되지는 않았다.

{
  "presets": ["next/babel"],
  "plugins": [
    ["styled-components", { "ssr": true, "displayName": true, "pure": true }]
  ]
}

새로운 방식 - Styeled-Components 적용(사용시)

아직 완벽하지 않다고 설명하지만, 이정도만 설정해줘도 나한텐 충분한것 같다.

next.config.js에서 옵션을 추가하기만 하면된다.

  • next.config.js
  compiler: {
    styledComponents: true,
  },

_error.tsx

처음에 생성되지 않는 이 파일은, 요청시 발생하는 각 상태코드 400 Series, 500 Series등 개발자의 입맛대로 디자인된 에러페이지를 구현하기 위한 파일이다.

각 에러마다 페이지 파일을 생성하는 것 보단, 대응하는 하나의 파일로 통합하면 더 효율 적일 것이다. 그래서 _error.tsx도 각 에러 발생시 하나의 파일로 통합하는 예제가 나와있다.

import WithLayout from 'components/common/Layout/WithLayout';
import { NextPage, NextPageContext } from 'next';

interface Props {
  statusCode?: number;
}

const Error: NextPage<Props> = ({ statusCode }) => {
  return (
    <WithLayout>
      <p>
        {statusCode
          ? `An error ${statusCode} occurred on server`
          : 'An error occurred on client'}
      </p>
    </WithLayout>
  );
};

Error.getInitialProps = ({ res, err }: NextPageContext) => {
  const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
  return { statusCode };
};

export default Error;
profile
나노튜브

0개의 댓글