앞서 next가 서버사이드렌더링을 편하게 해주기 때문에 설치해 사용했습니다.
클라이언트사이드렌더링은
브라우저 -> 프론트서버 -> (!) 브라우저 -> 프론트서버 (!) -> 백서버 -> 프론트서버 -> 브라우저
순서로 데이터를 요청하고 받아오게 되면서 (!) 사이의 단계일 때, 모양은 있지만 데이터가 없는 화면이 보여지게 됩니다.
서버사이드렌더링은 웹페이지를 리프레쉬 하면 브라우저에서 백엔드 서버로 요청을 보내 데이터를 받아오게 됩니다.
브라우저 -> 프론트서버 -> 백서버 -> 프론트서버 -> 브라우저
요청 한번으로 초기 로딩속도가 좀 빨라지는 듯한 느낌을 줍니다.
pages 의 index 페이지의 서버사이드렌더링을 위해 초기에 로딩되어야 하는 액션을 파악하고,
const Home = () => {
const dispatch = useDispatch();
const { me } = useSelector((state) => state.user);
const { mainPosts, hasMorePosts, loadPostsLoading } = useSelector((state) => state.post);
useEffect(() => {
dispatch({
type: LOAD_MY_INFO_REQUEST,
})
dispatch({
type: LOAD_POSTS_REQUEST,
});
}, []);
useEffect(() => {
function onScroll () {
//console.log(window.scrollY, document.documentElement.clientHeight, document.documentElement.scrollHeight);
if (window.scrollY + document.documentElement.clientHeight > document.documentElement.scrollHeight - 300) {
if (hasMorePosts && !loadPostsLoading) {
const lastId = mainPosts[mainPosts.length - 1]?.id;
dispatch({
type: LOAD_POSTS_REQUEST,
lastId,
});
}
}
}
window.addEventListener('scroll', onScroll);
return () => {
window.removeEventListener('scroll', onScroll)
}
}, [hasMorePosts, loadPostsLoading, mainPosts]);
return (
<AppLayout>
{me && <PostForm />}
{mainPosts.map((post) => <PostCard key={post.id} post={post} />)}
</AppLayout>
);
}
export default Home;
위에서는 :6 줄의 useEffect 부분이 데이터를 불러오는 디스패치
저 useEffect 부분을 Home 아래에 getServerSideProps 로 담아줍니다.
저기서 wrapper는 configureStore.js 에서 만든 그 wrapper 입니다.
export const getServerSideProps = wrapper.getServerSideProps((context) => {
context.store.dispatch({
type: LOAD_MY_INFO_REQUEST,
})
context.store.dispatch({
type: LOAD_POSTS_REQUEST,
});
});
그럼 이 친구들을 reducer/index 의 HYRATE 에서 받아서 실행을 해주는데 여긴 아직 데이터가 없습니다.
또 redux dev tools 로 보면 Diff 탭에서 user 와 post 정보가 index 안에 들어가 있어서 이것도 빼주도록 해야겠습니다.
root reducer 의 구조를 재정비 해보겠습니다.
import { HYDRATE } from 'next-redux-wrapper';
import { combineReducers } from 'redux';
import user from './user';
import post from './post';
const rootReducer = combineReducers({
index: (state = {}, action) => {
switch (action.type) {
case HYDRATE:
return { ...state, ...action.payload};
default:
return state;
}
},
user,
post,
});
export default rootReducer;
기존의 root reducer 입니다.
import { HYDRATE } from 'next-redux-wrapper';
import { combineReducers } from 'redux';
import user from './user';
import post from './post';
const rootReducer = ((state, action) => {
switch (action.type) {
case HYDRATE:
console.log('HYDRATE', action);
return action.payload;
default: {
const combineReducer = combineReducers({
user,
post,
});
return combineReducer(state, action);
}
}
});
export default rootReducer;
조금 더 확장 가능하게 펼쳐주고 index를 빼 user, post로 덮을 수 있게 했습니다.
그러면 이제 프론트에서 보내는 요청이 보내집니다. 이제 요청을 해결시켜서 완료하면 됩니다.
다시 index로 돌아가...
import { END } from 'redux-saga';
임포트 해주고...
export const getServerSideProps = wrapper.getServerSideProps(async (context) => {
context.store.dispatch({
type: LOAD_MY_INFO_REQUEST,
})
context.store.dispatch({
type: LOAD_POSTS_REQUEST,
});
context.store.dispatch(END);
await context.store.sagaTask.toPromise();
});
아래 두 줄을 추가해 줍니다. next-redux-wrapper 사용...
저 store.sagaTask 도 마찬가지로 configureStore.js부터 온 친구입니다.
이렇게 해주면 request를 success로 바꿀때까지 기다려줍니다. 메인페이지 서버사이드렌더링 완성!
하지만 아직...
제 새로고침을 하면 로그인 정보를 가져와주지 못합니다... ㅡ아니이이~~ 해주면 됩니다 ㅠ
확인 먼저 해보겠습니다.
내 정보 불러오는 라우터에 콘솔을 찍어보니...
헤더에 쿠키가 없자나?
이렇게 프론트는 로그인 되어 있다고 하는데 백에서는 로그인을 한 줄 모르는 사태가 나와버렸습니다.
클라이언트사이드렌더링은
브라우저 -> 백으로 데이터를 보낼 때, 브라우저가 쿠키를 담아서 보냅니다.
서버사이드렌더링은
프론트서버 -> 백서버로 데이터를 보내는데(위의 getServerSideProps) 여기서 (프론트)서버 -> (백)서버 사이의 쿠키는 자동으로 넘어가지 않습니다. 이걸 설정해주면 됩니다. 어떻게? axios에 직접 넣어서 전달해주겠습니다.
index 로 가서
import axios from 'axios';
axios 임포트 해주고
export const getServerSideProps = wrapper.getServerSideProps(async (context) => {
const cookie = context.req ? context.req.headers.cookie : '';
axios.defaults.headers.Cookie = '';
if (context.req && cookie) {
axios.defaults.headers.Cookie = cookie;
}
context.store.dispatch({
type: LOAD_MY_INFO_REQUEST,
})
context.store.dispatch({
type: LOAD_POSTS_REQUEST,
});
context.store.dispatch(END);
await context.store.sagaTask.toPromise();
});
// 이 모양이 기본꼴
// getStaticProps도 마찬가지로 작성하지만 데이터가 고정되어있는? 잘 안바뀔? 페이지에 사용
export const getServerSideProps = wrapper.getServerSideProps(async (context) => {
context.store.dispatch(END);
await context.store.sagaTask.toPromise();
});
cookie 관련해 추가해 줍니다.
쿠키를 보내는 조건을 서버일 때와 쿠키가 존재할 때만 보내도록 처리합니다.
이렇게 안하면 프론트 서버에서 누군가의 쿠키가 공유돼서 타인이 그 아이디로 활동할 수 있어요
그러면 이제 라우터에 콘솔을 다시 보면
cookie가 들어있네요! connect.sid가 제 정보를 담은 쿠키입니다.
이제 서버사이드렌더링에서도 새로고침을 하면 제 로그인 정보도 풀리지 않고 잘 로딩이 됩니다!
서버사이드렌더링이 필요한 페이지 모두 적용해주도록 합니다.
'project' 카테고리의 다른 글
[React] 인스타 클론코딩 기록 (4-1) - css 서버사이드렌더링 (0) | 2021.02.04 |
---|---|
[React] 인스타 클론코딩 기록 (3) - redux-saga, REST API (0) | 2021.02.03 |
[React] 인스타 클론코딩 기록 (2) - redux (0) | 2021.01.21 |
[React] 인스타 클론코딩 기록(1) (0) | 2021.01.20 |