• 카테고리

    질문 & 답변
  • 세부 분야

    풀스택

  • 해결 여부

    미해결

redux toolkit ssr 질문

23.08.11 21:39 작성 조회수 278

0

계속 똑같은 부분을 질문하고있는데 혹시 똑같은 오류가 발생하고 이 부분 코드는 양이 적고 코드 양이 적어서 오류의 원인을 아는데 용이 할꺼 같아 질문드립니다

pages/about.js

import React from "react";
import { useSelector } from "react-redux";
import Head from "next/head";

import { Avatar, Card } from "antd";
import AppLayout from "../components/AppLayout";
import wrapper from "../store/configureStore";
import { loadUserInfo } from "../reducers/user";

const Profile = () => {
  const { userInfo } = useSelector((state) => state.user);

  return (
    <AppLayout>
      <Head>
        <title>ZeroCho | NodeBird</title>
      </Head>
      {userInfo ? (
        <Card
          actions={[
            <div key="twit">
              짹짹
              <br />
              {userInfo.Posts.length}
            </div>,
            <div key="following">
              팔로잉
              <br />
              {userInfo.Followings.length}
            </div>,
            <div key="follower">
              팔로워
              <br />
              {userInfo.Followers.length}
            </div>,
          ]}
        >
          <Card.Meta
            avatar={<Avatar>{userInfo.nickname[0]}</Avatar>}
            title={userInfo.nickname}
            description="노드버드 매니아"
          />
        </Card>
      ) : null}
    </AppLayout>
  );
};

export const getStaticProps = wrapper.getStaticProps(
  (store) =>
    async ({ req }) => {
      console.log("getStaticProps");
      await store.dispatch(loadUserInfo(1));
    }
);

export default Profile;
reducers/user.js

export const loadUserInfo = createAsyncThunk(
  "/user/LoadUserInfo",
  async (data) => {
    const response = await axios.get(`/user/${data}`, data);
    return response.data;
  }
);

const userSlice = createSlice({
  name: "user",
  initialState,
  reducers: {
    addPostToMe(draft, action) {
      // 값을 하나만 전달해서 값이 바로 payload에 저장이된다
      // 내가 만든 포스트를 me state에 저장하는 reducer
      draft.me.Posts.unshift({ id: action.payload });
    },
    removePostOfMe(draft, action) {
      draft.me.Posts = draft.me.Posts.filter((v) => v.id !== action.payload);
    },
  },
  // 외부에서 action을 만든것은 extraReducers로 가져와서 사용한다 주로 createAsyncThunk로 action을 만들 때 사용한다
  extraReducers: (builder) =>
    builder
      .addCase([HYDRATE], (state, action) => ({
        ...state,
        ...action.payload.user,
      }))
      
      // 다른유저정보 가져오는 리듀서
      .addCase(loadUserInfo.pending, (state, action) => {
        state.loadUserInfoLoading = true;
        state.loadUserInfoError = null;
        state.loadUserInfoDone = false;
      })
      .addCase(loadUserInfo.fulfilled, (state, action) => {
        state.loadUserInfoLoading = false;
        state.me = action.payload;
        state.loadUserInfoDone = true;
      })
      .addCase(loadUserInfo.rejected, (state, action) => {
        state.loadUserInfoLoading = false;
        state.loadUserInfoError = action.error;
      })
     
      .addDefaultCase((state) => state),
});

이런 코드인데

 // 다른유저정보 가져오는 리듀서
      .addCase(loadUserInfo.pending, (state, action) => {
        state.loadUserInfoLoading = true;
        state.loadUserInfoError = null;
        state.loadUserInfoDone = false;
      })
      .addCase(loadUserInfo.fulfilled, (state, action) => {
        state.loadUserInfoLoading = false;
        state.me = action.payload;
        state.loadUserInfoDone = true;
      })
      .addCase(loadUserInfo.rejected, (state, action) => {
        state.loadUserInfoLoading = false;
        state.loadUserInfoError = action.error;
      })

제가 이것저것 하다가 알게된거 action에 이부분을 전부 주석처리하게 되면 에러가 안난다는 겁니다 그래서 결정적으로 initalstate에 있는 변수들을 변경하는 과정이 브라우저의 정보와 서버의 정보를 다르게만드는 일을 하는것 같은데 혹시 의심가시는거 있으신가요?

답변 1

답변을 작성해보세요.

0

인프런 AI 인턴님의 프로필

인프런 AI 인턴

2023.08.13

