강의

멘토링

로드맵

인프런 커뮤니티 질문&답변

hib4888님의 프로필 이미지
hib4888

작성한 질문수

[리뉴얼] React로 NodeBird SNS 만들기

서버사이드렌더링 준비하기

SSR의 state초기화관련 질문입니다.

작성

·

300

0

안녕하세요 제로초님 강의들으면서 해당 개념을 개인 프로젝트에서 구현하고 있는데 궁금한점이 있어서 질문드립니다.

메인페이지, 프로필페이지에서 게시글을 삭제하면 알람을 띄우고있습니다.

근데 메인페이지에서 게시글을 삭제한 뒤 프로필 페이지로 이동하면 알람이 또 뜨더라고요. (반대도 마찬가지입니다.)

제 예상으로는 SSR을 적용하면 페이지 이동시에 state가 초기화되어서 위 문제가 발생하지 않는다고 예상했는데 어떤 문제인지 제 생각과는 다르게 동작하더라고요.

혹시 어떤 부분이 문제가 있는지 피드백좀 부탁드려도될까요?

ezgif.com-gif-maker.gifezgif.com-gif-maker (1).gif

// index.js
import React, { useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Button, message } from 'antd';
import { PlusCircleOutlined } from '@ant-design/icons';
import Router from 'next/router';
import Link from 'next/link';

import wrapper from '../store/configureStore';
import axios from 'axios';
import { END } from 'redux-saga';

import AppLayout from '../components/AppLayout/';
import RecentPostList from '../components/HomePost/RecentPostList';
import HomeFooter from '../components/HomePost/HomeFooter';
import TopPostList from '../components/HomePost/TopPostList';
import { LOAD_RECENT_POSTS_REQUEST, LOAD_TOP_POSTS_REQUEST } from '../reducers/post';
import { LOAD_MY_INFO_REQUEST } from '../reducers/user';
import { 
  HomeWrapper, HomeLogoHeader, HomeLogoText, PageMainText, PageSubText, HomeInputWrapper,
  TopPostsWrapper, HomePosts, TopPostsIcon, HomePostsText, RecentPostsIcon
} from '../pagestyles';

const Home = () => {
  const dispatch = useDispatch();
  const { me, logInDone, logOutDone } = useSelector((state) => state?.user);
  const { topPosts, recentPosts, hasMorePosts, loadRecentPostsLoading, removePostDone } = useSelector((state) => state.post);

  useEffect(() => {
    if (logInDone) {
      message.success('반갑습니다. 맛있는 식사하세요', 1.5);
    }
  }, [logInDone]);

  useEffect(() => {
    if (logOutDone) {
      message.success('정상적으로 로그아웃되었습니다.', 1.5);
    }
  }, [logOutDone]);  

  useEffect(() => {
    if (removePostDone) {
      message.error('게시글이 삭제되었습니다.', 1.5);
    }
  }, [removePostDone]);
  
  const onSearch = useCallback((tag) => {
    Router.push(`/hashtag/${tag}`);
  }, []);

  useEffect(() => {
    function onScroll() {
      if (window.scrollY + document.documentElement.clientHeight > document.documentElement.scrollHeight - 300) {
        if (hasMorePosts && !loadRecentPostsLoading) {
          const lastId = recentPosts[recentPosts.length - 1]?.id;
          dispatch({
            type: LOAD_RECENT_POSTS_REQUEST,
            lastId,
          });    
        }
      }
    };    

    window.addEventListener('scroll', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
    }
  }, [hasMorePosts, loadRecentPostsLoading, recentPosts]);
  
  return (    
    <AppLayout>
      <HomeWrapper>
        <HomeLogoHeader>
          <HomeLogoText>
            <PageMainText className='bolder'>Recipe.io</PageMainText>
            <PageSubText>Have a delicious meal today</PageSubText>
          </HomeLogoText>
          {me && <Link href='/posting'><a><Button type='primary' size='large' icon={<PlusCircleOutlined />} >Create Recipe</Button></a></Link>}
        </HomeLogoHeader>
        
        <HomeInputWrapper 
          placeholder="Search for Hashtag"
          size='large' 
          allowClear="true" 
          enterButton 
          onSearch={onSearch}
        />
        
        <TopPostsWrapper>
          <HomePosts>
            <TopPostsIcon /> 
            <HomePostsText className='bold'>Top Posts</HomePostsText>
          </HomePosts>
          <TopPostList topPosts={topPosts} />
        </TopPostsWrapper>

        <div>
          <HomePosts>          
            <RecentPostsIcon />
            <HomePostsText className='bold'>Recent Posts</HomePostsText>
          </HomePosts>
          <RecentPostList recentPosts={recentPosts} />
        </div>
      </HomeWrapper>

      <HomeFooter />
    </AppLayout>
  )
};

export const getServerSideProps = wrapper.getServerSideProps(async (context) => {
  console.log(`context: ${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_TOP_POSTS_REQUEST,
  });
  context.store.dispatch({
    type: LOAD_RECENT_POSTS_REQUEST,
  });

  context.store.dispatch(END);
  await context.store.sagaTask.toPromise();
});

export default Home;
// profile.js
import React, { useEffect } from 'react';
import { useSelector } from 'react-redux';
import { message } from 'antd';
import Head from 'next/head';

import wrapper from '../store/configureStore';
import axios from 'axios';
import { END } from 'redux-saga';

import AppLayout from '../components/AppLayout/';
import MyInfo from '../components/MyPage/MyInfo';
import { LOAD_MY_INFO_REQUEST, LOAD_LIKED_POSTS_REQUEST, LOAD_BOARD_POSTS_REQUEST } from '../reducers/user';
import { MypageWrapper, MypageText, PageMainText, PageSubText } from '../pagestyles';

const profile = () => {     
  const { me } = useSelector((state) => state.user);  
  const { removePostDone } = useSelector((state) => state.post);  

  useEffect(() => {
    if (!me) { 
      message.error('로그인이 필요한 서비스입니다.', 1.5);
      Router.push('/');
    }
  }, [me]);
  
  useEffect(() => {
    if (removePostDone) {
      message.error('게시글이 삭제되었습니다.', 1.5);
    }
  }, [removePostDone]);
  
  return (
    <>
      <Head>
        <title>마이페이지 | Recipe.io</title>
      </Head>
      <AppLayout>
        <MypageWrapper>
          <MypageText>
            <PageMainText className='bolder'>MY PAGE</PageMainText>
            <PageSubText>Check out posts and likes through your page</PageSubText>
          </MypageText>

          <MyInfo />
        </MypageWrapper>
      </AppLayout>
    </>
  )
};

export const getServerSideProps = wrapper.getServerSideProps(async (context) => {
  console.log(`context: ${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_LIKED_POSTS_REQUEST,
  });
  context.store.dispatch({
    type: LOAD_BOARD_POSTS_REQUEST,
  });

  context.store.dispatch(END);
  await context.store.sagaTask.toPromise();
});

export default profile;

 

답변 1

0

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

처음에 NEXT_REDUX_WRAPPER_HYDRATE에서 removePostDone이 false일 때도 뜨는데요. 혹시 다른 곳에 뜨게 잘못 코딩되어있는 게 아닌가요??

hib4888님의 프로필 이미지
hib4888
질문자

답변감사합니다.

조언해주신대로 다른 컴포넌트들을 다시 확인해봐야겠습니다

hib4888님의 프로필 이미지
hib4888

작성한 질문수

질문하기