동기

구글에 검색했을때 나오는 프로젝트를 만들기 위해 SSR(서버 사이드 렌더링)을 지원하는 Next.js를 도입해서 구현해보고자 한다.

초기 로딩은 CSR(클라이언트 사이드 렌더링)보다 빠르지만 나머지 데이터를 요청하기 때문에 완전한 로딩까지 느리다는 단점이 있다. 

 

SSR의 2가지 종류

 - Pre-rendering :검색엔진을 식별해서 검색엔진 일때 백엔드 서버에서 요청한 데이터를 전달. 사용자가 접근할땐 기존 리액트 방식으로 렌더링

 - Server Side Rendering : 처음 페이지 방문만 html을 만들어서 주고 다른 페이지는 리액트의 CSR 방식으로 동작 

 

 

 

 

초기 세팅

 

설치

  npx create-next-app --typescript [폴더 이름] //생략 시 현재 디렉토리에 설치
  
  yarn create next-app --typescript [폴더 이름] //생략 시 현재 디렉토리에 설치

 

절대 경로

tsconfig.json
{
    //...
    "baseUrl": ".",
    //...
    "exclude": [
        "node_modules"
    ],
    "include": [
        "next-env.d.ts",
        "**/*.ts",
        "**/*.tsx"
    ]
}

 

 Eslint, prettier 설치

               prettier 
               eslint-plugin-prettier 
               eslint-config-prettier 
               eslint-plugin-import 
               eslint-plugin-react 
               eslint-plugin-react-hooks 
               @typescript-eslint/parser 
               @typescript-eslint/eslint-plugin

 

.eslintrc

{
      "extends": ["react-app", "prettier/prettier"],
      "plugins": ["react-hooks", "simple-import-sort", "prettier"],
      "rules": {
        "prettier/prettier": "error",
        "react-hooks/rules-of-hooks": "error",
        "simple-import-sort/imports": "error",
        "simple-import-sort/exports": "error",
        "no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 0 }],
        "comma-dangle": ["error", "always-multiline"],
        "object-curly-spacing": ["error", "always"],
        "space-in-parens": ["error", "never"],
        "computed-property-spacing": ["error", "never"],
        "comma-spacing": ["error", { "before": false, "after": true }],
        "eol-last": ["error", "always"],
        "quotes": ["error", "single"],
        "no-tabs": "error",
        "semi": ["error", "never"],
        "import/no-anonymous-default-export": 0,
        "object-shorthand": "error",
        "padding-line-between-statements": [
          "error",
          { "blankLine": "always", "prev": "*", "next": "return" }
        ],
        "@typescript-eslint/no-redeclare": 0
      }
    }

 

.prettier.json

    {
      "printWidth": 80,
      "semi": true,
      "singleQuote": true,
      "trailingComma": "all",
      "tabWidth": 2,
      "bracketSpacing": true,
      "endOfLine": "auto",
      "useTabs": false
    }

 

Styled-component 설치

// 기본 styled-components
npm i styled-components @types/styled-components

yarn add styled-components @types/styled-components


//스타일이 적용되기 전에 렌더링되는 현상을 방지하고, 문자열 안에 스타일 들어가는 것 처리를 위한 설치
yarn add -dev babel-plugin-styled-components

//전역 스타일링에서 이용하기 위함 
yarn add styled-reset

 

 

파일구조

public 						
├── images
├── css										
└── data						//간단한 페이지를 만들 때 저장할 데이터

