Next.js - getInitialProps, Link

BigbrotherShin·2020년 3월 24일
1

Frontend

목록 보기
14/31
post-thumbnail

참고

front/server.js

server.get('/hashtag/:tag', (req, res) => { // 동적 주소 get 요청
    return app.render(req, res, '/hashtag', { tag: req.params.tag });
  });
const express = require('express');
const next = require('next');
const morgan = require('morgan');
const cookieParser = require('cookie-parser');
const expressSession = require('express-session');
const dotenv = require('dotenv');

const dev = process.env.NODE_ENV !== 'production';
const prod = process.env.NODE_ENV === 'production';

dotenv.config();

const app = next({ dev });
const handle = app.getRequestHandler();

app.prepare().then(() => {
  const server = express();

  server.use(morgan('dev'));
  server.use(express.json());
  server.use(express.urlencoded({ extended: true }));
  server.use(cookieParser(process.env.COOKIE_SECRET));
  server.use(
    expressSession({
      resave: false,
      saveUninitialized: false,
      secret: process.env.COOKIE_SECRET,
      cookie: {
        httpOnly: true,
        secure: false,
      },
    }),
  );

  server.get('/hashtag/:tag', (req, res) => { // 동적 주소 get 요청
    return app.render(req, res, '/hashtag', { tag: req.params.tag });
  });

  server.get('/user/:id', (req, res) => {
    return app.render(req, res, '/user', { id: req.params.id });
  });

  server.get('*', (req, res) => {
    // 모든 get 요청 처리
    return handle(req, res);
  });

  server.listen(3060, () => {
    console.log('next+expresss running on port 3060');
  });
});

getInitialProps

Component.getInitialProps 는 Next가 추가한 React lifecycle의 일종으로서 componentDidMount보다 먼저 실행됨. 서버에서도 실행되고, 프론트에서도 실행
서버의 데이터를 미리 가져오거나, 서버에서 실행될 동작을 수행할 수 있음.

front/pages/_app.js

function NodeBird({ Component, pageProps }) {
  return <Component {...pageProps} />
} // Component에서 getInitialProps를 가지고 있다면 return 값을 pageProps에 넣어주어
// 해당 컴포넌트에서 props로 사용할 수 있게 함.


NodeBird.getInitialProps = async context => {
  console.log('CONTEXT', context);
  const { ctx, Component } = context; // next에서 넣어주는 context
  let pageProps = {};
  if (Component.getInitialProps) { 
    // Component (pages 폴더에 있는 컴포넌트)에 getInitialProps가 있다면 return 값을 pageProps에 넣음.
    pageProps = await Component.getInitialProps(ctx); // ctx를 컴포넌트에 넘겨준다.
  }
  return { pageProps };
};
// Only uncomment this getInitialProps if you have blocking data requirements for
// every single page in your application. This disables the ability to
// perform automatic static optimization, causing every page in your app to
// be server-side rendered.

export default NodeBird;

http://localhost:3060/hashtag/리엑트 접속 시 콘솔

console.log('CONTEXT', context)

