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

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

이승주님의 프로필 이미지
이승주

작성한 질문수

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

게시글, 댓글 saga 작성하기

대댓글 작성시 오류 발생

작성

·

470

0

안녕하세요~ 강의 잘 듣고 있습니다. 대댓글 작성시 아래와 같은 오류가 발생하는데 ㅠ 원인을 잘 모르겠습니다. postcard 컴포넌트에서 발생하는거 같은데 다시 영상보고 코드를 봐도 수정이 안되서 다음 진도를 못 나가고 있습니다. 확인 부탁드리겠습니다.

답변 4

0

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

post.Images?.[0] 으로 수정해보세요.

0

이승주님의 프로필 이미지
이승주
질문자

<sagas_post.js>

import axios from "axios";
import { delay, put, takeLatest, all, fork } from "redux-saga/effects";
import {
  ADD_POST_REQUEST,
  ADD_POST_SUCCESS,
  ADD_POST_FAILURE,
  ADD_COMMENT_REQUEST,
  ADD_COMMENT_SUCCESS,
  ADD_COMMENT_FAILURE,
} from "../reducers/post";

function addPostAPI(data) {
  return axios.post("/api/post", data);
}
//put은 디스패치
function* addPost(action) {
  try {
    //call은 데이터 나올떄까지 대기
    yield delay(1000);
    // const result = yield call(addPostAPI, action.data);

    yield put({
      type: ADD_POST_SUCCESS,
      data: action.data,
    });
  } catch (err) {
    yield put({
      type: ADD_POST_FAILURE,
      data: err.response.data,
    });
  }
}

function addCommentAPI(data) {
  return axios.post("/api/post/${data.postId}/comment", data);
}
//put은 디스패치
function* addComment(action) {
  try {
    //call은 데이터 나올떄까지 대기
    yield delay(1000);
    // const result = yield call(addPostAPI, action.data);
    console.log("addComment사가");
    yield put({
      type: ADD_COMMENT_SUCCESS,
      data: action.data,
    });
  } catch (err) {
    yield put({
      type: ADD_COMMENT_FAILURE,
      data: err.response.data,
    });
  }
}

function* watchAddPost() {
  yield takeLatest(ADD_POST_REQUEST, addPost);
}

function* watchAddComment() {
  yield takeLatest(ADD_COMMENT_REQUEST, addComment);
}

export default function* postSaga() {
  yield all([fork(watchAddPost), fork(watchAddComment)]);
}

0

이승주님의 프로필 이미지
이승주
질문자

<reducer_post.js>

import shortId from "shortid";
export const initialState = {
  mainPosts: [
    {
      id: 1,
      User: {
        id: 1,
        nickname: "제로초",
      },
      content: "첫번째 게시글#해시테그#익스프레스",
      Images: [
        {
          src: "https://bookthumb-phinf.pstatic.net/cover/137/995/13799585.jpg?update=20180726",
        },
        {
          src: "https://gimg.gilbut.co.kr/book/BN001958/rn_view_BN001958.jpg",
        },
        {
          src: "https://gimg.gilbut.co.kr/book/BN001998/rn_view_BN001998.jpg",
        },
      ],
      Comments: [
        {
          User: {
            nickname: "nero",
          },
          content: "우와 개정판이당",
        },
        {
          User: {
            nickname: "hero",
          },
          content: "얼른사고 싶어요~",
        },
      ],
      imagePaths: [],
      postAdded: false,
      addPostLoading: false,
      addPostDone: false,
      addPostError: null,
      addCommentLoading: false,
      addCommentDone: false,
      addCommentError: null,
    },
  ],
};
//액션 이름을 상수로 뺌
export const ADD_POST_REQUEST = "ADD_POST_REQUEST";
export const ADD_POST_SUCCESS = "ADD_POST_SUCCESS";
export const ADD_POST_FAILURE = "ADD_POST_FAILURE";
export const ADD_COMMENT_REQUEST = "ADD_COMMENT_REQUEST";
export const ADD_COMMENT_SUCCESS = "ADD_COMMENT_SUCCESS";
export const ADD_COMMENT_FAILURE = "ADD_COMMENT_FAILURE";
const ADD_POST = "ADD_POST";

export const addPost = (data) => ({
  type: ADD_POST_REQUEST,
  data,
});

export const addComment = (data) => ({
  type: ADD_COMMENT_REQUEST,
  data,
});

const dummyPost = (data) => ({
  id: shortId.generate(),
  content: data,
  User: {
    id: 1,
    nickname: "제로초",
  },
  Images: [],
  Comments: [],
});

