2020.01.23 next의 getInitialProps를 이용해 서버사이드렌더링 (SSR)하기

sykim·2020년 1월 23일
0
  1. 검색 엔진에게 노출되도록 도와준다

  2. 사용자를 고려한 측면에서 효율적인 퍼포먼스를 낸다


메인 페이지 ssr로 렌더링해보기

_app.js 안에 있던 ctx가 컴포넌트들로 전달이 된다

RgProject.getInitialProps = async (context) => {
    console.log('context', context)
    const { ctx, Component } = context;
    let pageProps = {};
    if (Component.getInitialProps) {
        pageProps = await context.Component.getInitialProps(ctx); 
    }
    return {pageProps};
};
  • 아래와 같이 메인(Home)컴포넌트에서 getInitialProps를 기재하여 context를 전달 받는다.
...
Home.getInitialProps = async (context) => {
    console.log(Object.keys(context))
};
...

홈 컴포넌트의 dispatch를 SSR 방식으로 옮기기

image.png

  • Object.keys(context)를 콘솔로 찍어보면 store가 존재.

  • store는 리덕스 store를 의미하고 이 store 안에는 dispatch와 getState(리덕스 state들을 가져올 수 있는 함수)가 존재한다.

  • 이 각각의 함수는 store.dispatch, store.getState로 사용이 가능하다.


Home.getInitialProps = async (context) => {
    console.log(Object.keys(context));
    context.store.dispatch({
        type: LOAD_MAIN_POSTS_REQUEST,
    });
};

_app.js에서 next용 리덕스 사가 설치

import withReduxSaga from 'next-redux-saga';
  • 위 모듈이 있어서 next에서 리덕스 사가를 사용할 수 있다.
export default withRedux((initialState, option) => {
    const sagaMiddleware = createSagaMiddleware();
    const middlewares = [sagaMiddleware];
    // const enhancer = compose(applyMiddleware(...middlewares));
    const enhancer = process.env.NODE_ENV === 'production'
    ? compose(applyMiddleware(...middlewares))
    : composeWithDevTools(
        applyMiddleware(...middlewares)
    );
    const store = createStore(reducer, initialState, enhancer);
    store.sagaTask = sagaMiddleware.run(rootSaga);
    return store;
})(withReduxSaga(RgProject));
  • withReduxSaga를 프로젝트 컴포넌트에 감싸주고 sagaMiddleware.run(rootSaga) 부분을 store.sagaTask에 넣어준다. (withReduxSaga가 내부에서 store.sagaTask 이 실행기를 필요로 하기 때문)

=> 해당 페이지가 마운트되었을 때 모든 데이터를 먼저 끼워주고 페이지를 렌더링해서 흰 배경에 데이터가 꽂히는 시간차 현상이 사라진다. 즉, 클라이언트 사이드에서의 componentWillMount랑 비슷한 역할을 한다.


page가 아닌 컴포넌트를 SSR로 연결해보기

AppLayout 컴포넌트의 아래 디스패치 부분은 _app.js 파일에 직접 기재를 해줘야 한다

useEffect(() => {
        if (!me) {
            dispatch ({
                type : LOAD_USER_REQUEST,
            });
        };
}, [me]);

_app.js

RgProject.getInitialProps = async (context) => {
    console.log('context', context)
    const { ctx, Component } = context;
    let pageProps = {};
    const state = ctx.store.getState();
    if (!state.user.me) {
        ctx.store.dispatch({
            type : LOAD_USER_REQUEST,
        })
    }
    if (Component.getInitialProps) {
        pageProps = await context.Component.getInitialProps(ctx); 
    }
    return { pageProps };
};
  • 이때 순서도 중요한데 위의 코드의 경우 LOAD_USER_REQUEST가 먼저 실행되고 그 후에 페이지로 getInitialProps를 내려 받은 메인페이지의 LOAD_MAIN_POSTS_REQUEST가 실행된다.

!! 중요

function loadUserAPI(userId) {
    return axios.get(userId ? `/user/${userId}` : '/user/', {
        withCredentials : true,
    });
}

위 코드와 같이 클라이언트에서 요청을 보낼 때는 브라우저가 쿠키를 같이 묶어준다. (withCredentials 사용 가능)
근데 서버사이트렌더링일 때는 브라우저가 없어서 우리가 직접 쿠키를 넣어줘야 한다.

RgProject.getInitialProps = async (context) => {
    console.log('context', context)
    const { ctx, Component } = context;
    let pageProps = {};
    const state = ctx.store.getState();
    const cookie = ctx.req.headers.cookie;
    // 모든 axios 헤더에 기본적으로 쿠키가 담기도록
    axios.defaults.headers.Cookie = cookie;
    console.log(cookie)
    if (!state.user.me) {
        ctx.store.dispatch({
            type : LOAD_USER_REQUEST,
        })
    }
    if (Component.getInitialProps) {
        pageProps = await context.Component.getInitialProps(ctx); 
    }
    return { pageProps };
};

image.png

  • 쿠키를 콘솔로 찍어보면 콘솔창에 쿠키가 보인다.
profile
블로그 이전했습니다

2개의 댓글

comment-user-thumbnail
2020년 1월 23일

next를 이용한 리덕스 사가 에러 찾는 법

export default withRedux((initialState, option) => {
    const sagaMiddleware = createSagaMiddleware();
// 이 부분
    const middlewares = [sagaMiddleware, (store) => (next) => (action) => {
        console.log(action);
        next(action);
    }];
    const enhancer = process.env.NODE_ENV === 'production'
    ? compose(applyMiddleware(...middlewares))
    : composeWithDevTools(
        applyMiddleware(...middlewares)
    );
    const store = createStore(reducer, initialState, enhancer);
    store.sagaTask = sagaMiddleware.run(rootSaga);
    return store;
})(withReduxSaga(RgProject));
1개의 답글