CONTEXT {
  AppTree: [Function: AppTree],
  Component: [Function: Hashtag] { getInitialProps: [AsyncFunction] },
  router: ServerRouter {
    route: '/hashtag',
    pathname: '/hashtag',
    query: { tag: '리엑트' },
    asPath: '/hashtag/%EB%A6%AC%EC%97%91%ED%8A%B8',
    isFallback: false
  },
  ctx: {
    err: undefined,
    req: IncomingMessage {
      _readableState: [ReadableState],
      readable: true,
      _events: [Object: null prototype],
      _eventsCount: 1,
      _maxListeners: undefined,
      socket: [Socket],
      connection: [Socket],
      httpVersionMajor: 1,
      httpVersionMinor: 1,
      httpVersion: '1.1',
      complete: true,
      headers: [Object],
      rawHeaders: [Array],
      trailers: {},
      rawTrailers: [],
      aborted: false,
      upgrade: false,
      url: '/hashtag/%EB%A6%AC%EC%97%91%ED%8A%B8',
      method: 'GET',
      statusCode: null,
      statusMessage: null,
      client: [Socket],
      _consuming: false,
      _dumped: false,
      next: [Function: next],
      baseUrl: '',
      originalUrl: '/hashtag/%EB%A6%AC%EC%97%91%ED%8A%B8',
      _parsedUrl: [Url],
      params: [Object],
      query: {},
      res: [ServerResponse],
      _startAt: [Array],
      _startTime: 2020-03-24T20:31:15.681Z,
      _remoteAddress: '::1',
      body: {},
      secret: 'cookiesecret',
      cookies: {},
      signedCookies: [Object: null prototype],
      _parsedOriginalUrl: [Url],
      sessionStore: [MemoryStore],
      sessionID: 'lO65UJW_h1vf7FS5Z4Nn_mb38st9kGYc',
      session: [Session],
      route: [Route],
      [Symbol(kCapture)]: false
    },
    res: ServerResponse {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      _last: false,
      chunkedEncoding: false,
      shouldKeepAlive: true,
      useChunkedEncodingByDefault: true,
      sendDate: true,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      _contentLength: null,
      _hasBody: true,
      _trailer: '',
      finished: false,
      _headerSent: false,
      socket: [Socket],
      connection: [Socket],
      _header: null,
      _onPendingData: [Function: bound updateOutgoingData],
      _sent100: false,
      _expect_continue: false,
      req: [IncomingMessage],
      locals: [Object: null prototype] {},
      _startAt: undefined,
      _startTime: undefined,
      writeHead: [Function: writeHead],
      __onFinished: [Function],
      end: [Function: end],
      [Symbol(kCapture)]: false,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype]
    },
    pathname: '/hashtag',
    query: { tag: '리엑트' },
    asPath: '/hashtag/%EB%A6%AC%EC%97%91%ED%8A%B8',
    AppTree: [Function: AppTree],
    store: {
      dispatch: [Function],
      subscribe: [Function: subscribe],
      getState: [Function: getState],
      replaceReducer: [Function: replaceReducer],
      [Symbol(observable)]: [Function: observable]
    },
    isServer: true
  }
}

Link/next 고급 사용법

React로 구현한 Single Page Application은 페이지를 이동할 때 서버에 새로운 요청을 하지 않음으로서 사용자 경험을 증대시킨다.

Next.jsLink컴포넌트는 링크를 클릭하더라도 서버에 새로운 요청을 하지 않을 수 있는 기능을 제공한다.

사용법

import Link from 'next/link'

function Home() {
  return (
    <div>
      <Link 
        href={{ pathname: '/about', query: { name: 'ZEIT' } }} // /about?name=ZEIT
        as={`/about/ZEIT`} // /about/ZEIT
      >
        <a>About us</a>
      </Link>
    </div>
  )
}

export default Home

front/pages/hashtag.js

import React from 'react';
import Link from 'next/link';

const Hashtag = ({ tag }) => {
  return (
    <Link
    href={{ pathname: '/hashtag/', query: { tag: tag } }}
    // href만 사용한다면 query string을 사용하게 된다. (/hashtag?tag=리엑트)
    as={`/hashtag/${tag}`} // as를 통해 sementic URL 사용이 가능해진다. (/hashtag/리엑트)
    >
      <a>
       {`#${tag}`}
      </a>
    </Link>
};

Hashtag.getInitialProps = async context => { // context: _app.js의 ctx를 props로 받음
  // Compoenet.getInitialProps 가 있으므로 _app.js에서 { pageProps } 를 props로 받음
  console.log('Hashtag getInitialProps: ', context.query.tag);
  return { tag: context.query.tag }; // _app.js에서 pageProps에 들어가서 Hashtag 컴포넌트에서 사용할 수 있게 함.
};

export default Hashtag;

콘솔 내용: Hashtag getInitialProps: 리엑트

profile
JavaScript, Node.js 그리고 React를 배우는

0개의 댓글