const dummyComment = (data) => ({
  id: shortId.generate(),
  content: data,
  User: {
    id: 1,
    nickname: "제로초",
  },
});

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case ADD_POST_REQUEST:
      return {
        ...state,

        addPostLoading: true,
        addPostDone: false,
        addPostError: null,
      };
    case ADD_POST_SUCCESS:
      return {
        ...state,
        // dummyPost앞에 작성해야 게시글 위에 올라감
        mainPosts: [dummyPost(action.data), ...state.mainPosts],
        addPostLoading: false,
        addPostDone: true,
      };
    case ADD_POST_FAILURE:
      return {
        ...state,
        addPostLoading: false,
        addPostError: action.error,
      };
    case ADD_COMMENT_REQUEST:
      const postIndex = state.mainPosts.findIndex(
        (v) => v.id === action.data.postId
      );
      const post = { ...state.mainPosts[postIndex] };
      post.Comments = [dummyComment(action.data.content), ...post.Comments];
      const mainPosts = [...state.mainPosts];
      mainPosts[postIndex] = post;
      return {
        ...state,

        mainPosts,
        addCommentLoading: false,
        addCommentDone: true,
      };
    case ADD_COMMENT_SUCCESS:
      return {
        ...state,
        // dummyPost앞에 작성해야 게시글 위에 올라감
        mainPosts: [dummyPost, ...state.mainPosts],
        addCommentLoading: false,
        addCommentDone: true,
      };
    case ADD_COMMENT_FAILURE:
      return {
        ...state,
        addCommentLoading: false,
        addCommentError: action.error,
      };
    default:
      return state;
  }
};

export default reducer;

0

이승주님의 프로필 이미지
이승주
질문자

<components_PostCard.js>

import React, { useState, useCallback } from "react";
import PropTypes from "prop-types";
import { useSelector, useDispatch } from "react-redux";
import { Card, Popover, Button, Avatar, List, Comment } from "antd";
import {
  RetweetOutlined,
  HeartOutlined,
  MessageOutlined,
  EllipsisOutlined,
  HeartTwoTone,
  ConsoleSqlOutlined,
} from "@ant-design/icons";
import Link from "next/link";
import moment from "moment";
import CommentForm from "./CommentForm";
import PostImages from "./PostImages";
import PostCardContent from "./PostCardContent";
const PostCard = ({ post }) => {
  console.log("포스트카드");
  console.log(post);
  const [Liked, setLiked] = useState(false);
  const [commentFormOpened, setCommentFormOpened] = useState(false);
  const onToggleLike = useCallback(() => {
    //   prev는 자동으로 이전 데이터가 들어가 있음. true는 false로 false는 true로
    setLiked((prev) => !prev);
  }, []);
  const onToggleComment = useCallback(() => {
    //   true는 false로 false는 true로
    setCommentFormOpened((prev) => !prev);
  }, []);
  //   const { id } = useSelector((state) => state.user.me && state.user.me.id);
  // 같은 문장인데 줄어 든거임.
  const id = useSelector((state) => state.user.me?.id);
  return (
    <div style={{ marginTop: 20 }}>
      <Card
        cover={post.Images[0] && <PostImages images={post.Images} />}
        // 배열 안에 jsx를 넣어줄거면 key를 넣어 줘야 한다.
        actions={[
          <RetweetOutlined key="retweet" />,
          Liked ? (
            <HeartTwoTone
              twoToneColor="#eb2f96"
              key="heart"
              onClick={onToggleLike}
            />
          ) : (
            <HeartOutlined key="heart" onClick={onToggleLike} />
          ),
          <MessageOutlined key="comment" onClick={onToggleComment} />,
          <Popover
            key="more"
            content={
              <Button.Group>
                {/* 로그린 했고  작성자 아이디랑  내아이디랑 같으면 수정 가능  */}
                {id && post.User.id === id ? (
                  <>
                    <Button>수정</Button>
                    <Button type="danger">삭제</Button>
                  </>
                ) : (
                  <Button>신고</Button>
                )}
              </Button.Group>
            }
          >
            <EllipsisOutlined />
          </Popover>,
        ]}
      >
        <Card.Meta
          //    유저 닉네임의 첫번째 글자
          avatar={<Avatar>{post.User.nickname[0]}</Avatar>}
          title={post.User.nickname}
          description={<PostCardContent postData={post.content} />}
        />
      </Card>
      {commentFormOpened && (
        <div>
          <CommentForm post={post} />
          <List
            header={`${post.Comments.length}개의 댓글`}
            itemLayout="horizontal"
            dataSource={post.Comments}
            // post.comments의 세부 항목이 item으로 들어감
            renderItem={(item) => (
              <li>
                <Comment
                  author={item.User.nickname}
                  avatar={<Avatar>{item.User.nickname[0]}</Avatar>}
                  content={item.content}
                />
              </li>
            )}
          />
        </div>
      )}
      {/* <CommentForm />
      <Comments /> */}
    </div>
  );
};

PostCard.propTypes = {
  // PropTypes.object이렇게 할 수 있는데 더 구체적으로 적어 줄수 있다.
  post: PropTypes.shape({
    id: PropTypes.number,
    User: PropTypes.object,
    content: PropTypes.string,
    createdAt: PropTypes.object,
    Comments: PropTypes.arrayOf(PropTypes.object),
    Images: PropTypes.arrayOf(PropTypes.string),
  }).isRequired,
};
export default PostCard;
이승주님의 프로필 이미지
이승주

작성한 질문수

질문하기