들어가며
넥스트를 사용하는 주 이유인 서버사이드 렌더링입니다. 서버사이드 렌더링을 하지 않을 거면 굳이 넥스트를 사용할 필요가 없고, 순수한 리액트로 하면 되죠. 하지만, 넥스트는 서버사이드 렌더링을 편하게 해줍니다. 이번 프로젝트는 핀터레스트 서비스이기때문에 서버사이드 렌더링의 필요이 크다고 생각해 넥스트를 사용하게 되었습니다.
Next 9버전에 들어서 서버사이드 렌더링 방법이 많이 바뀌었기 때문에 Next 8버전과는 다른 부분이 많습니다.
1. 서비스 작동원리 파악
front/pages/app.js
import React from 'react';
import Head from 'next/head';
import PropTypes from 'prop-types';
import 'antd/dist/antd.css';
import wrapper from '../store/configureStore';
const NodeBird = ({ Component }) => {
return (
<>
<Head>
<meta charSet='utf-8' />
<title>NodeBird</title>
</Head>
<Component />
</>
);
};
NodeBird.propTypes = {
Component: PropTypes.elementType.isRequired,
};
export default wrapper.withRedux(NodeBird); // wrapper 로 감쌈
front/store/configureStore.js
import { createWrapper } from 'next-redux-wrapper';
import { applyMiddleware, createStore, compose } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import createSagaMiddleeare from 'redux-saga';
import reducer from '../reducers';
import rootSaga from '../sagas';
const loggerMiddleware =
({ dispatch, gesState }) =>
(next) =>
(action) => {
console.log(action);
return next(action);
};
const configureStore = () => {
const sagaMiddleware = createSagaMiddleeare();
const middlewares = [sagaMiddleware, loggerMiddleware];
const enhancer = process.env.NODE_ENV === 'production' ? compose(applyMiddleware(...middlewares)) : composeWithDevTools(applyMiddleware(...middlewares));
const store = createStore(reducer, enhancer);
store.sagaTask = sagaMiddleware.run(rootSaga);
return store;
};
const wrapper = createWrapper(configureStore, {
debug: process.env.NODE_ENV === 'development',
});
export default wrapper;
- next-redux-wrapper 라이브러리로 만든 wrapper 로 지금 루트 파일이 되는 app.js 를 감싸고 있는데 이를 통해 개별 페이지들의 서버사이드 렌더링을 적용을 할 수 있음
- next 에서 서버사이드 렌더링용 메서드를 4가지를 제공함. 그치만, 그 4가지 방식 모두 리덕스랑 함께 사용할 때는 조금 문제가 생겨 next-redux-wrapper 가 제공하는 서버사이드 렌더링용 메서드와 함께 사용하는 것이 더 편리해 랑비ㅡ러리를 사용
2. 렌더링 전 서버 데이터 요청
- 메인 페이지에서 화면이 로딩되 후에 useEffect 를 통해 사용자정보와 게시글 정보를 받아오고 있음
- 여기 코드에서 화면이 처음 로딩될 때는 사용자 정보랑 게시글 정보가 없다가 나중에야 불러오니까 잠깐 동안 데이터의 공백이 발생.
- 그럼 처음부터 화면을 받아올 때부터 먼저 데이터를 불러올 수 있다면, 데이터가 채워진 채로 화면이 렌더링 될 것
- 그럼 이 메인 페이지의 컴포넌트보다 먼저 실행되는 것이 필요한데, 그게 바로 종류가 여러가지 있음
- 그 중 넥스트8버전에서 사용했던 getInitialProps 가 있지만, 조만간 9버전이 소규모 업데이트가 진행되면서 없어질 것 같아 9버전에서 새로 생성된 3가지 메서드를 사용했음
- getStaticProps, getStaticPaths, getServerSideProps
front/pages/index.js(메인페이지)
(...)
import wrapper from '../store/configureStore'
const Home = () => {
(...)
};
export const getServerSideProps = wrapper.getServerSideProps((store) => async () => {
store.dispatch({
type: LOAD_USER_REQUEST,
});
store.dispatch({
type: LOAD_POSTS_REQUEST,
});
store.dispatch(END);
await store.sagaTask.toPromise();
});
export default Home;
- 화면을 렌더링하기 전에 서버 쪽에서 먼저 실행을 합니다.
- getServerSideProps: 이것은 Next.js의 함수로, 데이터를 가져와 페이지 컴포넌트에 속성으로 전달할 수 있게 해줍니다. 이 함수는 모든 요청 중에 서버 측에서 실행됩니다.
- wrapper.getServerSideProps: 코드에 나와있는대로 Next.js 앱에 대한 래퍼를 사용하는 것으로 보입니다. 이 래퍼는 getServerSideProps 함수 내에서 Redux 스토어에 액세스할 수 있게 해줍니다.
- store: Redux 스토어입니다. 이것을 사용하여 액션을 디스패치합니다.
- async () => { /* ... */ }: 이것은 getServerSideProps에서 반환하는 비동기 함수입니다. 여기에서 데이터를 가져오는 논리를 수행합니다. 그러나 여기서는 외부 소스에서 데이터를 가져오는 것이 아니라 Redux 액션을 디스패치하고 있습니다.
- store.dispatch({ type: LOAD_USER_REQUEST });: 이 줄은 LOAD_USER_REQUEST 타입의 Redux 액션을 디스패치합니다. 이 액션 타입은 사용자 데이터를 로드하는 요청임을 나타냅니다.
- store.dispatch({ type: LOAD_POSTS_REQUEST });: 마찬가지로, 이 줄은 LOAD_POSTS_REQUEST 타입의 Redux 액션을 디스패치합니다. 이 액션 타입은 포스트 데이터를 로드하는 요청임을 나타냅니다.
- store.dispatch(END);: Redux Saga에게 모든 비동기 작업이 완료되었음을 나타내는 END 액션을 디스패치합니다.
- await store.sagaTask.toPromise();: Redux Saga의 작업이 완료될 때까지 기다리고, 작업이 완료된 후에 프로미스를 반환합니다.
서버 측 렌더링을 수행할 때 Redux 액션 및 Saga를 통해 비동기 작업을 처리하는 일반적인 패턴입니다. 작업이 완료된 후에 프로미스를 반환하여 Next.js가 서버 측 렌더링을 완료할 수 있게 합니다.
'Next.js' 카테고리의 다른 글
Next.js vs Vite.js (2) | 2024.03.29 |
---|---|
[NNN]_getStaticPaths (0) | 2024.02.26 |
[NNN]_CSS 서버사이드 렌더링 준비 (0) | 2024.02.25 |
[NNN]_트러블슈팅3_SSR시 쿠키 공유하기 (0) | 2024.02.24 |
CSR 과 SSR (0) | 2024.02.24 |