안녕하세요, 인프런 AI 인턴이에요.
질문해주신 내용을 보면, Redux Toolkit을 사용한 Server Side Rendering (SSR) 관련 질문이신 것 같은데요. 어떤 오류가 발생하고 있는지 구체적으로 설명해주시면 더 정확한 도움을 드릴 수 있을 것 같아요.
그리고 코드를 보니 loadUserInfo 액션을 호출하는 부분이 보이지 않는데, 해당 액션을 어떻게 정의하셨는지, 그리고 사용하시는 redux 관련 코드에 대해 더 자세한 정보를 주셔야 도움을 드릴 수 있을 것 같아요.

i1004gy님의 프로필

i1004gy

질문자

2023.08.14

pages/about.js

import React from "react";
import { useSelector } from "react-redux";
import Head from "next/head";

import { Avatar, Card } from "antd";
import AppLayout from "../components/AppLayout";
import wrapper from "../store/configureStore";
import { loadUserInfo } from "../reducers/user";

const Profile = () => {
  const { userInfo } = useSelector((state) => state.user);

  return (
    <AppLayout>
      <Head>
        <title>ZeroCho | NodeBird</title>
      </Head>
      {userInfo ? (
        <Card
          actions={[
            <div key="twit">
              짹짹
              <br />
              {userInfo.Posts.length}
            </div>,
            <div key="following">
              팔로잉
              <br />
              {userInfo.Followings.length}
            </div>,
            <div key="follower">
              팔로워
              <br />
              {userInfo.Followers.length}
            </div>,
          ]}
        >
          <Card.Meta
            avatar={<Avatar>{userInfo.nickname[0]}</Avatar>}
            title={userInfo.nickname}
            description="노드버드 매니아"
          />
        </Card>
      ) : null}
    </AppLayout>
  );
};

export const getStaticProps = wrapper.getStaticProps(
  (store) =>
    async ({ req }) => {
      console.log("getStaticProps");
      await store.dispatch(loadUserInfo(1));
    }
);

export default Profile;

이 코드를 보면 아래에

export const getStaticProps = wrapper.getStaticProps(
  (store) =>
    async ({ req }) => {
      console.log("getStaticProps");
      await store.dispatch(loadUserInfo(1));
    }
);

이 부분이 loadUserInfo를 호출하는 부분입니다

i1004gy님의 프로필

i1004gy

질문자

2023.08.14

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import _ from "lodash";
import axios from "axios";
import { HYDRATE } from "next-redux-wrapper";
//Hydrate 즉 Hydration 은서버사이드렌더링을 통한 이미 그려져 불러온 HTML들과 더불어 번들링 된 JS파일들을 클라이언트 단에서 상호작용할 수 있도록 융합되는 과정

export const initialState = {
  loadUserInfoLoading: false, //  유저 정보 가져오는 시도중
  loadUserInfoDone: null, //  유저 정보 가져오는 상태 체크
  loadUserInfoError: false, //  유저 정보 가져오는 것 에러
  
};
export const loadUserInfo = createAsyncThunk(
  "/user/LoadUserInfo",
  async (data) => {
    const response = await axios.get(`/user/${data}`, data);
    return response.data;
  }
);

const userSlice = createSlice({
  name: "user",
  initialState,
  extraReducers: (builder) =>
    builder
      .addCase([HYDRATE], (state, action) => ({
        ...state,
        ...action.payload.user,
      }))
     .addCase(loadMyInfo.pending, (state, action) => {
        state.loadMyInfoLoading = true;
        state.loadMyInfoError = null;
        state.loadMyInfoDone = false;
      })
      .addCase(loadMyInfo.fulfilled, (state, action) => {
        state.loadMyInfoLoading = false;
        state.me = action.payload;
        state.loadMyInfoDone = true;
      })
      .addCase(loadMyInfo.rejected, (state, action) => {
        state.loadMyInfoLoading = false;
        state.loadMyInfoError = action.error;
      })
      .addDefaultCase((state) => state),
});

제가 이상한 action을 가져왔네요 죄송합니다 이게 loadUserInfo 엑션입니다 여기서에서 createSlice부분에 state를 변경하는 코드가 에러가 발생하는 부분인거 같습니다 저곳의 state변경을 주석처리하면 에러가 발생하지 않습니다

Unhandled Runtime Error

Error: Hydration failed because the initial UI does not match what was rendered on the server. See more info here: https://nextjs.org/docs/messages/react-hydration-error

이게 에러 메시지이고

image이게 에러 화면 입니다