ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • NextJS
    Node.js/프론트엔드 2021. 11. 25. 12:30

    1. package.json

    package.json의 scripts의 build 명령어 => production 배포하기 전에 nextjs 어플리케이션 빌드(컴파일)

    start 명령어 => 컴파일된 어플리케이션을 production 모드에서 실행.

    nextjs는 자체 서버를 가지고 있기 때문에 start 명령어가 필요함. 완전한 static 사이트를 개발할 때는 build만 하면 됨.

    build => start 순서

     

    2. next.config.js

    nextjs 설정 파일

    reactStrictMode : unsafe life cycle, legacy api usage 등등 

     

    3. .next 폴더

    dev나 build 스크립트 실행 시 생김. nextjs 어플리케이션이 served됨. 

    build output 저장

     

    4. public 폴더

    public 리소스를 위한 폴더.

    전형적인 React 앱과 달리 index.html(single html page for react application) 포함하지 않음. 

    모든 html 파일들은 어플리케이션 타입에 따라 nextjs에 의해서 생성됨. 

    pre-rendering => 언제, 어떻게 html 파일들이 생성되는가

    const {params} = router.query 에서 params 값이 처음에 undefined로 찍히는 것은 pre-rendering과 관련 있다.

     

    5. pages 폴더

    index.js : 브라우저에 서브되는 파일

    _app.js : 레이아웃을 정의할 수 있는 파일

     

     

    실행 흐름

    npm run start / yarn start => _app.js => localhost:3000 입력 후 브라우저 navigate할 때 컴포넌트가 _app.js prop으로 들어감.

     

    React app에서의 라우팅

    1. third party package 설치

    2. 라우트 설정 위해 routes.js 작성

    3. 각 라우트에 해당하는 컴포넌트 파일 생성 후 routes.js에서 import. 그 다음 path 프로퍼티로 새로운 라우트 설정.

     

    Nextjs app에서의 라우팅

    1. 파일 시스템 기반 라우팅 메커니즘

    2. pages 폴더 안에 파일 생성하면 자동으로 라우트 역할을 한다.

     

    pages/docs/[...params].js 구조에서는 http://localhost:3000/docs/feature/concept 로 요청 시

    {params: ['feature', 'concept']}을 반환한다. http://localhost:3000/docs/로 요청 시 404 에러를 리턴한다. (catch all routes)

     

    pages/docs/[[...params]].js 구조에서는 http://localhost:3000/docs/로 요청 시 index.js 컴포넌트를 렌더링 해준다.

     

    client-side navigation

    next/link의 Link 컴포넌트는 어플리케이션 내에서의 클라이언트 사이드 라우팅에 사용된다. 만약 외부 웹사이트를 navigate하고 싶다면 plain old html anchor 태그를 사용해야 한다. 

    반면, plain old html anchor를 클라이언트 사이드 라우팅에 사용하면 새로운 서버 요청이 발생하고, 유지하고 있던 클라이언트 상태가 삭제된다.

    Link 컴포넌트에 replace 속성을 주면 새로운 url을 stack에 더하여 현재 history state를 교체한다.(Router.replace도 동일한 기능을 함)

     

    Pre-rendering & Data Fetching

    Pre-rendering은 무엇인가

    create-react-app으로 만든 리액트 앱을 실행시켰을 때 페이지 소스를 보면 inspect 창에서 본 것과 달리 <div id="root"></div> 안에 아무 것도 들어있지 않다.

    반면, nextjs 앱은 <div id="__next"><div><h1>Home Page</h1><a href="/blog">Blog</a><a href="/product">Products</a><button>Place Order</button></div> 이런 식으로 <div id="__next"></div> 안에 요소들이 들어있다.

     

    기본적으로, Next JS는 어플리케이션의 모든 페이지를 pre-render 한다. 

    pre-render : Next JS는 client-side JS에 의해 각 페이지의 HTML이 생성되기 전에 미리 생성한다.

     

    처음에 page가 served 됐을 때는 빈 <div id="root"></div> 태그만 있다가, 페이지에 해당하는 js가 로드되면 브라우저에서 실행되고, dom node들을 생성해서 root div에 마운트(Hydration)한다.

    즉, Pre-rendering은 브라우저에서 js가 실행되기 전에 미리 페이지에 필요한 데이터와 html을 함께 생성하는 프로세스를 의미한다.

    Pre-rendering 사용하는 이유

    production mode에서는 bulid 커맨드를 실행시켰을 때 한번만 pre-render가 된다. 

    development mode에서는 코드 변경을 반영해야하므로 모든 요청에 대해 pre-render가 발생한다.

     

    Pre-rendering 종류

    1. Static Generation

    html 페이지가 빌드 타임에 생성됨. 페이지가 한번만 빌드되고, CDN에 의해서 캐시되고, 즉시 클라이언트로 서브되기 때문에 가능하면 권장되는 방법이다. => huge performance boost (사용 예: 블로그 페이지, e-commerce 상품 페이지, document & 마케팅 페이지)

    Next JS는 기본적으로 모든 페이지를 pre-render하고, 모든 페이지의 html은 어플리케이션을 빌드할 때 자동으로 정적 생성된다.

    - without data

    - with data

    getStaticProps

    - 서버 사이드에서 실행됨

    - getStaticProps 안에 작성되는 코드는 브라우저로 보내지는 JS 번들에 포함 안 됨.

    - fs 모듈로 파일 시스템에 접근하거나, db에 쿼리하는 코드를 작성할 수 있음.

    - object를 리턴해야하고, object는 props 키(마찬가지로 object)를 포함해야 한다.

    - 빌드 타임에 실행되지만, 개발 환경에서는 모든 요청마다 실행된다.

    async function getStaticProps() {
        const response = await fetch('https://jsonplaceholder.typicode.com/users')
        const data = await response.json();
    
        return {
            props: {
                users: data
            }
        }
    }

     

    Link Pre-fetching

    viewport(initially of through scroll) 안에 <Link /> 컴포넌트는 Static Generation으로 인해 페이지에 상응하는 데이터와 함께 prefetched 될 것이다.

     

    getStaticPaths and fallback

    - fallback: false

    1. getStaticPaths로부터 리턴된 paths들이 빌드 타임에 getStaticProps에 의해 HTML로 렌더된다.

    2. fallback이 false로 설정되면, getStaticPaths로부터 반환되지 않은 paths들은 404 page를 보여준다.

    3. false 값은 적은 수의 paths를 pre-render 할 때 사용하기 적합하다. 새로운 page가 잘 추가되지 않을 경우.

    4. 게시글 수가 적은 블로그 사이트 

    - fallback: true

    1. getStaticPaths로부터 리턴된 paths들이 빌드 타임에 getStaticProps에 의해 HTML로 렌더된다. (fallback: false와 동일)

    2. 빌드 타임에 생성되지 않은 paths들이 404 페이지를 보여주지 않는다. 대신 해당 path의 첫 요청 시 페이지의 fallback 버전을 보여준다.

    3. 백그라운드에서 Next.js는 요청된 path의 HTML와 JSON을 statically generate하고, 이것은 getStaticProps를 실행시키는 것을 포함한다.

    4. 3번이 완료되면, 브라우저는 생성된 path를 위한 JSON을 받게 되고, 필요한 props와 함께 자동으로 페이지가 렌더링 되는 데 사용된다. 사용자 관점에서,  해당 페이지는 fallback 페이지에서 완전한 페이지로 swap된다.

    5. 동시에 Next.js는 새로운 pre-rendered 페이지들을 추적하기 때문에 동일한 path로의 연속적인 요청은 빌드 타임에 생성된 다른 pre-rendered 페이지들처럼 미리 생성된 page를 보여준다.

    fallback version of the page =&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;h1&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Loading...&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/h1&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;

    fallback이 true면 아직 백그라운드에서 HTML와 JSON을 정적 생성하는 중이라 id, title, body 등(props)에 접근하지 못한다고 표시하고, false면 바로 접근할 수 있다.

    존재하지 않는 페이지를 요청할 경우 notFound 옵션으로 404페이지를 불러올 수 있다

    - fallback: 'blocking'

    - Incremental Static Generation

    async function getStaticProps(context) {
    	const {params} = context;
        const response = await fetch(`http://localhost:4000/products/${params.productId}`)
        const data = await response.json();
    
        return {
            props: {
                product: data
            },
            revalidate: 30
        }
    }

    즉, Static Generation의 문제는 매 요청마다 data를 fetch하고, pre-render하는 게 불가능하다는 것이다.

    2. Server-side Rendering

    export async function getServerSideProps({articles}) {
    	const response = await fetch('http://localhost:4000/news')
        const data = await response.json()
        
        return {
        	props: {
            	articles: data,
            }
        }
    }

    서버가 매 요청마다 compute해야해서 static generation보다 느림.

    - with dynamic parameters

    pages/news/[category].js
    
    function ArticleListByCategory({articles, category}) {
    	//...
    }
    
    export default ArticleListByCategory
    
    export async function getServerSideProps(context) {
    	const {params} = context;
        const {category} = params;
        const response = await fetch(`http://localhost:4000/news?category=${category}`);
        const data = await response.json()
        
        return {
        	props: {
            	articles: data,
                category
            }
        }
    }

    - getServerSideProps context

    pages/news/[category].js
    
    function ArticleListByCategory({articles, category}) {
    	//...
    }
    
    export default ArticleListByCategory
    
    export async function getServerSideProps(context) {
    	const {params, req, res, query} = context;
        console.log(query) // { subcategory: 'football', category: 'sports' } 'localhost:3000/news/sports?subcategory=football'에서 쿼리 스트링 추출
        console.log(req.headers.cookie) // 쿠키 접근
        res.setHeader('Set-Cookie', ['name=leemagnon'])
        const {category} = params;
        const response = await fetch(`http://localhost:4000/news?category=${category}`);
        const data = await response.json()
        
        return {
        	props: {
            	articles: data,
                category
            }
        }
    }

    getStaticProps와 달리 요청 시 pre-render가 이루어지므로 .next(빌드 폴더)의 pages 폴더에 html 파일이 생기지 않는다.

     

     

    Client-side data fetching

    swr, react-query 로 대체 가능

     

    Pre-rendering & Data Fetching Summary

     

    API

     

    Styling

    Global styles

    _app.js에 import '../styles/globals.css' 작성. 모든 페이지에 적용됨.

    _app.js : 모든 페이지의 wrapper 컴포넌트

     

    Component styles

    파일명.module.css 네이밍 컨벤션을 따른다. css 모듈은 css 이름을 지역적으로 scope해서 이름 충돌 없이 여러 파일에서 동일한 이름을 사용할 수 있게 해준다.

     

    SASS or SCSS

    SASS는 CSS의 확장으로 변수, 함수 등 다른 연산 등 복잡한 feature들을 쉽게 빌드할 수 있는 기능들을 제공.

    // _colors.scss
    
    $orange: orange;
    $purple: purple;
    
    // About.module.scss
    @import 'colors';
    
    .highlightscss {
    	color: $orange;
    }
    
    // pages/about.js
    import styles from '../styles/About.module.scss'
    
    function About() {
    	return <div className={styles.highlightscss}>About Page</div>
    }
    
    export default About

     

    CSS-in-JS solution

    inline 스타일. styled-components, emoition 등이 있다.

    // pages/css-in-js.js
    
    function CSSJS() {
    	return <h2 style={{ color: 'red' }}>Hello World</h2>
    }
    
    export default CSSJS

    Layout 

    - 공통 layout 적용 안 하는 방법

     

    - Head 컴포넌트는 document의 head section을 관리하는 것을 도와준다.

    meta 태그는 SEO에 매우 중요하다.

     

    - 이미지 렌더링 최적화

    .webp 포맷으로 렌더링 해줌. 2.4MB Size -> 38.1KB 등 사이즈 엄청 줄여줌.

    lazy loading 지원. viewport의 계산된 거리에 도달할 때 이미지 로딩함. 특정 시점에 불필요한 이미지를 미리부터 로딩하지 않음.

     

    - 절대 경로

    jsconfig.json 작성

     

    next export

    'Node.js > 프론트엔드' 카테고리의 다른 글

    React  (0) 2021.12.03
    Redux의 원리와 불변성  (0) 2021.05.14
    가로 스크롤 만들기  (0) 2021.04.01
    [ESLint] eslint.workingDirectories 옵션  (0) 2020.12.27
    프론트엔드 개발 환경의 이해 - NPM  (0) 2020.12.01
Designed by Tistory.