// 어플리케이션의 기능에 사용되는 소스들
src                        			 
├── component  // 컴포넌트와 컨테이너 파일들
│     ├── common  // 공통적으로 사용되는 컴포넌트들(ex button, modal)
│     └── layout  // 각 페이지들의 전체 레이아웃이자 컴포넌트들의 모임
│            ├── app
│            ├── home
│            └── directory                  
│				
├── pages // 페이지를 담당하는 컴포넌트(폴더구조로 url 결정)
│	  └── [id]
│
├── core  
│     ├── config  // 어플리케이션에서 사용되는 설정들 모임
│     ├── provider               
│     │     ├── helper // store를 이용하는 공통 로직들 정리
│     │     └── provider  // store의 값들을 컨테이너에게 전달해 줌
│     ├── redux  // Ducks 패턴으로 redux를 정의
│     └── api  // axios를 이용하여 서버와의 통신 로직을 담당
│
├── util                          
│     ├── hooks  // 각 컴포넌트에서 사용되는 공통 로직들을 커스텀 훅으로 관리
│     └── reduxUtils  // redux ducks 패턴에서 사용되는 유틸함
│
│
└── styles
      ├── global-style.ts  // 전역 스타일 설정
      └── theme.ts //  공통적으로 사용할 스타일을 지정하는 부분

 

_document.tsx 파일 추가

_document.tsx파일에 css를 미리 적용할 수 있다. 

next.js에서 styled component를 적용할 때 css 로딩 지연으로 깜빡이는 모습을 방지하기 위함이다.
_document.tsx 파일  _document.js 파일은 pages폴더 내부에 존재하는 모든 페이지에 global한 설정값을 줄 수 있는 파일이다.
최상위 디렉토리에 _document.tsx 파일을 생성해서 아래 코드를 넣어준다.

import Document, { Html, Head, Main, NextScript } from "next/document";
import { ServerStyleSheet } from "styled-components";

export default class MyDocument extends Document {
  //--------------For styled-components only------------//
  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();
    }
  }
  //---------------------------------------------------//
  render() {
    return (
      <Html lang="en">
        <Head>

        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

 

 

레이아웃  사용 방법

 

MainLayout 컴포넌트

import MainHeader from 'components/Layout/MainHeader';
import React from 'react';
import Footer from 'components/Layout/Footer';
import Head from 'next/head';
import styled from 'styled-components';

type layoutProps={
  children:React.ReactNode | JSX.Element;
}

const MainLayout = ({children}:layoutProps ) => {
  return (
    <LayoutWrapper>
      <Head>
        <title>타이틀</title>
        <meta name="description" content="페이지 설명" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <MainHeader/>
        {children}
      <Footer/>
    </LayoutWrapper>
  );

}
export default MainLayout;

const LayoutWrapper=styled.div`
  width:100vw;
  backgroundColor:'#f6f7f9';
  
  /* max-width:1140px;
  min-width:280px; */
`

헤더와 푸터 등이 들어간다.

 

 

pages/index.tsx

import type { NextPage } from 'next';
import MainLayout from 'components/Layout/MainLayout';
import React from 'react';

import { useDispatch } from 'react-redux';
import { setWindowSize } from 'store/modules/slice/windowSizeSlice';

const Home: NextPage = () => {
  return (
      <MainLayout>
         ...  
      </MainLayout>
  )
}

export default Home

 

위 코드는 MainLayout 이라는 컴포넌트로 감싸주고 ... 이라는 부분에 children을 넣어주면 된다.

 

 

 

참고

 

초기 셋팅 : https://velog.io/@devstone/Next.js-Typescript-%EC%B4%88%EA%B8%B0%EC%84%B8%ED%8C%85-%ED%95%98%EA%B8%B0

절대 경로 설정 : https://flamingotiger.github.io/frontend/react/nextjs-absolute-path/ 

Styled-Components 셋팅 : https://yoon990.tistory.com/54

로컬 폰트 적용 : https://velog.io/@soonmac/Next.js-%EB%A1%9C%EC%BB%AC-%ED%8F%B0%ED%8A%B8-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0

파일 구조 : https://velog.io/@jodmsoluth/nextjs-%ED%8C%8C%EC%9D%BC%EA%B5%AC%EC%A1%B0

styled-components 셋팅: https://velog.io/@danmin20/Next.js-Typescript-Styled-component-%EC%89%BD%EA%B2%8C-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0

 

 

+ Recent posts