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

챠챠_님의 프로필 이미지
챠챠_

작성한 질문수

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

카카오톡 공유하기 & 강좌 마무리

Minified React error 콘솔에러 (hydrate)

작성

·

224

·

수정됨

0

안녕하세요 선생님

카카오톡 공유하기 까지 듣고 화면을 테스트해보고 있는데

Minified React error 이 에러가 떠서 질문드립니다.

 

메인화면, 상세보기에서 게시글이 있을때 나옵니다.

Uncaught Error: Minified React error #418; visit https://reactjs.org/docs/error-decoder.html?invariant=418 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
    at lg (framework-ecc4130bc7a58a64.js:9:46457)
    at i (framework-ecc4130bc7a58a64.js:9:121052)
    at oO (framework-ecc4130bc7a58a64.js:9:99019)
    at framework-ecc4130bc7a58a64.js:9:98886
    at oF (framework-ecc4130bc7a58a64.js:9:98893)
    at oS (framework-ecc4130bc7a58a64.js:9:93932)
    at x (framework-ecc4130bc7a58a64.js:33:1364)
    at MessagePort.T (framework-ecc4130bc7a58a64.js:33:1894)

검색해봤더니 hydrate 쪽 이슈더라구요.

혼자서 풀어보다가 잘 풀리지 않아서 여쭤봅니다.

혹시 제로초님은 저런 에러가 나지 않으시는지

난다면 어느 로직을 확인해야할지 조언 부탁드립니다.

/pages/post/[id].js

import axios from 'axios';
import Head from 'next/head';
import { useRouter } from 'next/router';
import { useSelector } from 'react-redux';

import AppLayout from '../../components/AppLayout';
import PostCard from '../../components/PostCard';
import { loadPost } from '../../reducers/post';
import { loadMyInfo } from '../../reducers/user';
import wrapper from '../../store/configurStore';

const Post = () => {
  const router = useRouter();
  const { id } = router.query;
  const { singlePost } = useSelector((state) => state.post);

  return (
    <AppLayout>
      {singlePost ? (
        <>
          <Head>
            <title>
              {singlePost?.User.nickname}
              님의 글
            </title>
            <meta name="description" content={singlePost.content} />
            <meta
              property="og:title"
              content={`${singlePost.User.nickname}님의 게시글`}
            />
            <meta property="og:description" content={singlePost.content} />
            <meta
              property="og:image"
              content={
                singlePost.Images[0]
                  ? singlePost.Images[0].src
                  : 'https://nodebird.com/favicon.ico'
              }
            />
            <meta
              property="og:url"
              content={`https://nodebird.com/post/${id}`}
            />
          </Head>
          <PostCard post={singlePost}>{id}번 게시글</PostCard>
        </>
      ) : (
        <div>존재하지 않는 게시물입니다.</div>
      )}
    </AppLayout>
  );
};

export const getServerSideProps = wrapper.getServerSideProps(
  (store) =>
    async ({ req, params }) => {
      const cookie = req ? req.headers.cookie : '';
      axios.defaults.headers.Cookie = '';
      // 쿠키가 브라우저에 있는경우만 넣어서 실행
      // (주의, 아래 조건이 없다면 다른 사람으로 로그인 될 수도 있음)
      if (req && cookie) {
        axios.defaults.headers.Cookie = cookie;
      }

      await store.dispatch(loadMyInfo());
      await store.dispatch(loadPost(params.id));
    },
);

export default Post;


/components/PostCard.js

