인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

Inflearn Community Q&A

dydcodydco0983's profile image
dydcodydco0983

asked

[Renewal] Creating NodeBird SNS with React

Share on KakaoTalk & End of Course

Minified React error 콘솔에러 (hydrate)

Written on

·

321

·

Edited

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;

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

reactreduxnode.jsexpressnext.js

Answer 1

0

zerocho님의 프로필 이미지
zerocho
Instructor

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

dydcodydco0983님의 프로필 이미지
dydcodydco0983
Questioner

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

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

답변 감사합니다!

dydcodydco0983님의 프로필 이미지
dydcodydco0983
Questioner

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

감사합니다!

zerocho님의 프로필 이미지
zerocho
Instructor

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

dydcodydco0983's profile image
dydcodydco0983

asked

Ask a question