import {
  RetweetOutlined,
  HeartOutlined,
  HeartTwoTone,
  MessageOutlined,
  EllipsisOutlined,
} from '@ant-design/icons';
import { Card, Popover, Button, Avatar, List } from 'antd';
import dayjs from 'dayjs';
import Link from 'next/link';
import PropTypes from 'prop-types';
import { useState, useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';

import CommentForm from './CommentForm';
import Followbutton from './FollowButton';
import PostCardContent from './PostCardContent';
import PostImages from './PostImages';
import {
  removePostRequestAction,
  likePostRequestAction,
  unLikePostRequestAction,
  retweetRequestAction,
} from '../reducers/post';

dayjs.locale('ko');

const PostCard = ({ post }) => {
  // const { me: {id} } = useSelector((state) => state.user);
  // const id = me && me.id;
  // const id = me?.id; // 옵셔널 체이닝 연산자
  const { removePostLoading } = useSelector((state) => state.post);
  const dispatch = useDispatch();
  const [commentFormOpend, setCommentFormOpend] = useState(false);

  const id = useSelector((state) => state.user.me?.id);
  const liked = post.Likers.find((d) => d.id === id);

  const onLike = useCallback(() => {
    if (!id) {
      alert('로그인이 필요합니다.');
    }
    dispatch(likePostRequestAction(post.id));
  }, [id]);
  const onUnLike = useCallback(() => {
    if (!id) {
      alert('로그인이 필요합니다.');
    }
    dispatch(unLikePostRequestAction(post.id));
  }, [id]);
  const onToggleComment = useCallback(() => {
    setCommentFormOpend((prev) => !prev);
  }, []);
  const onRemovePost = useCallback(() => {
    if (!id) {
      alert('로그인이 필요합니다.');
    }
    dispatch(removePostRequestAction({ id: post.id }));
  }, [id]);
  const onRetweet = useCallback(() => {
    if (!id) {
      alert('로그인이 필요합니다.');
    }
    dispatch(retweetRequestAction(post.id));
  }, [id]);
  return (
    <div style={{ marginTop: 10 }}>
      <Card
        cover={post.Images[0] && <PostImages images={post.Images} />}
        actions={[
          // 배열안에 들어가는 것들은 다 key를 넣어줘야 한다.
          <RetweetOutlined key="retweet" onClick={onRetweet} />,
          liked ? (
            <HeartTwoTone
              key="heart"
              twoToneColor="#eb2f96"
              onClick={onUnLike}
            />
          ) : (
            <HeartOutlined key="heart" onClick={onLike} />
          ),
          <MessageOutlined key="comment" onClick={onToggleComment} />,
          <Popover
            key="more"
            content={
              <Button.Group>
                {id && post.User?.id === id ? (
                  <>
                    <Button type="primary" key="modify">
                      수정
                    </Button>
                    <Button
                      type="danger"
                      key="delete"
                      onClick={onRemovePost}
                      loading={removePostLoading}
                    >
                      삭제
                    </Button>
                  </>
                ) : (
                  <Button type="dashed" key="report">
                    신고
                  </Button>
                )}
              </Button.Group>
            }
          >
            <EllipsisOutlined />
          </Popover>,
        ]}
        extra={id && <Followbutton post={post} />}
        title={
          post.RetweetId ? `${post.User.nickname}님이 리트윗하셨습니다.` : null
        }
      >
        {post.RetweetId && post.Retweet ? (
          <Card
            cover={
              post.Retweet.Images[0] && (
                <PostImages images={post.Retweet.Images} />
              )
            }
          >
            <div style={{ float: 'right' }}>
              {dayjs(post.createdAt).format('YYYY.MM.DD')}
            </div>
            <Card.Meta
              avatar={
                <Link href={`/user/${post.Retweet.User.id}`}>
                  <Avatar>{post.Retweet.User?.nickname[0]}</Avatar>
                </Link>
              }
              title={post.Retweet.User?.nickname}
              description={<PostCardContent postData={post.Retweet.content} />}
            />
          </Card>
        ) : (
          <>
            <div style={{ float: 'right' }}>
              {dayjs(post.createdAt).format('YYYY.MM.DD')}
            </div>
            <Card.Meta
              avatar={
                <Link href={`/user/${post.User.id}`}>
                  <Avatar>{post.User?.nickname[0]}</Avatar>
                </Link>
              }
              title={post.User?.nickname}
              description={<PostCardContent postData={post.content} />}
            />
          </>
        )}
      </Card>
      {commentFormOpend && (
        <div>
          {/* 게시글의 아이디 위해서 post 넘겨줌 */}
          <CommentForm post={post} />
          <List
            header={`${post.Comments.length}개의 댓글`}
            itemLayout="horizontal"
            dataSource={post.Comments}
            renderItem={(item) => (
              <List.Item key={item.id}>
                <List.Item.Meta
                  title={item.User.nickname}
                  avatar={
                    <Link href={`/user/${item.User.id}`}>
                      <Avatar>{item.User.nickname[0]}</Avatar>
                    </Link>
                  }
                  description={item.content}
                />
              </List.Item>
            )}
          />
        </div>
      )}
    </div>
  );
};

PostCard.propTypes = {
  post: PropTypes.shape({
    id: PropTypes.number,
    User: PropTypes.object,
    content: PropTypes.string,
    createdAt: PropTypes.string,
    Comments: PropTypes.arrayOf(PropTypes.object),
    Images: PropTypes.arrayOf(PropTypes.object),
    Likers: PropTypes.arrayOf(PropTypes.object),
    RetweetId: PropTypes.number,
    Retweet: PropTypes.objectOf(PropTypes.any),
  }).isRequired,
};

export default PostCard;

 여유되실때 한번 봐주시면 감사하겠습니다

답변 1

0

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

개발환경에서는 에러 안 났나요?? 저거 일일이 주석처리하며 에러 위치 찾아야합니다.

챠챠_님의 프로필 이미지
챠챠_
질문자

네 이번에 처음 나온 에러라서 찾아보다 여쭤봤었습니다.

흠.. 수업 다 듣고나서 찾아봐야겠습니다.

답변 감사합니다!

챠챠_님의 프로필 이미지
챠챠_
질문자

아 선생님은 저런 에러 한번도 안나오셨단 거죠?
주석하면서 찾아보면서 선생님 코드랑 비교하면서 봐야겠네요.

감사합니다!

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

하이드레이션 에러는 넥스트의 고질병같은 에러입니다. 엄청나게 많이 뜹니다.

챠챠_님의 프로필 이미지
챠챠_

작성한 질문수

질문하기